Dump of #328 (Magic Database)

@create #517 named Magic Database:Magic Database,Database
"#328.("storms") => E_PERM (Permission denied)
@prop #328."silent_storms" {} r
@prop #328."fw_combat_effect" #114198 r
@prop #328."ww_combat_effect" #14795 r
@prop #328."enchant_stats" {} r
;;#328.("enchant_stats") = {{"attack", 10}, {"dam", 3}, {"parryable", -5}, {"slowness", -1}, {"pen", -5}}
@prop #328."bc_combat_effect" #8048 r
@prop #328."barkskin_body_areas" {} r
;;#328.("barkskin_body_areas") = {#92227, #100494, #77303, #98861, #40357, #35454, #17709, #37195, #88604, #102165, #10807, #2616}
@prop #328."bs_combat_effect" #72201 r
@prop #328."sc_combat_effect" #78412 r
@prop #328."waterwhip" #37646 r
@prop #328."is_combat_effect" #89034 r
@prop #328."firebolt_msgs" {} r
;;#328.("firebolt_msgs") = {{"%N %<seems> unaffected by the firebolt spell energies."}, {"%N %<deflects> the spell with an occult gesture."}, {"%N %<jumps> nimbly to one side as a bolt of fire passes %o."}, {"A bolt of fire strikes %n enveloping %o in flame."}, {"The bolt burns itself out without penetrating your armour."}, {"The bolt fails to penetrate %D's armour."}, {"You feel a brief burning sensation.", "You feel a searing pain.", "Your flesh is blistered by the fiery touch.", "Your skin chars and peels away where the bolt struck!"}, {"%D is lightly toasted by the bolt.", "%D is scorched by the fiery blast.", "%D burns in the magickal flame!", "%D is deeply charred by eldritch fire!"}, {"The impact from the directed flame gives you a moment of vertigo.", "You black out briefly as the bolt strikes you.", "You struggle to remain conscious as the fiery blast strikes you.", "You are blasted and sent reeling by %N's firebolt."}, {"%D looks disoriented by the force of the fiery impact.", "%D is stunned by the fiery bolt.", "%D is blasted and stunned by %N's firebolt.", "%D is staggered by the tremendous power of %N's fiery blast."}, {"You are incinerated by %N's firebolt."}, {"The bolt of fire incinerates %D."}}
@prop #328."power_firebolt" "100 + 4 * qual" r
@prop #328."pen_firebolt" 15 r
@prop #328."power_lightning" "4 * max(3 * qual / 2 - random(qual + 10) + magic, 1)" r
@prop #328."pen_lightning" 3 r
@prop #328."effects" {} r
;;#328.("effects") = {{"resist_msg"}, {"deflect_msg"}, {"spell_dodge_msg"}, {"blast_msg"}, {"clang_msg"}, {"oclang_msg"}, {"scratch_msg"}, {"oscratch_msg"}, {"stun_msg"}, {"ostun_msg"}, {"kill_msg"}, {"okill_msg"}}
@prop #328."summonable_animals" {} r
;;#328.("summonable_animals") = {"%<%(rat%|mus%)%>", "%<%(badger%|meles%)%>", "%<%(cat%|feles%|feline%|tomcat%|puddytat%|pussy%|pussycat%)%>", "%<%(dog%|canis%|dawg%|rover%|fido%|hound%|mutt%|mastiff%)%>", "%<%(ram%|aries%|sheep%)%>", "%<%(boar%|verres%|pig%|hog%)%>", "%<%(ape%|simia%)%>", "%<%(tiger%|tigris%)%>", "%<%(bull%|taurus%|bos%|ox%|cow%)%>", "%<%(lion%|leo%)%>", "%<%(bear%|ursus%|grizzly%)%>", "%<%(hippopotamus%|hippo%)%>"}
@prop #328."summonable_animal" #75686 r
@prop #328."web_combat_effect" #55049 r
@prop #328."web_msgs" {} rc
;;#328.("web_msgs") = {{"%N %<seems> unaffected by the mystical webs."}, {"%N %<deflects> the spell with an occult gesture."}, {"%N %<dodges> to one side as a spider's web flies by %o."}, {"An enormous spider's web covers %n and envelopes %o in its sticky filaments."}, {"A spider's web tangles around %n and begins to envelop %o."}, {"%n %<is> completely enveloped in strands of web!  %S %<struggles> to break free, but can not."}, {"Filler"}, {"Filler"}, {"Filler"}, {"Filler"}, {"Filler"}, {"Filler"}}
@prop #328."lightning_msgs" {} rc
;;#328.("lightning_msgs") = {{"%N %<is> unaffected by the lightning from %d's storm."}, {"%N %<deflects> %d's lightning bolt with an occult gesture."}, {"%N %<leaps> aside as a lightning bolt strikes the ground beside %o."}, {"A lightning bolt lashes out from %d's storm, and strikes %n."}, {"The lightning bolt only flashes, and fails to do any significant damage."}, {"The lightning only gives off a flash of light, and appears to do no damage to %D."}, {"The small bolt only gives you a slight jolt.", "The thin arc of electricity hits you.", "The bright blue bolt strikes you forcefully.", "A blinding white-hot burst of electrical energy strikes you, sending agony through your very bones!"}, {"The small bolt only gives %d a slight jolt.", "The bright blue bolt strikes %d forcefully.", "A thin arc of electricity hits %d.", "A blinding white-hot burst of electrical energy cleaves into %d!"}, {"The small bolt only gives you a slight jolt, but also stuns you.", "The thunderbolt gives you a powerful shock, and stuns you.", "You are blasted by the lightning stroke, and sent reeling!", "The mighty thunderbolt crashes into you, racking your body with pain! Dark motes dance before your eyes!"}, {"The small bolt only gives %d a slight jolt, but stuns %[dpo].", "The thunderbolt gives %d a powerful shock, and stuns %[dpo].", "The lightning stroke blasts %d. and sends %[dpo] reeling!", "The mighty thunderbolt crashes into %d! For a moment, %d sways in pain."}, {"You are electrocuted by a lightning stroke from %n's storm."}, {"The lightning stroke from %n's storm electrocutes %D!"}}
@prop #328."iceshell_paralyzed_msg" "You're encased in a globe of ice.  You're not going to be doing much of anything." r
@prop #328."hf_combat_effect" #108334 r
@prop #328."rf_combat_effect" #107641 r
@prop #328."storm_task" 0 rc
@prop #328."acidrain" #83714 r
@prop #328."zombie" #14579 r
@prop #328."power_wound" "qual * 2" rc
@prop #328."pen_wound" 0 rc
@prop #328."wound_msgs" {} rc
;;#328.("wound_msgs") = {{"%N %<is> seemingly totally unaffected by the spell."}, {"%N %<shields> %r from the spell with the glow of life-energy."}, {"ERROR:  Successful dodge of wound spell."}, {"A black glow surrounds %n.", "Dark energies converge on %n."}, {"You notice a brief twinge of pain, but seem unharmed."}, {"%D winces, as if from a brief twinge of pain."}, {"You wince involuntarily from the pains running through your body.", "You jerk away as the spell inflicts excruciating agony."}, {"%D winces in pain.", "%D jerks away, as if in excruciating agony."}, {"You double over as you are caught in spasms of pain.", "You writhe in tortured agony as your body contracts in paroxysms of shuddering pain."}, {"%D doubles over as %[dps] is caught in spasms of pain.", "%D writhes in tortured agony as %[dpp] body contracts in paroxysms of shuddering pain."}, {"The incredible agony of your demise is brief, but not brief enough.  As you scream involuntarily from the pain inflicted by the spell, you feel your body falling towards the ground.  Yet you never seem to reach it, as you seem to keep falling into a void of eternal darkness and silence."}, {"%D screams and falls to the ground, quite obviously dead, %[dpp] body still contorted unnaturally from the agony of %[dpp] demise."}}
@prop #328."spell_deflection_msg" "%T %<deflects> the spell with an occult gesture." rc
@prop #328."entangle_paralyzed_msg" "You are entangled in numerous vines. You can't move to do much." rc
@prop #328."en_combat_effect" #63769 rc
@prop #328."peaceful_spells" {} rc
;;#328.("peaceful_spells") = {"heal", "barkskin", "harden", "waterwhip", "predict", "storm", "spellsing", "reverser", "appraise", "stoneflesh", "forcewall"}
@prop #328."pu_combat_effect" #6749 r
@prop #328."gr_combat_effect" #103657 rc
@prop #328."dispel_skills" {} r
;;#328.("dispel_skills") = {#69062, #73102, #54026, #17251, #68846, #74393, #60016, #70784, #14725, #110969, #85976}
@prop #328."dispel_objects" {} r
;;#328.("dispel_objects") = {#37646, #60126, #26790, #14282}
@prop #328."dispel_room_skills" {} r
;;#328.("dispel_room_skills") = {#56377, #109701}
@prop #328."self_cast_allowed" {} r
;;#328.("self_cast_allowed") = {"windwall", "barkskin", "recall", "reverser", "viper_bite", "backfire", "reverse_missiles", "mirrors", "petal", "spellsing", "bladebarrier", "parch"}
@prop #328."self_cast_difficult" {} r
;;#328.("self_cast_difficult") = {"heal", "forcewall", "dispel", "haste", "slow", "wound", "iceshell"}
@prop #328."magic_techniques" {} r
;;#328.("magic_techniques") = {#46377, #10887, #7127, #86906, #80069}
@prop #328."magic_forms" {} r
;;#328.("magic_forms") = {#37142, #104445, #74932, #89057, #104447, #62036, #81055, #99395, #16045, #97757}
@prop #328."pen_magic_fist" 8 rc
@prop #328."magic_fist_msgs" {} rc
;;#328.("magic_fist_msgs") = {{"%N %<is> unaffected by the apparition."}, {"%N %<deflects> the spell with an occult gesture."}, {"%N %<jumps> nimbly to one side as the ghostly fist passes %o."}, {"A phantasmic fist strikes %n, slamming %o backwards."}, {"The spectral fist smashes harmlessly against your armour."}, {"The spectral fist fails to penetrate %D's armour."}, {"You feel strong shove against your chest.", "You have the wind knocked out of you.", "Your ribcage creaks painfully from the force of the blow.", "Your ribs crack beneath the cogent force!"}, {"%D is shoved backwards by the fist.", "%D is winded by the ethereal blow.", "%D groans from the magickal smash!", "%D is blasted backwards by the conjured fist!"}, {"The impact from the disembodied fist gives you a moment of vertigo.", "You black out briefly as the fist strikes you.", "You struggle to remain standing as the magickal knuckles strike you.", "You are slammed and sent reeling by %N's magickal fist."}, {"%D looks disoriented by the force of the impact.", "%D is stunned by the magickal punch.", "%D is slammed backwards and stunned by %N's imaginary fist.", "%D is staggered by the tremendous power of %N's convoked fist."}, {"You are crushed by %N's phantom fist."}, {"The phantom fist crushes %D."}}
@prop #328."power_magic_fist" "99 + 5 * qual" rc
@prop #328."rm_armour_effect" #4718 rc
@prop #328."viper_bite" #15706 r
@prop #328."bf_combat_effect" #66404 r
@prop #328."pen_backfire" 20 rc
@prop #328."power_backfire" "50 + 3 * qual" rc
@prop #328."backfire_msgs" {} rc
;;#328.("backfire_msgs") = {{"%N %<ignores> the flames surrounding %d."}, {"%N deftly %<avoids> the flames surrounding %d."}, {"%N deftly %<avoids> the flames surrounding %d."}, {"Flames leap hungrily from %d's aura of fire towards %n."}, {"The flames fizzle out without penetrating your armour."}, {"The flames fail to penetrate %d's armour."}, {"You feel a brief burning sensation.", "You feel a searing pain.", "Your flesh is blistered by the fiery touch.", "Your skin chars and peels away where the flames touch!"}, {"%D is lightly toasted by the flames.", "%D is scorched by the fiery blast.", "%D burns in the magickal flame!", "%D is deeply charred by eldritch fire!"}, {"You are blasted and sent reeling by the flare of fire.", "You struggle to remain standing as the fiery aura washes over you.", "You black out briefly as the fire washes over you."}, {"%D looks disoriented by the flames washing over %[dpo].", "%D is charred and stunned by the fiery aura."}, {"You are incinerated by %n's aura of fire."}, {"The aura of fire incinerates %d."}}
@prop #328."sc_recovery_effect" #2984 rc
@prop #328."ll_death_effect" #7798 rc
@prop #328."gemini" #841 rc
@prop #328."team_peaceful_spells" {} rc
;;#328.("team_peaceful_spells") = {"haste", "mindprobe", "forcewall", "dispel", "recall"}
"#328.("range_spells") => E_PERM (Permission denied)
@prop #328."firebolt_dodgeable" 1 r
@prop #328."magic_fist_dodgeable" 1 r
@prop #328."hawthorn" #9293 r
@prop #328."beguile_no_coin_msg" "" r
@prop #328."beguile_gemless_msg" "%N %<digs> through %p pockets for gems, but %<finds> none." r
@prop #328."beguile_coinless_msg" "%N %<digs> through %p pockets for coins, but %<finds> none." r
@prop #328."stoneflesh_body_areas" {} rc
;;#328.("stoneflesh_body_areas") = {#109325, #103101, #115737, #103409, #105490, #28095, #110369, #82818, #89829, #4296, #112455, #106204}
@prop #328."sf_combat_effect" #10170 rc
@prop #328."resurrection_effect" #6727 rc
@prop #328."bb_combat_effect" #107507 rc
@prop #328."bladebarrier_msgs" {} rc
;;#328.("bladebarrier_msgs") = {{"A blade whips harmlessly by %n."}, {"%N %<pulls> back quickly enough to avoid the dicing daggers."}, {"%N %<pulls> back quickly enough to avoid the dicing daggers."}, {"A flashing blade collides with %n."}, {"The dagger clangs harmlessly off your armour."}, {"The dagger clangs harmlessly off %d's armour."}, {"You cringe as the dagger nicks skin.", "You feel a sharp pain as the dagger rends flesh.", "The steel blade cuts deep.", "Your flesh is torn from the bone!"}, {"%D cringes as the dagger nicks flesh.", "%D is sliced by the sharp blade.", "%D is gashed painfully!", "The dagger gashes deep into %d!"}, {"The loss of blood gives you a moment of vertigo.", "You are stunned briefly by the pain.", "You stifle a scream as the dagger tears into you.", "You cry out as the blade cuts bone!"}, {"%D looks disoriented by the loss of blood.", "%D is stunned by the loss of blood.", "%D is sliced and stunned by the vicious blade.", "%D is staggered by the tremendous gash by %n's blades."}, {"The daggerwound tears you open and you fall to death from bloodloss."}, {"The blade gashes deep, killing %d."}}
@prop #328."power_bladebarrier" "100 + 2 * qual" rc
@prop #328."pen_bladebarrier" 20 rc
@prop #328."daggerstorm" #116964 rc
@prop #328."cs_combat_effect" #53998 rc
@prop #328."anjru" #4993 rc
"#328.("harden_threshold") => E_PERM (Permission denied)
@prop #328."ss_combat_effect" #91034 r
@prop #328."magic_form_colours" {} r
;;#328.("magic_form_colours") = {"brown", "blue", "violet", "dark red", "green", "bright red", "pearly blue", "orange", "dark brown", "silver"}
@prop #328."magic_technique_colours" {} r
;;#328.("magic_technique_colours") = {"white", "gold", "constantly fluctuating", "black", "purple"}
@prop #328."spell_msg_db" #12328 r
@prop #328."dirge_effects" #103369 rc
@prop #328."check_effect" #1218 rc
@prop #328."shortblades" {} rc
;;#328.("shortblades") = {#8751, #8169, #1053, #3089}
@prop #328."dodge_magic_fist" -25 rc
@prop #328."dodge_firebolt" 5 rc
"#328.("bb_warehouse") => E_PERM (Permission denied)
@prop #328."allowed_barkskin_armour" {} rc
;;#328.("allowed_barkskin_armour") = {#28148, #58725, #4399, #73064, #13557, #102014, #114120, #99354, #77977, #66768}
"#328.("rot_puzzle") => E_PERM (Permission denied)
@prop #328."self_default_spells" {} rc
;;#328.("self_default_spells") = {"recall", "haste"}
;;#328.("worth") = -1
;;#328.("condition") = E_NONE
;;#328.("odrop_failed_msg") = "%<tries> to drop %t but fails!"
;;#328.("odrop_succeeded_msg") = "%<drops> %t."
;;#328.("otake_succeeded_msg") = "%<picks> up %t."
"#328.("key") => E_PERM (Permission denied)
;;#328.("aliases") = {"Magic Database", "Database"}
;;#328.("description") = "A tangible object for use in the LambdaMOO RPG."
;;#328.("object_size") = {238046, 1141286558}

@verb #328:"unslow" this none this
@program #328:unslow
{doll, s0} = args;
(rpg = $local.rpg):secure();
doll:set_att("slowness", s0);
rpg:rpg_announce_all((target = doll.character).location, target.name, "'s speed picks up suddenly.");
"THX1138 26-Mar-1997 -- New security and scatter assignment.";
.

@verb #328:"heal" this none this
@program #328:heal
(rpg = $local.rpg):secure();
{by, doll, qual, ?spell = rpg:match_skill(verb), @extra} = args;
"mod = `by.anima / 25 ! E_DIV => 0';";
"qual = qual + mod;";
target = doll.character;
inj = doll.inj;
amount = (3 + random(5)) + (qual / 9);
spell = spell ? spell | rpg:match_skill(verb);
if (typeof(effect = doll:magic_effects(by, doll, qual, spell, @extra)) != NUM)
  return effect;
elseif ((qual = qual - effect) <= 0)
  rpg:say_action("%N %<seems> unaffected by the healing spell energies.", target);
elseif (inj > amount)
  doll:set_att("inj", inj - amount);
  rpg:say_action("Some of %n's injuries are healed.", target);
else
  doll:set_att("inj", 0);
  rpg:say_action("%N's injuries are healed.", target);
  if ((by != doll) && (random(50) == 5))
    by:mod_anima(1, rpg.death_db.anima_slip_msgs[1]);
  endif
endif
if (cloud = doll:read_misc_note("stinkcloud"))
  if (qual > cloud[1])
    doll:erase_misc_note("stinkcloud");
    this.sc_combat_effect:remove(doll);
    rpg:say_action("%N %<recovers> from the effects of the stinking cloud.", target);
  endif
endif
if ((doll.deathless && (!((this.is_combat_effect in doll.freeze_effects) || (parent(target) == this.zombie)))) && (rpg:trusted(by.character) || (caller == #95506)))
  doll.deathless = 0;
  this.pu_combat_effect:remove(doll);
  if (i = $list_utils:assoc("unputrefy", doll.schedule, 2))
    doll:cancel(i);
  endif
  rpg:say_action("%N's wounds cease bleeding.", target);
endif
"DR 02-AUG-94 -- Changed security, added magic_effect code.";
"Profane 27-JAN-96 1325EST -- changed setting .inj directly to :set_att";
"Profane 2-MAY-96 2022EST -- commented out magic_effect bit.";
"Profane 15-MAY-96 1223EST -- added healing of stinkclouds.";
"Profane 13-DEC-96 2032PST -- put in :magic_effects call.. thought it was already here, where'd it go?";
"THX (#105941) - Wed Feb 18, 1998 - Added a bunch of checks to ensure that 'spell' is being defined before sending to the magic_effects check.";
"Profane (#30788) - Tue May  1, 2001 - Add ability for gms and the vicar to cure putrefied peeps.";
"Hydros (#106189) - Fri Nov  26, 2004 - Added karmic slide towards Good for a complete successful heal of anyone that isn't oneself. Healing friends is good, and often so is healing an enemy. So let's avoid philosophical debate or ticky checks of .aggressor and whatnot, okay?";
.

@verb #328:"blastfork" this none this
@program #328:blastfork
":blastfork(caster, strikes, quality[, interval])";
{by, strikes, qual, ?interval = 0} = args;
(rpg = $local.rpg):secure();
if (!valid(doll = rpg:get_doll(by)))
  return;
endif
{this, threshold} = {rpg.magic_db, rpg.idle_threshold};
time = max(interval, 7);
this.storms = listappend(this.storms, {task_id(), by, strikes, qual, interval});
while ((i = task_id() in this:storm_id(this.storms)) && (this.storms[i][3] > 0))
  (rpg.gm_lag < threshold) && suspend(max((time + rpg.gm_lag) + 4, 9));
  if (i = task_id() in this:storm_id(this.storms))
    this.storms[i][3] = this.storms[i][3] - 1;
    current = this.storms[i][4] = max(this.storms[i][4] - 1, 1);
    if ((by in connected_players()) || (!is_player(by)))
      this:blast(by, by.location, current);
    else
      if (!(j = task_id() in this:storm_id(this.silent_storms)))
        this:endblast(by);
      else
        this.silent_storms = listdelete(this.silent_storms, j);
      endif
      return 0;
    endif
  elseif (j = task_id() in this:storm_id(this.silent_storms))
    this.silent_storms = listdelete(this.silent_storms, j);
    return 0;
  endif
endwhile
this:endblast(by);
if (i = task_id() in this:storm_id(this.storms))
  this.storms = listdelete(this.storms, i);
endif
"DR 25-MAR-94 -- Hacked so that if the storm was killed with the 'silent' optional second argument to kill_storm, it will not call :endblast on exiting...it assumes that whatever killed it handled the message.";
"DR 02-APR-94 -- Added a check to make sure the player is still connected before dispensing another jolt :) ... in response to a report from Miles that people were casting storms then disconnecting to avoid being hit by the monsters they were killing!";
"Miles 5/22/94  added $code_utils:verb_loc() as suggested by Isagi so players can't cast additional storms using staffs from kids of .magic_db";
"Profane 6-JUN-96 2001EDT -- Changed For loop to while loop, dynamically referencing # of strikes each iteration; so that other spells/things can muck with a storm's number of strikes.";
"Profane 7-JUN-96 1615EDT -- Qual goes down by one with every strike.";
"THX 23-Jun-1997 --  Changed the way storm info works _all_ the time since the info keeps changing with each strike.  Always use task_id() [using this:storm_id() to find the slot] now to identify a given storm.  Always use listdelete (instead of setremove).  Moved a few evals out of the while loop.";
"THX (#105941) - Wed Nov 18, 1998 - Changed it to not suspend at all if the lag is too high.  :blast() then kills the storm.";
.

@verb #328:"blast" this none this
@program #328:blast
{by, area, qual} = args;
(rpg = $local.rpg):secure();
{threshold, c_u} = {rpg.idle_threshold, $command_utils};
att = rpg:get_doll(by);
peaceful = `area.peaceful ! ANY => 0';
for targ in (setremove(att.aggressor, by))
  if (rpg.gm_lag > threshold)
    this:kill_storm(by);
  else
    rpg:s_i_n();
  endif
  if (("cavern" in area.room_data) || ("structure" in area.room_data))
    qual = (2 * qual) / 3;
  endif
  if ((targ in rpg.pcs) && ((!is_player(targ)) || (`idle_seconds(targ) ! ANY => 181' < threshold)))
    (targ.location == area) && this:zap(by, @peaceful ? {by, qual * 2} | {targ, qual}, area);
  else
    att.aggressor = setremove(att.aggressor, targ);
  endif
endfor
if (att.aggressor)
  rpg:s_i_n(0);
  att.aggressor && (peaceful || att:invite((att.slowness / 2) + 1, {2}));
elseif (random(5) == 4)
  source = (length(this:storms_cast_by(by)) > 1) ? "One of the storms over " | "The storm over ";
  rpg:rpg_announce_all(area, source, by.name, "'s head gives a grumble of thunder.");
endif
"Mooshie (#106469) - Fri Oct 24 20:27:43 1997 PDT - Updated, streamlined a bit and added :suspend_if_needed";
"THX (#105941) - Wed Nov 18, 1998 - Cleaned up the code.  Added a splurge that eats the storm if the lag exceeds rpg.idle_threshold.";
"THX (#105941) - Tue Jun 13, 2000 - Altered to handle peaceful rooms.  No doll:invite is called and the caster becomes the target for all non-caster targets.";
"THX (#105941) - Mon Jul  3, 2000 - Altered qual by factor of 2/3 if storm is just outside the structure/cavern.";
"Hannibal (#104762) - Sat Dec 23, 2000 - Changed line 15 to < threshold so players can zap players again.";
.

@verb #328:"storm" this none this
@program #328:storm
":storm(caster-doll, dummy-arg, quality, skillobject)";
{bydoll, dummy, qual, skillobj} = args;
(rpg = $local.rpg):secure();
area = (by = bydoll.character).location;
qual = qual - (length(this:storms_cast_by(by)) * (25 + random(25)));
duration = ((qual * 5) + 30) + random(20);
origstrikes = duration / 10;
firstrike = 0;
if (("cavern" in area.room_data) || ("structure" in area.room_data))
  qual = qual / 2;
endif
if (qual > 50)
  rpg:playertell(by, "You conjure up a raging electrical storm in ", area.name, ".");
  rpg:rpg_announce_all_but(area, {by}, by.name, " has conjured a rampant electrical storm that rages over ", by.pp, " head.");
  strikes = origstrikes + 2;
  firstrike = 1;
elseif (qual > 15)
  rpg:playertell(by, "You summon a thunderstorm in ", area.name, ".");
  rpg:rpg_announce_all_but(area, {by}, by.name, " has summoned a miniature thunderstorm that roars above ", by.po, ".");
  strikes = origstrikes + 1;
elseif (qual > 0)
  rpg:playertell(by, "You call up a minor storm in ", area.name, ".");
  rpg:rpg_announce_all_but(area, {by}, by.name, " has called up a minor storm that grumbles over ", by.po, ".");
  strikes = origstrikes;
else
  if (!this:storms_cast_by(by))
    return rpg:rpg_announce_all(area, "Nothing happens.");
  else
    return rpg:rpg_announce_all(area, "The clouds above ", by.name, "'s head rage and hurl rain down upon ", by.po, ".");
  endif
endif
if (firstrike)
  strikes = strikes - 1;
  this:blast(by, area, qual);
endif
this:blastfork(by, strikes, qual, 10);
"quinn 19-NOV-93 0839: More storms, less quality.";
"6/1/95 Slither -- changed the announce_all's to announce_all_but's as Quisinart suggested.";
"THX (#105941) - Mon Jul  3, 2000 - Drop qual by factor of 2 if cast attempt is in \"cavern\" or \"structure\" area.";
"Hydros (#106189) - Thu Dec 9, 2004 - Changed \"Nothing happens\" to a rainy message while the player has previously cast storms raging.  More in line with LRPG gradation spell casting messages (e.g., Haste's `<player>'s motion speeds up briefly, before returning to its former level.' msg).";
.

@verb #328:"endblast" this none this
@program #328:endblast
":endblast(by) => Simply prints messages signalling the end of a storm spell.";
(rpg = $local.rpg):secure();
{by} = args;
rpg:playertell(by, "The storm over your head slowly dissipates, leaving ", where_storm = by.location.name, " calm once again.");
rpg:rpg_announce_all_but(by.location, {by}, "The thunderstorm over ", by.name, "'s head dissipates, leaving ", where_storm, " calm once again.");
where = $code_utils:verb_loc();
for info in (where.silent_storms)
  if (!$code_utils:task_valid(info[1]))
    where.silent_storms = setremove(where.silent_storms, info);
  endif
endfor
"THX1138 23-Jun-1997 --  Modernized and cleaned up a bit.  rpg_announce, scatter, reduced evals, ...";
.

@verb #328:"storms_cast_by" this none this
@program #328:storms_cast_by
":storms_cast_by(who) => a list of lists of info on storms cast by `who'.";
" Each list is in the format {task_id, caster, strikes, qual, interval}";
{who} = args;
$local.rpg:secure();
this = $code_utils:verb_loc();
storms = {};
for storm in (this.storms)
  {tid, caster, strikes, qual, interval} = storm;
  if (!$code_utils:task_valid(tid))
    this.storms = setremove(this.storms, storm);
  elseif (caster == who)
    storms = {@storms, storm};
  endif
endfor
return storms;
"DR 18-MAR-94: Added security, callable only by trusted folks.";
"Miles 5/22/94  added $code_utils:verb_loc() as suggested by Isagi so that players can't cast additional storms using staffs from kids of .magic_db";
.

@verb #328:"kill_storm" this none this rx
@program #328:kill_storm
":kill_storm(caster|task_id [, \"silent\"]) => Either cancel all storms cast by `player', or a specific storm specified by task_id (given by this:storms_cast_by).  If the\"silent\" parameter is called, the forked task associated with the storm will not cause a message showing the storm's dissipation to be displayed.";
{query, ?silent = 0} = args;
this = $code_utils:verb_loc();
if (!$local.rpg:trusted(caller_perms()))
  return E_PERM;
elseif (typeof(query) == OBJ)
  stormcount = 0;
  for storm in (this:storms_cast_by(query))
    this.storms = setremove(this.storms, storm);
    stormcount = stormcount + 1;
    silent && (this.silent_storms = setadd(this.silent_storms, storm));
  endfor
  return stormcount;
elseif (typeof(query) != NUM)
  return E_INVARG;
elseif (i = $list_utils:assoc(query, this.storms))
  this.storms = setremove(this.storms, i);
  silent && (this.silent_storms = setadd(this.silent_storms, i));
  return 1;
else
  return E_NONE;
endif
"DR 25-MAR-94 -- Hacked to add the kludge that allows a storm to be silently dissipated, in order for Frobno's magic storm-sweeping amulet and other such things to control how it looks when the storm goes away.";
"Miles 5/22/94  added $code_utils:verb_loc() as suggested by Isagi so that players can't cast additional storms by using staffs from kids of .magic_db";
.

@verb #328:"storms_active_in" this none this
@program #328:storms_active_in
":storms_active_in(room) => a list of lists of info on storms raging in the given location.";
" Each list is in the format {task_id, caster, strikes, qual, interval}";
{where} = args;
$local.rpg:secure();
this = $code_utils:verb_loc();
people = where:contents();
storms = {};
for storm in (this.storms)
  {tid, caster, strikes, qual, intvl} = storm;
  if (!$code_utils:task_valid(tid))
    this.storms = setremove(this.storms, storm);
  elseif (caster in people)
    storms = {@storms, storm};
  endif
endfor
return storms;
"Miles 5/22/94  added $code_utils:verb_loc() as suggested by Isagi so that players can't cast additional storms using staffs from kids of .magic_db";
.

@verb #328:"teleport" this none this
@program #328:teleport
":teleport(doll, where, [leave_msg, [oleave_msg, [arrive_msg, oarrive_msg, [nogo_msg, [onogo_msg]]]]]]) => send the doll to where";
"An RPG-teleportation, without exit penalties.";
(rpg = $local.rpg):secure();
nogo_msg = "Your surroundings flicker for a moment, as if your position were uncertain.";
onogo_msg = "%N flashes out of existence for a brief moment.  The air crackles around %p body.";
leave_msg = "The image of %l gains resolution in your mind.  Space warps around your body and suddenly...";
oleave_msg = "%N flashes out of existence, leaving only an electric tinge of magick.";
arrive_msg = "After a brief moment of vertigo, you look around.";
oarrive_msg = "%N flashes into existence, glancing around with desperate anxiety.";
{doll, where, ?leave_msg, ?oleave_msg, ?arrive_msg, ?oarrive_msg, ?nogo_msg, ?onogo_msg} = args;
who = doll.character;
old = who.location;
if (rpg:is_rpg_room(where = $object_utils:isa(where, $exit) ? where.dest | where))
  `where:bless_for_teleport(who) ! E_VERBNF';
endif
if (!where:acceptable(who))
  rpg:rpg_announce_all_but(old, {who}, $string_utils:pronoun_sub(onogo_msg, who, old, where));
  rpg:playertell(who, $string_utils:pronoun_sub(nogo_msg, who, old, where));
else
  rpg:rpg_announce_all_but(old, {who, old, where}, $string_utils:pronoun_sub(oleave_msg, who, old, where));
  rpg:playertell(who, $string_utils:pronoun_sub(leave_msg, who, old, where));
  `old:bless_exit(who) ! ANY => 0';
  (checker = this.check_effect):add(@check = {doll, old, {checker.owner}, "Attempted blocking of teleport.", ((((tostr(who.name) + " failed move checking being sent from ") + old.name) + " to ") + where.name) + "."});
  if ((widx = $list_utils:assoc(callers()[1][1], rpg.approved_wizmoves)) && (widx[2] == callers()[1][2]))
    rpg:wizmove(who, where);
  else
    who:moveto(where);
  endif
  checker:remove(@check);
  if (((who.location == old) && rpg:trusted_verb(old, "_eject")) && is_player(who))
    old:_eject(who);
    return;
  endif
  rpg:rpg_announce_all_but(who.location, {who}, $string_utils:pronoun_sub(oarrive_msg, who, old, where));
  rpg:playertell(who, $string_utils:pronoun_sub(arrive_msg, who, old, where));
endif
"Profane 27-MAY-96 1716EST - Added porting for AHaBs with exit homes and cleaned up flow of verb.";
"Irin Sat Feb 15 14:51:23 1997 PST -- Rewrite to update and add optional custom messages.";
"Mooshie (#106469) - Sun Oct 12 00:14:39 1997 PDT - Added :bless_for_teleport call for RPG rooms";
"dalton (#116601) - Fri Dec 27, 2002 - Added check effect for blocking by custom :moveto.";
"Hydros (#106189) - Sun Sep 4, 2005 - Converted to use $local.rpg:wizmove() in cases where the calling object-and-verb are in $local.rpg.approved_wizmoves.";
"                                   - Left in all the old extra measures we had taken for cases of people hacking their :moveto() etc., in case some monkey ever hacks some way around move() or something.";
.

@verb #328:"recall" this none this
@program #328:recall
":recall(OBJ by_doll, OBJ target_doll, NUM qual, OBJ skillobjeect, [ OBJ destination, [STR leave_msg, [STR oleave_msg, [STR arrive_msg, [STR oarrive_msg, [STR nogo_msg, [STR onogo_msg]]]]]]])";
"teleport caster to home, or other specified destination.";
(rpg = $local.rpg):secure();
{by_doll, target_doll, qual, skillobject, ?dest = target_doll.character.home, @msgs} = args;
who = target_doll.character;
by = by_doll.character;
if (!`$recycler:valid(dest) ! ANY')
  dest = $player_start;
endif
qual = (qual * by_doll.size) / target_doll.size;
if (who.location == dest)
  rpg:rpg_announce_all(who.location, "Nothing happens.");
  return E_NONE;
elseif (typeof(effect = target_doll:magic_effects(@args)) != NUM)
  return effect;
elseif ((qual = qual - effect) < 0)
  rpg:say_action("%N %<seems> unaffected by the recall magicks.", who);
  return E_NONE;
elseif ((by != who) && ((by in target_doll.aggressor) || (!is_player(who))))
  if ((target_doll.magic_theory > 0) && (rpg.skills.recall:resolve(who, -qual) > 0))
    return rpg:rpg_announce_all(who.location, this:spell_deflection_msg(who, by));
  endif
endif
if ((((is_player(who) && $object_utils:isa(dest, $room)) && (!$perm_utils:controls(who, dest))) && (!(who in dest.residents))) && (!$code_utils:verb_or_Property(dest, "accept_rpg_teleport")))
  `who.location:_eject(who) ! ANY';
  rpg:rpg_announce_all(who.location, "Nothing happens.  Well, nothing except that ", who.name, " is suddenly missing.");
  return E_PERM;
else
  if (by_doll.lifelinked && (by_doll.lifelinked[1] == who))
    this:unlink(by_doll, target_doll);
  endif
  if (by != who)
    fork (0)
      if (by.location != who.location)
        by_doll.aggressor = setremove(by_doll.aggressor, who);
        target_doll.aggressor = setremove(target_doll.aggressor, by);
      endif
    endfork
  endif
  return this:teleport(target_doll, dest, @msgs);
endif
"Profane 28-MAY-96 836EDT -- Changed so AHaBs can recall, also added possibility or resisting spell.";
"Irin Sat Feb 15 13:44:09 1997 PST -- Rewrite to pass custom teleport messages and destinations.";
"THX (#105941) - Wed Oct 13, 1999 - Added splurge so that if you recall someone that you're lifelinked to, you'll be unlinked.";
"THX (#105941) - Tue Dec 14, 1999 - Changed it so that players can't change Eir props to reject a recall.";
"Profane (#30788) - Sun Dec 24, 2000 - Invalid dests get rerouted to $player_start";
"THX (#105941) - Fri Apr 13, 2001 - Fork off a command to remove aggressor stuff if the target actually moves.";
.

@verb #328:"print_storms" this none this
@program #328:print_storms
"print_storms(no args | OBJ)";
"returns {} or a list of strings formatted for player:tell_lines()";
"If argument is given and is a room, print info for that room, if it is not a room, print storms cast by that object. If no argument, do all storms globally";
{?what = 0} = args;
(rpg = $local.rpg):secure();
db = rpg.magic_db;
if (typeof(what) != OBJ)
  storms = db.storms;
elseif ($object_utils:isa(what, $room))
  storms = db:storms_active_in(what);
else
  storms = db:storms_cast_by(what);
endif
if (!storms)
  return {};
endif
biglist = stormlist = {};
{l_u, s_u} = {$list_utils, $string_utils};
for i in [1..length(storms)]
  who = storms[i][2];
  qual = storms[i][4];
  if (ind = l_u:iassoc(who, stormlist))
    if (qual > 50)
      stormlist[ind][2] = stormlist[ind][2] + 1;
    elseif (qual > 15)
      stormlist[ind][3] = stormlist[ind][3] + 1;
    else
      stormlist[ind][4] = stormlist[ind][4] + 1;
    endif
  else
    raging = thunder = minor = 0;
    if (qual > 50)
      raging = 1;
    elseif (qual > 15)
      thunder = 1;
    else
      minor = 1;
    endif
    stormlist = {@stormlist, {who, raging, thunder, minor}};
  endif
endfor
for j in [1..length(stormlist)]
  plyr = stormlist[j][1];
  flags = `plyr.location.room_data ! ANY => {}';
  outdoors = ("cavern" in flags) || ("structure" in flags);
  text = {};
  r = stormlist[j][2];
  t = stormlist[j][3];
  m = stormlist[j][4];
  if (r == 1)
    text = {@text, outdoors ? "an electrical storm" | "a raging electrical storm"};
  elseif (r > 1)
    text = {@text, tostr(r) + " raging electrical storms"};
  endif
  if (t == 1)
    text = {@text, "a thunderstorm"};
  elseif (t > 1)
    text = {@text, tostr(t) + " thunderstorms"};
  endif
  if (m == 1)
    text = {@text, "a minor storm"};
  elseif (m > 1)
    text = {@text, tostr(m) + " minor storms"};
  endif
  shortlist = outdoors ? tostr(plyr.name, " has ", s_u:english_list(text), " raging outside.") | tostr(plyr.name, " has up ", s_u:english_list(text), ".");
  biglist = {@biglist, shortlist};
endfor
return biglist;
"Profane 17-DEC-95 1211EST -- (re)modified to do storms_cast_by as well.";
"THX (#105941) - Mon Jul  3, 2000 - Alter messages if the caster is sheltered.";
.

@verb #328:"forcewall" this none this
@program #328:forcewall
"Conjure up a wall of force.";
{mage_doll, doll, qual, skillobj, @extra} = args;
char = doll.character;
(rpg = $local.rpg):secure();
if (typeof(effect = doll:magic_effects(@args)) != NUM)
  return effect;
elseif ((qual = qual - effect) <= 0)
  rpg:say_action("%N %<seems> unaffected by the magick.", char);
  return;
endif
note = doll:read_misc_note("force_wall");
qual = qual + random(10);
if (typeof(note) == LIST)
  "mage_doll = (note[1] == doll) ? doll | mage_doll;";
  wall_qual = max(qual, note[2]);
else
  wall_qual = qual;
endif
if ((note == E_PROPNF) || (qual > note[2]))
  doll:write_misc_note("force_wall", {mage_doll, wall_qual});
  this.fw_combat_effect:add(doll);
  if (i = $list_utils:iassoc("unwall", doll.schedule, 2))
    rpg:say_action("The frosted haze around %n goes opaque for an instant, then fades back into milky translucence.", char);
    doll.schedule[i][3] = doll.schedule[i][3] + (30 * qual);
    doll.schedule[i][4] = {doll, qual, skillobj};
  else
    rpg:say_action("A hum of magic rises around %n, surrounding %o with a frosted haze of force.", char);
    doll:schedule(this, "unwall", time() + (60 * qual), {doll, qual, skillobj});
  endif
else
  rpg:say_action("The frosted haze around %n wavers, but remains unchanged.", char);
endif
"Irin Mon Sep 23 11:04:39 1996 PDT -- changed so that the person the field's on becomes the default owner, and the strength is that of the strongest spell.  Why?  Because it seemed like a good idea at the time.";
"Hydros (#106189) - Tue Apr 12, 2005 - Changed some logic errors that were causing messages to print to the wrong player.";
"                                    - Added/repaired instances where a more powerful cast gives the new caster `ownership' of the wall.";
.

@verb #328:"unwall" this none this
@program #328:unwall
":unwall(caster doll, target doll, qual, spell, @extra args)";
"Remove a conjured wall of force.";
{doll, att, qual, spell, @extra} = args;
(rpg = $local.rpg):secure();
this.fw_combat_effect:remove(att);
{c_doll, wall} = att:read_misc_note("force_wall");
att:erase_misc_note("force_wall");
if (i = $list_utils:iassoc("unwall", att.schedule, 2))
  att:cancel(att.schedule[i]);
endif
char = att.character;
rpg:say_action("The wall of frosted haze around %n drops and a magickal hum is silenced.", char);
tellguy = (char == c_doll.character) ? "you" | char.name;
this:tell_player(c_doll, ("You feel a burden of sustained magick lifted from your mind as the force wall around " + tellguy) + " drops.");
"Hydros (#106189) - Tue Apr 12, 2005 - Made c_doll, (not the argument `doll'), receive the `burden...lifted' message.";
"                                    - Substituted in the word `you' if the caster is surrounded by the wall.";
.

@verb #328:"calm*_winds" this none this
@program #328:calm_winds
(rpg = $local.rpg):secure();
doll = args[1];
who = doll.character;
qual = args[3];
loc = who.location;
if (rpg:trusted_verb(loc, "weather_effect") && (typeof(effect = loc:weather_effect(@args)) != NUM))
  return effect;
else
  storms = this:storms_active_in(loc);
  rpg:rpg_announce_all_but(loc, {}, "Winds begin to swirl about. . .");
  for storm in (storms)
    if ((storm[2] == who) || (qual > storm[4]))
      this:kill_storm(storm[1], 1);
      type = (storm[4] > 50) ? "raging electrical storm" | ((storm[4] > 15) ? "thunderstorm" | "minor storm");
      rpg:rpg_announce_all_but(loc, {}, storm[2].name, "'s ", type, " is dispersed by the winds.");
    endif
  endfor
  dolls = rpg:find_chars(loc)[2];
  for victim in (dolls)
    if (x = $list_utils:iassoc("unwind", victim.schedule, 2))
      if ((victim == doll) || (rpg.skills.calm_winds:resolve(who, -victim.schedule[x][4][1]) > 0))
        victim:cancel(victim.schedule[x]);
        rpg.magic_db:unwind(victim);
      endif
    endif
  endfor
  rpg:rpg_announce_all_but(loc, {}, "The wind dies down again.");
endif
.

@verb #328:"shatter" this none this
@program #328:shatter
{doll, target, qual, skill, @extra} = args;
(rpg = $local.rpg):secure();
by = doll.character;
cond = target.condition;
tloc = target.location;
if (valid(tdoll = rpg:get_doll(tloc)))
  doll.act = -120;
  "Try setting act to -120 so caster doesn't swing at target.";
  doll.aggressor = setadd(doll.aggressor, tdoll.character);
  tdoll.aggressor = setadd(tdoll.aggressor, by);
  doll:do_attack_notification(tdoll.character, skill);
  name = (tloc.name + "'s ") + target:title();
  if (typeof(effect = tdoll:magic_effects(doll, tdoll, qual, skill, @extra)) != NUM)
    return effect;
  elseif ((qual = qual - effect) <= 0)
    return rpg:say_action(strsub("%N's %thing seems unaffected by the shatter spell energies.", "%thing", target:title()), (tloc in rpg.pcs) ? tloc | by);
  elseif (((tdoll != doll) && (tdoll.magic_theory > 0)) && (rpg.skills.shatter:resolve(tloc, -qual) > 0))
    rpg:rpg_announce_all(by.location, this:spell_deflection_msg(tloc, by));
    return;
  endif
else
  name = target:title();
endif
"Right. now the actual shattering bit:";
if (rpg.object_db:is_money(target))
  cond = target.quantity;
  dam = random(max(qual / 5, 1));
  new_cond = cond - dam;
  target:set_quantity((new_cond < 1) ? 0 | new_cond);
  if (target.quantity < 1)
    target:destroy(target);
  endif
  word = (dam == 1) ? "One" | ((dam == cond) ? "All" | "Some");
  word = (word + " of ") + ((!index(name, tloc.name)) ? "the " | "");
  rpg:rpg_announce_all(by.location, word, name, " shatter!");
elseif (rpg.object_db:is_gem(target))
  cond = target:get_true_value();
  app = target:get_appraised_value();
  if ((qual / 100) < (cond / (10 ^ length(tostr(cond)))))
    rpg:rpg_announce_all(by.location, name, " vibrates momentarily, but nothing unusual happens.");
  elseif (random(100) <= (qual / cond))
    target:moveto(rpg.gem_dispenser);
    rpg:rpg_announce_all(by.location, name, " shatters into a thousand slivers!");
  else
    dam = max(random(qual), qual / 3);
    target:set_true_value(cond - dam);
    app ? target:set_appraised_value(max(app - dam, 1)) | "";
    if (target:get_true_value() < 1)
      target:moveto(rpg.gem_dispenser);
      rpg:rpg_announce_all(by.location, name, " shatters into a thousand slivers!");
    else
      rpg:rpg_announce_all(by.location, name, " cracks!");
    endif
  endif
else
  if ((qual < (cond / (1 + (qual / 10)))) || (!(breakable = typeof(cond) == NUM)))
    rpg:rpg_announce_all(by.location, name, " vibrates momentarily, but nothing unusual happens.");
  elseif ((random(100) <= (10 - (cond / 10))) && breakable)
    target:("break")();
  elseif (breakable)
    dam = max(random(qual), qual / 3);
    target:set_condition(cond - dam);
    if (target.condition < 1)
      target:("break")();
    else
      rpg:rpg_announce_all(by.location, name, " cracks and corrodes!");
    endif
  endif
endif
"Profane 30-MAY-97 2050PDT - added gem and money shattering.";
.

@verb #328:"windwall" this none this
@program #328:windwall
{mage_doll, doll, qual, skillobj, @extra} = args;
(rpg = $local.rpg):secure();
by = mage_doll.character;
this.ww_combat_effect:add(mage_doll);
if (i = $list_utils:iassoc("unwind", mage_doll.schedule, 2))
  mage_doll.schedule[i][3] = min(mage_doll.schedule[i][3] + (qual * 30), time() + rpg.vacuum.monster_stash_update_int);
  rpg:say_action("The winds around %n howl and moan.", by);
else
  mage_doll:schedule(this, "unwind", time() + (qual * 60), {min(qual, 200)});
  rpg:say_action("Wind begins to blow toward %n. . . Picking up speed, they whirl about %o, shrieking and moaning.  Soon, %n %<is> encased in an impenetrable wall of wind.", by);
  if (i = $list_utils:iassoc("bite", mage_doll.schedule, 2))
    mage_doll:cancel(mage_doll.schedule[i]);
    mage_doll:erase_misc_note("biting_cloud");
    this.bc_combat_effect:remove(mage_doll);
    rpg:say_action("The winds tear the cloud of insects surrounding %n asunder!", by);
  endif
  if ((sc = this.sc_combat_effect) in mage_doll.view_effects)
    mage_doll:erase_misc_note("stinkcloud");
    sc:remove(mage_doll);
    rpg:say_action("The winds disperse the putrid cloud of stinkiness.", by);
  endif
endif
.

@verb #328:"unwind" this none this
@program #328:unwind
(rpg = $local.rpg):secure();
doll = args[1];
by = doll.character;
this.ww_combat_effect:remove(doll);
rpg:say_action("The shrieking winds encircling %n suddenly die away, leaving an eerie calm.", by);
.

@verb #328:"stinkcloud" this none this
@program #328:stinkcloud
{caster, targets, qual, skillobj, @extra} = args;
(rpg = $local.rpg):secure();
by = caster.character;
targets = (typeof(targets) == LIST) ? targets | {targets};
rpg:say_action("%N %<summons> a cloud of noxious green vapours. . .", by);
for doll in (targets)
  targ = doll.character;
  rpg:say_action("The vapours coil about %n.", targ);
  if (typeof(e = doll:magic_effects(caster, doll, qual, rpg.skills.(verb))) != NUM)
    return e;
  else
    qual = qual - e;
  endif
  if (x = $list_utils:iassoc("bite", doll.schedule, 2))
    if (qual > (doll.schedule[x][4][1] * 2))
      doll:cancel(doll.schedule[x]);
      doll:erase_misc_note("biting_cloud");
      this.bc_combat_effect:remove(doll);
      rpg:say_action("The cloud of insects about %n is dispersed by the vapours.", targ);
    endif
  endif
  if ((roll = rpg.skills.shock:resolve(targ, -qual)) > 0)
    rpg:say_action("%N %<is> unaffected.", targ);
  else
    doll:write_misc_note("stinkcloud", {qual, time()});
    this.sc_combat_effect:add(doll);
    rpg:say_action("%N %<succumbs> to the vapours, choking and coughing.", targ);
  endif
  rpg:say_action("The vapours about %n slowly dissipate into nothingness.", targ);
  rpg:s_i_f_i_n();
endfor
.

@verb #328:"predict" this none this
@program #328:predict
if ((rpg = $local.rpg):trusted(caller_perms()))
  doll = args[1];
  qual = args[3];
  who = doll.character;
  loc = who.location;
  predict = rpg.skills.predict;
  storms = this:storms_active_in(loc);
  for storm in (storms)
    word = rpg:rate_skill(storm[4], 175);
    text = ((storm[2].name + " has conjured a storm of ") + word) + " strength.";
    if (predict:resolve(who, qual - storm[4]) > 0)
      dur = storm[3] * (10 + (rpg.rpg_lag_sum / length(rpg.rpg_lag)));
      text = ((text + " It will last another ") + $time_utils:english_time(dur)) + ".";
    endif
    rpg:playertell(who, text);
  endfor
  dolls = rpg:find_chars(loc)[2];
  for doll in (dolls)
    doll:check_schedule();
    if (i = "unwind" in $list_utils:slice(doll.schedule, 2))
      wqual = doll.schedule[i][4][1];
      word = rpg:rate_skill(wqual, 175);
      text = ((doll.character.name + " is surrounded by a wind wall of ") + word) + " strength.";
      if (predict:resolve(who, qual - wqual) > 0)
        dur = doll.schedule[i][3] - time();
        text = ((text + "  It will last another ") + $time_utils:english_time(dur)) + ".";
      endif
      rpg:playertell(who, text);
    endif
  endfor
else
  return E_PERM;
endif
.

@verb #328:"harden" this none this
@program #328:harden
{doll, what, qual, skill, @extra} = args;
(rpg = $local.rpg):secure();
who = doll.character;
area = who.location;
cond = o_cond = what.condition;
maximum = 100 + (min(qual, 130) / 2);
"maximum = 100 + (qual / 2);";
"maximum = 100 + min(qual, 150) / 2 + toint(max(tofloat(qual) - 150.0, 0.0) ^ (1.0 / 1.2));";
rpg:rpg_announce_all(area, who.name, "'s ", what:title(), " begins to gleam brightly . . .");
if (cond > maximum)
  rpg:rpg_announce_all(area, ". . . but fades quickly.");
else
  {threshold, pay_xtra} = this.harden_threshold;
  if (over_the_top = max(maximum - threshold, 0))
    maximum = random(over_the_top) + threshold;
  endif
  if (typeof(what:set_condition(max(what.condition, maximum))) == ERR)
    return who:tell(what:titlec(), " seems to be having trouble hardening. Tell a GM.");
  endif
  dur = (qual * 10) + random(33);
  rpg:rpg_announce_all(area, who.name, "'s ", what:title(), " seems tougher and more resilient!");
  for item in (doll.schedule)
    if ((item[2] == "unharden") && (item[4][1] == what))
      doll:cancel(item);
      item[3] = item[3] + (dur / 2);
      doll:schedule(@item);
      return;
    endif
  endfor
  if ((premium = (maximum - max(pay_xtra, o_cond)) > 0) && $recycler:valid(mounted = `caller.mounted ! ANY => $nothing'))
    premium = random(random(premium));
    tvalue = mounted:get_true_value();
    tvalue = (typeof(tvalue) == NUM) ? tvalue | 0;
    mounted:set_true_value(tvalue - premium);
    avalue = mounted:get_appraised_value();
    if (avalue)
      mounted:set_appraised_value(max(((avalue - premium) + random(9)) - random(9), 1));
    endif
  endif
  doll:schedule(this, "unharden", time() + dur, {what, o_cond});
  return dur;
endif
"Profane (#30788) - Thu Feb 22, 2001 - high end hardening is tougher";
"Profane (#30788) - Fri Mar  2, 2001 - Or not. mathematical analysis indicated all that happened was a higher std dev. forget it.";
"THX (#105941) - Mon Apr 16, 1801 - Changed the way things harden over 180 condition by randomizing the maximum over 180.";
.

@verb #328:"unharden" this none this
@program #328:unharden
{doll, what, cond} = args;
(rpg = $local.rpg):secure();
if (rpg.object_db:is_thing(what))
  if (what:set_condition(min(cond, what.condition)))
    if ($object_utils:isa(where = what.location, $room))
      what:room_announce_all(what:titlec(), " seems to weaken. . .");
    elseif (valid(rpg:get_doll(where)))
      what:room_announce_all(where.name, "'s ", what:title(), " seems to weaken. . .");
    endif
  else
    what:room_announce_all(what:titlec(), " seems to be having a problem unhardening. Contact a GM.");
  endif
endif
"THX (#105941) - Wed May  9, 2001 - Updated code.";
"Profane (#30788) - Tue Mar 12, 2002 - Don't announce if we're in a container on a person";
.

@verb #328:"enchant" this none this
@program #328:enchant
if (!(db = $local.rpg):trusted(caller_perms()))
  return E_PERM;
else
  doll = args[1];
  by = doll.character;
  staff = args[2];
  qual = args[3];
  dur = ((60 * qual) / 30) + random(60);
  try
    staff:do_enchant(qual, dur);
    $local.rpg:say_action("%N's %t seems to glow briefly.", by, staff);
  except (E_VERBNF)
    $local.rpg:rpg_announce_all(by.location, "Nothing happens.");
  endtry
endif
"Profane 3-JUN-97 1821PDT -- added try statement.";
.

@verb #328:"unenchant" this none this
@program #328:unenchant
if (!(db = $local.rpg):trusted(caller_perms()))
  return E_PERM;
else
  by = args[1].character;
  staff = args[2];
  if ($recycler:valid(staff) && $object_utils:isa(staff, $local.rpg.staff))
    for set in (this.enchant_stats)
      staff.(set[1]) = staff.(set[1]) - set[2];
      if (staff.(set[1]) == parent(staff).(set[1]))
        clear_property(staff, set[1]);
      endif
    endfor
    staff.enchantment = 0;
    name = valid(x = $local.rpg:get_doll(staff.location)) ? (staff.location.name + "'s ") + staff:title() | staff:title();
    loc = valid(x) ? staff.location.location | staff.location;
    $local.rpg:rpg_announce_all(loc, name, " seems to flicker briefly.");
  endif
endif
.

@verb #328:"biting_cloud" this none this
@program #328:biting_cloud
{att, doll, qual, @extra_args} = args;
by = att.character;
target = doll.character;
(rpg = $local.rpg):secure();
nat_prot = (target in rpg.vacuum.monsters) ? target.nat_prot | (((this.bs_combat_effect in doll.combat_effects) || (this.sf_combat_effect in doll.combat_effects)) ? 50 | 0);
if (x = $list_utils:iassoc("bite", doll.schedule, 2))
  rpg:say_action("The cloud of insects around %n shimmers for a moment.", target);
  doll.schedule[x][4][3] = time();
  doll:write_misc_note(verb, by);
else
  rpg:say_action("A cloud of insects gathers about %n, quickly growing into a dark, swarming mass!", target);
  if (typeof(effect = doll:magic_effects(@args)) != NUM)
    return effect;
  else
    qual = qual - effect;
  endif
  skill = doll.(verb);
  qual = (min(qual, 150) + ((skill > 20) ? random(skill) | 0)) + ((skill > 25) ? random(skill) | 0);
  if ((qual / 9) <= nat_prot)
    rpg:say_action("%N %<seems> unconcerned.", target);
  elseif (((doll != att) && (doll.magic_theory > 0)) && (rpg.skills.(verb):resolve(target, -qual) > 0))
    rpg:say_action("%N %<disperses> the insects with a quick counterspell.", target);
  else
    strikes = max(((qual + random(20)) / random(10)) - 1, 1);
    interval = max(100 - qual, 20);
    this.bc_combat_effect:add(doll);
    doll:write_misc_note(verb, by);
    this:bite(doll, strikes, interval, time());
  endif
endif
.

@verb #328:"bite" this none this
@program #328:bite
{doll, strikes, interval, time} = args;
(rpg = $local.rpg):secure();
target = doll.character;
stamina = rpg.skills.stamina;
hitloc = doll:random_hit_location();
x = time() - time;
strikes = strikes - max(x / interval, 1);
if (strikes > 0)
  doll:schedule(this, "bite", time() + interval, {strikes, interval, time});
else
  this.bc_combat_effect:remove(doll);
  doll:erase_misc_note("biting_cloud");
  rpg:say_action("The cloud of insects surrounding %n disperses.", target);
  return;
endif
if ((hitloc.nat_prot > 0) || ((!is_player(target)) && (target.nat_prot > (strikes / 4))))
  return rpg:say_action("Swarms of insects buzz about %n, but %s %<seems> unconcerned.", target);
endif
if (stamina:resolve(target, -(strikes * 3)) > 0)
  rpg:say_action("%N %<slaps> at the insects surrounding %o.", target);
else
  dam = max(random(12) + $math_utils:random_range(2), 0);
  if ((!is_player(target)) && (!target.alive))
  elseif (dam <= 0)
    rpg:say_action("%N %<grimaces> as insects crawl over %p body.", target);
  else
    doll:set_att("inj", doll.inj + dam);
    shockmod = hitloc:shock_mod(target, rpg.monster, rpg.weapon);
    stunmod = hitloc:stun_resistance(target, rpg.monster, rpg.weapon);
    try_ = rpg.skills.shock:resolve(target, shockmod);
    if (try_ > (-2 * doll.end))
      rpg:say_action("%N %<is> painfully stung by the cloud of insects surrounding %o.", target, target, target.location, {}, target);
    elseif (try_ > ((-4 * doll.end) - stunmod))
      rpg:say_action("%N %<is> painfully stung and sent reeling by the cloud of insects surrounding %o.", target, target, target.location, {}, target);
      doll.act = max(doll.act - 20, -20);
    elseif ((note = doll:read_misc_note("biting_cloud")) != E_PROPNF)
      doll:erase_misc_note("biting_cloud");
      rpg:playertell(target, "You have been stung to death by the cloud of insects surrounding you!  Your soul is returned to the cycle of transmigrations.");
      rpg:rpg_announce_all_but(target.location, {target}, $string_utils:pronoun_sub("%N %<is> stung to death by the cloud of insects surrounding %o!", target));
      doll:die(note, this);
    endif
  endif
endif
"Irin Thu Oct 10 11:23:19 1996 PDT -- Fixed bug whereby barkskin was resulting in permanent clouds.";
"Profane (#30788) - Sat Jan  2, 1999 - add highlighting of damage";
"THX (#105941) - Mon Dec  6, 1999 - Added code to save the 'caster' in the doll's misc_notes so that a killer can be attributed as needed.  Feh.";
.

@verb #328:"barkskin" this none this
@program #328:barkskin
(rpg = $local.rpg):secure();
doll = args[1];
by = doll.character;
qual = args[3];
"for armour in (doll.wearing)";
if (typeof(worn = this:barkskin_check_for_unacceptable_armour(doll)) == LIST)
  "if ($math_utils:sum(armour.cover))";
  rpg:rpg_announce_all_but(by.location, {by}, "Nothing happens.");
  return rpg:playertell(by, ("You'll first have to remove your " + $string_utils:english_list(worn)) + ".");
endif
"endfor";
if (k = $list_utils:iassoc("unstone", doll.schedule, 2))
  rpg:playertell(by, "Your flesh has been petrified.  It cannot be changed to vegetable matter now.");
elseif (k = $list_utils:iassoc("unbark", doll.schedule, 2))
  dur = (qual * qual) / 2;
  doll.schedule[k][3] = doll.schedule[k][3] + dur;
  rpg:say_action("%N %<stiffens> for a moment.", by);
else
  dur = qual * qual;
  doll.body_areas = this.barkskin_body_areas;
  this.bs_combat_effect:add(doll);
  doll:mod_att("slowness", 5);
  doll:schedule(this, "unbark", time() + dur, {qual});
  rpg:say_action("%N %<shudders> and %<begins> to undergo a strange transformation!  %P limbs stiffen and become gnarled, and %p whole body seems to turn into wood!", by);
endif
"Profane (#30788) - Sun Mar 28, 1999 - changed the slowness bit to just straight-up mod the att, like it shoulda been.";
"THX (#105941) - Mon Apr 16, 2001 - Put in a check for stoneflesh.  Players were abusing this to get scads of \"unstone\"'s in the way of an \"unspeed\".";
"Hydros (#106189) - Fri May 27, 2005 - Allowed certain types of armour to be worn in conjunction with barkskin.";
"                                    - Added messaging to let the caster know specifically what they need to remove to cast.";
.

@verb #328:"unbark" this none this
@program #328:unbark
(rpg = $local.rpg):secure();
{doll, ?qual} = args;
who = doll.character;
doll.body_areas = rpg.doll.body_areas;
doll:mod_att("slowness", -5);
this.bs_combat_effect:remove(doll);
rpg:say_action("%N's skin transforms back to normal.", who);
.

@verb #328:"hail" this none this
@program #328:hail
(rpg = $local.rpg):secure();
{att, targets, qual, skillobj, @extra} = args;
by = att.character;
`targets[1] ! E_TYPE => targets = {targets}';
"Cheesy way to get an objnum to be a list.";
dam = toint(tofloat(125 + (5 * qual)) / sqrt(tofloat(num_targ = length(targets))));
rpg:say_action("As %n %<finishes> %p incantation, a great hail of sharp stones begins to fall!", by);
for doll in (targets)
  if ($object_utils:isa(doll, rpg.doll))
    this:stone(att, doll, qual, skillobj, dam, num_targ);
  endif
endfor
rpg:say_action("%N's conjured hail of stones ceases.", by);
.

@verb #328:"lifelink" this none this
@program #328:lifelink
if ((rpg = $local.rpg):trusted(caller_perms()))
  {att, doll, qual} = args[1..3];
  by = att.character;
  target = doll.character;
  if (att.lifelinked || doll.lifelinked)
    return;
  endif
  rpg:say_action("Strange glowing energies whirl about %n!", target);
  rpg:playertell(target, by:titlec(), " is attempting to forge a lifelink with you!");
  if (typeof(effect = doll:magic_effects(att, doll, qual, rpg.skills.(verb))) != NUM)
    return effect;
  else
    qual = qual - effect;
    if (qual < 1)
      rpg:say_action("The swirling energies about %n collapse with no effect.", target);
    else
      if (((by in doll.aggressor) || (target in att.aggressor)) || (!is_player(target)))
        if ((doll.magic_theory > 0) && (rpg.skills.(verb):resolve(target, -qual) > 0))
          rpg:rpg_announce_all(target.location, this:spell_deflection_msg(target, by));
          return;
          "Irin Fri Sep 20 22:01:41 1996 PDT -- Fixed what I think is a bug, that prevented non-players from being lifelinked.";
        endif
      endif
      rpg:say_action("The energies collapse into %n, causing %o to cry out!", target);
      rpg:playertell(by, "Lifelink to ", target.name, " established.");
      rpg:playertell(target, "Your life is now linked to that of ", by.name, ".");
      this.ll_death_effect:add_link(att, doll, qual);
    endif
  endif
else
  return E_PERM;
endif
"Hannibal - inserted code to check that neither doll is lifelinked currently.";
"THX (#105941) - Wed Oct 13, 1999 - Changed to use doll.lifelinked instead of misc_note \"lifelink\".";
"THX (#105941) - Thu Apr  6, 2000 - Changed to use this.ll_death_effect:add_link().";
.

@verb #328:"stealskill" this none this
@program #328:stealskill
{attk, doll, qual, spell, skill, @extra} = args;
(rpg = $local.rpg):secure();
{by, target, toady} = {attk.character, doll.character, rpg.toady};
skill_name = skill.aliases[1];
attk_val = attk.(skill_name);
doll_val = doll.(skill_name);
if (doll_val < 1)
  rpg:playertell(by, target.name, " doesn't seem to be skilled in ", skill:title(), ".");
elseif ((attk_val >= doll_val) || (attk_val > 24))
  rpg:playertell(by, target.name, "'s skill pales in comparison to your own; don't waste your time.");
elseif (rpg.object_db:is_spell_skill(skill) && (attk_val < 1))
  rpg:playertell(by, "You haven't the foggiest notion where in ", target.name, "'s mind that knowledge might lie.");
elseif ((which = skill_name in $list_utils:slice(doll.att_schedule, 1)) && (doll.att_schedule[which][4][4][1] != attk))
  "i.e. Target already had this skill stolen by someone else.";
  rpg:playertell(by, target.name, "'s skill in ", skill:title(), " seems to be somehow missing.");
elseif (typeof(effect = doll:magic_effects(attk, doll, qual, rpg.skills.(verb))) != NUM)
  return effect;
else
  if (0 || ((qual = qual - effect) < 1))
    return rpg:playertell(by, "Nothing happens.");
  elseif ((doll.magic_theory > 1) && (rpg.skills.(verb):resolve(target, -qual, 1) > 0))
    rpg:rpg_announce_all(target.location, this:spell_deflection_msg(target, by));
  else
    amt = max(min(doll_val - attk_val, ((doll_val - attk_val) * qual) / (skill.learn * 2)), 1);
    "amt = max(amt / (attk_val || 1), 1);";
    amt = min(amt, 25 - attk_val);
    amt = max(amt, 1);
    dur = (qual * 30) / amt;
    this:desteal(attk, doll, skill);
    rpg:playertell(target, "You feel a FORCE enter your mind and RIP away your knowledge of ", skill:title(), "!");
    rpg:playertell(by, "You gain insight as ", target.name, "'s knowledge of ", skill:title(), " merges with your mind.");
    attk:att_schedule(skill_name, dur + time(), amt, {this, "unsteal", 0, {doll, attk_val, skill}});
    doll:att_schedule(skill_name, dur + time(), -amt, {this, "unsteal", 0, {attk, attk_val, skill}});
    this.ss_combat_effect:add(attk);
    this.ss_combat_effect:add(doll);
  endif
endif
"Profane (#30788) - Wed Jun  7, 2000 - no stealing spells that you have no previous knowledge of";
"Profane (#30788) - Thu Jun 22, 2000 - Shove the un-stealing resetting bit into :desteal";
"Profane (#30788) - Thu Jun 22, 2000 - Disallow stealing skills that have been stolen by someone else";
"Profane (#30788) - Thu May  3, 2001 - Closed loophole that allowed stealing of spellsongs";
"Profane (#30788) - Tue May  8, 2001 - now adding an effect for detect magic purposes";
.

@verb #328:"mindprobe" this none this
@program #328:mindprobe
{att, doll, qual, probeskill, @extra} = args;
(rpg = $local.rpg):secure();
by = att.character;
target = doll.character;
linelen = player:linelen();
text = {$string_utils:center(target:titlec(), linelen)};
if (typeof(effect = doll:magic_effects(att, doll, qual, probeskill)) != NUM)
  return effect;
elseif ((qual = qual - effect) < 1)
  rpg:playertell(by, "Nothing happens.");
elseif ((doll.magic_theory > 0) && (probeskill:resolve(target, -qual) > 0))
  rpg:rpg_announce_all(target.location, this:spell_deflection_msg(target, by));
else
  for skill in (rpg.attributes)
    text = {@text, (skill:title() + ": ") + rpg:skill_rate(doll.(skill.aliases[1]), 25)};
  endfor
  text = {text[1], @$string_utils:columnize(text[2..$], 2, linelen)};
  text = {@text, $string_utils:space(linelen, "-")};
  for weapon in (doll:wielding())
    line = ((((weapon:title() + ": ") + rpg:skill_rate(doll.(weapon.skill.aliases[1]), 25)) + " (") + rpg:skill_rate(doll:total(weapon.skill), doll:potential_total(weapon.skill))) + ")";
    text = {@text, line};
  endfor
  text = {@text, $string_utils:space(linelen, "-")};
  spells = $object_utils:descendants(rpg.spell_skill_object);
  amt = max(qual / random(3), 1);
  which = {};
  x = 0;
  n = 0;
  while ((x < amt) && (n < 20))
    spell = spells[random($)];
    spells = setremove(spells, spell);

>> Network buffer overflow: 2179 lines of output to you have been lost <<
{mage_doll, doll, qual, skillobj, @extra} = args;
(rpg = $local.rpg):secure();
if (doll:read_misc_note("mirrors") == E_PROPNF)
  this.gemini:doppelganger(doll.character, max(1, qual / 20), qual / 2, time() + (40 * qual));
else
  rpg:rpg_announce_all_but(doll.character.location, {}, "Nothing happens.");
endif
.

@verb #328:"is_peaceful_spell" this none this
@program #328:is_peaceful_spell
{caster, target, spell} = args;
if (spell in this.peaceful_spells)
  return 1;
elseif (((spell in this.team_peaceful_spells) && (target in caster.armour_effects)) && (caller_perms() in $local.rpg.gms))
  return 1;
elseif (spell == "haste")
  return target.slowness > 10;
elseif ((spell == "dispel") && (parent(target) == $local.rpg.doll))
  if (($set_utils:intersection({"bite", "disentangle", "unfear"}, $list_utils:slice(target.schedule, 2)) || (target:read_misc_note("stinkcloud") != E_PROPNF)) || (target in this.hf_combat_effect.held))
    return 1;
  endif
endif
return 0;
.

@verb #328:"playbalance" this none this
@program #328:playbalance
$local.rpg:secure();
forms = minus = {};
for t in (this.magic_forms)
  forms = {@forms, {t, t.minus}};
  minus = {@minus, t.minus};
endfor
return $list_utils:sort(forms, minus);
.

@verb #328:"hawthorn" this none this
@program #328:hawthorn
":hawthorn(caster-doll, target-doll, quality)";
{att, doll, qual} = args[1..3];
(rpg = $local.rpg):secure();
skill = rpg.skills.(verb);
"if (typeof(effect = doll:magic_effects(att, doll, qual, skill)) != NUM)";
"  return effect;";
"else";
"  qual = qual - effect;";
"endif";
{by, target, mod, thorn, odb} = {att.character, doll.character, 0, this.(verb), rpg.object_db};
loc = by.location;
rpg:say_action(("A wicked-looking thorn sprouts near the end of %n's " + caller.name) + ".", by);
if (qual > 0)
  if (loc != target.location)
    exits = loc:exits();
    what = where = {};
    for x in (exits)
      while (`valid(x.succ_exit) ! ANY')
        x = x.succ_exit;
      endwhile
      if (odb:is_missile_exit(x))
        d = x.dest.contents;
        what = {@what, @d};
        where = {@where, @$list_utils:make(length(d), x)};
      endif
    endfor
    mod = (target in what) ? where[target in what].cover | 999;
  endif
  {pen, dam, achilles} = {thorn.pen, thorn.dam, {doll.body_areas[1]}};
  lowest = doll:armour(pen, achilles[1], by, thorn, dam, qual);
  for t in (setremove(doll.body_areas, achilles[1]))
    if (lowest > (current = doll:armour(pen, t, by, thorn, dam, qual)))
      achilles = {t};
      lowest = current;
    elseif (lowest == current)
      achilles = setadd(achilles, t);
    endif
  endfor
  this.sc_recovery_effect:remove(att);
  thorn:deliver_payload(att, target, -mod, achilles[random($)], 0);
  this.sc_recovery_effect:add(att);
else
  return rpg:rpg_announce_all(loc, target.name, " seems unaffected by the spell.");
endif
"THX (#105941) - Fri Mar 17, 2000 - Commented out the magic_effects check.  The spell produces a physical missile, let the target dodge that for once.";
.

@verb #328:"special_hawthorn" this none this
@program #328:special_hawthorn
{att, targ_doll, qual, zap_result} = args;
$local.rpg:secure();
if (parent(targ_doll) == $local.rpg.doll)
endif
.

@verb #328:"get_messages(obsolete)" this none this
@program #328:get_messages(obsolete)
"Copied from Soulsong (#17367):get_messages by StarDancer (#82267) Thu May 30 17:50:05 1996 PDT";
"OBJ - the staff calling this verb, OBJ - caster, OBJ - target, STR - message property";
{staff, caster, target, msgs_prop} = args;
messages = {};
rpg = $local.rpg;
cu = $code_utils;
rpg.magic_db.zap_staves = setadd(rpg.magic_db.zap_staves, staff);
for idx in [1..12]
  {effect} = this.effects[idx];
  if (idx < 5)
    if (!rpg:trusted_verb(target, effect))
      msg_list = staff.(msgs_prop)[idx];
    else
      msg_list = (typeof(msg = cu:verb_or_property(target, effect)) == LIST) ? msg | {msg};
    endif
  else
    msg_list = staff.(msgs_prop)[idx];
  endif
  effect_msg = ((idx < 7) || (idx > 10)) ? msg_list[random($)] | msg_list;
  messages = {@messages, effect_msg};
endfor
if (rpg:trusted_property(target, "spell_deflection_msg"))
  messages[2] = target.spell_deflection_msg;
endif
return messages;
"Mooshie (#106469) - Sun Feb  1, 1998 - Sped up a bit and added check for .spell_deflection_msg on target, to intercept deflect messages if monster owners want to.";
.

@verb #328:"magic_fist(specific_implementation)" this none this
@program #328:magic_fist(specific_implementation)
":magic_fist(caster-doll, target-doll, quality)";
(rpg = $local.rpg):secure();
{att, doll, qual} = args[1..3];
magic_fist = rpg.skills.magic_fist;
if (typeof(effect = doll:magic_effects(att, doll, qual, magic_fist)) != NUM)
  return effect;
else
  qual = qual - effect;
endif
this:gen_zap(att, doll, qual, magic_fist, "magic_fist", 1);
.

@verb #328:"firebolt magic_fist" this none this
@program #328:firebolt
":firebolt(caster-doll, target-doll, quality)";
":magic_fist(caster-doll, target-doll, quality)";
{att, doll, qual} = args[1..3];
(rpg = $local.rpg):secure();
skill = rpg.skills.(verb);
dodgeable = doll:cannot_defend() ? 0 | this.(verb + "_dodgeable");
"this:gen_zap(att, doll, (qual * att.size) / doll.size, skill, verb, dodgeable);";
this:gen_zap(att, doll, qual, skill, verb, dodgeable);
"Hydros (#106189) - Tue Nov 1, 2005 - :hit_result already divides qual by target size, so why do it here?";
"                                   - Also, why should a bigger mob cast a bigger firebolt?  Just change the pen/power and messaging if you want that.";
.

@verb #328:"firebolt(specific_implementation)" this none this
@program #328:firebolt(specific_implementation)
":firebolt(caster-doll, target-doll, quality)";
(rpg = $local.rpg):secure();
{att, doll, qual, @extra} = args;
firebolt = rpg.skills.firebolt;
dodgeable = 1;
this:gen_zap(att, doll, qual, firebolt, "firebolt", dodgeable);
"Mooshie (#106469) - Tue Oct 14 05:56:02 1997 PDT - Scattering assignment. Whee!";
.

@verb #328:"glyph(old)" this none this
@program #328:glyph(old)
{bydoll, todoll, qual, skillobj, target} = args;
(rpg = $local.rpg):secure();
where = (by = bydoll.character).location;
targname = target.aliases[1];
flipflop = (verb == "glyph") ? -1 | 1;
if ((qual = qual / 25) < 1)
  rpg:rpg_announce_all(where, "Nothing happens.");
  return;
elseif ((rpg.object_db:is_rpg_room(where) && (!rpg.object_db:is_rpg_metaroom(where))) && qual)
  set = (typeof(temp = where:read_misc_note("g&r")) == LIST) ? temp | {};
  duration = time() + (30 * (qual + flipflop));
  if (indx = target in set)
    if (time() > set[indx + 2])
      set[indx..indx + 2] = {};
      qual = min(10, qual);
      where:write_misc_note("g&r", {@set, target, qual * flipflop, duration});
    else
      qual = ((prev = set[indx + 1]) < 0) ? min(10, qual) + prev | min(10, qual + prev);
      where:write_misc_note("g&r", listset(set, qual * flipflop, indx + 1));
    endif
  else
    qual = min(10, qual + flipflop);
    where:write_misc_note("g&r", {@set, target, qual * flipflop, duration});
  endif
  where.combat_effects = setadd(where.combat_effects, this.gr_combat_effect);
  where.magic_effects = setadd(where.magic_effects, this.gr_combat_effect);
  if (qual > 8)
    msg = tostr("%N %<carves> powerful ", verb, "s in the ground.  Arcing electricity and splashes of magic essence bathe ", where:title(), " in a flickering pallid glow.");
  elseif (qual > 4)
    msg = tostr("%N carefully %<etches> ", verb, "s into the ground beneath %p feet that throb a dark blue.");
  else
    msg = tostr("%N %<scratches> out meaningless ", verb, "s and symbols on the ground that flicker with meager sputterings of magic.");
  endif
  rpg:say_action(msg, by);
else
  msg = tostr("%N slowly %<begins> scrawling dark ", verb, "s in the ground, but they flare briefly before dissolving.");
  return rpg:say_action(msg, by);
endif
"Profane (#30788) - Tue May 25, 1999 - spell doesn't work in metarooms, and GM doesn't own any parents in the hierarchy to make it work. oh well.";
.

@verb #328:"stealskill(old)" this none this
@program #328:stealskill(old)
(rpg = $local.rpg):secure();
{att, doll, qual, spell, skill, @extra} = args;
by = att.character;
target = doll.character;
skill_name = skill.aliases[1];
att_val = att.(skill_name);
toady = rpg.toady;
doll_val = doll.(skill_name);
if (((att_val >= doll_val) || (doll_val < 1)) || (att_val > 24))
  rpg:playertell(by, target.name, " doesn't seem to be very skilled in ", skill:title(), ".");
elseif (typeof(effect = doll:magic_effects(att, doll, qual, rpg.skills.(verb))) != NUM)
  return effect;
else
  if (0 || ((qual = qual - effect) < 1))
    return rpg:playertell(by, "Nothing happens.");
  elseif ((doll.magic_theory > 1) && (rpg.skills.(verb):resolve(target, -qual) > 0))
    rpg:rpg_announce_all(target.location, this:spell_deflection_msg(target, by));
  else
    amt = max(min(doll_val - att_val, ((doll_val - att_val) * qual) / (skill.learn * 2)), 1);
    amt = max(amt / (att_val || 1), 1);
    amt = min(amt, 25 - att_val);
    dur = (qual * 30) / amt;
    for item in (att.schedule)
      if ((item[2] == "unsteal") && (item[4][2] == skill))
        toady:call_verb(item[1], item[2], {att, @item[4]});
        att.schedule = setremove(att.schedule, item);
      endif
    endfor
    for item in (doll.schedule)
      if ((item[2] == "unsteal") && (item[4][2] == skill))
        toady:call_verb(item[1], item[2], {att, @item[4]});
        doll.schedule = setremove(doll.schedule, item);
      endif
    endfor
    target:tell("You feel a FORCE enter your mind and RIP away your knowledge of ", skill:title(), "!");
    rpg:playertell(by, "You gain insight as ", target.name, "'s knowledge of ", skill:title(), " merges with your mind.");
    "doll:mod_att(skill_name, -amt);";
    "att:mod_att(skill_name, amt);";
    "att:schedule(this, \"unsteal\", dur + time(), {att_val, skill});";
    "doll:schedule(this, \"unsteal\", dur + time(), {doll_val, skill});";
    att:att_schedule(skill_name, dur + time(), amt, {rpg.magic_db, "unsteal", 0, {att, att_val, skill}});
    doll:att_schedule(skill_name, dur + time(), -amt, {rpg.magic_db, "unsteal", 0, {att, att_val, skill}});
  endif
endif
.

@verb #328:"gen_dodgable_spell(old)" this none this
@program #328:gen_dodgable_spell(old)
"Copied from webs (#55049):gen_dodgable_spell by Isagi (#21077) Fri Jul  5 13:31:27 1996 PDT";
":gen_spell(caster-doll, target-doll, quality, skillobj, spell_name, dodgeable, [spell_db[, FLAG_natural_attack]])";
"Original verb taken from #328:gen_zap and modified to allow for non-damaging, but dodgable, magic projectiles.";
"The optional argument, castobj lets the GM use objs that aren't kids of the spell db (#328)";
(rpg = $local.rpg):secure();
att = args[1];
targ_doll = args[2];
qual = args[3];
by = att.character;
target = targ_doll.character;
area = target.location;
skilldb = rpg.skills;
dodge = skilldb.dodge;
skillobj = args[4];
spell_name = args[5];
dodgeable = args[6];
magic = targ_doll:get_att("magic_theory");
spell_msgs = spell_name + "_msgs";
spell_power = "power_" + spell_name;
spell_pen = "pen_" + spell_name;
magic_db = (length(args) > 6) ? args[7] | this;
FLAG_natural_attack = 0;
FLAG_natural_attack = (length(args) > 7) ? !(!args[8]) | 0;
stu = $string_utils;
if (valid(att))
  att:do_attack_notification(target, skillobj);
endif
targ_doll:set_att("aggressor", setadd(targ_doll:get_att("aggressor"), by));
att:set_att("aggressor", setadd(att:get_att("aggressor"), target));
gen_zap_msgs = this:get_messages(magic_db, by, target, spell_msgs);
if (typeof(effect = targ_doll:magic_effects(@args)) != NUM)
  if (!FLAG_natural_attack)
    return effect;
  endif
elseif ((qual = qual - effect) <= 0)
  resist_msg = stu:pronoun_sub(gen_zap_msgs[1], by, dobj = target);
  rpg:rpg_announce_all(area, resist_msg);
elseif ((((targ_doll != att) && (magic > 0)) && (skillobj:resolve(target, -qual) > 0)) && (!FLAG_natural_attack))
  deflect_msg = stu:pronoun_sub(gen_zap_msgs[2], by, dobj = target);
  rpg:rpg_announce_all(area, deflect_msg);
elseif ((dodgeable && (targ_doll != att)) && (dodge:resolve(target, 30 - targ_doll:get_att("slowness")) > qual))
  dodge_msg = stu:pronoun_sub(gen_zap_msgs[3], by, dobj = target);
  rpg:rpg_announce_all(area, dodge_msg);
else
  actual_verb_name = "DO_" + tostr(spell_name);
  value = magic_db:(actual_verb_name)(att, targ_doll, qual);
  if (((typeof(value) == LIST) && (length(value) > 0)) && (typeof(value[1]) == NUM))
    blast_msg = stu:pronoun_sub(gen_zap_msgs[3 + value[1]], by, dobj = target);
  else
    blast_msg = stu:pronoun_sub(gen_zap_msgs[4], by, dobj = target);
  endif
  rpg:rpg_announce_all(area, blast_msg);
  return value;
endif
.

@verb #328:"check_live_target" this none this
@program #328:check_live_target
":check_live_target(OBJ caster character, [OBJ target, OBJ spell, NUM boolean peaceful]) => doll of target || $nothing. For spells that want one living target.";
{by, ?tstr = dobjstr, ?spell = $nothing, ?peaceful = 0} = args;
(rpg = $local.rpg):secure();
tstr = tostr(tstr);
target = valid(x = by:my_match_object(tstr)) ? x | dobj;
{loc, mdb, odb} = {by.location, rpg.magic_db, rpg.object_db};
no_target = ((!valid(target)) || (target.location != loc)) && (target != loc);
if (no_target && (spell in mdb.range_spells))
  exits = loc:exits();
  what = {};
  for x in (exits)
    while (`valid(x.succ_exit) ! ANY')
      x = x.succ_exit;
    endwhile
    if (odb:is_missile_exit(x))
      what = {@what, @x.dest.contents};
    endif
  endfor
  target = $match_utils:match(tstr, what);
  if (target in what)
    no_target = 0;
  endif
elseif (no_target && valid(`target = loc:match_for_spell(by, tstr, spell, peaceful) ! ANY => $nothing'))
  (loc.owner in rpg.gms) && (no_target = 0);
endif
caster = rpg:get_doll(by);
doll = rpg:get_doll(target);
peaceful = peaceful ? peaceful | mdb:is_peaceful_spell(caster, doll, spell);
if (no_target)
  rpg:playertell(by, "I don't see any `", dobjstr ? dobjstr | tstr, "' here.");
elseif ((!$object_utils:isa(doll, rpg.doll)) && valid(spell))
  verbname = "receive_" + spell.aliases[1];
  (`rpg:trusted_verb(target, verbname) ! E_INVIND => 0' && target:(verbname)(dobj, spell)) || rpg:playertell(by, "My God! ", target.name, " seems to be immune to Illuminati magick.");
elseif (((!peaceful) && is_player(target)) && ((!$object_utils:connected(target)) || ((idle_seconds(target) > rpg.idle_threshold) && ((time() - doll.last_attack) > rpg.idle_threshold))))
  rpg:playertell(by, "Hold your magic!  By the edict of the great wizard, bystanders must not be molested.");
elseif (((((!peaceful) && is_player(target)) && (target != player)) && (!(target in caster.aggressor))) && rpg.rpg_options:get(caster.rpg_options, "peacenik"))
  rpg:playertell(by, "Whoa!  You don't really want to commit violence upon your fellow human beings, do you?  Even if it is ", target:title(), ".  After all, love is the most powerful weapon.  Play nice, now.");
elseif ((valid(other = doll.duelee) && (`rpg:get_doll(other).duelee ! E_INVIND' == target)) && (!((is_player(by) ? by | (valid(master = `by:master() ! E_VERBNF => target') ? master | target)) in {target, other})))
  rpg:playertell(by, target.name, " is involved in a duel with ", doll.duelee.name, "; do not interfere.");
elseif (((valid(other = doll.duelee) && (rpg:get_doll(other).duelee == by)) && is_player(target)) && (!(target in {by, other})))
  rpg:playertell(by, "You're busy duelling ", other.name, "; finish that fight first.");
elseif (rpg.death_db.pkp_effect:check_for_PK(by, target, spell))
  rpg:playertell(by, target.name, " appears to be under the aegis of His Eminence, whose wrath is not to be invoked.");
else
  return doll;
endif
return $nothing;
"Profane 22-DEC-95 2008EST -- Added.";
"THX (#105941) - Sun May 31, 1998 - Scatter assignment.  Checking for custom receive_\"spell\" on !alive targets too.";
"Profane (#30788) - Mon Jul  6, 1998 - uncommented the bit about setting tstr- a plain scatter dun hack it. Also error trap receive_* call";
"Profane (#30788) - Wed Apr 21, 1999 - check new peacenik option";
"THX (#105941) - Mon Jan  3, 2000 - Commented the tstr length(args) stuff and replaced with a tostr().  Put in  some stuff to check peaceful for the peacenik stuff.";
"THX (#105941) - Tue Mar  7, 2000 - Added a check for \"range_spells\" ... maybe we can make some spells work through missile-transmitting exits.";
"THX (#105941) - Wed May 10, 2000 - Moved this to rpg.magic_db since it never used 'this' while on the foci anyway.  Both armour and weapons can use the same verb.";
"Profane (#30788) - Thu May 31, 2001 - No 3rd party attacks during duels";
.

@verb #328:"triage" this none this
@program #328:triage
"=> Finds all critter/players in the area the spell is cast who has inj.>0 and retrieves their $local.rpg.skills.shock:penalty_trusted(doll) to sort out the most in need of help to the least in need of help.";
{by, doll, qual, skillobj, @extra} = args;
(rpg = $local.rpg):secure();
{target, shock} = {by.character, rpg.skills.shock};
{threshold, place, data, injured} = {shock.injury, target.location, {}, {}};
"data = data for critters/players";
for what in (place.contents)
  if (valid(dollx = rpg:get_doll(what)))
    if (dollx.inj || ((dollx.ins + dollx.fat) / threshold))
      injured = {@injured, what};
      data = {@data, (dollx:total("shock") - shock:penalty_trusted(dollx)) - (!(!dollx.inj))};
    endif
  endif
endfor
if (!data)
  rpg:playertell(target, "You sense nothing having injuries nearby.");
else
  "sort injured critters and players from serious to not so serious";
  injured = $list_utils:sort(injured, data);
  rpg:playertell(target, "You sense the following in your area:");
  tries = max(qual / 9, 1);
  for who in (injured)
    rpg:look_health(who);
    sum = 0;
    for i in [1..tries]
      if (shock:resolve(who, 0, 1) <= (-2 * (rpg:get_doll(who).end + (is_player(who) ? 20 | 10))))
        sum = sum + 1;
      endif
    endfor
    pct = toint((100.0 * tofloat(sum)) / tofloat(tries));
    word = pct ? tostr($string_utils:english_number(pct), " `percentum'") | "not";
    rpg:playertell(target, who:title(), " is ", word, " likely to die soon.");
  endfor
endif
"THX (#105941) - Mon May 22, 2000 - Added from demonrider's development magic_db.";
"THX (#105941) - Wed Dec 20, 2000 - Factored in the doll's total shock too.  The penalty was only half the story.";
"Profane (#30788) - Mon Feb 26, 2001 - % based estimation of chance of survival";
"THX (#105941) - Fri Mar 23, 2001 - Changed the '%' to 'percentum' just to make it seem more arcane.  Or something.  Changed it to print an :english_number too; it just looks better that way.";
"THX (#105941) - Sat Mar 24, 2001 - Added exhausted/fatigued rpgers into the fray ... if Eir insanity/fatigue is high enough.  Added a \"tiebreaker\" term into 'data' since actual wounds are required before a shock failure will result in death.";
.

@verb #328:"beguile" this none this
@program #328:beguile
{att, def, qual, skill, @extra} = args;
by = att.character;
targ = def.character;
(rpg = $local.rpg):secure();
if (typeof(effect = def:magic_effects(att, def, qual, skill)) != NUM)
  return effect;
elseif ((qual = qual - effect) <= 0)
  rpg:say_action("%N %<seems> unaffected by the song.", targ);
elseif (def.magic_theory && (skill:resolve(targ, -qual, 1) > 0))
  rpg:say_action("%N %<finds> the tune pleasing, but technically lacking.", targ);
elseif (`targ.animal ! ANY => 0' && (!(targ.animal in {"golem", "corpse", "demon", "humanoid"})))
  rpg:say_action("%N is enchanted by the tune.", targ);
  rpg:playertell(by, "However, as a simple animal, ", targ.name, " doesn't know valuables from hashbrowns.");
elseif (((note = def:read_misc_note("Beguiled")) != E_PROPNF) && (note > time()))
  this:beguile_abuse(att, def, by, targ, qual);
elseif (def:cannot_move() || def.paralyzed)
  rpg:say_action("%N %<is> enchanted by the tune, but cannot move to comply with the commandments woven into the song.", targ);
else
  is_player(targ) || (qual = qual - random(33));
  if (qual > 80)
    fork (0)
      def.aggressor = setremove(def.aggressor, by);
    endfork
    def.act = min(def.act, 0);
    att.act = min(att.act, 0);
  endif
  this:beguile_success(att, def, by, targ, qual);
  "def:write_misc_note(\"Beguiled\", time() + qual * 2)";
endif
"THX (#105941) - Wed May 17, 2000 - Added this from Hydros' beta-test magic_db.";
"THX (#105941) - Sun Jun 11, 2000 - Hacked in a splurge to attempt to circumvent invite if the qual is high enough.";
.

@verb #328:"beguile_success" this none this
@program #328:beguile_success
{att, def, by, targ, qual} = args;
(rpg = $local.rpg):secure();
"The successful cast of the Beguile spell.  Depending on the qual, it forces a target to hand over certain valuables (gems and coins) to the caster.";
"The below variables, from 'crit_gem_hit' to 'min_hit,' let the later :beguile_messages verb know for what (cast quality) circumstance it should be acting.  The other two, 'has_coins' and 'has_gems,' are also for messaging. These note whether the target has any coins or gems on them to actually give away.";
{crit_gem_hit, crit_cash_hit, med_hit, min_hit, has_coins, has_gems} = {0, 0, 0, 0, 0, 0};
{odb, o_u, weapon} = {rpg.object_db, $object_utils, rpg.weapon};
if (qual >= 150)
  "Critical spell hit.";
  for pos in (o_u:all_contents(targ))
    if (odb:is_gem(pos) && (!o_u:isa(pos.location, weapon)))
      "The check above stops gems from being stolen if 'mounted' for spell usage.";
      crit_gem_hit = 1;
      "Target hands over a gem to the caster.  The `if (has_gems)' hack below prevents all gems from being lost.";
      pos:moveto(by);
      has_gems = 1;
    endif
    if (has_gems)
      break;
    endif
  endfor
  for pos in (o_u:all_contents(targ))
    if (odb:is_money(pos))
      crit_cash_hit = 1;
      "Target hands all eir cash over to the caster.";
      pos:distribute(by, pos.quantity);
      has_coins = 1;
    endif
  endfor
elseif (qual >= 125)
  "Medium spell hit.";
  "Here, e only hands over half of any gold/copper/silver e has, unless e has very little to spare, in which case, we yoink it all.";
  for pos in (o_u:all_contents(targ))
    if (odb:is_money(pos))
      med_hit = 1;
      if ((pos.unit_worth < 100) || (pos.unit_worth == 1))
        pos:distribute(by, pos.quantity);
      else
        new_value = pos.quantity / 2;
        pos:distribute(by, new_value);
      endif
      has_coins = 1;
    endif
  endfor
elseif (qual >= 80)
  "Minimal_hit";
  "Hands over 1/4 of cash.";
  for pos in (o_u:all_contents(targ))
    if (odb:is_money(pos))
      min_hit = 1;
      if ((pos.unit_worth < 100) || (pos.unit_worth == 1))
        pos:distribute(by, pos.quantity);
      else
        new_value = pos.quantity / 4;
        pos:distribute(by, new_value);
      endif
      has_coins = 1;
    endif
  endfor
endif
this:beguile_messages(by, targ, crit_gem_hit, crit_cash_hit, med_hit, min_hit, has_gems, has_coins, qual);
"Hydros (#106189) - Sun Mar 20, 2005 - Added a hack that prevented crit hits from parting a victim from -all- of eir gems.  Now e will hand over only one along with the gold.";
.

@verb #328:"beguile_abuse" this none this
@program #328:beguile_abuse
{att, def, by, targ, qual} = args;
(rpg = $local.rpg):secure();
dobj = by;
rpg:say_action("%N %<searches> madly on %r for some sort of token for %d's playing, and finding some random small object, ~perhaps~ a coin, %<hurls> it at %d.", targ);
"Just a small punishment for abusing the spell. The double-entendre is in the abuse the player will ~receive~ for their actions. It doesn't do much damage, but it in no way aids them.";
tqual = rpg.skills.throw:resolve(targ, 0, 1);
dqual = rpg.skills.dodge:resolve(by, 0, 1);
if ((result = tqual - dqual) <= 0)
  barea = def.body_areas[random($)];
  barea = barea.name;
  if (def:wearing(barea) == {})
    rpg:tell_damage(by, ("The small projectile strikes your " + barea) + ", leaving a nasty red mark.");
    rpg:rpg_announce_all_but(by.location, {by}, "The projectile strikes ", by.name, " on the ", barea, ", leaving a nasty red mark.");
    att:mod_att("inj", 3);
  else
    "If they are wearing armour on the body area chosen, it does no damage -- it's just a small, random throw.  No biggie.";
    rpg:say_action(("The small projectile glances off %n's armoured " + barea) + ".", by);
  endif
else
  rpg:say_action("%N %<dodges> the small, random, hurled object.", by);
endif
.

@verb #328:"beguile_messages" this none this
@program #328:beguile_messages
{by, targ, critg, critc, med, min, gems, coins, qual} = args;
"The messaging for the Beguile successful casts.  Assuming the player has gems/gold, and depending on the qual, it will display different messages to correspond with the qual checks in the preceding :beguile_success() verb.  If no gems or cash are present, alternate messages are displayed.";
(rpg = $local.rpg):secure();
dobj = by;
tdoll = rpg:get_doll(targ);
if (critg || critc)
  rpg:playertell(targ, "You are dazzled by memories and good times from your past, all dancing through your mind, to the song's mesmorizing tune.");
  rpg:say_action("Simply enchanted by the melody, %n %<digs> through %p various pockets and pouches...", targ);
  if (critg && gems)
    "If spell is critical and has gems on them.";
    rpg:say_action("%N %<produces> a gemstone and generously %<hands> it over to %d.", targ);
  endif
  if (critc && coins)
    "Same as before, but checks for money in target's contents.";
    rpg:say_action("%N %<produces> a stash of coins and generously hands it over to %d.", targ);
  endif
  tdoll:write_misc_note("Beguiled", time() + qual);
elseif (med && coins)
  rpg:playertell(targ, "The song inspires you and lifts your spirits, and your resolve.");
  rpg:say_action("%N %<tosses> %d some coins.", targ);
  tdoll:write_misc_note("Beguiled", time() + qual);
elseif (min && coins)
  rpg:playertell(targ, "The song brightens your day.");
  rpg:say_action("%N %<tosses> a few coins to %d.", targ);
  tdoll:write_misc_note("Beguiled", time() + qual);
elseif ((!med) && (!med))
  rpg:say_action("%N %<seems> unaffected by the song.", targ);
  return;
endif
if ((!coins) && (!gems))
  rpg:say_action(this.beguile_gemless_msg, targ);
elseif (!coins)
  rpg:say_action(this.beguile_coinless_msg, targ);
endif
"Hydros (#106189) - Sun Mar 20, 2005 - Changed messaging to reflect a hack added to :beguile_success() which prevents -all- gems from being handed over on a crit hit. Now we hand over only one.";
.

@verb #328:"desteal" this none this
@program #328:desteal
":desteal(attacker_doll, target_doll, which skill) -> reset both attacker and target's <skill> back to normal. for use with stealskill";
(rpg = $local.rpg):secure();
{attk, doll, skill} = args;
skill_name = skill.aliases[1];
"First reset attacker's skill.";
for item in (attk.att_schedule)
  {attr, time, mod, element} = item;
  if ((attr == skill_name) && (element[2] == "unsteal"))
    rpg.toady:call_verb(this, "unsteal", {attk, @element[4]});
    attk:att_unschedule(@item);
    "Now reset whoever the attacker stole this skill from.";
    who = element[4][1];
    for who_item in (who.att_schedule)
      {who_attr, who_time, who_mod, who_element} = who_item;
      if ((who_attr == skill_name) && (who_element[2] == "unsteal"))
        rpg.toady:call_verb(this, "unsteal", {who, @who_element[4]});
        who:att_unschedule(@who_item);
      endif
    endfor
  endif
endfor
"Now reset target";
for item in (doll.att_schedule)
  {attr, time, mod, element} = item;
  if ((attr == skill_name) && (element[2] == "unsteal"))
    rpg.toady:call_verb(this, "unsteal", {doll, @element[4]});
    doll:att_unschedule(@item);
    "Now reset whoever the target stole this skill from.";
    who = element[4][1];
    for who_item in (who.att_schedule)
      {who_attr, who_time, who_mod, who_element} = who_item;
      if ((who_attr == skill_name) && (who_element[2] == "unsteal"))
        rpg.toady:call_verb(this, "unsteal", {who, @who_element[4]});
        who:att_unschedule(@who_item);
      endif
    endfor
  endif
endfor
.

@verb #328:"print_storms(old)" this none this
@program #328:print_storms(old)
"print_storms(no args | OBJ)";
"returns {} or a list of strings formatted for player:tell_lines()";
"If argument is given and is a room, print info for that room, if it is not a room, print storms cast by that object. If no argument, do all storms globally";
{?what = 0} = args;
(rpg = $local.rpg):secure();
db = rpg.magic_db;
if (typeof(what) != OBJ)
  storms = db.storms;
elseif ($object_utils:isa(what, $room))
  storms = db:storms_active_in(what);
else
  storms = db:storms_cast_by(what);
endif
if (!storms)
  return {};
endif
biglist = stormlist = {};
{l_u, s_u} = {$list_utils, $string_utils};
for i in [1..length(storms)]
  who = storms[i][2];
  qual = storms[i][4];
  if (ind = l_u:iassoc(who, stormlist))
    if (qual > 50)
      stormlist[ind][2] = stormlist[ind][2] + 1;
    elseif (qual > 15)
      stormlist[ind][3] = stormlist[ind][3] + 1;
    else
      stormlist[ind][4] = stormlist[ind][4] + 1;
    endif
  else
    raging = thunder = minor = 0;
    if (qual > 50)
      raging = 1;
    elseif (qual > 15)
      thunder = 1;
    else
      minor = 1;
    endif
    stormlist = {@stormlist, {who, raging, thunder, minor}};
  endif
endfor
for j in [1..length(stormlist)]
  plyr = stormlist[j][1];
  text = {};
  r = stormlist[j][2];
  t = stormlist[j][3];
  m = stormlist[j][4];
  if (r == 1)
    text = {@text, "a raging electrical storm"};
  elseif (r > 1)
    text = {@text, tostr(r) + " raging electrical storms"};
  endif
  if (t == 1)
    text = {@text, "a thunderstorm"};
  elseif (t > 1)
    text = {@text, tostr(t) + " thunderstorms"};
  endif
  if (m == 1)
    text = {@text, "a minor storm"};
  elseif (m > 1)
    text = {@text, tostr(m) + " minor storms"};
  endif
  flags = `plyr.location.room_data ! ANY => {}';
  shortlist = (("cavern" in flags) || ("structure" in flags)) ? tostr(plyr.name, " has ", strsub(s_u:english_list(text), "raging ", ""), " raging outside.") | tostr(plyr.name, " has up ", s_u:english_list(text), ".");
  biglist = {@biglist, tostr(plyr.name, " has up ", s_u:english_list(text), ".")};
endfor
return biglist;
"Profane 17-DEC-95 1211EST -- (re)modified to do storms_cast_by as well.";
.

@verb #328:"mergestorm" this none this
@program #328:mergestorm
{att, dummy, qual, skillobj} = args;
by = att.character;
(rpg = $local.rpg):secure();
this = $code_utils:verb_loc();
storms = this:storms_cast_by(by);
stormfront = length(storms);
if (stormfront < 2)
  return rpg:announce_all_but(by.location, {}, "Nothing happens.");
endif
keeper = storms[1];
for storm in (setremove(storms, keeper))
  {tid, caster, strikes, qual, interval} = storm;
  (qual > keeper[4]) && (keeper = storm);
endfor
{keeperqual, n} = {keeper[4], 0};
for storm in (setremove(storms, keeper))
  {tid, caster, strikes, qual, interval} = storm;
  n = n + ((strikes * qual) / keeperqual);
  this.storms = setremove(this.storms, storm);
  this.silent_storms = setadd(this.silent_storms, storm);
endfor
for storm in (this.storms)
  {tid, caster, strikes, qual, interval} = storm;
  if (by == caster)
    this.storms[storm in this.storms] = {tid, caster, strikes + n, qual + stormfront, interval};
    break storm;
  endif
endfor
rpg:say_action("With a surge of unrelenting meteorological power, %N's storms come together with a clash of thunder and blinding flare of light.", by);
.

@verb #328:"gen_zap(old)" this none this
@program #328:gen_zap(old)
":gen_zap(OBJ caster-doll, OBJ target-doll, INT quality, OBJ skillobj, STR spell_name, FLAG dodgeable, [OBJ staff, [FLAG natural_attack]])";
"Original verb taken from #328:firebolt, and modded so 1 verb c'n handle *all* the spells that jus do damage.";
"This verb'll handle ice-bolts, magic missiles, an jus 'bout anythin a GM c'n think of.";
"The optional argument, staff, lets the GM use objs that aren't kids of the spell db (#328)";
(rpg = $local.rpg):secure();
{att, targ_doll, qual, skillobj, spell_name, dodgeable, ?staff = this, ?natural_attack = 0} = args;
by = att.character;
target = targ_doll.character;
area = target.location;
dodge = rpg.skills.dodge;
magic = targ_doll.magic_theory;
spell_msgs = spell_name + "_msgs";
spell_power = "power_" + spell_name;
spell_pen = "pen_" + spell_name;
spell_dodge = "dodge_" + spell_name;
spell_special = "special_" + spell_name;
stu = $string_utils;
att:do_attack_notification(target, skillobj);
is_player(target) || target:set_last_attacker(by);
targ_doll.aggressor = setadd(targ_doll.aggressor, by);
att.aggressor = setadd(att.aggressor, target);
{resist_msg, deflect_msg, dodge_msg, blast_msg, @efx_messages} = this:get_messages(staff, by, target, spell_msgs);
no_imp = (by == target) ? 1 | 0;
if (typeof(effect = att:casting_effects(att, targ_doll, qual, skillobj)) != NUM)
  return effect;
elseif ((!natural_attack) && (typeof(effect = targ_doll:magic_effects(att, targ_doll, qual, skillobj)) != NUM))
  return effect;
elseif (is_player(target) && (!(target in connected_players())))
  by:tell("Hold your magic! By the edict of the Great Wizard, bystanders must not be molested!");
elseif ((!natural_attack) && ((qual = qual - effect) <= 0))
  resist_msg = stu:pronoun_sub(resist_msg, by, dobj = target);
  rpg:rpg_announce_all_but(area, {}, resist_msg);
elseif ((((!natural_attack) && (targ_doll != att)) && (magic > 0)) && (skillobj:resolve(target, -qual, no_imp) > 0))
  deflect_msg = stu:pronoun_sub(deflect_msg, by, dobj = target);
  rpg:rpg_announce_all_but(area, {}, deflect_msg);
elseif ((dodgeable && (targ_doll != att)) && (dodge:resolve(target, staff:(spell_dodge)(att, targ_doll, qual) - targ_doll.slowness, no_imp) > qual))
  dodge_msg = stu:pronoun_sub(dodge_msg, by, dobj = target);
  rpg:rpg_announce_all_but(area, {}, dodge_msg);
else
  blast_msg = stu:pronoun_sub(blast_msg, by, dobj = target);
  rpg:rpg_announce_all_but(area, {}, blast_msg);
  power = staff:(spell_power)(qual, magic, targ_doll);
  pen = staff:(spell_pen)(qual, magic);
  zap_result = targ_doll:hit_result(power, pen, by, skillobj);
  if (zap_result)
    this:do_zap(zap_result, efx_messages, by, target, staff);
    "Ok, new bit for spells doing funky special damage.  Replaces the call to :fire_effect.";
    `staff:(spell_special)(att, targ_doll, qual, zap_result) ! E_VERBNF';
  else
    "Only happens in the case of bad perms or .end == 0";
    resist_msg = stu:pronoun_sub(resist_msg, by, dobj = target);
    rpg:rpg_announce_all_but(area, {}, resist_msg);
  endif
endif
"Profane 27-JUN-96 848EDT -- Added check for disconnected players.";
"Mooshie (#106469) - Sun Feb  1, 1998 - Did some scattering and speeding up.";
"Irin - Added hook for special effect if the spell goes through.  Fire damage, etc.  Also added dodge difficulty and possibility of a non-magickal attack (undeflectable && no magic resistance.)";
"Hannibal - Added casting_effects check for the casting doll.";
"Hannibal 2/9/2000 - Added call to :set_last_attacker if the target is an RPG monster.";
.

@verb #328:"stoneflesh" this none this
@program #328:stoneflesh
{doll, who, qual, skill, @extra} = args;
(rpg = $local.rpg):secure();
who = doll.character;
for armour in (doll.wearing)
  if ($math_utils:sum(armour.cover))
    return rpg:playertell(who, "You'll have to remove what you're wearing first.");
  endif
endfor
if (!(this.bs_combat_effect in doll.magic_effects))
  return rpg:say_action("Aside from a stiff neck, %n %<seems> entirely unaffected.", who);
elseif (qual < 0)
  rpg:playertell(who, "By combining magickal forms, you try to cast Skin of the Mountain.");
  rpg:say_action("%N's flesh begins to stiffen further...but returns to its wooden form.");
else
  rpg:playertell(who, "By combining magickal forms, you try to cast Skin of the Mountain.");
  "We remove the barkskin and add the stoneflesh.";
  doll.body_areas = this.stoneflesh_body_areas;
  this.bs_combat_effect:remove(doll);
  this.sf_combat_effect:add(doll);
  if ((doll:get_att("CAPterram") >= 30) || (doll:get_att("CAPherbam") >= 30))
    doll:write_misc_note("Stoneflesh_Bonus", random(10));
  else
    doll:write_misc_note("Stoneflesh_Bonus", 0);
  endif
  "Specialists gain a bonus to the effect--this can be seen in #328.sf_combat_effect:armour_effect and :magic_effect.";
  "Here, we add 1 to the 5 slowness caused by barkskin, totalling 6.";
  doll:mod_att("slowness", 1);
  rpg:say_action("%N's gnarled, bark-like skin shudders and hardens further, fading to a cold grey as it petrifies into stone.", who);
  "Removing the barkskin schedule and adding that of stoneflesh.";
  while (x = $list_utils:iassoc("unbark", doll.schedule, 2))
    doll:cancel(doll.schedule[x]);
  endwhile
  doll:schedule(this, "unstone", time() + ((qual * qual) / 2), {qual});
endif
"THX (#105941) - Fri Sep  1, 2000 - Added from Hydros' prototype.";
.

@verb #328:"unstone" this none this
@program #328:unstone
{doll, @extra} = args;
(rpg = $local.rpg):secure();
doll.body_areas = rpg.doll.body_areas;
norm = is_player(doll.character) ? rpg.doll.slowness | target:get_iprop("slowness");
doll:set_att("slowness", norm);
announce = this.sf_combat_effect in doll.armour_effects;
this.sf_combat_effect:remove(doll);
doll:erase_misc_note("Stoneflesh_Bonus");
if (i = $list_utils:iassoc("unstone", doll.schedule, 2))
  doll:cancel(doll.schedule[i]);
endif
announce && rpg:say_action("%N's petrified skin transforms back to normal.", doll.character);
.

@verb #328:"parch" this none this
@program #328:parch
{att, doll, qual, skillobj, @useless} = args;
(rpg = $local.rpg):secure();
by = att.character;
target = doll.character;
area = target.location;
if (typeof(effect = doll:magic_effects(@args)) != NUM)
  return effect;
elseif ((qual = qual - effect) <= 0)
  rpg:rpg_announce_all(area, target.name, " seems unaffected by the spell.");
elseif (((doll != att) && (doll.magic_theory > 0)) && (rpg.skills.parch:resolve(target, -qual, 1) > 0))
  rpg:rpg_announce_all(area, this:spell_deflection_msg(target, by));
elseif ($list_utils:assoc("unparch", doll.schedule, 2))
  rpg:rpg_announce_all(area, "Nothing happens.");
else
  dur = (qual + random(20)) * 2;
  this.cs_combat_effect:add(doll);
  doll:schedule(this, "unparch", time() + dur, {by, qual});
  rpg:rpg_announce_all_but(area, {target}, target:titlec(), " coughs dryly.");
  target:tell("Your mouth dries out, your throat spasms, and your tongue becomes a bit of cracked leather as all moisture in your respiratory system leaches out and evaporates.");
endif
.

@verb #328:"unparch" this none this
@program #328:unparch
(rpg = $local.rpg):secure();
{doll, @useless} = args;
this.cs_combat_effect:remove(doll);
doll.character:tell("It appears your salivary glands have started working again, moistening your mouth and throat once again.");
.

@verb #328:"unharden(old)" this none this
@program #328:unharden(old)
if (!(db = $local.rpg):trusted(caller_perms()))
  return E_PERM;
else
  what = args[2];
  cond = args[3];
  if ($object_utils:isa(what, $local.rpg.thing))
    if (what:set_condition(min(cond, what.condition)))
      if ($object_utils:isa(where = what.location, $room))
        what:room_announce_all(what:titlec(), " seems to weaken. . .");
      else
        what:room_announce_all(where.name, "'s ", what:title(), " seems to weaken. . .");
      endif
    else
      what:room_announce_all(what:titlec(), " seems to be having a problem unhardening. Contact a GM.");
    endif
  endif
endif
.

@verb #328:"rot" this none this
@program #328:rot
"Copied from spellies (#7994):328rot by Caste (#112369) Thu Aug  9 13:29:56 2001 PDT";
{att, target, qual, skill, @rest} = args;
(rpg = $local.rpg):secure();
victim = att.character;
flagevil = 0;
if (`target.is_corpse ! E_PROPNF, E_PERM')
  by = att.character;
  victim = target.victim;
  doll = `target.doll ! ANY => `victim.doll ! ANY => $nothing'';
  if (!$recycler:valid(doll))
    rpg:rpg_announce_all(by.location, "Nothing happens.");
  elseif (qual < 1)
    by:tell("The magickal energies needed to rot ", target.name, " are too much for you.");
    rpg:rpg_announce(by.location, "Nothing happens.");
    att:check_ins(stats / 5);
  elseif (target.time_of_death > time())
    rpg:say_action("Some powerful charm protects the %t from %n's spell magicks!", by, target);
  else
    rpg:say_action("The %t squirms with maggots as %n %<chants>.", by, target);
    target.time_of_death = target.time_of_death - random(qual * 9);
    if (is_player(victim))
      target:maybe_rot();
    else
      ((target.time_of_death + victim.gestate) < time()) && target:rot();
    endif
  endif
else
  by = att.character;
  tloc = target.location;
  cond = target.condition;
  "Is the target on a player? If so check for deflection, and check if it will actually affect the object.";
  if (valid(tdoll = rpg:get_doll(tloc)))
    "Check for backfire, iceshell, or biting_cloud on the targeted player... return without reducing the condition if backfire or iceshell, or increase the qual if biting_cloud.";
    effect = tdoll:magic_effects(att, tdoll, qual, skill);
    if ($list_utils:iassoc("bite", tdoll.schedule, 2))
      qual = qual + 15;
      rpg:rpg_announce_all(by.location, "The biting cloud around ", tloc.name, " shimmers, and some land on ", target.name, " to join in the frenzy.");
    elseif ($list_utils:iassoc("unbackfire", tdoll.schedule, 2))
      return rpg:rpg_announce_all(by.location, "The backfire surrounding ", tloc.name, " incinerates the insects as they appear.");
    elseif (tdoll in this.is_combat_effect.shelled)
      rpg:rpg_announce_all(by.location, "The iceshell around ", tloc.name, " prevents the insects from forming.");
      return;
      "Ok those should be stuck in the appropriate effobjs someday, but screw that for now.";
    elseif (typeof(effect) != NUM)
      return effect;
    elseif ((qual = qual - effect) <= 0)
      rpg:say_action("Nothing happens.");
      return;
    elseif (((tdoll != att) && (tdoll.magic_theory > 0)) && (rpg.skills.rot:resolve(tloc, -qual) > 0))
      rpg:rpg_announce_all(by.location, this:spell_deflection_msg(tloc, by));
      return;
    endif
    att.act = -120;
    att.aggressor = setadd(att.aggressor, tdoll.character);
    tdoll.aggressor = setadd(tdoll.aggressor, by);
    att:do_attack_notification(tdoll.character, skill);
    name = (tloc.name + "'s ") + target:title();
  else
    name = target:title();
  endif
  "Ok, let's rot the damned thing.";
  if ((qual < (cond / (1 + (qual / 10)))) || (!(breakable = typeof(cond) == NUM)))
    rpg:rpg_say_announce_all(by.location, "Tiny insects crawl all over ", name, ", trying to devour it, but don't seem to be doing any harm.");
  elseif ((random(100) <= (10 - (cond / 10))) && breakable)
    rpg:rpg_announce_all(by.location, "Tiny insects crawl all over ", name, " gnawing and biting into it.");
    target:("break")();
    flagevil = 1;
  elseif (breakable)
    dam = max(random(qual), qual / 3);
    rpg:rpg_announce_all(by.location, "Tiny insects crawl all over ", name, " gnawing and biting into it.");
    target:set_condition(cond - dam);
    if (target.condition < 1)
      target:("break")();
      flagevil = 1;
    else
      rpg:rpg_announce_all(by.location, "The tiny insects suddenly dissappear, but you can see that they've left their mark.");
    endif
  endif
endif
if ((flagevil && (by != victim)) && (random(50) == 5))
  att:mod_anima(1, rpg.death_db.anima_slip_msgs[2]);
endif
"Hydros (#106189) - Fri Nov  26, 2004 - Added flags in cases of corpse :break(), and if true, make the caster EVILLER. Mwaha.";
.

@verb #328:"dirge" this none this
@program #328:dirge
"A way to mourn the passing of friends, enemies, and that guy next door to whom you were indifferent but with the cool helmet you liked.";
(rpg = $local.rpg):secure();
{att, def, qual, skill, @extra} = args;
by = att.character;
if (qual < 25)
  rpg:say_action("The tune is melancholy, but hardly played with enough skill to inspire.", by);
else
  {players, dolls} = rpg:find_chars(by.location);
  "NPCs don't count as mourners.";
  players = rpg.user_utils.hydros:exclude_all_but_type(players, $player);
  dolls = {};
  for y in (players)
    rpg:s_i_f_i_n(0);
    if (valid(dolly = rpg:get_doll(y)))
      dolls = setadd(dolls, dolly);
    endif
  endfor
  mourners_mod = max(6, 16 - length(players));
  "Caster's skill can give slight modification to the spell's effect.";
  if (qual > 100)
    mourners_mod = mourners_mod - 1;
  elseif (qual > 150)
    mourners_mod = mourners_mod - 2;
  endif
  for x in (dolls)
    rpg:s_i_f_i_n(0);
    this:dirge_effect(x, mourners_mod, length(players), qual);
  endfor
endif
"dalton (#116601) - Sat Dec  7, 2002 - Added this from Hydros' beta-test magic_db.";
.

@verb #328:"undirge" this none this
@program #328:undirge
(rpg = $local.rpg):secure();
{doll} = args;
this.dirge_effects:remove(doll);
doll:erase_misc_note("Heard_Dirge");
rpg:playertell(doll.character, "The world seems dreary and more mundane as the dirge fades to distant memory.");
"dalton (#116601) - Sat Dec  7, 2002 - Added this from Hydros' beta-test magic_db.";
.

@verb #328:"dirge_effect" this none this
@program #328:dirge_effect
"Called from $local.rpg.magic_db:dirge().  Doll is the effected doll, mourners_mod[ifier] is based on how many mourners show up to hear the dirge, len is the number of aforementioned mourners, qual is the quality of the spell.";
(rpg = $local.rpg):secure();
{doll, mourners_mod, len, qual} = args;
"Folks who actually read in a text-based environment are A-OKAY.";
if ((!`doll.character.ascii_intolerant ! ANY => 0') && ("lyrics" in doll.rpg_options))
  mourners_mod = mourners_mod - 1;
endif
"Next line purposely left outside of next conditional.";
"If more people show up and dirge is played again, effect, but not duration, can be increased for all.";
"We're encouraging interaction, baby.";
doll:write_misc_note("Heard_Dirge", mourners_mod);
if (!(this.dirge_effects in doll:get_att("casting_effects")))
  this.dirge_effects:add(doll);
  doll:schedule(this, "undirge", (time() + (len * 200)) + (qual * 10), {});
  rpg:playertell(doll.character, "You swell with a newfound sense of life amidst great loss.");
else
  rpg:say_action("%N %<bows> %p head slightly as the dirge plays on.", doll.character);
endif
"dalton (#116601) - Sat Dec  7, 2002 - Added this from Hydros' beta-test magic_db.";
.

@verb #328:"fortify" this none this
@program #328:fortify
"This verb added for the sake of <focus>.fortify_cast_msg.";
(rpg = $local.rpg):secure();
rpg.magic_db:harden(@args);
.

@verb #328:"bladebarrier(old)" this none this
@program #328:bladebarrier(old)
"The LRPG Bladebarrier spell.  Requires a minimum of 2 blades to function, maximum 10.";
{doll, targ, qual, skill, @extra} = args;
(rpg = $local.rpg):secure();
who = doll.character;
dag_count = 0;
dag_list = {};
dag_skills = {rpg.skills.dagger_melee, rpg.skills.dagger_throw};
if ((doll.CAPrego < 25) || (qual < 99))
  "Weaker mages in this spell won't be able to animate as many daggers.";
  dag_max = (random(2) + random(3)) + 1;
  "The 'break' function below makes the loop its in act funny. This '+ 1' equation in the preceding line seems to fix that. <shrug>";
else
  dag_max = 10;
endif
if (this.bb_combat_effect in (v_e = doll.view_effects))
  "So players can actually practice this spell without having to go and get a zillion more daggers.";
  doll.last_attack = time() + rpg.gm_lag;
  return rpg:say_action("%N's blade barrier flashes and quickens.", who);
endif
for x in (who.contents)
  rpg:s_i_f_i_n();
  "  if (`x.skill ! ANY' in dag_skills)";
  if ($object_utils:isoneof(x, this.shortblades) && (x.owner == this.owner))
    dag_count = dag_count + 1;
    dag_list = setadd(dag_list, x);
    if (dag_count >= dag_max)
      break;
    endif
  endif
endfor
if (caller in doll.wielding)
  doll.wielding = setremove(doll.wielding, caller);
  caller:do_unwield(who);
  rpg:say_action(((("%N %<" + caller.osheathe) + "/") + caller.sheathe) + "> %p %t.", who, caller);
endif
if (!dag_count)
  rpg:playertell(who, "You haven't any proper blades to animate.");
elseif (dag_count < 2)
  rpg:playertell(who, "You'll need more daggers for that.");
elseif (!doll:hands_free())
  rpg:playertell(who, "You'll need at least one hand free for that.");
elseif (this.ww_combat_effect in v_e)
  rpg:playertell(who, "Your wall of wind would interfere with the barrier you wish to cast.");
elseif (this.bf_combat_effect in v_e)
  rpg:playertell(who, "Your aura of flame would interfere with the barrier you wish to cast.");
elseif (this.rm_armour_effect in doll.armour_effects)
  rpg:playertell(who, "Your reverser field would interfere with the barrier you wish to cast.");
else
  this:bladebarrier_success(who, doll, dag_count, dag_list);
endif
"Profane (#30788) - Tue Jan 9, 2001 - Added from Hydros (#106189)'s prototype.";
"THX (#105941) - Thu Jan 18, 2001 - Changed to allow weapon.skill in {dagger_melee, dagger_throw} since daggers switch back and forth depending on how they are handled.";
.

@verb #328:"bladebarrier_success(old)" this none this
@program #328:bladebarrier_success(old)
{who, doll, dag_count, dag_list} = args;
(rpg = $local.rpg):secure();
this.bb_combat_effect:add(doll);
doll:write_misc_note("Blade_Barrier", dag_count);
this:remove_blades_for_barrier(dag_list);
doll:schedule(this, "unbladebarrier", (time() + 3600) + (dag_count * 10), {});
rpg:say_action(("%N %<tosses> " + $string_utils:english_number(dag_count)) + " daggers into the air. They rest there, suspended, then dart back and begin slowly, ominously, to circle %o.", who);
rpg:say_action("The animated knives rapidly pick up speed until they form a flashing barrier of blades around %n.", who);
"Profane (#30788) - Tue Jan 9, 2001 - Added from Hydros (#106189)'s prototype.";
.

@verb #328:"remove_blades_for_barrier(old)" this none this
@program #328:remove_blades_for_barrier(old)
"Copied from Aquam Arcane (#87189):remove_blades_for_barrier by Hydros (#106189) Tue Jan  9 00:15:23 2001 PST";
(rpg = $local.rpg):secure();
{daggers} = args;
for d in (daggers)
  d:moveto(rpg.cemetery);
endfor
.

@verb #328:"bladebarrier" this none this
@program #328:bladebarrier
"The LRPG Bladebarrier spell.  Requires a minimum of 2 blades to function, maximum 10.";
{doll, targ, qual, skill, @extra} = args;
(rpg = $local.rpg):secure();
who = doll.character;
dag_list = {};
dag_skills = {rpg.skills.dagger_melee, rpg.skills.dagger_throw};
if ((doll.CAPrego < 25) || (qual < 99))
  "Weaker mages in this spell won't be able to animate as many daggers.";
  dag_max = (random(2) + random(3)) + 1;
  "The 'break' function below makes the loop its in act funny. This '+ 1' equation in the preceding line seems to fix that. <shrug>";
else
  dag_max = 10;
endif
if (this.bb_combat_effect in (v_e = doll.view_effects))
  "So players can actually practice this spell without having to go and get a zillion more daggers.";
  doll.last_attack = time() + rpg.gm_lag;
  return rpg:say_action("%N's blade barrier flashes and quickens.", who);
endif
for x in (who.contents)
  rpg:s_i_f_i_n();
  "  if (`x.skill ! ANY' in dag_skills)";
  if ($object_utils:isoneof(x, this.shortblades) && (x.owner == this.owner))
    dag_list = setadd(dag_list, x);
    if ((dag_count = length(dag_list)) >= dag_max)
      break;
    endif
  endif
endfor
if (caller in doll.wielding)
  doll.wielding = setremove(doll.wielding, caller);
  caller:do_unwield(who);
  rpg:say_action(((("%N %<" + caller.osheathe) + "/") + caller.sheathe) + "> %p %t.", who, caller);
endif
if (!(dag_count = length(dag_list)))
  rpg:playertell(who, "You haven't any proper blades to animate.");
elseif (dag_count < 2)
  rpg:playertell(who, "You'll need more daggers for that.");
elseif (!doll:hands_free())
  rpg:playertell(who, "You'll need at least one hand free for that.");
elseif (this.ww_combat_effect in v_e)
  rpg:playertell(who, "Your wall of wind would interfere with the barrier you wish to cast.");
elseif (this.bf_combat_effect in v_e)
  rpg:playertell(who, "Your aura of flame would interfere with the barrier you wish to cast.");
elseif (this.rm_armour_effect in doll.armour_effects)
  rpg:playertell(who, "Your reverser field would interfere with the barrier you wish to cast.");
else
  this:bladebarrier_success(who, doll, dag_list);
endif
"Profane (#30788) - Tue Jan 9, 2001 - Added from Hydros (#106189)'s prototype.";
"THX (#105941) - Thu Jan 18, 2001 - Changed to allow weapon.skill in {dagger_melee, dagger_throw} since daggers switch back and forth depending on how they are handled.";
"Hydros (#106189) - Tue Apr 5, 2005 - A complete overhaul to allow the daggers to come back intact.  Every related verb revised. (And dalton made it use .shortblades, but never left a comment, *tsk*.)";
.

@verb #328:"bladebarrier_success" this none this
@program #328:bladebarrier_success
{who, doll, dag_list} = args;
(rpg = $local.rpg):secure();
dag_count = length(dag_list);
this.bb_combat_effect:add(doll);
doll:write_misc_note("BB_Daggers", dag_list);
this:remove_blades_for_barrier(dag_list);
doll:schedule(this, "unbladebarrier", (time() + 3600) + (dag_count * 10), {});
rpg:say_action(("%N %<tosses> " + $string_utils:english_number(dag_count)) + " daggers into the air. They rest there, suspended, then dart back and begin slowly, ominously, to circle %o.", who);
rpg:say_action("The animated knives rapidly pick up speed until they form a flashing barrier of blades around %n.", who);
"Profane (#30788) - Tue Jan 9, 2001 - Added from Hydros (#106189)'s prototype.";
"Hydros (#106189) - Tue Apr 5, 2005 - We now move the dagger objects to a safe spot until the spell is dropped, and whatever remains is returned to the caster.";
.

@verb #328:"remove_blades_for_barrier" this none this
@program #328:remove_blades_for_barrier
"Copied from Aquam Arcane (#87189):remove_blades_for_barrier by Hydros (#106189) Tue Jan  9 00:15:23 2001 PST";
(rpg = $local.rpg):secure();
{daggers} = args;
for d in (daggers)
  d:moveto(rpg.magic_db.bb_warehouse);
endfor
.

@verb #328:"unbladebarrier(old)" this none this
@program #328:unbladebarrier(old)
"Copied from Aquam Arcane (#87189):unbladebarrier by Hydros (#106189) Tue Jan  9 00:15:12 2001 PST";
"Drop the barrier of blades, under the assumption that the daggers will not simply fall to the ground, but have taken enough whirling magickal abuse to be destroyed.";
(rpg = $local.rpg):secure();
{doll, @extra} = args;
this.bb_combat_effect:remove(doll);
doll:erase_misc_note("Blade_Barrier");
if (i = $list_utils:iassoc("unbladebarrier", doll:get_att("schedule"), 2))
  doll:cancel(doll:get_att("schedule")[i]);
endif
rpg:say_action("%N's barrier of blades is dropped, leaving nothing but shattered remains.", doll.character);
.

@verb #328:"unbladebarrier" this none this
@program #328:unbladebarrier
"Copied from Aquam Arcane (#87189):unbladebarrier by Hydros (#106189) Tue Jan  9 00:15:12 2001 PST";
"Drop the barrier of blades, under the assumption that the daggers will not simply fall to the ground, but have taken enough whirling magickal abuse to be destroyed.";
(rpg = $local.rpg):secure();
{doll, @extra} = args;
resting_place = (doll.bladebarrier > 25) ? doll.character | doll.character.location;
drop_msg = (doll.bladebarrier > 25) ? "%N's barrier of blades slows to a standstill, and each glinting blade comes to rest in %p hands." | "%N's barrier of blades is dropped, clattering to the ground in a pile of metal.";
this.bb_combat_effect:remove(doll);
daggers = doll:read_misc_note("BB_Daggers");
for x in (daggers)
  x:moveto(resting_place);
endfor
doll:erase_misc_note("BB_Daggers");
if (i = $list_utils:iassoc("unbladebarrier", doll:get_att("schedule"), 2))
  doll:cancel(doll:get_att("schedule")[i]);
endif
rpg:say_action(drop_msg, doll.character);
"Hydros (#106189) - Tue Apr 5, 2005 - Daggers now return to the caster's location, or to the caster if eir skill > 25.";
.

@verb #328:"daggerstorm(old)" this none this
@program #328:daggerstorm(old)
"Ripped almost entirely from #328:Hawthorn(). Dodgable, exit-crossing spell attacks are good things.";
{doll, tdoll, qual, skill} = args;
(rpg = $local.rpg):secure();
{by, targ, mod, odb, storm} = {doll.character, tdoll.character, 0, rpg.object_db, this.(verb)};
loc = by.location;
qual = (qual * 10) / tdoll.size;
if (!(daggers = doll:read_misc_note("Blade_Barrier")))
  return rpg:playertell(by, "You don't have a sphere of daggers from which to unleash a daggerstorm.");
elseif (qual > 0)
  if (loc != targ.location)
    exits = loc:exits();
    what = where = {};
    for x in (exits)
      while (`valid(x.succ_exit) ! ANY')
        x = x.succ_exit;
      endwhile
      if (odb:is_missile_exit(x))
        d = x.dest.contents;
        what = {@what, @d};
        where = {@where, @$list_utils:make(length(d), x)};
      endif
    endfor
    mod = (targ in what) ? where[targ in what].cover | 999;
  endif
  {pen, dam, part} = {storm.pen, storm.dam, {tdoll.body_areas}};
  this.sc_recovery_effect:remove(doll);
  storm:deliver_payload(doll, targ, daggers, part[random($)], 0);
  this:unbladebarrier(doll);
  this.sc_recovery_effect:add(doll);
else
  return rpg:rpg_announce_all(loc, targ.name, " seems unaffected by the spell.");
endif
"Profane (#30788) - Tue Jan 9, 2001 - Added from prototype by Hydros (#106189).";
.

@verb #328:"daggerstorm" this none this
@program #328:daggerstorm
"Ripped almost entirely from #328:Hawthorn(). Dodgable, exit-crossing spell attacks are good things.";
{doll, tdoll, qual, skill} = args;
(rpg = $local.rpg):secure();
{by, targ, mod, odb, storm} = {doll.character, tdoll.character, 0, rpg.object_db, this.(verb)};
loc = by.location;
qual = (qual * 10) / tdoll.size;
flag_fired = 0;
if ((daggers = doll:read_misc_note("BB_Daggers")) == E_PROPNF)
  return rpg:playertell(by, "You don't have a sphere of daggers from which to unleash a daggerstorm.");
elseif (qual > 0)
  if (loc != targ.location)
    exits = loc:exits();
    what = where = {};
    for x in (exits)
      while (`valid(x.succ_exit) ! ANY')
        x = x.succ_exit;
      endwhile
      if (odb:is_missile_exit(x))
        d = x.dest.contents;
        what = {@what, @d};
        where = {@where, @$list_utils:make(length(d), x)};
      endif
    endfor
    mod = (targ in what) ? where[targ in what].cover | 999;
  endif
  {pen, dam, part} = {storm.pen, storm.dam, {tdoll.body_areas}};
  this.sc_recovery_effect:remove(doll);
  storm:deliver_payload(doll, targ, length(daggers), part[random($)], 0);
  flag_fired = 1;
else
  return rpg:rpg_announce_all(loc, targ.name, " seems unaffected by the spell.");
  flag_fired = 1;
endif
if (flag_fired)
  this.bb_combat_effect:remove(doll);
  doll:erase_misc_note("BB_Daggers");
  for x in (daggers)
    rpg:s_i_n();
    x:moveto(rpg.cemetery);
  endfor
  if (i = $list_utils:iassoc("unbladebarrier", doll:get_att("schedule"), 2))
    doll:cancel(doll:get_att("schedule")[i]);
  endif
endif
"Profane (#30788) - Tue Jan 9, 2001 - Added from prototype by Hydros (#106189).";
"Hydros (#106189) - Tue Apr 5, 2005 - Changed to handle daggers now being returnable to caster. More upgrades coming.";
.

@verb #328:"barkskin_check_for_unacceptable_armour" this none this
@program #328:barkskin_check_for_unacceptable_armour
"Except certain armour from the not-allowed-to-be-worn-with-barkskin list.  Keep track of the exceptions, in case we ever feel like telling a player what they're wearing that -is- appropriate.";
doll = args[1];
rpg = $local.rpg;
if ((wearing = doll.wearing) == {})
  return 0;
else
  excepted = {};
  amulets = rpg.object_db.obj_amulet;
  allowed = {@this.allowed_barkskin_armour, @amulets};
  for x in (wearing)
    if ($object_utils:isoneof(x, allowed))
      wearing = setremove(wearing, x);
      excepted = setadd(excepted, x);
    endif
  endfor
  if (length(wearing))
    return wearing;
  else
    return 0;
  endif
endif
"Hydros (#106189) - Fri May 27, 2005 - Added, from a suggestion by Vorsyth (#121070).";
.

@verb #328:"gen_zap(NOV2005old)" this none this
@program #328:gen_zap(NOV2005old)
":gen_zap(OBJ caster-doll, OBJ target-doll, INT quality, OBJ skillobj, STR spell_name, FLAG dodgeable, [OBJ staff, [FLAG natural_attack]])";
"Original verb taken from #328:firebolt, and modded so 1 verb c'n handle *all* the spells that jus do damage.";
"This verb'll handle ice-bolts, magic missiles, an jus 'bout anythin a GM c'n think of.";
"The optional argument, staff, lets the GM use objs that aren't kids of the spell db (#328)";
{att, targ_doll, qual, skillobj, spell_name, dodgeable, ?staff = this, ?natural_attack = 0} = args;
(rpg = $local.rpg):secure();
if (!rpg:is_alive(targ_doll))
  return;
endif
by = att.character;
target = targ_doll.character;
area = target.location;
dodge = rpg.skills.dodge;
magic = targ_doll.magic_theory;
spell_msgs = spell_name + "_msgs";
spell_power = "power_" + spell_name;
spell_pen = "pen_" + spell_name;
spell_dodge = "dodge_" + spell_name;
spell_special = "special_" + spell_name;
stu = $string_utils;
att:do_attack_notification(target, skillobj);
is_player(target) || target:set_last_attacker(by);
targ_doll.aggressor = setadd(targ_doll.aggressor, by);
att.aggressor = setadd(att.aggressor, target);
{resist_msg, deflect_msg, dodge_msg, blast_msg, @efx_messages} = this:get_messages(staff, by, target, spell_msgs);
no_imp = (by == target) ? 1 | 0;
if ((caller != this) && (typeof(effect = att:casting_effects(att, targ_doll, qual, skillobj)) != NUM))
  return effect;
elseif ((!natural_attack) && (typeof(effect = targ_doll:magic_effects(att, targ_doll, qual, skillobj)) != NUM))
  return effect;
elseif (is_player(target) && (!(target in connected_players())))
  by:tell("Hold your magic! By the edict of the Great Wizard, bystanders must not be molested!");
elseif ((!natural_attack) && ((qual = qual - effect) <= 0))
  rpg:say_action(resist_msg, target, dobj = by);
elseif ((((!natural_attack) && (targ_doll != att)) && (magic > 0)) && (skillobj:resolve(target, -qual, no_imp) > 0))
  rpg:say_action(deflect_msg, target, dobj = by);
elseif ((dodgeable && (targ_doll != att)) && (dodge:resolve(target, staff:(spell_dodge)(att, targ_doll, qual) - targ_doll.slowness, no_imp) > qual))
  rpg:say_action(dodge_msg, target, dobj = by);
else
  rpg:say_action(blast_msg, target, dobj = by);
  power = staff:(spell_power)(qual, magic, targ_doll);
  pen = staff:(spell_pen)(qual, magic);
  zap_result = targ_doll:hit_result(power, pen, by, skillobj);
  if (zap_result)
    this:do_zap(zap_result, efx_messages, by, target, staff);
    "Ok, new bit for spells doing funky special damage.  Replaces the call to :fire_effect.";
    `staff:(spell_special)(att, targ_doll, qual, zap_result) ! E_VERBNF';
  else
    "Only happens in the case of bad perms or .end == 0";
    rpg:say_action(resist_msg, target, dobj = by);
  endif
endif
"Profane 27-JUN-96 848EDT -- Added check for disconnected players.";
"Mooshie (#106469) - Sun Feb  1, 1998 - Did some scattering and speeding up.";
"Irin - Added hook for special effect if the spell goes through.  Fire damage, etc.  Also added dodge difficulty and possibility of a non-magickal attack (undeflectable && no magic resistance.)";
"Hannibal - Added casting_effects check for the casting doll.";
"Hannibal 2/9/2000 - Added call to :set_last_attacker if the target is an RPG monster.";
"THX (#105941) - Wed Aug 30, 2000 - Changed to two incidences of resist_msg and single incidences of deflect_msg,dodge_msg, and blast_msg to use rpg:say_action()  This required changes to the messages in the spell_msgs properties as well (reversing %N and %D since the _target_ is performing the actions here).";
"THX (#105941) - Tue Mar 20, 2001 - Only call casting_effects here if the caller != this.  Otherwise, focus:spell() has already called it.";
.

@verb #328:"(NOV2005OLD)power_*" this none this
@program #328:(NOV2005OLD)power_
"Copied from Enhanced Generic Staff (#27735):power_* by StarDancer (#82267) Thu May 30 20:55:31 1996 PDT";
result = $no_one:eval(strsub(strsub(this.(verb), "qual", tostr(args[1])), "magic", tostr(args[2])));
return result[2];
.

"***finished***