A dump of #74 (Doll)

@create $root_class named generic voodoo doll:PC,generic,voodoo,doll
"#74.("storm") => E_PERM (Permission denied)
"#74.("total") => E_PERM (Permission denied)
"#74.("pick") => E_PERM (Permission denied)
"#74.("machete") => E_PERM (Permission denied)
"#74.("dagger_melee") => E_PERM (Permission denied)
"#74.("dagger_throw") => E_PERM (Permission denied)
"#74.("weight") => E_PERM (Permission denied)
"#74.("trick") => E_PERM (Permission denied)
"#74.("pray") => E_PERM (Permission denied)
"#74.("persuade") => E_PERM (Permission denied)
"#74.("convince") => E_PERM (Permission denied)
"#74.("star") => E_PERM (Permission denied)
"#74.("search") => E_PERM (Permission denied)
"#74.("notice") => E_PERM (Permission denied)
"#74.("observe") => E_PERM (Permission denied)
"#74.("javelin_melee") => E_PERM (Permission denied)
"#74.("javelin_throw") => E_PERM (Permission denied)
"#74.("longbow") => E_PERM (Permission denied)
"#74.("drawbow") => E_PERM (Permission denied)
"#74.("crossbow") => E_PERM (Permission denied)
"#74.("longarms") => E_PERM (Permission denied)
"#74.("missiles") => E_PERM (Permission denied)
"#74.("sai") => E_PERM (Permission denied)
"#74.("heal") => E_PERM (Permission denied)
"#74.("heat") => E_PERM (Permission denied)
"#74.("schedule") => E_PERM (Permission denied)
"#74.("baxe2") => E_PERM (Permission denied)
"#74.("baxe1") => E_PERM (Permission denied)
"#74.("baxe") => E_PERM (Permission denied)
"#74.("forcewall") => E_PERM (Permission denied)
"#74.("free_will") => E_PERM (Permission denied)
"#74.("slow") => E_PERM (Permission denied)
"#74.("knives") => E_PERM (Permission denied)
"#74.("tclimb") => E_PERM (Permission denied)
"#74.("kat2") => E_PERM (Permission denied)
"#74.("kat1") => E_PERM (Permission denied)
"#74.("teeth") => E_PERM (Permission denied)
"#74.("tenta") => E_PERM (Permission denied)
"#74.("claw") => E_PERM (Permission denied)
"#74.("natweap") => E_PERM (Permission denied)
"#74.("scim") => E_PERM (Permission denied)
"#74.("wclim") => E_PERM (Permission denied)
@prop #74."last_action" 0 r
"#74.("throw") => E_PERM (Permission denied)
"#74.("furn") => E_PERM (Permission denied)
"#74.("paralyzed") => E_PERM (Permission denied)
"#74.("scyth") => E_PERM (Permission denied)
"#74.("swim") => E_PERM (Permission denied)
"#74.("econf") => E_PERM (Permission denied)
"#74.("gadget") => E_PERM (Permission denied)
"#74.("sanity") => E_PERM (Permission denied)
"#74.("ins") => E_PERM (Permission denied)
@prop #74."wearing" {} r
@prop #74."wielding" {} r
"#74.("climb") => E_PERM (Permission denied)
"#74.("staff") => E_PERM (Permission denied)
"#74.("firebolt") => E_PERM (Permission denied)
"#74.("kites") => E_PERM (Permission denied)
"#74.("shield") => E_PERM (Permission denied)
"#74.("kat") => E_PERM (Permission denied)
"#74.("rap") => E_PERM (Permission denied)
"#74.("size") => E_PERM (Permission denied)
"#74.("tclub") => E_PERM (Permission denied)
"#74.("club") => E_PERM (Permission denied)
@prop #74."character" 0 r
"#74.("shock") => E_PERM (Permission denied)
"#74.("dodge") => E_PERM (Permission denied)
"#74.("slowness") => E_PERM (Permission denied)
"#74.("act") => E_PERM (Permission denied)
@prop #74."aggressor" {} r
"#74.("brswd") => E_PERM (Permission denied)
"#74.("melee") => E_PERM (Permission denied)
"#74.("pot") => E_PERM (Permission denied)
"#74.("inj") => E_PERM (Permission denied)
"#74.("app") => E_PERM (Permission denied)
"#74.("pcn") => E_PERM (Permission denied)
"#74.("wil") => E_PERM (Permission denied)
"#74.("emp") => E_PERM (Permission denied)
"#74.("int") => E_PERM (Permission denied)
"#74.("agl") => E_PERM (Permission denied)
"#74.("dex") => E_PERM (Permission denied)
"#74.("end") => E_PERM (Permission denied)
"#74.("str") => E_PERM (Permission denied)
"#74.("lockpick") => E_PERM (Permission denied)
"#74.("last_attack") => E_PERM (Permission denied)
@prop #74."hands" 2 r
"#74.("recall") => E_PERM (Permission denied)
"#74.("pacify") => E_PERM (Permission denied)
"#74.("fat") => E_PERM (Permission denied)
"#74.("stamina") => E_PERM (Permission denied)
@prop #74."body_areas" {} r
;;#74.("body_areas") = {#39284, #60746, #61688, #61605, #9655, #51172, #75697, #38715, #52374, #47817, #33584, #54898}
"#74.("combat_effects") => E_PERM (Permission denied)
"#74.("death_effects") => E_PERM (Permission denied)
"#74.("longsword") => E_PERM (Permission denied)
"#74.("misc_notes") => E_PERM (Permission denied)
"#74.("thieving") => E_PERM (Permission denied)
@prop #74."natural_weapons" {} r
;;#74.("natural_weapons") = {#9130}
@prop #74."claimed_objects" {} r
@prop #74."max_claims" 21 r
"#74.("brawl") => E_PERM (Permission denied)
"#74.("moofu") => E_PERM (Permission denied)
"#74.("magic_effects") => E_PERM (Permission denied)
"#74.("magic_theory") => E_PERM (Permission denied)
"#74.("creo") => E_PERM (Permission denied)
"#74.("intellego") => E_PERM (Permission denied)
"#74.("muto") => E_PERM (Permission denied)
"#74.("perdo") => E_PERM (Permission denied)
"#74.("rego") => E_PERM (Permission denied)
"#74.("corporem") => E_PERM (Permission denied)
"#74.("ignem") => E_PERM (Permission denied)
"#74.("vim") => E_PERM (Permission denied)
"#74.("animalem") => E_PERM (Permission denied)
"#74.("mentem") => E_PERM (Permission denied)
"#74.("aquam") => E_PERM (Permission denied)
"#74.("herbam") => E_PERM (Permission denied)
"#74.("imagonem") => E_PERM (Permission denied)
"#74.("terram") => E_PERM (Permission denied)
"#74.("depress") => E_PERM (Permission denied)
"#74.("calm_winds") => E_PERM (Permission denied)
"#74.("shatter") => E_PERM (Permission denied)
"#74.("rounds") => E_PERM (Permission denied)
"#74.("windwall") => E_PERM (Permission denied)
"#74.("stinkcloud") => E_PERM (Permission denied)
"#74.("predict") => E_PERM (Permission denied)
"#74.("whip") => E_PERM (Permission denied)
"#74.("harden") => E_PERM (Permission denied)
"#74.("enchant") => E_PERM (Permission denied)
"#74.("biting_cloud") => E_PERM (Permission denied)
"#74.("barkskin") => E_PERM (Permission denied)
"#74.("hail") => E_PERM (Permission denied)
"#74.("lifelink") => E_PERM (Permission denied)
"#74.("stealskill") => E_PERM (Permission denied)
"#74.("mindprobe") => E_PERM (Permission denied)
"#74.("waterwhip") => E_PERM (Permission denied)
"#74.("iceshell") => E_PERM (Permission denied)
"#74.("dispel") => E_PERM (Permission denied)
"#74.("claim") => E_PERM (Permission denied)
"#74.("auram") => E_PERM (Permission denied)
"#74.("shocking_grasp") => E_PERM (Permission denied)
"#74.("unarmed") => E_PERM (Permission denied)
"#74.("greatsword") => E_PERM (Permission denied)
"#74.("summon") => E_PERM (Permission denied)
"#74.("flail") => E_PERM (Permission denied)
"#74.("hafted") => E_PERM (Permission denied)
"#74.("sword") => E_PERM (Permission denied)
"#74.("weap1") => E_PERM (Permission denied)
"#74.("weap2") => E_PERM (Permission denied)
"#74.("flex") => E_PERM (Permission denied)
"#74.("proj") => E_PERM (Permission denied)
"#74.("current_armours") => E_PERM (Permission denied)
"#74.("CAPweap2") => E_PERM (Permission denied)
"#74.("CAPweap1") => E_PERM (Permission denied)
"#74.("CAPhafted") => E_PERM (Permission denied)
"#74.("CAPknives") => E_PERM (Permission denied)
"#74.("CAPflex") => E_PERM (Permission denied)
"#74.("CAPunarmed") => E_PERM (Permission denied)
"#74.("CAPmelee") => E_PERM (Permission denied)
"#74.("CAPkat") => E_PERM (Permission denied)
"#74.("CAPbaxe") => E_PERM (Permission denied)
"#74.("CAPmissiles") => E_PERM (Permission denied)
"#74.("CAPproj") => E_PERM (Permission denied)
"#74.("CAPcreo") => E_PERM (Permission denied)
"#74.("CAPintellego") => E_PERM (Permission denied)
"#74.("CAPmuto") => E_PERM (Permission denied)
"#74.("CAPrego") => E_PERM (Permission denied)
"#74.("CAPperdo") => E_PERM (Permission denied)
"#74.("CAPmagic_theory") => E_PERM (Permission denied)
"#74.("CAPcorporem") => E_PERM (Permission denied)
"#74.("CAPignem") => E_PERM (Permission denied)
"#74.("CAPvim") => E_PERM (Permission denied)
"#74.("CAPanimalem") => E_PERM (Permission denied)
"#74.("CAPmentem") => E_PERM (Permission denied)
"#74.("CAPaquam") => E_PERM (Permission denied)
"#74.("CAPherbam") => E_PERM (Permission denied)
"#74.("CAPimagonem") => E_PERM (Permission denied)
"#74.("CAPterram") => E_PERM (Permission denied)
"#74.("CAPauram") => E_PERM (Permission denied)
"#74.("CAPsword") => E_PERM (Permission denied)
"#74.("CAPthrow") => E_PERM (Permission denied)
"#74.("CAPshield") => E_PERM (Permission denied)
"#74.("CAPnatweap") => E_PERM (Permission denied)
"#74.("know") => E_PERM (Permission denied)
"#74.("athl") => E_PERM (Permission denied)
"#74.("CAPathl") => E_PERM (Permission denied)
"#74.("CAPknow") => E_PERM (Permission denied)
"#74.("conjure") => E_PERM (Permission denied)
"#74.("misc_effects") => E_PERM (Permission denied)
"#74.("shortbow") => E_PERM (Permission denied)
"#74.("ravenfear") => E_PERM (Permission denied)
"#74.("holdfoe") => E_PERM (Permission denied)
"#74.("charmfoe") => E_PERM (Permission denied)
@prop #74."lights" {} r
"#74.("bloom") => E_PERM (Permission denied)
"#74.("haste") => E_PERM (Permission denied)
"#74.("acidrain") => E_PERM (Permission denied)
"#74.("animate") => E_PERM (Permission denied)
"#74.("wound") => E_PERM (Permission denied)
"#74.("drain") => E_PERM (Permission denied)
@prop #74."deathless" 0 rc
"#74.("tangle") => E_PERM (Permission denied)
"#74.("appraise") => E_PERM (Permission denied)
"#74.("glyph") => E_PERM (Permission denied)
"#74.("rune") => E_PERM (Permission denied)
"#74.("teleport") => E_PERM (Permission denied)
@prop #74."born" 0 r
"#74.("spellsing") => E_PERM (Permission denied)
@prop #74."rpg_options" {} r
;;#74.("rpg_options") = {"lyrics", "APB", "auction"}
@prop #74."light" 0 r
@prop #74."room_light" 0 r
@prop #74."slowness_mod" 1.0 r
"#74.("att_schedule") => E_PERM (Permission denied)
@prop #74."armour_effects" {} r
"#74.("retrieve") => E_PERM (Permission denied)
@prop #74."light_values" {} r
"#74.("chemical_effects") => E_PERM (Permission denied)
"#74.("freeze_effects") => E_PERM (Permission denied)
"#74.("view_effects") => E_PERM (Permission denied)
"#74.("chill") => E_PERM (Permission denied)
"#74.("magic_fist") => E_PERM (Permission denied)
"#74.("reverser") => E_PERM (Permission denied)
"#74.("detect") => E_PERM (Permission denied)
"#74.("viper_bite") => E_PERM (Permission denied)
"#74.("casting_effects") => E_PERM (Permission denied)
"#74.("backfire") => E_PERM (Permission denied)
"#74.("lifelinked") => E_PERM (Permission denied)
"#74.("connect_effects") => E_PERM (Permission denied)
"#74.("rot") => E_PERM (Permission denied)
"#74.("mirrors") => E_PERM (Permission denied)
"#74.("hawthorn") => E_PERM (Permission denied)
"#74.("RESISTanimalem") => E_PERM (Permission denied)
"#74.("RESISTaquam") => E_PERM (Permission denied)
"#74.("RESISTauram") => E_PERM (Permission denied)
"#74.("RESISTcorporem") => E_PERM (Permission denied)
"#74.("RESISTherbam") => E_PERM (Permission denied)
"#74.("RESISTignem") => E_PERM (Permission denied)
"#74.("RESISTimagonem") => E_PERM (Permission denied)
"#74.("RESISTmentem") => E_PERM (Permission denied)
"#74.("RESISTterram") => E_PERM (Permission denied)
"#74.("RESISTvim") => E_PERM (Permission denied)
"#74.("beguile") => E_PERM (Permission denied)
"#74.("lifeseconds") => E_PERM (Permission denied)
@prop #74."mounted" #-1 r
"#74.("ride") => E_PERM (Permission denied)
"#74.("bladebarrier") => E_PERM (Permission denied)
"#74.("CAPride") => E_PERM (Permission denied)
"#74.("CAPthieving") => E_PERM (Permission denied)
"#74.("parch") => E_PERM (Permission denied)
@prop #74."duelee" #-1 r
@prop #74."illuminance" 0 r
@prop #74."resurrected" 0 r
"#74.("move_effects") => E_PERM (Permission denied)
"#74.("anima") => E_PERM (Permission denied)
"#74.("fishing") => E_PERM (Permission denied)
@prop #74."valid_pkp_targets" {} rc
@prop #74."INvalid_pkp_targets" {} rc
"#74.("key") => E_PERM (Permission denied)
;;#74.("aliases") = {"PC", "generic", "voodoo", "doll"}
;;#74.("description") = "A little humanoid doll hung from a peg on the wall.  Every now and then it twitches feebly."
;;#74.("object_size") = {155877, 1141286558}

@verb #74:"get" this none none rxd
@program #74:get
player:tell("As you approach the voodoo doll, a little hooded man shouts at you to leave it alone and starts beating you with a cane stick until you do.");
.

@verb #74:"attack" this none this
@program #74:attack
":attack(OBJ target, LIST weapons[, NUM modifier, OBJ area, NUM no_invite])";
{target, weapons, ?bmods = 0, ?barea = 0, ?no_invite = 0} = args;
(rpg = $local.rpg):secure();
location = (by = this.character).location;
doll = rpg:get_doll(target);
if (!this:legal_target(target, doll, caller))
  return;
elseif (typeof(weapons) == LIST)
  weapon = weapons[1];
else
  weapon = weapons;
  weapons = {weapons};
endif
barea = (typeof(barea) == OBJ) ? barea | doll:random_hit_location();
if (typeof(effect = this:combat_effects(target, weapon)) != NUM)
  return effect;
endif
bmods = (bmods + effect) - this:encumbrance();
try
  if ((!valid(weapon)) || (typeof(use = weapon:used(by, target)) != NUM))
    "If :used returns a non-numeric value, the weapon is unusable.";
    return;
  elseif (weapon.owner in rpg.gms)
  elseif (weapon.owner in rpg.ex_gms)
    "Give a break until we can get it @disowned.";
  elseif (`verb_info(weapon, "0") ! E_VERBNF')
    "OK, only penalize them if it has verbs defined..";
    rpg:playertell(by, "You feel foolish!");
    this.int = max(this.int - 1, 1);
    this.pot = 0;
    return;
  endif
except (E_TYPE)
  raise(E_TYPE, ((("Error: " + by.name) + " is passing invalid weapons list (") + $string_utils:from_list(weapons, ", ")) + ")");
except (E_VERBNF)
  raise(E_VERBNF, ("Error:  \"Weapon\" " + tostr(weapon)) + " does not define a :used verb.");
endtry
this:do_attack_notification(target, weapon);
is_player(target) || target:set_last_attacker(by);
weapon:announce_swing(by, target);
weapon:track_usage(1);
weap_dam = weapon:dam(by, target);
attack_bonus = weapon:attack(by, target);
mods = (bmods + use) - (length(weapons) * 10);
str_bonus = (weapon:max_str_bonus(by, target) * this.str) / 25;
squeeze = `max(0, weapon:length(by, target) - location.space) ! ANY => 0';
mods = mods - (3 * squeeze);
damage = ((2 * weap_dam) + str_bonus) - squeeze;
if (((squeeze > 3) && is_player(by)) && (random(25) == 1))
  rpg:playertell(by, "The room is a little small to ", weapon.swing, " your ", weapon.name, " properly.");
endif
"Damage will never be greater than 3 * base.";
"damage = min(damage, 3 * weap_dam)";
"'y' is encumbrance penalty of the weapon.  Wielding a heavy weapon with more than one hand reduces the total penalty.";
"A weak person wielding a heavy weapon will be greatly penalized.";
y = str_bonus - ((3 * (weap_enc = weapon:attack_encumbrance(this))) / (1 + weapon:hands(by, target)));
"y = str_bonus - 3 * (weap_enc = weapon:encumbrance(this)) / 5 / (1 + weapon:hands(by, target));";
if (y < 0)
  mods = mods + (5 * y);
  damage = damage + (y / 2);
endif
foes = 0;
for foe in (this.aggressor)
  if (foe.location == location)
    foes = foes + 1;
  endif
endfor
mods = mods - (max(0, foes - 1) * 15);
connects = ((qual = weapon:skill(by, target):resolve(by, mods)) + attack_bonus) > -25;
if (connects)
  doll:respond(by, qual, damage, weapon, barea);
  this.connect_effects && this:connect_effects(by, target, barea, weapon, qual, 0, 99, 0);
elseif (!weapon:critical_miss(by, target, qual))
  "No critical miss.";
  weapon:announce_miss(by, target, qual);
endif
this:check_fatigue(weap_enc);
weapons = listdelete(weapons, 1);
"If we attack and die for some reason during the attack, no invitation will take place.  Everybody stands around shocked.  If the target dies, everything is fine.";
if ((by in rpg.pcs) && (is_player(by) || by.alive))
  this.aggressor = setadd(this.aggressor, target);
  if (weapons)
    this.act = this.act - max(1, weapon:slowness(by, target));
    this:attack(target, weapons, bmods - 10, barea, 1);
  endif
  if (valid(doll) && (parent(doll) != $garbage))
    doll.aggressor = setadd(doll.aggressor, by);
  endif
  no_invite || this:invite(this.slowness, {1, by, target, weapon});
endif
"Quinn 05-JAN-93: Use verbs for referencing weapon properties.";
"Quinn 28-FEB-93: Won't fork notification if a fork is already pending.";
"Quinn 11-AUG-93: Big re-write.  Created :legal_target and :do_attack_notifica ion to do the mundane chore of checking and notifying targets; cut down the unwieldly verb size; added some minor comments.";
"Quinn 11-AUG-93: Send weapon to :respond.";
"Quinn 22-OCT-93: Added combat_effect.";
"Quinn 19-NOV-93: Added body area support, :critical_miss.  New args to :Respond.";
"Ogwul 01-JAN-94: Allowed :attack to be called without adding the target to the aggressor list and without :invite via the no_invite parameter.";
"Profane 29-DEC-95 1451EST -- Added windwall check thing.";
"Irin Sun Nov  3 10:49:52 1996 PST -- Bwahaha, attack penalty if fighting more than one foe.";
"Irin Thursday, January 30 -- Rewrite of the :invite section to correspond to changes.  Don't use rpg:trusted.";
"Profane 31-MAR-97 1341PST -- s/valid/$recycler:valid/85";
"Profane 15-APR-97 1518PST -- TRY statement for weapon:used";
"THX 18-May-1997  -- Fixed so that foe.location checks against by.location rather than this.location (one's doll is never in the same place as one's attackers).  Added a weeny fatigue check after the (connects) conditional.";
"Profane 3-OCT-97 -- added check for non-gm owned weapons.";
"THX (#105941) - Wed Apr  8, 1998 - Removed this.paralyzed and entangled checks.  this:legal_target now does the checks via this:cannot_move().  subst this.character -> by from line 4 onwards and changed the \"I'm still alive.\" check before the invite call since critters always return a valid doll.";
"Profane (#30788) - Sun May  2, 1999 - add inelegant backfire check circa 90-92";
"Profane (#30788) - Sun May 23, 1999 - windwall moved off into armour_effect";
"THX (#105941) - Mon Feb  5, 2001 - Added a usage tracker call (argument == 1) for swings/attacks.";
"Hydros (#106189) - Sat Jul 23, 2005 - Removed the `2 *' from line 48. Did you know that damage is (was) doubled THREE TIMES in combat code?";
"Hydros (#106189) - Fri Jul 29, 2005 - Change of plans.  The system is built around this equation, so we're changing armour to be more effective instead.";
"dalton (#116601) - Fri Oct  7, 2005 - Modified lines 44 & 69 to reduce effect of weapon's attack stat.";
.

@verb #74:"invite" this none this
@program #74:invite
":invite(NUM slowness[LIST {NUM respond, OBJ attacker, OBJ target, OBJ weapon} which hasn't been used in a long time.]) => All dolls in this doll's character's location, + the target of the attack (wherever they are) get a chance to act.";
{slow, @extra} = args;
(rpg = $local.rpg):secure();
if (!valid(where = (by = this.character).location))
  return this.act = 0;
endif
"If invite has already been called, we just decrement .act and return.  The first call to invite will take it from there.  This saves repeating many tasks for each separate invite.";
{rpgdoll, idle_threshold} = {rpg.doll, rpg.idle_threshold};
for c in (callers())
  {thing, verbname, progger, verbloc, typist} = c;
  if ((verbname == "invite") && (verbloc == rpgdoll))
    this.act = this.act - slow;
    return;
  endif
endfor
"Hey, we're actually going to try to use those extra args";
{?respond = 1, ?attacker = this, ?target = $nothing, ?weapon = $nothing} = `extra[1] ! E_RANGE => extra';
dolls = rpg:find_chars(where)[2];
"In case target is in a different room from the attacker.  This could conceivably allow the target free attacks if they're involved in melee, but hopefully getting shot at will negate the advantage here.";
if (valid(target) && valid(d = rpg:get_doll(target)))
  dolls = setadd(dolls, d);
endif
"Check schedules, and increment .act.  Dolls that had left over act will get a bit of a bonus, dolls that were in the negative will lose some of that.";
for doll in (dolls)
  try
    if ((is_player(char = doll.character) && (`idle_seconds(char) ! ANY => 181' > idle_threshold)) || `char.location.peaceful ! ANY => 0')
      doll.aggressor = {};
      dolls = setremove(dolls, doll);
    else
      doll.act = ((doll.act / 2) + 60) - ((char == attacker) ? doll.slowness | 0);
      (random(9) < 9) && doll:check_schedule();
    endif
  except (E_PROPNF, E_INVIND)
    dolls = setremove(dolls, doll);
  endtry
endfor
this.act = this.act - slow;
if ((!respond) || (length(dolls) <= 1))
  return;
endif
acts = short_of_time = 0;
"Find the doll with the highest .act.  Decrement the .act.  call eir :act verb.  Repeat until we run out of time, or all have acts of less than 0.  If we run out of time, before at least 3 have acted, suspend, instead of quitting.";
loops = 1;
while (dolls && ((!short_of_time) || (acts < 3)))
  max = -1;
  fast_dolls = {};
  for doll in (dolls)
    if (`doll.aggressor ! ANY => {}')
      "Took out the 2 * doll.slowness for testing purposes ... put the 2 * back in if you like. - THX";
      doll_act = doll.act = doll.act - (loops * doll.slowness);
      if (doll_act >= 0)
        if (doll_act > max)
          fast_dolls = {doll};
          max = doll_act;
        elseif (doll_act == max)
          fast_dolls = setadd(fast_dolls, doll);
        endif
      else
        dolls = setremove(dolls, doll);
      endif
    else
      dolls = setremove(dolls, doll);
    endif
  endfor
  for doll in (fast_dolls)
    if (`doll.character ! ANY' == 0)
      return 0;
    elseif (`doll.character.location.peaceful ! ANY => 0')
    else
      try
        doll.act = doll.act - (loops * doll.slowness);
        doll:act();
        acts = acts + 1;
      except e (ANY)
        return 0;
      endtry
    endif
  endfor
  if ((short_of_time = (ticks_left() < 8000) || (seconds_left() < 2)) && (acts < 3))
    rpg:s_i_n();
  endif
  "loops = loops + 0.1;";
endwhile
return 0;
"Irin Thursday, Jan 30, 1997 -- complete rewrite and redesign.  I think this is vaguely what it was supposed to do, before it got broken.";
"Probably not the same implementation though.  Now, the acting doll calls it's :invite(), rather than inviting the target.";
"Profane 2-MAY-97 1539PDT -- $recycler:valid check  line 54";
"Profane 7-MAY-97 1714PDT -- removed valid check, fixed scattering of extra, s/target/d/21";
"THX - 7-May-1997 -- De-ticked.  Gave :invite() a way out if there is only one doll going into the while loop.  Set idle check to idle_threshold.  Stopped doing doll:schedule calls on idle/disconnected players.  Removed dolls with no aggressors from the big while-loop ... it's up to hit aggressor to tell the damn doll that it's under attack.  Made the first for-loop in the while-loop a little smarter thus reducing the number of iterations of the second for-loop.   Seems to be working fine now.";
"Profane 10-JUN-97 1855PDT -- Try statement in schedule checking loop, line 24-34";
"THX 17-Jul-1997  -- Put error-catching in doll.aggressor to catch recycled dolls without TBing.  Not that it would happen alot, but it'll save G_M from having to look for an error (only happens with gursts).";
"THX (#105941) - Wed Nov  8, 2000 - Check for char.location.peaceful right in the loop that accumulates the dolls that matter _and_ just before the doll:act() is called in case the beggar has moved.";
"Hannibal (#104762) - Tue July 31, 2001 - Make sure doll is valid before setting act, summonables hit this.";
"Profane (#30788) - Wed Aug  1, 2001 - Refactor act reduction within one round. Now more loops reduce act more. effect: tone down haste.";
.

@verb #74:"hit_aggressor" this none this
@program #74:hit_aggressor
rpg = $local.rpg;
if (rpg.rpg_options:get(this.rpg_options, "defend"))
  this.act = this.act - 1;
elseif (this.aggressor = setremove(this.aggressor, me = this.character))
  rpg:secure();
  here = me.location;
  if (0)
    this:arm();
    return $failed_match;
  else
    try
      weapon = (random(2) == 1) ? this:wielding()[1] | $list_utils:random_element(this:wielding());
    except (E_RANGE)
      this:arm();
      return $failed_match;
    endtry
    if ((weapon.contents && rpg.object_db:is_missile_weapon(weapon)) && (random(100) <= weapon:speed()))
      weapon = weapon.contents[1];
    endif
  endif
  if ((missile = rpg.object_db:is_missile_ammo(weapon)) && rpg:trusted_verb(here, "Missile_dests"))
    locs = here:missile_dests(me);
  else
    locs = {here};
  endif
  while (this.aggressor)
    targ = (is_player(me) || (!$recycler:valid(me))) ? $nothing | me:pick_aggressor();
    if (!valid(targ))
      targ = this.aggressor[random($)];
    endif
    "if (`targ.location.peaceful ! ANY => 0')";
    "  this.aggressor = setremove(this.aggressor, targ);";
    if ((targ.location in locs) && ((`idle_seconds(targ) ! ANY => 181' < rpg.idle_threshold) || ((!is_player(targ)) && targ.alive)))
      "if ((targ.location in locs) && (((!is_player(targ)) && targ.alive) || ((targ in connected_players()) && (`idle_seconds(targ) ! ANY => 181' < rpg.idle_threshold))))";
      if (missile)
        weapon:deliver_payload(this, targ, weapon, 0, 0, 1);
      elseif (weapon.owner in rpg.gms)
        this:attack(targ, weapon);
      else
        this.wielding = setremove(this.wielding, weapon);
      endif
      return targ;
    else
      this.aggressor = setremove(this.aggressor, targ);
    endif
  endwhile
endif
return $nothing;
"THX (#105941) - Thu Jul 29, 1999 - Changed missiles to call :deliver_payload() instead of this:attack() directly.";
"THX (#105941) - Fri Feb 16, 2001 - Stuffed in an explicit .peaceful check and streamlined the valid target check.";
"THX (#105941) - Wed Mar 21, 2001 - Decrement doll.act for those who are +defend.   Otherwise they grab the invite loop and hold it all to themselves.";
"Mooshie (#106469) - Sun Mar  3, 2002 - Commented out the explicit .peaceful check. The room's :combat_effect should handle this (to deliver messages, if needed, for example). A peaceful room-flag will be forthcoming to make this more generic.";
.

@verb #74:"respond" this none this
@program #74:respond
":respond(attacker, quality, damage, weapon, loc)";
"attacker -> character initiating the attack";
"quality  -> quality of the attack (final hit roll)";
"damage   -> damage to be applied";
"weapon   -> weapon used to attack";
"loc      -> body area object of hit location";
{by, qual, dam, weapon, loc} = args;
(rpg = $local.rpg):secure();
if (typeof(weapon) != OBJ)
  return this:respond_passed_numbers(by, qual, dam, weapon, loc);
endif
if (!this.armour_effects)
elseif (typeof(effect = this:armour_effects(by, qual, dam, weapon, loc)) == NUM)
  qual = qual - effect;
else
  return effect;
endif
me = this.character;
if (this:cannot_defend())
  return this:receive_damage(dam * (1 + random(3)), max(1, weapon:pen(by, me)), qual, loc, by, weapon);
endif
locstr = loc:title(me);
penalty = -((max(0, length(this.aggressor) - 1) * 20) + this:encumbrance());
parryable = weapon:parryable(by, me) + loc:parry_mod(me, by, weapon);
space = `by.location.space ! ANY';
gms = rpg.gms;
for thing in (this:wielding())
  try
    if (valid(thing))
      if (thing.owner in gms)
        if ((typeof(u = thing:used(me, by)) == NUM) && (this.act > -11))
          mods = ((thing:parry(by, me, weapon) - qual) + parryable) + penalty;
          length = thing:length(me, by);
          mods = mods - `max(3 * space, 0) ! ANY => 0';
          mods = mods + 10;
          if ((best = thing:skill(me, by):resolve(me, mods + u)) > 0)
            this.act = this.act - max(1, (length + thing:slowness(me, by)) / 12);
            thing:announce_parry(by, me, locstr, weapon, qual, best);
            this:check_fatigue(thing:encumbrance());
            weapon:notify_parry(by, me, locstr, thing, qual, best);
            weapon:track_usage(2);
            this.connect_effects && this:connect_effects(by, me, loc, weapon, qual, 0, 99, 1);
            (bydoll = rpg:get_doll(by)).connect_effects && bydoll:connect_effects(me, by, loc, thing, best, 0, 99, 1);
            return;
          endif
        endif
      elseif (thing.owner in rpg.ex_gms)
      else
        rpg:playertell(me, "You feel strangely weaker!");
        this.end = max(this.end - 1, 1);
        this.pot = 0;
        return;
      endif
    else
      this.wielding = setremove(this.wielding, thing);
      return;
    endif
  except (E_VERBNF)
    "It's some non-weapon that's gotten in there somehow.";
    this.wielding = setremove(this.wielding, thing);
  endtry
endfor
dodgable = weapon:dodgable(by, me);
mods = ((((this:hands_free() ? 25 | 0) + dodgable) + penalty) - qual) - `max(0, 50 - space) ! ANY => 0';
mods = mods + 10;
if (rpg.skills.dodge:resolve(me, mods) > 0)
  weapon:track_usage(3);
  this.act = this.act - 1;
  return weapon:announce_dodge(by, me, locstr);
endif
this:receive_damage(dam * (1 + random(3)), max(1, weapon:pen(by, me)), qual, loc, by, weapon);
"Quinn 17-FEB-93: hacked to send attacker to :receive_damage.";
"Quinn 11-AUG-93: Receive weapon (if any) from caller and send to :Receive_damage.";
"Quinn 22-OCT-93: Receive and send hit location.  Removed some literal dodge resolve.";
"Quinn 19-NOV-93: Major re-write.  Cut a lot of kruft.  Redirect old-style calls to :respond_passed_numbers.";
"Profane 9-FEB-95 23:06EST: Added 'qual' and 'try' to :announce_parry call in preparation for weapon degrading.";
"Profane 2-13-95 20:47EST: Added call to weapon:notify_parry for weapon damage hook.";
"Profane 2-9-96 1637EST- Added paralyzation check.";
"Irin Sun Nov  3 10:56:34 1996 PST -- Added evil check for fighting lotsa people penalty thingie.";
"THX 12-MAR-1997 -- Killed off a list indexing, reversed the order of the announce_parry and the notify_parry, and cleaned a wee bit.";
"Profane 18-MAY-97 1541PDT -- added fatigue check to complement one in :attack";
"Profane 12-JUL-97 2147PDT -- TRY statement in the parry bit.";
"Profane 3-OCT-97 -- added check for non-gm owned weapons.";
"THX (#105941) - Mon Apr  6, 1998 - Sped things up by rearranging the conditional.  Remember, set up your conditional path for the _most common_ route the code takes!  Changed this.paralyzed check to this:cannot_move().";
"Irin Thu Jun 11 19:03:52 1998 PDT -- changed :cannot_move to :cannot_defend.";
"Profane (#30788) - Sun May  2, 1999 - insert inelegant backfire checks circa line 38-42";
"THX (#105941) - Mon Nov  1, 1999 - Changed backfire effect to use connect_effects instead.";
"THX (#105941) - Thu Feb  1, 2001 - Decrement this.act by parrying_weapon:slowness() if parry successful.";
.

@verb #74:"set_att" this none this
@program #74:set_att
":set_att(att, amt)";
"Sets the doll's `att' property to `amt'.";
{att, amt} = args;
(rpg = $local.rpg):secure();
if (this == rpg.doll)
  "Grand_Master can set $local.rpg.doll stats manually.";
  return E_NACC;
elseif (caller == this)
  "THX (#105941) - Sun Mar  7, 1999 - Dolls check rpg:approve_att_change() before calling this, so don't do it twice.";
elseif (rpg:approve_att_change(player, {this, att, amt}, callers()) == E_PERM)
  return E_PERM;
endif
if (att == "inj")
  if (link = this.lifelinked)
    return rpg.magic_db.ll_death_effect:do_lifelink(amt, link);
  elseif (this.deathless && (amt < this.inj))
    return;
  endif
elseif (att == "slowness")
  this.slowness_mod = rpg.slowness_mods[max(min(amt, 10), 1)];
elseif (att in {"ins", "fat"})
  this:track_callers(att, caller == this, callers = callers(1));
  (i = $list_utils:iassoc(#7970, callers, 4)) && ((callers[i + 1][3] in rpg.gms) || rpg:log_error((((("Possible hack: " + toliteral(callers)) + " ... Player: ") + #20:nn(player)) + " ... Victim: ") + #20:nn(this)));
endif
if (`parent(this).(att) ! ANY => E_RECMOVE' == amt)
  clear_property(this, att);
else
  return `this.(att) = amt ! ANY';
endif
return `this.(att) ! ANY';
.

@verb #74:"die" this any none rxd
@program #74:die
":die(whoby OBJ [, weapon OBJ])";
{whoby, ?weapon = $nothing} = args;
(rpg = $local.rpg):secure();
victim = this.character;
if (is_player(victim) && (typeof(`idle_seconds(victim) ! ANY') == ERR))
  return E_INVIND;
  "elseif (this:incorporeal(weapon))";
  "  return 0;";
elseif (typeof(effect = this:death_effects(whoby)) != NUM)
  "If a :death_effect does not return a number, then abort the death.";
  return effect;
endif
cp = caller_perms();
where = victim.location;
contents = where:contents();
fork (rpg.gm_lag)
  rpg:check_notification(contents, "notify_death", {victim, whoby});
  `this:murder_anima(whoby) ! E_VERBNF => 0';
endfork
if (is_player(victim))
  (victim == victim.owner) && rpg.death_db:record_death(cp = valid(cp) ? cp | caller, whoby);
  if (((!$object_utils:isa(victim, $guest)) && (this != rpg.object_db.suit_doll)) && (!(rpg.death_db.pkp_effect in this.view_effects)))
    rpg:respawn_non_pkp_pc(this);
  else
    rpg:die(this);
  endif
else
  victim.alive = 0;
  victim:die(whoby);
  `this.aggressor = {} ! ANY';
endif
"Irin Tue Apr 22 11:21:43 1997 PDT -- Disconnected players don't die.";
"THX - 23-Apr-1997 --  Set monster.alive to 0 before passing to monster:die() since they take so long sometimes.";
"THX (#105941) - Wed Oct 13, 1999 - Commented out the explicit lifelink, poison, holdfoe, and iceshell checks and changed them all to use death_effect.";
"Hydros (#106189) - Sun Oct 10, 2004 - Added a call to :murder_anima.  Applies to killed (N)PCs.";
"Hydros (#106189) - Sun Oct 16, 2005 - Added a hook to rpg:respawn_non_pkp_pc(), for folks that have volunteered into the playerkiller realm.";
.

@verb #74:"receive_damage" this none this
@program #74:receive_damage
":receive_damage(dam, pen, qual, hitloc, attacker, weapon)";
"dam      -> damage to be applied";
"pen      -> penetration of the weapon";
"qual     -> quality of the attack (final hit roll)";
"hitloc   -> body area object of hit location";
"attacker -> character initiating the attack";
"weapon   -> weapon used to attack";
(rpg = $local.rpg):secure();
try
  {dam, pen, qual, loc, whoby, weapon} = args;
except (E_ARGS)
  return this:receive_damage_passed_messages(@args);
endtry
"if (this:incorporeal(weapon))";
"  return 0;";
"endif";
me = this.character;
effect = 0;
if (this.connect_effects && (effect = this:connect_effects(whoby, me, loc, weapon, qual, dam, pen, 1)))
  if (typeof(effect) == NUM)
    dam = dam - effect;
  else
    return effect;
  endif
endif
if (rpg.object_db:is_critical_hit(weapon) && weapon:critical_hit(whoby, me, qual, loc, dam))
  "weapon handled a critical hit";
  return;
endif
locstr = loc:title(me);
protection = this:armour(pen, loc, whoby, weapon, dam, qual);
size = this.size;
if (((dam - protection) / size) <= 0)
  armour = this.current_armours[2];
  absorbed = `max(armour:absorption(whoby, me, weapon, loc, dam), 1) ! ANY => 1';
  absorbed = absorbed * weapon:con(whoby, me);
  if (rpg:trusted_property(me, "nat_prot"))
    absorbed = absorbed * ((n = me.nat_prot) ? max(1, 2 + (n / 10)) | 1);
  endif
  if ((dam / max(1, size * absorbed)) > 0)
    dam = dam / (size * absorbed);
    sev = ((5 * dam) + (this.inj / 3)) / max(this.end, 5);
    bad = {"slightly bruised", "slightly bruised", "bruised", "bruised", "bruised", "badly bruised"}[min(sev + 1, 6)];
  else
    if (valid(armour = this.current_armours[1]))
      armour:announce_clang(whoby, me, locstr, weapon, qual);
    else
      rpg:rpg_announce_all(me.location, me.name, " is unharmed by the blow.");
    endif
    weapon:notify_clang(whoby, me, loc, armour, qual);
    return -1;
  endif
else
  dam = (((dam - protection) / size) + weapon:special_damage(me, dam, pen, qual, loc, whoby)) || 0;
  sev = ((2 * dam) + (this.inj / 3)) / max(this.end, 5);
  bad = {"scratched", "slightly wounded", "hit", "hit", "hit", "severely wounded"}[min(sev + 1, 6)];
endif
inj = this.inj;
this:mod_att("inj", dam);
shockmod = loc:shock_mod(me, whoby, weapon);
stunmod = loc:stun_resistance(me, whoby, weapon);
try_ = rpg:resolve(me, "shock", shockmod);
if (try_ > (-2 * this.end))
  rpg:check_notification(me, "notify_hit", {whoby, me, weapon, locstr, bad, dam});
  weapon:announce_hit(whoby, me, locstr, bad, 0);
  weapon:hit_special_effect(whoby, me, qual, dam, loc);
  return -2;
elseif ((!inj) || (try_ > ((-2 * (this.end + (is_player(me) ? 20 | 10))) - stunmod)))
  rpg:check_notification(me, "notify_hit", {whoby, me, weapon, locstr, bad, dam});
  weapon:announce_hit(whoby, me, locstr, bad, 1);
  weapon:track_usage(4);
  this.act = max(this.act - 20, -20);
  weapon:hit_special_effect(whoby, me, qual, dam, loc);
  return -3;
elseif (rpg:is_alive(this))
  weapon:announce_kill(whoby, me, locstr);
  weapon:track_usage(5);
  this:die(whoby, weapon);
  weapon:hit_special_effect(whoby, me, qual, dam, loc);
  return -9;
endif
"THX (#105941) - Mon Jul 20, 1998 - Made it programmatically impossible to die unless already injured.";
"THX (#105941) - Wed Jan  6, 1999 - Re-added stunmod modifier and fixed the logic in line 64.";
"THX (#105941) - Sun Jan 10, 1999 - Removed the ugly tangle receive_damage check/call and moved it to the tangle:armour_effect.";
"Profane (#30788) - Sun May  2, 1999 - insert inelegant checks for backfire circa lines 46-48, 64-66, 73-75";
"Profane (#30788) - Sun May 23, 1999 - Moved iceshell off into an armour_effect";
"THX (#105941) - Mon Nov  1, 1999 - Moved the explicit backfire calls to :connect_effects.";
"THX (#105941) - Mon May 29, 2000 - Added a rpg.object_db:is_critical_hit(weapon) check to allow Grand_Master to exclude poorly constructed critical_hit verbs.";
"THX (#105941) - Thu May 17, 2001 - Put in an rpg:is_alive(this) check in before calling this:die() ... maybe we'll have fewer double corpses.";
.

@verb #74:"act" this none this
@program #74:act
(rpg = $local.rpg):secure();
if (this:cannot_cast())
  "Assume that the monster would hit somebody if they can't cast.";
  "This could be wrong, but monsters don't yet check if they can cast?  Maybe after talking with various monster owners?  Things that block casting, but not attacking seem rare at the moment...";
  if (this:cannot_attack())
    return;
  endif
  return this:hit_aggressor();
endif
if (is_player(who = this.character))
  this:hit_aggressor();
else
  rpg:s_i_n(1);
  "Assume that their act verb is trusted, if they're not a player and have a doll.";
  who:act();
endif
.

@verb #74:"protection" this none this
@program #74:protection
":protection(penetration, hit-location, attacker, weapon-used, dam, [qual])";
rpg = $local.rpg;
{pen, loc, attacker, ?weapon = rpg.weapon, ?dam = 0, ?qual = 0} = args;
me = this.character;
pierce = `weapon:pierce(attacker, me, qual) ! ANY => 0';
r = `random(100) + pierce ! E_TYPE => random(100)';
max = 0;
amax = 1;
best_prot = best_abs = $nothing;
for armour in (this:wearing())
  if (armour:cover(loc) > r)
    protection = armour:protection(attacker, me, weapon, loc, pen, dam);
    absorb = armour:absorption(attacker, me, weapon, loc, dam);
    if (max < protection)
      max = protection;
      best_prot = armour;
    endif
    if (amax < absorb)
      amax = absorb;
      best_abs = armour;
    endif
  endif
endfor
this.current_armours = {best_prot, best_abs};
max = max + ((typeof(loc) == NUM) ? this.body_areas[loc]:nat_prot(me, attacker, weapon) | loc:nat_prot(me, attacker, weapon));
object_db = rpg.object_db;
if (((loc == 4) && (max < 10)) && (r < 75))
  for q in (this.wielding)
    if (object_db:is_shield(q))
      max = 10;
    endif
  endfor
endif
return max(pen * max, 0);
"Quinn 11-AUG-93 1010: Re-written to send attacker and weapon (if given) to the new armour:protection() verb; changed literal shield reference to rpg.shield";
"Quinn 16-OCT-93 0115: Use armour:cover verb instead of propref.";
"Profane 20-JAN-96 0939EST: Added nat_prot modifer for body areas.";
"Irin Wed Sep  4 22:50:51 1996 PDT: Added piercing.";
"Irin Mon Sep 16 18:58:46 1996 PDT -- Added check for absorption, added passing of armour used through .current_armours property.  Nasty hack, but can't otherwise alter the return value.";
"Irin Sun Sep 22 13:15:28 1996 PDT--added optional 'qual' argument, to pass through to :pierce.";
"Irin Mon Sep 23 20:02:36 1996 PDT -- Changed to use call to new object_db for faster execution.";
"THX1138 - 26-Apr-1997 -- Used a scatter assignment and reduced the number of assignments to reduce tickage.";
.

@verb #74:"encumbrance" this none this
@program #74:encumbrance
":encumbrance() -> Total encumbrance of this character.  Usually [0..10].";
if (is_player(char = this.character))
elseif (load = `char:encumbrance() ! E_VERBNF, E_PERM')
  return load;
endif
if (strength = this.str)
  if (typeof(this.weight) == NUM)
    return this.weight;
  else
    weight = this.weight = $local.rpg:weight(char, this) / max(1, strength + (this.size - 10));
    "((weight > 12) && is_player(char)) && $local.rpg.object_db.ol_effect:add(this);";
    return weight;
  endif
else
  return 0;
endif
"Quinn 21-AUG-93 0706: Hacked to return character:encumbrance if exists and is owned by a GM.";
"Profane 2/5/95 13:48EST - fixed use of `STR' as a variable name.";
"StarDancer Oct 21 06:39 1995 PDT - took out call to $object_utils:has_callable_verb, made verb !d";
"Hydros (#106189) - Tue Jan 17, 2006 - I prefer penalizing stat resolves on a sliding scale than just paralyzing them for overload, so I removed it.";
.

@verb #74:"get_att" this none this
@program #74:get_att
"{att} = args; ... documentation only";
if (caller_perms() in $local.rpg.gms)
  return `this.(args[1]) ! ANY';
  "THX (#105941) - Sun Mar 29, 1998 - The following is a nasty hack we used against loser hackers.  Removed for speed.";
  "programmers = $list_utils:slice(callers(), 3);";
  "for loser in ({@rpg.fishy, @rpg.macro_users})";
  "  if (loser in programmers)";
  "    raise(E_PERM);";
  "  endif";
  "endfor";
else
  return E_PERM;
endif
.

@verb #74:"schedule" this none this
@program #74:schedule
":schedule(OBJ object, STR verb, NUM time, LIST args)";
"Schedule this doll to call object:(verb)(doll, @args) at time.";
"The time is approximate, since the schedule is checked only when :invite is called.";
$local.rpg:secure();
this.schedule = listappend(this.schedule, args);
.

@verb #74:"cancel" this none this
@program #74:cancel
":cancel(LIST schedule element)";
if ($local.rpg:trusted(caller_perms()))
  this.schedule = setremove(this.schedule, @args);
endif
.

@verb #74:"get_schedule" this none this
@program #74:get_schedule
{?item = 0} = args;
$local.rpg:secure();
if (item)
  r = {};
  for w in (this.schedule)
    if (w[1] == item)
      r = listappend(r, w);
    endif
  endfor
  return r;
endif
return this.schedule;
"THX (#105941) - Mon Jan 31, 2000 - Updated verb and added security.";
.

@verb #74:"recycle" this none this
@program #74:recycle
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
  rpg = $local.rpg;
  if (z = this in rpg.dolls)
    rpg.pcs = listdelete(rpg.pcs, z);
    rpg.dolls = listdelete(rpg.dolls, z);
  endif
  rpg.saved_dolls = setremove(rpg.saved_dolls, this);
  pass(@args);
else
  return E_PERM;
endif
"Profane 28-MAR-97 1132PST -- added caller == this bit to line 1.";
"Profane (#30788) - Sat Jan  2, 1999 - remove from saved_dolls too.";
.

@verb #74:"arm" this none this
@program #74:arm
"Grab a weapon and wield it.  Called when an unarmed character is attacked.";
(rpg = $local.rpg):secure();
me = this.character;
if (this.wielding || (rpg:trusted_verb(me, verb) && me:(verb)()))
  "Either character is already wielding a weapon, or";
  "The character's :arm verb has taken care of it.";
  return E_NONE;
endif
weapon = rpg.weapon;
for w in (me:contents())
  if ($object_utils:isa(w, weapon) && (this:hands_free() >= w.hands))
    this.wielding = {w};
    w:do_wield(me);
    rpg:playertell(me, "You ", w.wield, " your ", w.name, ".");
    me.location:announce_all_but({me}, $string_utils:pronoun_sub(((("%n %<" + w.owield) + "> %p ") + w:title()) + ".", me));
    "    this:invite((this.slowness + w:slowness()) / 3, {2, w})";
    "     w:wield() ";
    return;
  endif
endfor
this.act = 0;
"DR 01-AUG-94 -- Just call weapon:wield instead of doing it manually.";
"Profane 1/30/95 20:29EST - added trustworthiness check for custom :arm verbs.";
.

@verb #74:"total" this none this
@program #74:total
":total(skill) -> Total (percentage-based) rank for the given skill.";
"Governing attributes are totaled and added to the raw rank for 'skill', then governing skills are each :totaled in turn and the final sum is returned.";
if (caller_perms() in (rpg = $local.rpg).gms)
  {att} = args;
  att_obj = rpg:match_skill(att);
  skill = 0;
  for dep_att in (att_obj.dep_names)
    try
      skill = skill + this.(dep_att);
    except (E_PROPNF)
      "Some G_M's been adding skills the wrong way 'round again..";
      $mail_agent:send_message(this, {this.owner}, "E_PROPNF in :total", ("#74:total is giving a property not found for #74." + tostr(dep_att)) + "... fix it.");
    except (E_TYPE)
      "Stat got messed up somehow.";
      $mail_agent:send_message(this.owner, {this.character.owner}, ("Your " + tostr(dep_att)) + " skill is messed up.", "The above mentioned skill was found set to a non-numeric value, and has been reset to zero. You might want to have a gm check into this, eh?");
      this.(dep_att) = 0;
    endtry
  endfor
  return skill;
else
  return E_PERM;
endif
"Irin Fri Sep  6 19:07:10 1996 PDT -- changed to newtotal code.  This should save around 100 ticks per call to :resolve.";
"Profane (#30788) - Wed Dec 31, 1997 - added some error trapping";
.

@verb #74:"improve" this none this
@program #74:improve
":improve(<skill>, [no dependant improvements]) Make a check for improvement of the given skill.";
"Character has a 1 in skill.learn chance of improvement for each skill.";
"  Skills will not naturally raise higher than 30 (or the relevant skill cap, whichever is lower.)";
"  If a skill is maxed, player has a chance of gaining potential; a chance decreasing with the amount e's been given thus far.";
"  If the player did not gain potential either, then e has a chance of raising eir skill cap in the relevant area.";
"If 'skill' did not improve, the same process is repeated for one of the skills that control it, and so on, unless the `no dependent improvements' flag is given and true.";
{skill, ?no_dep = 0, ?callers = 0} = args;
(rpg = $local.rpg):secure();
callers = callers ? callers | callers();
if (callers()[$][2] == "eval")
  rpg:log_error("Doll:improve() called with caller stack: " + toliteral(callers()));
endif
if (((ease = skill.learn) > 0) && (random(ease) == 1))
  skill_prop = skill.aliases[1];
  if (((skill_val = this.(skill_prop)) == 0) && (!`skill:improve_learn_ok() ! ANY'))
    "This checks to see if the player's skill is going from 0 to 1 in a spell skill: which would imply from no knowledge to some knowledge. This shouldn't happen through skill improvement (which could happen in the deflection of the spell, were this check left out); it should only come through `learning' the spell in some specific way: scroll, teaching, etc,";
    return;
  elseif (((real_val = this:get_unmod_att(skill_prop)) < `random(cap = skill:cap(this)) ! E_INVARG => 0') && rpg:approve_att_change(this.character, {this, skill_prop, real_val + 1}, callers))
    value = this.(skill_prop) = skill_val + 1;
    this:maybe_tell_improve(skill, value);
  elseif (random(600) > this.total)
    this.total = this.total + 1;
    this.pot = this.pot + 1;
    "Added this last section to deal with the skill caps.  A player with a low enough total and a skill cap of 10 or below has a chance to be charged potential here and raise the cap by 5.";
  elseif (((skill_val + 2) > cap) && (random(585) > this.total))
    if (skill.cap_names)
      boosted = $list_utils:random_element(skill.cap_names);
      bskill = rpg:match_skill(boosted[4..$]);
      if (this.(boosted) < 11)
        this.total = this.total + (bskill.cap_boost_mult * ((this.(boosted) > 10) ? 2 | 1));
        this.(boosted) = this.(boosted) + 5;
      endif
    endif
    "-----------End skill cap section----------";
  endif
elseif ((!no_dep) && (deps = {@skill.dependant[1], @skill.dependant[2]}))
  rpg:s_i_n(1);
  this:improve(deps[random($)], 0, callers);
endif
"Quinn 11-AUG-93 0923: Added comments.";
"Irin Fri Sep  6 20:31:00 1996 PDT -- added call to :cap.";
"Irin Wed Oct  2 15:40:22 1996 PDT -- Allowed boosting of skill caps.";
"THX1138 - 17-May-1997  -- Cleaned up a bit.  Added a call to maybe_tell_improve so that players might get some feedback when their skills improve (only against monsters for combat/magick skills tho').";
"THX1138 - 18-May-1997  -- Put a :suspend_if_needed() call in before subsequent calls to improve in the last elseif conditional.";
"Mooshie (#106469) - Mon Oct  6 09:55:24 1997 PDT - Added hook to catch rising stats on disconnected players.";
"Profane (#30788) - Mon Oct  6 18:35:35 1997 PDT - Changed mooshie's hook to raise E_PERM after logging- let's try and stop these tasks, eh?";
"Profane (#30788) - Wed Oct 29 14:19:55 1997 PST - Added check to prevent raising spells from 0 to 1.";
"Mooshie (#106469) - Fri Nov 28 20:27:46 1997 PST - Modified check to go through skill's :improve_learn_ok, thus giving us a little more control over what can be learned via :improve and what can't.";
"Mooshie (#106469) - Fri Jan 23, 1998 - Snuck an :approve_att_change check in there.";
"Profane (#30788) - Sun Oct 11, 1998 - s/15/10/20 and s/16/11/25 since we now have a limit on the number of 20's caps.";
.

@verb #74:"moveto" this none this
@program #74:moveto
if ($local.rpg:trusted(caller_perms()))
  return pass(@args);
else
  player:tell("From ", this, " (#74:moveto).  You have no business messing with the doll anyway, get a GM to move it for you if perchance it is bothering you.");
endif
.

@verb #74:"mod_att" this none this
@program #74:mod_att
":mod_att(STR attribute, NUM value[, NUM min_value[, NUM max_value]])";
" Modifies the given attribute by `value', keeping it greater than or equal to min_value, and less than or equal to max_value.  Default min_value is -$maxint. Default max_value is $maxint.";
" => E_NACC,    if this is the generic doll.";
" => E_TYPE,    if attribute is not numerical.";
" => E_INVARG,  if any argument 2.. isn't numerical.";
" => new_value, if all was successful.";
{att, mod, ?min = -$maxint, ?max = $maxint} = args;
(rpg = $local.rpg):secure();
if ($code_utils:verb_loc() == this)
  return E_NACC;
elseif (typeof(old = this.(att)) != NUM)
  return E_TYPE;
elseif (typeof(mod) != NUM)
  return E_INVARG;
elseif (rpg:approve_att_change(player, {this, att, old + mod}, callers()))
  return this:set_att(att, min(max(old + mod, min), max));
else
  return E_PERM;
endif
"Profane 18-AUG-96 1404PDT -- hacked to call :set_att instead of directly setting att.. so's lifelinking crap will get checked.";
"THX (#105941) - Sat Apr  4, 1998 - Made some hacks for speed.  Saved old to #74:mod_att(old).";
.

@verb #74:"long_enough" this none this
@program #74:long_enough
":long_enough() => Newer spiffier more dynamic :long enough.  Tells whether we should allow an action, based on the doll's last action and the general lag of G_M's queue. -- Irin.";
(rpg = $local.rpg):secure();
return (time() - this.last_attack) > rpg.gm_lag;
.

@verb #74:"wearing" this none this
@program #74:wearing
":wearing([STR|OBJ body_area])";
"If a body area is given (either by object or name), return clothing worn on that area.  Else, return the entire property value.";
{?area = 0} = args;
if (area == 0)
  return this.wearing;
else
  area = (typeof(area) == OBJ) ? area | this:match_body_area(area);
  duds = {};
  for dud in (this.wearing)
    if (dud:cover(area))
      duds = {@duds, dud};
    endif
  endfor
  return duds;
endif
"Quinn 08-MAR-93 xxxx: Added.";
"Quinn 18-NOV-93 0404: Use objects/strings instead of indices.";
.

@verb #74:"hit_location_names" this none this
@program #74:hit_location_names
return `this.character.locations ! ANY => {"head", "left arm", "right arm", "chest", "neck", "abdomen", "right hand", "left hand", "right leg", "left leg", "left foot", "right foot"}';
"Returns hit location names of this doll -- either a property on the doll's character, or the hard-coded default.  --Quinn (11-MAR-93) after the big crash...SIGH";
.

@verb #74:"hands_free" this none this
@program #74:hands_free
":hands_free() => How many hands this character has free, subtracting total hands from those required for all wielded weapons.";
"Actually, this should be hacked even further, to count all unsheathed weapons as being carried, and thus in the character's hands.  Realistic, but sticky with regards to monster setup, and the potential to be quite annoying.";
hands = this.hands;
for w in (this.wielding)
  hands = hands - w.hands;
endfor
return max(hands, 0);
"Quinn 29-JUL-93 0107-ET: Added.";
.

@verb #74:"check_fat*igue" this none this
@program #74:check_fatigue
":check_fat(amount) -> Test the character's stamina.";
{mod, ?announce = 1} = args;
(rpg = $local.rpg):secure();
rpg.fat_callers = setadd(rpg.fat_callers, callers()[1][2..4]);
if (is_player(by = this.character))
else
  announce = (by.animal in {0, "humanoid"}) ? announce | 0;
  mod = mod / 12;
endif
mod = toint(tofloat(mod) / this.slowness_mod);
amt = (mod * random(3)) / ((((this.end * 7) + (this.wil * 3)) / 8) + random(5));
res = rpg:resolve(by, "stamina", 55 - mod);
"old formula: this.fat = this.fat + mod * 3 / ((this.end * 7 + this.wil * 3) / (10 * random(3))) -- THX screwed up.;";
if (res < -80)
  this.act = this.act - 50;
  this.fat = (this.fat + 1) + (2 * amt);
  announce && rpg:say_action("%N %<staggers> wearily, %p tongue wagging and knees buckling with weariness.", by);
elseif (res < -20)
  this.act = this.act - 20;
  this.fat = (this.fat + 1) + ((3 * amt) / 2);
  announce && rpg:say_action("%N %<wipes> %p brow and %<pants> heavily.", by);
elseif (res <= 0)
  this.act = this.act - 10;
  this.fat = this.fat + amt;
  announce && rpg:say_action("%N %<begins> to tire from such intense activity.", by);
else
  this.fat = this.fat + (amt / random(2));
endif
"Profane 18-MAY-97 1604PDT - monsters never get fatigued. Lucky bastards.";
"Profane (#30788) - Sat Feb 14, 1998 - Hasting yourself fatigues you much more easily.";
"Irin Mar 15, 1998, Reduce haste slam";
"THX (#105941) - Sat Jan 15, 2000 - Made it always !announce unless 'by' is a player, a non-animal NPC, or a \"humanoid\" NPC.";
.

@verb #74:"match_body_area" this none this
@program #74:match_body_area
":match_body_area(STR name) -> OBJ body-area-object";
{namestr} = args;
a = $string_utils:match(namestr, areas = this.body_areas, "aliases");
if (valid(a))
elseif (areas == (rpg = $local.rpg).doll.body_areas)
  if (rpg:trusted_property(who = this.character, "locations"))
    if (x = namestr in who.locations)
      return areas[x];
    endif
  endif
endif
return a;
"Quinn 18-NOV-93 0201: Added.";
"Irin Wed Sep 25 21:05:33 1996 PDT -- Added possibility of matching different body area names than those from the aliases.";
"THX (#105941) - Thu Feb  3, 2000 - Don't match with .locations if the body_areas are different than that of rpg.doll's.";
.

@verb #74:"random_hit_location" this none this
@program #74:random_hit_location
":random_hit_location() -> {OBJ body_area[, ...]}";
"Determine which body area the attack has hit.";
areas = this.body_areas;
if (is_player(c = this.character))
elseif (area = c:random_hit_location())
  return area;
elseif (length(c.locations) < length(areas))
  areas = areas[1..length(c.locations)];
endif
while (areas)
  where = areas[random($)];
  if (random(100) <= `where.coverage ! E_PROPNF, E_TYPE => 0')
    return where;
  endif
  areas = setremove(areas, where);
endwhile
return this.body_areas[1];
"Quinn 18-NOV-93 0201: Added.";
"Profane (#30788) - Wed Jan  3, 2001 - HACK to truncate area list to the number defined in a monster's .locations; tho of course you all should still make your own custom body areas.";
.

@verb #74:"respond_passed_numbers" this any none rx
@program #74:respond_passed_numbers
if (!(rpg = $local.rpg):trusted(caller_perms()))
  return E_PERM;
endif
by = args[1];
qual = args[2];
dam = args[3];
pen = args[4];
dodgable = args[5];
parryable = args[6];
"Quinn 11-AUG-93 0948: Receive weapon (if any) from caller.";
weapon = (length(args) > 6) ? args[7] | $nothing;
me = this.character;
if (this:cannot_defend())
  return this:receive_damage_passed_messages(dam * (1 + random(3)), pen, "Your armoured %w deflects the blow.", ("The blow is stopped by the armour on " + me.name) + "'s %w.", "You are %b on the %w.", me.name + " is %b on the %w but keeps fighting.", "You are %b on the %w and briefly stunned.", me.name + " is %b on %p %w and stunned.", by.name + " has killed you! Your soul is returned to the cycle of transmigrations.", ((by.name + " killed ") + me.name) + "!", by, weapon);
endif
best = 0;
hand = 0;
for thing in (this:wielding())
  mods = (thing:parry(by, me) - qual) + parryable;
  if ($object_utils:has_property(by.location, "space"))
    if (by.location.space < (length = thing:length(me, by)))
      mods = mods - (3 * (length - by.location.space));
    endif
  endif
  "try = (u = thing:used(by, me)) ? -1 | thing:skill(me, by):resolve(me, mods);";
  "Quinn 10-AUG-93 0111-ET: Replaced the above with the below.  okay?";
  try_ = (typeof(u = thing:used(me, by)) != NUM) ? -1 | thing:skill(me, by):resolve(me, mods + u);
  hand = hand + thing:hands(me, by);
  if (try_ > best)
    best = try_;
    omsg = ((("parries with " + me.pp) + " ") + thing.name) + ".";
    msg = ("You parry with your " + thing.name) + ".";
  endif
endfor
if (hand < 3)
  mods = ((((25 * (2 - hand)) - this.inj) - (2 * this:encumbrance())) - qual) + dodgable;
  try_ = #19856:resolve(me, mods);
  if (try_ > best)
    best = try_;
    omsg = "dodges.";
    msg = "You dodge.";
  endif
endif
if (best > 0)
  rpg:rpg_announce_all_but(me.location, {me}, me.name, " ", omsg);
  rpg:playertell(me, msg);
else
  "Quinn 17-FEB-93: hacked to send attacker";
  "Quinn 11-AUG-93: hacked to send weapon (if any)";
  this:receive_damage_passed_messages(dam * (1 + random(3)), pen, "Your armoured %w deflects the blow.", ("The blow is stopped by the armour on " + me.name) + "'s %w.", "You are %b on the %w.", me.name + " is %b on the %w but keeps fighting.", "You are %b on the %w and briefly stunned.", me.name + " is %b on %p %w and stunned.", by.name + " has killed you! Your soul is returned to the cycle of transmigrations.", ((by.name + " killed ") + me.name) + "!", by, weapon);
endif
"Profane 9-FEB-96 1648EST -- Added paralyzation stuff.";
.

@verb #74:"receive_damage_passed_messages" this none this
@program #74:receive_damage_passed_messages
(rpg = $local.rpg):secure();
{dam, pen, clang, oclang, wound, owound, stun, ostun, kill, okill, ?whoby = $nothing, ?weapon = rpg.weapon} = args;
"if (this:incorporeal(weapon))";
"  return 0;";
"endif";
me = this.character;
"...hacked to use hit_location_names 11-MAR-93 quinn...";
places = (this.body_areas == rpg.doll.body_areas) ? this:hit_location_names() | $list_utils:map_verb(this.body_areas, "title");
loc = {1, 2, 2, 3, 3, 4, 5, 6, 6, 7, 7}[random(11)];
loc = (loc > length(places)) ? random(length(places)) | loc;
locstr = places[loc];
effect = 0;
if (this.connect_effects && (effect = this:connect_effects(whoby, me, this.body_areas[loc], weapon, random(33), dam, pen, 1)))
  if (typeof(effect) == NUM)
    dam = dam - effect;
  else
    return effect;
  endif
endif
if (dam <= 0)
  return 0;
endif
mess = {};
for indx in [3..10]
  mess = {@mess, strsub(args[indx], "%w", places[loc])};
endfor
{clang, oclang, wound, owound, stun, ostun, kill, okill} = mess;
dam = (dam - this:armour(pen, loc, whoby, weapon, dam)) / this.size;
if (dam <= 0)
  if (clang)
    is_player(me) && rpg:playertell(me, clang);
  endif
  if (oclang)
    rpg:rpg_announce_all_but(me.location, {me}, $string_utils:pronoun_sub(oclang, me));
  endif
  return -1;
endif
inj = this.inj;
this:mod_att("inj", dam);
if (!(bad = (3 * dam) / this.end))
  bad = "scratched";
elseif (bad == 1)
  bad = "slightly wounded";
elseif (bad < 4)
  bad = "hit";
else
  bad = "seriously wounded";
endif
try_ = rpg:resolve(me, "shock", -{0, 10, 0, 20, 20, 20, 20}[loc]);
if ((!inj) || (try_ > (-2 * this.end)))
  if (wound)
    is_player(me) ? rpg:tell_damage(me, strsub(wound, "%b", bad)) | rpg:check_notification(me, "notify_hit", {whoby, me, weapon, locstr, bad, dam});
  endif
  if (owound)
    rpg:rpg_announce_all_but(me.location, {me}, $string_utils:pronoun_sub(strsub(owound, "%b", bad), me));
  endif
  return -2;
elseif ((!this.inj) || (try_ > ((-4 * this.end) - {20, 40, 50, 80, 80, 55, 55}[loc])))
  if (stun)
    is_player(me) ? rpg:tell_damage(me, strsub(stun, "%b", bad)) | rpg:check_notification(me, "notify_hit", {whoby, me, weapon, locstr, bad, dam});
  endif
  if (ostun)
    rpg:rpg_announce_all_but(me.location, {me}, $string_utils:pronoun_sub(strsub(ostun, "%b", bad), me));
  endif
  this.act = this.act - 20;
  if (this.act < -20)
    this.act = -20;
  endif
  return -3;
else
  if (kill)
    is_player(me) && rpg:tell_damage(me, kill);
  endif
  if (okill)
    "rpg:rpg_announce_all_but(me.location, {me}, $string_utils:pronoun_sub(okill, me));";
    rpg:rpg_announce_all_but(me.location, {me}, $string_utils:pronoun_sub(strsub(okill, "%b", bad), me));
  endif
  this:die(whoby, weapon);
  return -9;
endif
"THX (#105941) - Mon Jul 20, 1998 - Made it programmatically impossible to die unless already injured.";
"Profane (#30788) - Sun May 23, 1999 - iceshell and tangle moved to armour_effects";
"Hydros (#106189) - Wed Oct 5, 2005 - `okill' wasn't being subbed to handle `bad'. Fixed.";
.

@verb #74:"death_effects" this none this rx
@program #74:death_effects
":death_effect([killer])";
"Give the attacker's location, and any objects affecting em, a chance to influence eir death.";
"The `death effects' property should be a list of objects whose :death_effect verb will be called with the args (corpse, killer).  Killer may, in some cases, be $no_one.";
"If a non-Numeric value is returned by any object, the death is cancelled.  Any other value is, for now, discarded.  It may apply to a shock check later, in case you'd like to return something.";
"Keep in mind that the `%N has killed %d!' message has already been printed by the time the effect is called.  Returning a non-numeric will prevent the recycling of the doll, but you'll have to announce your own explanations.";
{?killer = $no_one} = args;
corpse = this.character;
(rpg = $local.rpg):secure();
if (rpg:trusted_verb(corpse.location, "death_effect") && (typeof(effect = corpse.location:death_effect(corpse, killer)) != NUM))
  "Room stopped the death.";
  return effect;
endif
{death_db, trash, gms, o_u, s_u} = {rpg.death_db, $garbage, rpg.gms, $object_utils, $set_utils};
effect = 0;
for effobj in (this.death_effects)
  if ((!valid(effobj)) || (parent(effobj) == trash))
    this.death_effects = setremove(this.death_effects, effobj);
    death_db.ok_death_effects = setremove(death_db.ok_death_effects, effobj);
  elseif (s_u:intersection(o_u:has_verb(effobj, "death_effect"), death_db.ok_death_effects) || (corpse in gms))
    "... do nothing ...";
  else
    this.death_effects = setremove(this.death_effects, effobj);
  endif
endfor
for effobj in (this.death_effects)
  if (typeof(e = effobj:death_effect(corpse, killer)) != NUM)
    return e;
  else
    effect = effect + e;
  endif
endfor
return effect;
.

@verb #74:"initialize" this none this
@program #74:initialize
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
  pass(@args);
  $quota_utils:object_bytes(this);
  this.last_action = this.last_attack = this.born = time();
else
  return E_PERM;
endif
"Quinn 16-DEC-93 1300-ET: $quota_utils:object_size.";
"Mooshie (#106469) - Sun Dec  7 02:42:13 1997 PST - Added .born set.";
"THX (#105941) - Sat Oct 28, 2000 - Set .last_attack and .last_action as well.  What the heck.";
.

@verb #74:"legal_target" this none this
@program #74:legal_target
":legal_target(target, doll, weapon)";
"=> True if this character may attack 'target'";
"=> Else explain the failure and return false.";
rpg = $local.rpg;
by = this.character;
{target, ?doll = rpg:get_doll(target), ?weapon = this:wielding()[1]} = args;
if (by == target)
  rpg:playertell(by, "If you want to commit seppuku there are cleaner and neater ways.");
elseif (paralysis = this:cannot_attack())
  callers = callers(1);
  first = callers[1][2..4];
  if (first in rpg.legal_paralysis)
  else
    rpg.legal_paralysis = {@rpg.legal_paralysis, first};
    rpg:log_error(tostr("Paralysis caught by legal_target: ", $string_utils:nn(caller), " ", toliteral(callers)));
  endif
  string = (typeof(paralysis) == STR) ? paralysis | "You're paralyzed, you can't do that.";
  (length(callers()) > 7) || rpg:playertell(by, string);
elseif (!valid(doll))
  `target:no_strike(by, weapon) ! E_VERBNF => 0' || rpg:playertell(by, $string_utils:pronoun_sub("At the last moment, you realise %n isn't worth killing, and spare %p worthless life.", target));
elseif (is_player(target) && (`idle_seconds(target) ! ANY => 1000' > rpg.idle_threshold))
  rpg:playertell(by, "Attack a comatose player?  Forget it, you evil swine.");
elseif ((is_player(target) && (!(target in this.aggressor))) && rpg.rpg_options:get(this.rpg_options, "peacenik"))
  rpg:playertell(by, "Whoa! You don't really want to commit violence upoon 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 = this.duelee) && (`rpg:get_doll(other).duelee ! E_INVIND' == by)) && is_player(target)) && (!(target in {by, other})))
  rpg:playertell(by, "You're busy duelling ", other.name, "; finish that fight first.");
elseif (by.location != target.location)
  if ((valid(weapon) && rpg.object_db:is_missile_ammo(weapon)) && ((dests = $code_utils:verb_or_property(by.location, "dests")) != E_PROPNF))
    "Allow players with missile weapons to shoot non-hated targets in rooms listed as valid target locations by other rooms with support for range   weapons...(whew!)";
    for each_destination in (dests)
      if (target.location == each_destination)
        "Have to put a PKP check in here too.  Some odd verb calls made it possible to kill protected players when both the attacker and defender were being bombarded by arrows from the Old Soldier. -Kagan";
        if (rpg.death_db.pkp_effect:check_for_PK(by, target, weapon))
          rpg:playertell(by, target.name, " appears to be under the aegis of His Eminence, whose wrath is not to be invoked.");
          return 0;
        else
          return 1;
        endif
      endif
    endfor
    rpg:playertell(by, "You're not in the room with that.");
    "elseif (!(target in this.aggressor))";
    "Old way.  Allow attacks on hated targets.  Probably bad.";
  endif
elseif (rpg.death_db.pkp_effect:check_for_PK(by, target, weapon))
  rpg:playertell(by, target.name, " appears to be under the aegis of His Eminence, whose wrath is not to be invoked.");
else
  return 1;
endif
return 0;
"Quinn 11-AUG-93 0900: Added.  Different message when attempting to attack an unconnected player.";
"Quinn 22-AUG-93 1409: Use :contents instead of .location check, to provide for metaroom support.";
"DR 16-MAY-94 -- Reinstated missile weapon support.";
"Quinn Sep-20-94 1340: Forbid attacks on players idle more than 3 minutes as well as those not connected.";
"Quinn Sep-23-94 0721: Figure in last attack when considering idleness.";
"Profane (#30788) - Tue Mar 17, 1998 - munged the logic a bit, replaced contents() checks with .location checks";
"Irin Fri May 29 -- Change to use doll:cannot_attack.  Eliminate out of date missile weapon check.  Completely disallow attacks not in room, except with missile weapons.";
"Profane (#30788) - Wed Apr 21, 1999 - check new `peacenik' option";
"Profane (#30788) - Thu May 31, 2001 - No 3rd party attacks during duels";
.

@verb #74:"write_misc_note" this none this
@program #74:write_misc_note
":write_misc_note(name, value)";
"Store a miscellaneous note on the voodoo doll.";
$local.rpg:secure();
{index, value} = args;
notes = this.misc_notes;
for i in [1..length(notes)]
  if (notes[i][1] == index)
    this.misc_notes[i][2] = value;
    return i;
  endif
endfor
if (index in {"temple_rank", "Mages_guild_info"})
  this.misc_notes = listinsert(notes, args);
  return 1;
else
  this.misc_notes = listappend(notes, args);
  return length(notes) + 1;
endif
.

@verb #74:"read_misc_note" this none this
@program #74:read_misc_note
":read_misc_note(name)";
"View a miscellaneous note on the voodoo doll.";
$local.rpg:secure();
{index} = args;
for elm in (this.misc_notes)
  {elm1, elm2} = elm;
  if (elm1 == index)
    return elm2;
  endif
endfor
return E_PROPNF;
"THX1138 16-DEC-1997 - Used a couple scatters to reduce the ticks.";
.

@verb #74:"erase_misc_note" this none this
@program #74:erase_misc_note
":erase_misc_note(name)";
"Remove a miscellaneous note from the voodoo doll.";
if (!$local.rpg:trusted(caller_perms()))
  return E_PERM;
endif
notes = this.misc_notes;
index = args[1];
for i in [1..length(notes)]
  if (notes[i][1] == index)
    this.misc_notes = listdelete(notes, i);
    size = length(notes) - 1;
    if (size == 0)
      clear_property(this, "misc_notes");
    endif
    return size;
  endif
endfor
return E_PROPNF;
"Isagi 29-SEP-94: Use clear_property() to clear the list when it becomes empty.";
.

@verb #74:"check_schedule" this none this
@program #74:check_schedule
":check_schedule()";
"Check the items in the doll's schedule, firing those which need firing.";
rpg = $local.rpg;
if (valid(character = this.character) && (parent(character) != $garbage))
  toady = rpg.toady;
else
  rpg.recycler:_recycle(this);
  return;
endif
if (schedule = this.att_schedule)
  for line in (schedule)
    {stat, time, mod, ?element = {}} = line;
    if ((time() > time) && (time > 0))
      schedule = setremove(schedule, line);
      cap = is_player(character) ? `rpg:match_skill(stat):cap(this) ! ANY => $maxint' | $maxint;
      "Now check for other things affecting same stat.";
      for line_two in (schedule)
        rpg:s_i_f_i_n();
        if ((line_two[1] == stat) && (line_two != line))
          cap = cap + line_two[3];
        endif
      endfor
      this.(stat) = min(this.(stat) - mod, cap);
      if (element)
        toady:call_verb(element[1], element[2], {this, @element[4]});
      endif
      if (schedule)
        this.att_schedule = schedule;
      else
        clear_property(this, "att_schedule");
      endif
      return 1;
    elseif (!(mod || element))
      this.owner:tell(this);
    endif
  endfor
endif
if (this.schedule)
  for line in (this.schedule)
    {q1, q2, time, q4} = line;
    "Extra (line in schedule) to be sure the element wasn't removed during a suspending call to toady.";
    if ((line in this.schedule) && (time <= time()))
      this.schedule = setremove(this.schedule, line);
      result = (valid(q1) && (parent(q1) != $garbage)) ? `toady:call_verb(q1, q2, {this, @q4}) ! ANY' | 1;
      if (typeof(result) == ERR)
        raise(result, (((((((this.name + "'s schedule resulted in ") + tostr(result)) + " from a call to ") + $string_utils:nn(q1)) + ":") + q2) + " with args ") + $string_utils:from_list({this, @q4}, ", "));
      endif
      return 1;
    endif
  endfor
endif
return 0;
"THX1138 (#105941) - Tue Mar 24, 1998 - Added this verb to replace old system of att_scheduling.  This now returns after servicing any one scheduled task.  The next scheduled task can wait for the next clock/round, dammit.";
"Hydros (#106189) - Wed Jan 26, 2004 - rpg:s_i_f_i_n() in line 18 as a quicksolve to tracebacks with Patch.";
.

@verb #74:"wielding" this none this
@program #74:wielding
":wielding()";
"-> List of weapons this character is wielding,";
"   or a list of natural weapons if wielding nothing.";
return this.wielding || (this.hands ? this.natural_weapons | {});
"DR 17-AUG-94 -- Added hands check as per Isagi.";
.

@verb #74:"add_claimed_object" this none this
@program #74:add_claimed_object
":add_claimed_object(obj[, ignore_quota])";
"Add the given object to this.claimed_objects, keeping a record of all items claimed.";
"If the total number of claimed objects is greater than or equal to this.max_claims, and ignore_quota is not given or false, E_QUOTA is returned.";
rpg = $local.rpg;
if (!rpg:trusted(caller_perms()))
  return E_PERM;
endif
claimed_objs = this:claimed_objects();
ignore_quota = (length(args) > 1) && args[2];
if ((!ignore_quota) && (length(claimed_objs) >= this.max_claims))
  return E_QUOTA;
else
  this.claimed_objects = setadd(claimed_objs, args[1]);
  return 1;
endif
"Profane 5-APR-95 20:03EST - s/.claimed_objects/:claimed_objects()/8";
.

@verb #74:"remove_claimed_object" this none this
@program #74:remove_claimed_object
":remove_claimed_object(obj)";
"Remove the given object from this.claimed_objects.";
rpg = $local.rpg;
if (!rpg:trusted(caller_perms()))
  return E_PERM;
endif
this.claimed_objects = setremove(this.claimed_objects, args[1]);
return 1;
.

@verb #74:"claimed_objects" this none this
@program #74:claimed_objects
":claimed_objects()";
"Verify claimed objects list and return the clean result.";
userobj = this.character;
claimed = {};
for c in (this.claimed_objects)
  if (`c.claimed_by ! ANY => $nothing' == userobj)
    claimed = {@claimed, c};
  endif
endfor
return this.claimed_objects = claimed;
.

@verb #74:"att_schedule" this none this
@program #74:att_schedule
":att_schedule(STR attname, NUM time, NUM modifier, [LIST schedule element])";
"  to be used in conjunction with :get_unmod_att() and :get_att_mod()";
"    TIME is in the form of 'time()' plus a modifier.  EX:  'time() + 3 * 60' means the change only lasts three minutes.";
"  => E_TYPE  That attribute is not a number";
"  => E_INVARG  The modifier that you gave is not a number";
"<Schedule element> should be in the same form as a element for a doll's schedule: {OBJ Object on which verb is to be called, STR Name of verb, NUM dummy argument, LIST arguments to verb}/";
(rpg = $local.rpg):secure();
{att, time, mod, ?element = {}} = args;
if (typeof(original = this.(att)) != NUM)
  return E_TYPE;
elseif (typeof(mod) != NUM)
  return E_INVARG;
elseif (!mod)
  return E_NACC;
elseif (rpg:approve_att_change(player, {this, att, newval = original + mod}, callers()) == E_PERM)
  return E_PERM;
endif
schedule = this.att_schedule;
if (typeof(schedule) != LIST)
  schedule = {};
endif
reset = {att, time, mod, element};
schedule = listappend(schedule, reset);
this.att_schedule = schedule;
this.(att) = newval;
return {1, this.(att)};
"Mooshie (#106469) - Fri Jun  5, 1998 - Snuck in a check to $local.rpg:approve_att_change.";
.

@verb #74:"get_unmod_att" this none this
@program #74:get_unmod_att
":get_unmod_att(STR attname) ==> NUM unmodified_attribute";
$local.rpg:secure();
{stat} = args;
mod = this.(stat);
try
  for line in (this.att_schedule)
    {what, dummy, plus, @rest} = line;
    if (what == stat)
      mod = mod - plus;
    endif
  endfor
  return mod;
except (ANY)
  return mod;
endtry
"Mooshie (#106469) - Thu Feb  5, 1998 - Rewrote.";
.

@verb #74:"magic_effects" this none this
@program #74:magic_effects
":magic_effects(OBJ caster doll, OBJ target doll, NUM quality of spell, OBJ skill of spell) -- check objects affecting the target doll `magically' and give them a chance to influence/stop the spell.";
{cdoll, doll, qual, spell, @extra} = args;
(rpg = $local.rpg):secure();
effect = 0;
ok_effect = $nothing;
"Unlike :combat_effects(), we don't check the area's :magic_effect, since that's _offensive_, and this verb is _defensive_.";
for effobj in (this.magic_effects)
  if (`effobj.owner in rpg.gms ! ANY')
    try
      e = effobj:magic_effect(cdoll, doll, qual, spell);
      ok_effect = effobj;
    finally
      if (ok_effect != effobj)
        if (effobj in {this.character, rpg.monster})
          e = 0;
        else
          "If the verb on the _monster_ is broken, the monster is broken and we can just keep letting errors raise themselves.";
          this.magic_effects = setremove(this.magic_effects, effobj);
          e = E_NONE;
        endif
      endif
    endtry
    if (typeof(e) != NUM)
      return e;
    else
      effect = effect + e;
    endif
  else
    this.magic_effects = setremove(this.magic_effects, effobj);
    msg = ((("[ " + $string_utils:nn(this)) + " ]: ") + $string_utils:nn(effobj)) + " was found in .magic_effects.";
    rpg:log_error(msg);
  endif
endfor
return effect;
"Profane 18-MAY-97 -- Mail to magic_effect verb owner for errors.";
"Mooshie (#106469) - Mon Nov 17 08:18:22 1997 PST - Get rid of effects that don't define a trusted :magic_effect verb";
"THX (#105941) - Thu Oct 14, 1999 - Removed all references to :magic_effect() calls and stopped checking magic_effect props on the dolls.";
.

@verb #74:"hit_result" this none this rx
@program #74:hit_result
"(NUM dam, NUM pen, [OBJ whoby], [OBJ weapon])";
"returns 0 if there's no effect, or returns a list of the following values:";
"bad => a number from 0 (the attack bounced off this.character's armor) to 5 (he's dead, Jim)";
"place_name => a string indicating where this.character got hit.";
"stun => 1 if this.character got stunned, 0 if not.";
if (!(rpg = $local.rpg):trusted(caller_perms()))
  return E_PERM;
elseif (this.end <= 0)
  return 0;
endif
{dam, pen, ?whoby = $nothing, ?weapon = $nothing} = args;
me = this.character;
bad = 0;
stun = 0;
shock = rpg.skills.shock;
"...hacked to use hit_location_names 11-MAR-93 quinn...";
places = this:hit_location_names();
loc = {1, 2, 2, 3, 3, 4, 5, 6, 6, 7, 7}[random(11)];
loc = (loc > length(places)) ? random(length(places)) | loc;
place_name = places[loc];
effect = 0;
if (this.connect_effects && (effect = this:connect_effects(whoby, me, this.body_areas[loc], weapon, random(33), dam, pen, 1)))
  if (typeof(effect) == NUM)
    dam = dam - effect;
  else
    return 0;
  endif
endif
"Quinn 11-AUG-93 0958: Hacked to send attacked and weapon to :armour.";
dam = (dam - this:armour(pen, loc, whoby, weapon, dam)) / this.size;
if (dam <= 0)
  return {bad, place_name, stun};
endif
inj = this.inj;
this:mod_att("inj", dam);
if (!(bad = (2 * dam) / this.end))
  bad = 1;
elseif (bad == 1)
  bad = 2;
elseif (bad < 4)
  bad = 3;
else
  bad = 4;
endif
try_ = rpg:resolve(me, shock, -{0, 10, 0, 20, 20, 20, 20}[loc]);
if (try_ > (-2 * this.end))
  rpg:check_notification(me, "notify_hit", {whoby, me, weapon, place_name, bad, dam});
  return {bad, place_name, stun};
elseif ((!inj) || (try_ > ((-4 * this.end) - {20, 40, 50, 80, 80, 55, 55}[loc])))
  this.act = this.act - 20;
  if (this.act < -20)
    this.act = -20;
  endif
  stun = 1;
  rpg:check_notification(me, "notify_hit", {whoby, me, weapon, place_name, bad, dam});
  return {bad, place_name, stun};
else
  bad = 5;
  this:die(whoby, weapon);
  return {bad, place_name, stun};
endif
"This verb is intended to eventually replace :receive_damage_passed_messages. As is, the aforementioned verb does not properly handle lightning or firebolts, strangulation, (as in 'fangs and crushing coils', object #906) cones of frost, (remember the wyverns?) or any other situation where one wishes to indicate severity and/or a stun result in an unconventional manner.";
"THX1138 16-FEB-1997 - Scatter assignment and shot the heads off a few ticks.";
"Profane (#30788) - Fri Oct 31 15:30:20 1997 PST - trap for the case of having less than 7 body areas.";
.

@verb #74:"potential_total" this none this
@program #74:potential_total
":potential_total(skill) -> Total (percentage-based) possible rank for the given skill.";
"Governing attributes are totaled and added to the raw rank for 'skill', then governing skills are each :potential_totaled in turn and the final sum is returned.";
{stat} = args;
(rpg = $local.rpg):secure();
att_obj = rpg:match_skill(stat);
{deps1, deps2} = att_obj.dependant;
skill = 25;
for dep_att in (deps1)
  skill = skill + 25;
endfor
for dep_att in (deps2)
  skill = skill + this:potential_total(dep_att);
endfor
return skill;
.

@verb #74:"get_atts" this none this
@program #74:get_atts
"Just like :get_att(), only returns multiple atts. Yum.";
$local.rpg:secure();
stuff = {};
for att in (args)
  stuff = {@stuff, this.(att)};
endfor
return stuff;
"Profane 31-AUG-96 1857PDT -- Added at Irin's suggestion. Save ticks from security check when getting big lists of a doll's atts.";
.

@verb #74:"misc_effects" this none this
@program #74:misc_effects
":misc_effect(<what>, [extra args....]) => returns a list of misc_effects on the doll, of the specified flag.  The data is stored in .misc_effects, which is of the format (<object>, <effect>).  The returned list is all return values of objects that have that particular effect.";
rpg = $local.rpg;
if (!rpg:trusted(caller_perms()))
  return E_PERM;
endif
{eff_type, @extra_args} = args;
results = {};
for effect in (this.misc_effects)
  if (effect[2] == eff_type)
    results = {@results, effect[1]:misc_effect(@extra_args)};
  endif
endfor
return results;
.

@verb #74:"do_attack_notification" this none this
@program #74:do_attack_notification
":do_attack_notification(target, weapon)";
"Do notification of this character attacking 'target' with 'weapon'.";
(rpg = $local.rpg):secure();
{target, weapon} = args;
"Notify both rooms if target is not in character's location.";
folks_list = (by = this.character).location:contents();
if (target.location != by.location)
  folks_list = {@folks_list, @target.location:contents()};
endif
"Do an extra :notify_aggression if character is drawing first blood.";
if (target in this.aggressor)
  notlist = {"notify_attack"};
else
  this:first_blood(by, target);
  notlist = {"notify_aggression", "notify_attack"};
endif
notargs = {setremove(folks_list, by), "", {by, target, weapon}};
for notverb in (notlist)
  rpg.notifier:check_notification(@listset(notargs, notverb, 2));
endfor
"Quinn 11-AUG-93 0902: Added.  :notify_aggression is now called correctly.";
"Froxx 4/11/94: suspend according to lag";
"Miles 6/16/94: $login:current_lag() => -1 so added abs() ...";
"... suggested fix by Dred ...";
"Irin -- Turned off to save ticks.";
"THX 13-FEB-97: Version 1.8.0 update, de-ticked and re-added.";
"THX 18-Jun-1997  -- s /$login:current_lag()/rpg.gm_lag/ in the :check_notification fork.";
"Irin Tue Apr 14 22:23:33 1998 PDT -- Let $local.rpg.notifier handle the forking.";
.

@verb #74:"maybe_tell_improve" this none this
@program #74:maybe_tell_improve
{skill, level} = args;
rpg = $local.rpg;
if ((parent(skill) == rpg.skill_object) || (skill.aliases[1] in rpg.magic_db.peaceful_spells))
  "elseif ((level < 6) || this:false_fight())";
elseif (this:false_fight())
  return;
endif
rpg:secure();
choices = {"proficient", "secure", "competent", "capable"};
rpg:tell_damage(this.character, "You feel more ", choices[random($)], " ", rpg.skills:skill_action_msg(skill), ".");
"Mooshie (#106469) - Mon Oct  6 23:17:02 1997 PDT - Stream-lined a bit and hacked to use $local.rpg.skills:skill_action_msg";
"Profane (#30788) - Tue Jan  6, 1998 - added highlighting bit";
"Mooshie (#106469) - Mon Dec 28, 1998 - Highlighting through $local.rpg:tell_damage.";
"Hydros (#106189) - Fri Mar 11, 2005 - Removed the bit about level < 6. Why shouldn't you be able to see yourself improving at low levels?";
.

@verb #74:"false_fight" this none this
@program #74:false_fight
"for t in (this.aggressor)";
"  if (is_player(t))";
"    \"THX (#105941) - Sun Mar 21, 1999 - If you're sparring with only a player, you get no messages.  Why?  Because macroers like to use second characters ... legal alts or otherwise.\";";
"  else";
return 0;
"  endif";
"endfor";
"return 1;";
"Hydros (#106189) - Fri Mar 11, 2005 - While I don't condone or condemn the use of alts for sparring, I'd note that this change has not actually stopped the practice.  Thus, it has been nullified.";
.

@verb #74:"add_light(obsolete)" this none this
@program #74:add_light(obsolete)
"Usage: add_light(OBJ light) => Adds object to this.lights";
"       remove_light(ANY light) => Removes element from this.lights";
$local.rpg:secure();
{what} = args;
where = what in this.lights;
if (verb == "add_light")
  if (where)
  else
    this.lights = {@this.lights, what};
    this.light_values = {@this.light_values, {0, 0}};
    this:update_light(what);
  endif
else
  if (where)
    this.lights = listdelete(this.lights, where);
    this.light_values = listdelete(this.lights, where);
    this:check_lights();
  endif
endif
return this.lights;
"Mooshie (#106469) - Mon Jan 19, 1998 - Added.";
.

@verb #74:"check_lights(obsolete)" this none this
@program #74:check_lights(obsolete)
if ($local.rpg:trusted(caller_perms()))
  total = room_total = 0;
  val = this.light_values;
  for light in (lights = this.lights)
    indx = light in lights;
    {doll, room} = vals;
    total = total + doll;
    room_total = room_total + room;
  endfor
  this.room_light = room_total;
  this.light = total;
else
  return E_PERM;
endif
.

@verb #74:"armour" this none this
@program #74:armour
":armour(penetration, hit-location[, attacker, weapon-used, damage]) => NUM amount of protection this.character has at hit-location.";
"{pen, loc, attacker, weapon, ?dam = 0, @useless_cruft} = args;";
(rpg = $local.rpg):secure();
if (rpg:trusted_verb(char = this.character, verb))
  return char:armour(@args);
else
  return this:protection(@args);
endif
.

@verb #74:"setadd_att setremove_att listappend_att listdelete_att listset_att" this none this
@program #74:setadd_att
"Usage: *_att(STR att, @ARGS)";
"Attempts to set the given at to the value returned by:";
"BUILTIN(current att value, @ARGS) => Where `BUILTIN' is the beginning of whatever verb name is used.";
"";
"Example:";
";#doll.combat_effects";
"=> {#2}";
";#doll:setadd_att(\"combat_effects\", #3)";
"=> {#2, #3}";
(rpg = $local.rpg):secure();
{att, @rest} = args;
try
  result = call_function(verb[1..$ - 4], this.(att), @rest);
except e (ANY)
  return e[1];
endtry
return (rpg:approve_att_change(player, {this, result}, callers()) == E_PERM) ? E_PERM | this:set_att(att, result);
"Mooshie (#106469) - Sat Feb 14, 1998 - Added.";
.

@verb #74:"immobilized immobilised" this none this
@program #74:immobilized
"Returns a string if the doll.character has been immobilized by some dastardly effect incl. old-style paralysis";
$local.rpg:secure();
return this:cannot_move();
"THX (#105941) - Wed Apr  8, 1998 - This verb is for gamemasters to call the (speedier) :cannot_move() that is used in the combat core.";
.

@verb #74:"check_ins" this none this
@program #74:check_ins
"Copied from $local.rpg.doll (#1325):check_ins by Irin (#106049) Fri Feb 27 00:33:38 1998 PST";
":check_ins(INT amount) => Slam the poor schmuck with mental damage.  amount should be 5-20 for minimal effect, 30-70 for moderate effect, and 100+ for major effect.  Now we nail them with stat loss if they're too fatigued.";
{val, ?announce = 1} = args;
(rpg = $local.rpg):secure();
rpg.ins_callers = setadd(rpg.ins_callers, callers()[1][2..4]);
by = this.character;
amt = (val * 2) / this.wil;
resolve = rpg:resolve(by, "sanity", 50 - val, callers()[1][4] == rpg.dungeon);
if (resolve < (-(170 + this.sanity)))
  this.ins = (this.ins + 3) + (3 * amt);
  if (is_player(by) && (callers()[1][2] != "depress"))
    this.act = (-$minint) / 2;
    this.last_attack = time() + 900;
    stat = {"int", "emp", "wil"}[random($)];
    if ((random(5) == 1) && (this.(stat) > (7 + random(7))))
      this.(stat) = this.(stat) - 1;
      announce && rpg:playertell(by, ("Extreme mental stress has caused your " + rpg.skills.(stat):title()) + " to fade.");
    endif
  endif
  announce && rpg:playertell(by, "You just can't take the stress anymore.  You curl up in a ball and start to whimper.");
  announce && rpg:rpg_announce_all_but(by.location, {by}, by.name, " just can't take it anymore.  ", by.ps, " curls up in a ball and starts to whimper.");
elseif (resolve < -50)
  this.act = this.act - 30;
  this.ins = (this.ins + 2) + (2 * amt);
  announce && rpg:playertell(by, "Your vision blurs and you cannot help but start to gibber.");
  announce && rpg:rpg_announce_all_but(by.location, {by}, by.name, "'s eyes glaze over, and ", by.ps, " begins to gibber.");
elseif (resolve < 0)
  this.act = this.act - 15;
  this.ins = (this.ins + 1) + ((3 * amt) / 2);
  announce && rpg:playertell(by, "You hold your head and scream in mental anguish.");
  announce && rpg:rpg_announce_all_but(by.location, {by}, by.name, " holds ", by.pp, " head and screams in mental anguish.");
else
  this.ins = this.ins + amt;
endif
.

@verb #74:"chemical_effects" this none this
@program #74:chemical_effects
"Snagged from $local.rpg.doll (#1325):chemical_effects by Irin (#106049) Mon Mar 23 08:47:00 1998 PST";
":chemical_effects(OBJ source, OBJ chemical, INT qual) => Return a number that affects the quality.  Negative decreases quality, positive increases.  Or, a non INT value to cancel the chemical.";
(rpg = $local.rpg):secure();
{source, chemical, qual} = args;
effect = 0;
ok_effect = $nothing;
for effobj in (this.chemical_effects)
  if (`effobj.owner in rpg.gms ! ANY')
    try
      e = effobj:chemical_effect(this, source, chemical, qual);
      ok_effect = effobj;
    finally
      if (effobj != ok_effect)
        this.chemical_effects = setremove(this.chemical_effects, effobj);
        e = 0;
      endif
    endtry
    if (typeof(e) != INT)
      return e;
    else
      effect = effect + e;
    endif
  else
    this.chemical_effects = setremove(this.chemical_effects, effobj);
    msg = ((("[ " + $string_utils:nn(this)) + " ]: ") + $string_utils:nn(effobj)) + " was found in .chemical_effects.";
    rpg:log_error(msg);
  endif
endfor
return effect;
.

@verb #74:"receive_damage_poison" this none this
@program #74:receive_damage_poison
"Copied from $local.rpg.doll (#1325):receive_damage_poison by Irin (#106049) Wed Mar 11 20:56:06 1998 PST";
":receive_damage_poison(OBJ source, OBJ poison, INT quality, LIST body areas)";
"Some random poison has affected the target.  Nail em with it.";
(rpg = $local.rpg):secure();
{source, poison, qual, areas} = args;
target = this.character;
for area in (areas)
  if (!(area in this.body_areas))
    areas = setremove(areas, area);
  endif
endfor
if (is_player(target))
  return poison:effect(this, source, qual, areas);
else
  return this.character:receive_damage_poison(@args);
endif
.

@verb #74:"acceptable" this none this
@program #74:acceptable
{thing} = args;
for item in (callers())
  if (item[1..4] == {#1140, "new_direction", #2693, #1140})
    return 1;
  endif
endfor
.

@verb #74:"combat_effects" this none this
@program #74:combat_effects
"Snagged from $local.rpg.doll (#1325):combat_effects by Irin (#106049) Mon Mar 23 09:09:46 1998 PST";
":combat_effect(target[, weapon])";
"Give the attacker's location, and any objects affecting em, a chance to influence combat.";
"The `combat effects' property should be a list of objects whose :combat_effect verb will be called with the args (by, target, weapon).";
"If a non-Numeric value is returned by any object, the attack is cancelled.";
"Else, the sum of all effects is returned as a mod to the attack.";
(rpg = $local.rpg):secure();
by = this.character;
{target, ?weapon = rpg.weapon} = args;
effect = 0;
if (rpg:trusted_verb(by.location, "combat_effects") && (typeof(effect = by.location:combat_effects(by, target, weapon)) != NUM))
  "Room stopped the combat.";
  return effect;
endif
ok_effect = $nothing;
for effobj in (this.combat_effects)
  if (`effobj.owner in rpg.gms ! ANY')
    try
      e = effobj:combat_effect(by, target, weapon);
      ok_effect = effobj;
    finally
      if (effobj != ok_effect)
        this.combat_effects = setremove(this.combat_effects, effobj);
        e = 0;
      endif
    endtry
    if (typeof(e) != INT)
      return e;
    else
      effect = effect + e;
    endif
  else
    this.combat_effects = setremove(this.combat_effects, effobj);
    msg = ((("[ " + $string_utils:nn(this)) + " ]: ") + $string_utils:nn(effobj)) + " was found in .combat_effects.";
    rpg:log_error(msg);
  endif
endfor
return effect;
"Profane 25-MAY-97 2306PDT -- mail errors to owner of effobj.";
"Mooshie (#106469) - Mon Nov 17 08:21:03 1997 PST - Added check for trusted :combat_effect verb. Remove if effobj doesn't have one.";
"Irin March-98 -- Let's try letting errors go through and make cute lil TBs.";
"Mooshie (#106469) - Sun Mar  3, 2002 - The call to the room's :combat_effect had been misspelled as :combat_effects with an 's'. There don't appear to be any descendents of $local.rpg.dungeon that define :combat_effects verbs, though, so I've changed it back.";
"Profane (#30788) - Wed Mar  6, 2002 - um no. you broke it, it should be calling :combat_effectS. changed back";
.

@verb #74:"att_unschedule" this none this
@program #74:att_unschedule
":att_unschedule(STR attname, NUM time, NUM modifier, [LIST schedule element])";
"  to be used in conjunction with :get_unmod_att() and :get_att_mod()";
"    TIME is in the form of 'time()' plus a modifier.  EX:  'time() + 3 * 60' means the change only lasts three minutes.";
"  => E_TYPE  That attribute is not a number";
"  => E_INVARG  The modifier that you gave is not a number";
"<Schedule element> should be in the same form as a element for a doll's schedule: {OBJ Object on which verb is to be called, STR Name of verb, NUM dummy argument, LIST arguments to verb}/";
$local.rpg:secure();
{att, time, mod, ?element = {}} = args;
if (typeof(this.(att)) != NUM)
  return E_TYPE;
elseif (typeof(mod) != NUM)
  return E_INVARG;
endif
if (typeof(schedule = this.att_schedule) == LIST)
  reset = {att, time, mod, element};
  for t in (schedule)
    if (t == reset)
      this.att_schedule = setremove(schedule, reset);
      this.(att) = this.(att) - mod;
      return {1, this.(att)};
    endif
  endfor
else
  this.att_schedule = {};
endif
return 0;
.

@verb #74:"may_not_move cannot_cast cannot_attack cannot_defend cannot_move cannot_talk cannot_emote cannot_hear cannot_see cannot_smell cannot_walk cannot_wake cannot_stand cannot_rest cannot_sleep cannot_take cannot_resolve" this none this
@program #74:may_not_move
":cannot_*([FLAG announce]) => Is who allowed to do whatever.  Consult all active .freeze_effects.  If any freezing effect returns true, return true.";
"If announce is true, the effect should tell this.character why they're paralyzed.  Otherwise, don't.";
"Freeze_effects should probably try to remove themselves if they're not in use, so as to be nice to the tickbeast.";
for call in (callers())
  if (call[2] == "cannot_see")
    return this.paralyzed;
  endif
endfor
if (f = this.freeze_effects)
  {?announce = 0} = args;
  announce = announce && is_player(this.character);
  $local.rpg:secure();
  type = verb[8..$];
  for effect in (f)
    if (r = effect:frozen(this, type, announce))
      return r;
    endif
  endfor
endif
"-- Begin temporary hack that should be deleted as soon as paralysis is tranfered. --";
"for t in (this.combat_effects)";
"  if (t in $local.rpg.object_db.immobilized_effects)";
"    return t.paralyzed_msg;";
"  endif";
"endfor";
"-- End cheap hack --";
return this.paralyzed;
"THX (#105941) - Tue Feb  1, 2000 - Commented cheap hack.";
.

@verb #74:"armour_effects" this none this
@program #74:armour_effects
":armour_effects(OBJ attacker, NUM quality, NUM damage, OBJ weapon, OBJ loc) -- check objects affecting the target doll `armourally' and give them a chance to influence/stop the attack.";
(rpg = $local.rpg):secure();
{by, qual, dam, weapon, loc} = args;
"Unlike :combat_effects(), which is _offensive_, this verb is _defensive_.";
effect = 0;
ok_effect = $nothing;
for effobj in (this.armour_effects)
  if (`effobj.owner in rpg.gms ! ANY')
    try
      e = effobj:armour_effect(by, qual, dam, weapon, loc);
      ok_effect = effobj;
    finally
      if (ok_effect != effobj)
        if (effobj in {this.character, rpg.monster})
          e = 0;
        else
          "If the verb on the _monster_ is broken, the monster is broken and we can just keep letting errors raise themselves.";
          this.armour_effects = setremove(this.armour_effects, effobj);
          e = E_NONE;
        endif
      endif
    endtry
    if (typeof(e) == NUM)
      effect = effect + e;
    else
      return e;
    endif
  else
    this.armour_effects = setremove(this.armour_effects, effobj);
    msg = ((("[ " + $string_utils:nn(this)) + " ]: ") + $string_utils:nn(effobj)) + " was found in .armour_effects.";
    rpg:log_error(msg);
  endif
endfor
return effect;
.

@verb #74:"aggressor" this none this
@program #74:aggressor
":aggressor() => returns a list of foes who are in combat with this.character.  Automatically cleans out the aggressor list of things not in room.";
$local.rpg:secure();
loc = this.character.location;
for n in (this.aggressor)
  if (`n.location != loc ! ANY => 1')
    this.aggressor = setremove(this.aggressor, n);
  endif
endfor
this.aggressor = setremove(this.aggressor, this.character);
return this.aggressor;
.

@verb #74:"detect_magic_msg" this none this
@program #74:detect_magic_msg
"The default doll detect magic should just look at magic_effects and report anything that has a valid detect_magic_msg.";
{bydoll, qual} = args;
(rpg = $local.rpg):secure();
{rpgdoll, o_u, msg} = {rpg.doll, $object_utils, {}};
for d in ($set_utils:union(this.magic_effects, this.view_effects))
  if (parent(d) == rpgdoll)
    "Remove teammates ... otherwise players will detect all the objects in Eir temmate's magic_effects too.";
  elseif (o_u:has_callable_verb(d, "detect_magic_msg"))
    msg = {@msg, @(typeof(f = d:detect_magic_msg(bydoll, qual)) == LIST) ? f | {f}};
  endif
endfor
"Could possibly scroll through doll's players contents for rpg objects, but that would be costly, plus we're not intending to have a target for Perceiving the Powers of Magic, so it would be painful to figure out if this is the caster's doll. Just make them drop stuff on the ground before casting detect!";
return msg;
"Dred 2-12-99: Created new spell result.";
"THX (#105941) - Sat May 27, 2000 - Changed the callers() slice check to just remove all rpg.doll kids from the doll's magic_effects for the purposes of detect.";
"THX (#105941) - Fri May 18, 2001 - Changed to go through UNION of magic_effects and view_effects.";
.

@verb #74:"casting_effects" this none this
@program #74:casting_effects
":casting_effects(OBJ caster doll, OBJ target doll, NUM quality of spell, OBJ skill of spell) -- check objects affecting the caster doll `magickally' and give them a chance to influence/stop the spell.";
(rpg = $local.rpg):secure();
{cdoll, doll, qual, spell} = args;
ok_effect = $nothing;
effect = 0;
for effobj in (this.casting_effects)
  if (`effobj.owner in rpg.gms ! ANY')
    try
      e = effobj:casting_effect(cdoll, doll, qual, spell);
      ok_effect = effobj;
    finally
      (ok_effect == effobj) || (e = 0);
    endtry
    if (typeof(e) != NUM)
      return e;
    else
      effect = effect + e;
    endif
  else
    this.casting_effects = setremove(this.casting_effects, effobj);
    msg = ((("[ " + $string_utils:nn(this)) + " ]: ") + $string_utils:nn(effobj)) + " was found in .casting_effects.";
    rpg:log_error(msg);
  endif
endfor
return effect;
.

@verb #74:"encumbrance(old)" this none this
@program #74:encumbrance(old)
":encumbrance() -> Total encumbrance of this character.  Usually [0..10].";
if ((!is_player(char = this.character)) && (load = `char:encumbrance() ! E_VERBNF, E_PERM'))
  return load;
endif
if (strength = this.str)
  if (typeof(this.weight) == NUM)
    return this.weight / strength;
  else
    return (this.weight = ((rpg = $local.rpg):weight(char, this) + (strength / 2)) + ((rpg.magic_db.en_combat_effect in this.combat_effects) * 9)) / strength;
  endif
else
  return 0;
endif
"Quinn 21-AUG-93 0706: Hacked to return character:encumbrance if exists and is owned by a GM.";
"Profane 2/5/95 13:48EST - fixed use of `STR' as a variable name.";
"StarDancer Oct 21 06:39 1995 PDT - took out call to $object_utils:has_callable_verb, made verb !d";
"THX1138 01-Jul-1997 --  added additional encumbrance due to entanglement ... broke it ... then made the verb +d.";
.

@verb #74:"clear_good_spells" this none this
@program #74:clear_good_spells
$local.rpg:secure();
n = 0;
for t in (this.schedule)
  {what, effect, time, various} = t;
  n = n + 1;
  if (effect in {"unwind", "unwall", "unbark", "unstone", "unreverse", "unbladebarrier", "unviper", "unbackfire"})
    this.schedule[n][3] = time();
  elseif (effect == "unspeed")
    if (various[1] > this.slowness)
      "They're hasted...";
      this.schedule[n][3] = time();
    endif
  endif
endfor
.

@verb #74:"first_blood" this none this
@program #74:first_blood
":first_blood(OBJ attacking character, OBJ target character)";
"Mark this doll (which should be the doll of BY, making that argument redundant, but whatever) with a `First Blood' note if BY has attacked TARGET first. Since this has no meaning for NPCs, call this verb only in PvP situations.";
{by, target} = args;
rpg = $local.rpg;
if ((is_player(target) && is_player(by)) && (caller_perms() == rpg.owner))
  if ((note = this:read_misc_note(verb)) != E_PROPNF)
    for t in (note)
      {who, time} = t;
      if ((time() > time) || (who == target))
        note = setremove(note, t);
      endif
    endfor
  else
    note = {};
  endif
  "Check for teammates with first blood:";
  locations = setadd({by.location}, target.location);
  tdoll = rpg:get_doll(target);
  for item in ({tdoll, @tdoll.armour_effects})
    if (((parent(item) == rpg.doll) && (item.character.location in locations)) && item:drew_first_blood(by))
      return;
      "i.e. If I'm hitting back at my attacker (who attacked me first and thus has first blood), I do not get first blood on anyone e's teamed with.";
    endif
  endfor
  if ((this.duelee == target) && (tdoll.duelee == by))
    return;
  endif
  (by == target) || this:write_misc_note(verb, {@note, {target, time() + 9999}});
endif
"Profane (#30788) - Sat May 19, 2001 - Add teammate checking to avoid 3rd party guilt-free pk-ing.";
"Profane (#30788) - Thu May 31, 2001 - new bit for duels- no first blood marks.";
"THX (#105941) - Wed Jun  6, 2001 - Added check for teammate location to prevent certain cases of exploitation of this new rule.";
.

@verb #74:"add_teammate remove_teammate" this none this
@program #74:add_teammate
":add_teammate(doll)  - Adds (removes) 'this' to (from) doll's armour and magic effects.";
{doll} = args;
if ($local.rpg:is_grandmaster(caller_perms()))
  if (index(verb, "re"))
    doll.armour_effects = setremove(doll.armour_effects, this);
    doll.magic_effects = setremove(doll.magic_effects, this);
  else
    parent = parent(this);
    n = 1;
    for t in (doll.armour_effects)
      if (n)
        if (this in doll.armour_effects)
          n = 0;
        elseif (parent(t) != parent)
          doll.armour_effects = listinsert(doll.armour_effects, this, n);
          n = 0;
        endif
        n && (n = n + 1);
      endif
    endfor
    (this in doll.armour_effects) || (doll.armour_effects = setadd(doll.armour_effects, this));
    n = 1;
    for t in (doll.magic_effects)
      if (n)
        if (this in doll.magic_effects)
          n = 0;
        elseif (parent(t) != parent)
          doll.magic_effects = listinsert(doll.magic_effects, this, n);
          n = 0;
        endif
        n && (n = n + 1);
      endif
    endfor
    (this in doll.magic_effects) || (doll.magic_effects = setadd(doll.magic_effects, this));
  endif
else
  raise(E_PERM);
endif
.

@verb #74:"armour_effect" this none this
@program #74:armour_effect
{char, teammate} = {this.character, caller.character};
if (char.location == teammate.location)
  {by, qual, dam, weapon, loc} = args;
  if (by == teammate)
    return 0;
  elseif (by == char)
    (rpg = $local.rpg):secure();
    doll = rpg:get_doll(by);
    doll:remove_teammate(caller);
    caller:remove_teammate(doll);
    return 0;
  elseif (by in this.aggressor)
  else
    (rpg = $local.rpg):secure();
    if (valid(doll = rpg:get_doll(by)) && (doll:drew_first_blood(teammate) || ((!is_player(by)) && (!(by in caller.aggressor)))))
      doll:first_blood(by, char);
      this.aggressor = setadd(this.aggressor, by);
      doll.aggressor = setadd(doll.aggressor, char);
    else
      return 0;
    endif
  endif
  return this.melee / 12;
endif
return 0;
"THX (#105941) - Sat Oct  9, 1999 - This is the teammate effect for the doll.  Don't confuse this with doll:armour_effects.";
.

@verb #74:"magic_effect" this none this
@program #74:magic_effect
{char, teammate} = {this.character, caller.character};
if (char.location == teammate.location)
  {doll, qual, skillobj, target_doll} = args;
  by = doll.character;
  if (by == teammate)
    return 0;
  elseif (by == char)
    $local.rpg:secure();
    doll:remove_teammate(caller);
    caller:remove_teammate(doll);
    return 0;
  elseif (by in this.aggressor)
  else
    (rpg = $local.rpg):secure();
    if (valid(doll) && (doll:drew_first_blood(teammate) || ((!is_player(by)) && (!(by in caller.aggressor)))))
      doll:first_blood(by, char);
      this.aggressor = setadd(this.aggressor, by);
      doll.aggressor = setadd(doll.aggressor, char);
    else
      return 0;
    endif
  endif
  return (this.melee + this.magic_theory) / 12;
endif
return 0;
"THX (#105941) - Sat Oct  9, 1999 - This is the teammate effect for the doll.  Don't confuse this with doll:magic_effects.";
.

@verb #74:"do_lifelink(old)" this none this
@program #74:do_lifelink(old)
"Do the injury distribution for a lifelink.";
if ((rpg = $local.rpg):trusted(caller_perms()))
  {inj, link} = args;
  who = this.character;
  {other, qual} = link;
  if (!valid(doll = rpg:get_doll(other)))
    this:erase_misc_note("lifelink");
    return;
  endif
  curr = this.inj;
  amt = inj - curr;
  amt1 = amt / 2;
  amt2 = amt - amt1;
  if (!(this.deathless && (amt2 < 0)))
    this.inj = max(this.inj + amt2, 0);
  endif
  if (valid(doll) && (!(doll.deathless && (amt1 < 0))))
    doll.inj = max(doll.inj + amt1, 0);
  endif
  qual = qual - abs(amt);
  rpg:playertell(who, "You feel some of your ", (amt2 > 0) ? "pain" | "health", " drain away through your lifelink with ", other.name, ".");
  if (amt1 > 0)
    rpg:playertell(other, "You scream in pain as wounds mystically appear on your body!");
    rpg:rpg_announce_all_but(other.location, {other}, $string_utils:pronoun_sub("%N %<screams> in pain as wounds mystically appear on %p body.", other));
  else
    rpg:playertell(other, "Your wounds mystically heal!");
    rpg:rpg_announce_all_but(other.location, {other}, other.name, "'s wounds mystically heal!");
  endif
  if (rpg.skills.lifelink:resolve(who, qual) < 1)
    doll:erase_misc_note("lifelink");
    this:erase_misc_note("lifelink");
    rpg:playertell(who, "Your lifelink with ", other.name, " collapses from the strain!");
    rpg:playertell(other, "Your lifelink with ", who.name, " collapses from the strain!");
  else
    this:write_misc_note("lifelink", {other, qual});
    doll:write_misc_note("lifelink", {who, qual});
  endif
else
  return E_PERM;
endif
"THX1138 16-FEB-1997 - Got rid of a couple list indexings with a scatter and vaporized a couple ticks.";
.

@verb #74:"magic_effect(old)" this none this
@program #74:magic_effect(old)
":magic_effects(OBJ caster doll, OBJ target doll, NUM quality of spell, OBJ skill of spell) -- check objects affecting the target doll `magically' and give them a chance to influence/stop the spell.";
{cdoll, doll, qual, spell, @extra} = args;
(rpg = $local.rpg):secure();
effect = $object_utils:has_property(doll, "magic_effect") ? doll.magic_effect | 0;
(parent(doll) == #74) || (rpg.owner.magic_effect_argdoll = setadd(rpg.owner.magic_effect_argdoll, doll));
"Unlike :combat_effects(), we don't check the area's :magic_effect, since that's _offensive_, and this verb is _defensive_.";
ok_effect = $nothing;
for effobj in (this.magic_effects)
  if (`effobj.owner in rpg.gms ! ANY')
    try
      e = effobj:magic_effect(cdoll, doll, qual, spell);
      ok_effect = effobj;
    finally
      if (ok_effect != effobj)
        if (effobj in {this.character, rpg.monster})
          e = 0;
        else
          "If the verb on the _monster_ is broken, the monster is broken and we can just keep letting errors raise themselves.";
          this.magic_effects = setremove(this.magic_effects, effobj);
          e = E_NONE;
        endif
      endif
    endtry
    if (typeof(e) != NUM)
      return (verb == "magic_effect") ? -200 | e;
    else
      effect = effect + e;
    endif
  else
    this.magic_effects = setremove(this.magic_effects, effobj);
    msg = ((("[ " + $string_utils:nn(this)) + " ]: ") + $string_utils:nn(effobj)) + " was found in .magic_effects.";
    rpg:log_error(msg);
  endif
endfor
if (verb == "magic_effect")
  rpg.owner.magic_effect_callers = setadd(rpg.owner.magic_effect_callers, (parent(caller) == #74) ? #74 | caller);
  return qual - effect;
else
  return effect;
endif
"Profane - 2/4/95 11:17EST - Added.";
"Profane - 2/7/95 11:04EST - Added check for any .magic_effect prop on target doll (which shouldn't be there, but for backwards compatibility..)";
"Renamed verb to ``magic_effect*s'', if called as ``magic_effect'' will always return numeric value, again for backwards compatibility. Moved old :magic_effect to ``magic_effect(old)''.";
"THX1138 16-FEB-1997 - Scatter assignment and killed a couple ticks.";
"Profane 18-MAY-97 -- Mail to magic_effect verb owner for errors.";
"Mooshie (#106469) - Mon Nov 17 08:18:22 1997 PST - Get rid of effects that don't define a trusted :magic_effect verb";
"Irin Wed Mar 25 15:18:06 1998 PST less ticks.  Tastes great.";
.

@verb #74:"die(old)" this any none rxd
@program #74:die(old)
":die(whoby OBJ [, weapon OBJ])";
(rpg = $local.rpg):secure();
{whoby, ?weapon = $nothing} = args;
cp = caller_perms();
if (is_player(this.character) && (typeof(`idle_seconds(this.character) ! ANY') == ERR))
  return E_INVIND;
elseif (this:incorporeal(weapon))
  return 0;
endif
victim = this.character;
where = victim.location;
if (typeof(effect = this:death_effects(whoby)) != NUM)
  "If a :death_effect does not return a number, then abort the death.";
  return effect;
endif
contents = where:contents();
fork (2 * rpg.gm_lag)
  rpg:check_notification(contents, "notify_death", {victim, whoby});
endfork
"if (link = this:read_misc_note(\"lifelink\"))";
"  {linkee, qual} = link;";
"  doll = rpg:get_doll(linkee);";
"  this:erase_misc_note(\"lifelink\");";
"  doll:erase_misc_note(\"lifelink\");";
"  rpg:say_action(\"%N %<collapses> to the ground, crying out in pain and anguish!\", linkee);";
"  rpg:playertell(linkee, \"Your lifelink with \", this.character.name, \" collapses in a surge of negative energy -- \", this.character.name, \" has died!\");";
"  if ((rpg.skills.shock:resolve(linkee, qual) < 1) && (doll.end > 1))";
"    rpg:playertell(linkee, \"As the negative energies surge through you, you feel a portion of your vitality drain away!\");";
"    doll:att_schedule(\"end\", time() + (((60 * 60) * 24) * 3), -1);";
"  endif";
"endif";
"Check poisoning:";
"for item in (rpg.poison.data)";
"  if (item[2] == this)";
"    rpg.poison.data = setremove(rpg.poison.data, item);";
"  endif";
"endfor";
"Un `freeze' 'em";
"rpg.magic_db.hf_combat_effect:remove_held(this);";
"rpg.magic_db.is_combat_effect:remove_shelled(this);";
if (is_player(victim))
  (victim == victim.owner) && rpg.death_db:record_death(cp = valid(cp) ? cp | caller, whoby);
  rpg:die(this);
else
  victim.alive = 0;
  victim:die(whoby);
  `this.aggressor = {} ! ANY';
endif
"Profane 27-JAN-96 1405EST -- Added lifelink bit.";
"Irin Tue Apr 22 11:21:43 1997 PDT -- Disconnected players don't die.";
"THX - 23-Apr-1997 --  Set monster.alive to 0 before passing to monster:die() since they take so long sometimes.";
"Profane (#30788) - Wed Oct  7, 1998 - cancel delayed poisoning of dead people. what a mess this verb is becoming.";
"Profane (#30788) - Sat Feb 13, 1999 - try calls to iceshell and holdfoe removes to see if that helps clean those objects up.";
"THX (#105941) - Wed Oct 13, 1999 - Commented out the explicit lifelink, poison, holdfoe, and iceshell checks and changed them all to use death_effect.";
.

@verb #74:"armour(old)" this none this
@program #74:armour(old)
":armour(penetration, hit-location[, attacker, weapon-used, damage]) => NUM amount of protection this.character has at hit-location.";
(rpg = $local.rpg):secure();
{pen, loc, attacker, weapon, ?dam = 0, @useless_cruft} = args;
char = this.character;
if (info = this:read_misc_note("force_wall"))
  {c_doll, amount} = info;
  caster = `c_doll.character ! E_PROPNF, E_INVIND => char';
  plus = rpg:resolve(caster, "forcewall", amount - (dam / 10), (!typeof(`connected_seconds(caster) ! ANY')) == INT);
  caster = (is_player(caster) && (`idle_seconds(caster) ! ANY => 181' > rpg.idle_threshold)) ? char | caster;
  c_doll = rpg:get_doll(caster);
  this:write_misc_note("force_wall", {c_doll, amount - 1});
  if (plus < (random(5) + ((random(30) == 1) ? random(35) | 0)))
    this.magic_effects = setremove(this.magic_effects, rpg.magic_db.fw_combat_effect);
    this.combat_effects = setremove(this.combat_effects, rpg.magic_db.fw_combat_effect);
    for n in (this.schedule)
      if (n[2] == "unwall")
        this:cancel(n);
      endif
    endfor
    this:erase_misc_note("force_wall");
    rpg:say_action("The shield of magickal force around %n shatters from the blow.  Tiny shimmering glasslike shards are hurled outward in an impressive, if harmless, display of magickal energies.", char);
    rpg:playertell(caster, ("A backlash of magickal energies from your forcewall " + ((caster == char) ? "" | (("around " + char.name) + " "))) + "briefly distorts your senses.");
    c_doll:check_ins(dam / 5);
    plus = 0;
  elseif (plus < 25)
    if (random(5) == 1)
      if (caster == char)
        rpg:playertell(caster, "You struggle to maintain your wall of force.");
        rpg:rpg_announce_all_but(char.location, {char}, "The shield of magickal force around ", char.name, " fluctuates with the force of the blow.");
      else
        rpg:playertell(caster, ("You struggle to maintain the wall of force around " + char.name) + ".");
        rpg:say_action("The shield of magickal force around %n fluctuates with the force of the blow.", char);
      endif
    endif
    c_doll:check_ins(dam / 10);
    plus = plus / 3;
  else
    plus = min(dam / 2, plus);
  endif
else
  plus = 0;
endif
if (rpg:trusted_verb(char, "armour"))
  return char:armour(@args) + plus;
else
  return this:protection(@args) + plus;
endif
"Mooshie (#106469) - Sun Oct 26 23:43:35 1997 PST - Added check for being  connected as a condition of an improve chance.";
"Irin Thu Jan 22 22:03:56 1998 PST: Cleaned up and allowed forcewall to fail from an attack.  Maybe this will take care of the 3 week forcewall problem.  Who knows?";
.

@verb #74:"connect_effects" this none this
@program #74:connect_effects
":connect_effects(OBJ attacker, OBJ target, OBJ loc, OBJ weapon, NUM quality, NUM damage, NUM pen, BOOL defending) -- check objects affecting the target doll `connectally' and give them a chance to influence/stop the attack.";
{by, me, loc, weapon, qual, dam, pen, defending} = args;
(rpg = $local.rpg):secure();
effect = 0;
ok_effect = $nothing;
for effobj in (this.connect_effects)
  if (`effobj.owner in rpg.gms ! ANY')
    try
      e = effobj:connect_effect(by, me, loc, weapon, qual, dam, pen, defending);
      ok_effect = effobj;
    finally
      if (ok_effect != effobj)
        if (effobj in {this.character, rpg.monster})
          e = 0;
        else
          this.connect_effects = setremove(this.connect_effects, effobj);
          e = E_NONE;
        endif
      endif
    endtry
    if (typeof(e) == NUM)
      effect = effect + e;
    else
      return e;
    endif
  else
    this.connect_effects = setremove(this.connect_effects, effobj);
    msg = ((("[ " + $string_utils:nn(this)) + " ]: ") + $string_utils:nn(effobj)) + " was found in .connect_effects.";
    rpg:log_error(msg);
  endif
endfor
return effect;
.

@verb #74:"receive_damage(old)" this none this
@program #74:receive_damage(old)
":receive_damage(dam, pen, qual, hitloc, attacker, weapon)";
"dam      -> damage to be applied";
"pen      -> penetration of the weapon";
"qual     -> quality of the attack (final hit roll)";
"hitloc   -> body area object of hit location";
"attacker -> character initiating the attack";
"weapon   -> weapon used to attack";
(rpg = $local.rpg):secure();
try
  {dam, pen, qual, loc, whoby, weapon} = args;
except (E_ARGS)
  return this:receive_damage_passed_messages(@args);
endtry
if (this:incorporeal(weapon))
  return 0;
endif
me = this.character;
if (weapon:critical_hit(whoby, me, qual, loc, dam))
  "weapon handled a critical hit";
  return;
endif
locstr = loc:title(me);
protection = this:armour(pen, loc, whoby, weapon, dam, qual);
size = this.size;
if (((dam - protection) / size) <= 0)
  armour = this.current_armours[2];
  absorbed = `max(armour:absorption(whoby, me, weapon, loc, dam), 1) ! ANY => 1';
  absorbed = absorbed * weapon:con(whoby, me);
  if (rpg:trusted_property(me, "nat_prot"))
    absorbed = absorbed * ((n = me.nat_prot) ? max(1, 2 + (n / 10)) | 1);
  endif
  if ((dam / (size * absorbed)) > 0)
    dam = dam / (size * absorbed);
    sev = ((5 * dam) + (this.inj / 3)) / max(this.end, 5);
    bad = {"slightly bruised", "slightly bruised", "bruised", "bruised", "bruised", "badly bruised"}[min(sev + 1, 6)];
  else
    if (valid(armour = this.current_armours[1]))
      armour:announce_clang(whoby, me, locstr, weapon, qual);
    else
      rpg:rpg_announce_all(me.location, me.name, " is unharmed by the blow.");
    endif
    weapon:notify_clang(whoby, me, loc, armour, qual);
    if (this:read_misc_note("backfire"))
      rpg.magic_db.bf_combat_effect:defensive_effect(whoby, me, loc, weapon, qual);
    endif
    return -1;
  endif
else
  dam = (((dam - protection) / size) + weapon:special_damage(me, dam, pen, qual, loc, whoby)) || 0;
  sev = ((2 * dam) + (this.inj / 3)) / max(this.end, 5);
  bad = {"scratched", "slightly wounded", "hit", "hit", "hit", "severely wounded"}[min(sev + 1, 6)];
endif
this:mod_att("inj", dam);
shockmod = loc:shock_mod(me, whoby, weapon);
stunmod = loc:stun_resistance(me, whoby, weapon);
try_ = rpg:resolve(me, "shock", shockmod);
if (try_ > (-2 * this.end))
  rpg:check_notification(me, "notify_hit", {whoby, me, weapon, locstr, bad, dam});
  weapon:announce_hit(whoby, me, locstr, bad, 0);
  weapon:hit_special_effect(whoby, me, qual, dam);
  if (this:read_misc_note("backfire"))
    rpg.magic_db.bf_combat_effect:defensive_effect(whoby, me, loc, weapon, qual);
  endif
  return -2;
elseif ((!this.inj) || (try_ > ((-2 * (this.end + (is_player(me) ? 20 | 10))) - stunmod)))
  rpg:check_notification(me, "notify_hit", {whoby, me, weapon, locstr, bad, dam});
  weapon:announce_hit(whoby, me, locstr, bad, 1);
  this.act = max(this.act - 20, -20);
  weapon:hit_special_effect(whoby, me, qual, dam);
  if (this:read_misc_note("backfire"))
    rpg.magic_db.bf_combat_effect:defensive_effect(whoby, me, loc, weapon, qual);
  endif
  return -3;
else
  weapon:announce_kill(whoby, me, locstr);
  this:die(whoby, weapon);
  weapon:hit_special_effect(whoby, me, qual, dam);
  return -9;
endif
"THX (#105941) - Mon Jul 20, 1998 - Made it programmatically impossible to die unless already injured.";
"THX (#105941) - Wed Jan  6, 1999 - Re-added stunmod modifier and fixed the logic in line 64.";
"THX (#105941) - Sun Jan 10, 1999 - Removed the ugly tangle receive_damage check/call and moved it to the tangle:armour_effect.";
"Profane (#30788) - Sun May  2, 1999 - insert inelegant checks for backfire circa lines 46-48, 64-66, 73-75";
"Profane (#30788) - Sun May 23, 1999 - Moved iceshell off into an armour_effect";
.

@verb #74:"respond(old)" this none this
@program #74:respond(old)
":respond(attacker, quality, damage, weapon, loc)";
"attacker -> character initiating the attack";
"quality  -> quality of the attack (final hit roll)";
"damage   -> damage to be applied";
"weapon   -> weapon used to attack";
"loc      -> body area object of hit location";
{by, qual, dam, weapon, loc} = args;
(rpg = $local.rpg):secure();
if (typeof(weapon) != OBJ)
  return this:respond_passed_numbers(by, qual, dam, weapon, loc);
endif
if (!this.armour_effects)
elseif (typeof(effect = this:armour_effects(by, qual, dam, weapon, loc)) == NUM)
  qual = qual - effect;
else
  return effect;
endif
me = this.character;
if (this:cannot_defend())
  return this:receive_damage(dam * (1 + random(3)), max(1, weapon:pen(by, me)), qual, loc, by, weapon);
endif
locstr = loc:title(me);
penalty = -((max(0, length(this.aggressor) - 1) * 20) + this:encumbrance());
parryable = weapon:parryable(by, me) + loc:parry_mod(me, by, weapon);
space = `by.location.space ! ANY';
gms = rpg.gms;
for thing in (this:wielding())
  try
    if (valid(thing))
      if (thing.owner in gms)
        if (typeof(u = thing:used(me, by)) == NUM)
          mods = ((thing:parry(by, me, weapon) - qual) + parryable) + penalty;
          length = thing:length(me, by);
          mods = mods - `max(3 * space, 0) ! ANY => 0';
          if ((best = thing:skill(me, by):resolve(me, mods + u)) > 0)
            thing:announce_parry(by, me, locstr, weapon, qual, best);
            this:check_fatigue(thing:encumbrance());
            weapon:notify_parry(by, me, locstr, thing, qual, best);
            if (this:read_misc_note("backfire"))
              rpg.magic_db.bf_combat_effect:defensive_effect(by, me, loc, weapon, qual);
            elseif (rpg:get_doll(by):read_misc_note("backfire"))
              rpg.magic_db.bf_combat_effect:defensive_effect(me, by, loc, thing, best);
            endif
            return;
          endif
        endif
      else
        rpg:playertell(me, "You feel strangely weaker!");
        this.end = max(this.end - 1, 1);
        this.pot = 0;
        return;
      endif
    else
      this.wielding = setremove(this.wielding, thing);
      return;
    endif
  except (E_VERBNF)
    "It's some non-weapon that's gotten in there somehow.";
    this.wielding = setremove(this.wielding, thing);
  endtry
endfor
dodgable = weapon:dodgable(by, me);
mods = ((((this:hands_free() ? 25 | 0) + dodgable) + penalty) - qual) - `max(0, 50 - space) ! ANY => 0';
if (rpg.skills.dodge:resolve(me, mods) > 0)
  return weapon:announce_dodge(by, me, locstr);
endif
this:receive_damage(dam * (1 + random(3)), max(1, weapon:pen(by, me)), qual, loc, by, weapon);
"Quinn 17-FEB-93: hacked to send attacker to :receive_damage.";
"Quinn 11-AUG-93: Receive weapon (if any) from caller and send to :Receive_damage.";
"Quinn 22-OCT-93: Receive and send hit location.  Removed some literal dodge resolve.";
"Quinn 19-NOV-93: Major re-write.  Cut a lot of kruft.  Redirect old-style calls to :respond_passed_numbers.";
"Profane 9-FEB-95 23:06EST: Added 'qual' and 'try' to :announce_parry call in preparation for weapon degrading.";
"Profane 2-13-95 20:47EST: Added call to weapon:notify_parry for weapon damage hook.";
"Profane 2-9-96 1637EST- Added paralyzation check.";
"Irin Sun Nov  3 10:56:34 1996 PST -- Added evil check for fighting lotsa people penalty thingie.";
"THX 12-MAR-1997 -- Killed off a list indexing, reversed the order of the announce_parry and the notify_parry, and cleaned a wee bit.";
"Profane 18-MAY-97 1541PDT -- added fatigue check to complement one in :attack";
"Profane 12-JUL-97 2147PDT -- TRY statement in the parry bit.";
"Profane 3-OCT-97 -- added check for non-gm owned weapons.";
"THX (#105941) - Mon Apr  6, 1998 - Sped things up by rearranging the conditional.  Remember, set up your conditional path for the _most common_ route the code takes!  Changed this.paralyzed check to this:may_not_move().";
"Irin Thu Jun 11 19:03:52 1998 PDT -- changed :may_not_move to :cannot_defend.";
"Profane (#30788) - Sun May  2, 1999 - insert inelegant backfire checks circa line 38-42";
.

@verb #74:"attack(old)" this none this
@program #74:attack(old)
":attack(OBJ target, LIST weapons[, NUM modifier, OBJ area, NUM no_invite])";
{target, weapons, ?bmods = 0, ?barea = 0, ?no_invite = 0} = args;
(rpg = $local.rpg):secure();
location = (by = this.character).location;
doll = rpg:get_doll(target);
if (!this:legal_target(target, doll, caller))
  return;
elseif (typeof(weapons) == LIST)
  weapon = weapons[1];
else
  weapon = weapons;
  weapons = {weapons};
endif
barea = (typeof(barea) == OBJ) ? barea | doll:random_hit_location();
if (typeof(effect = this:combat_effects(target, weapon)) != NUM)
  return effect;
endif
bmods = (bmods + effect) - this:encumbrance();
try
  if ((!valid(weapon)) || (typeof(use = weapon:used(by, target)) != NUM))
    "If :used returns a non-numeric value, the weapon is unusable.";
    return;
  elseif ((!(weapon.owner in rpg.gms)) && `verb_info(weapon, "0") ! E_VERBNF')
    "OK, only penalize them if it has verbs defined..";
    rpg:playertell(by, "You feel foolish!");
    this.int = max(this.int - 1, 1);
    this.pot = 0;
    return;
  endif
except (E_TYPE)
  raise(E_TYPE, ((("Error: " + by.name) + " is passing invalid weapons list (") + $string_utils:from_list(weapons, ", ")) + ")");
except (E_VERBNF)
  raise(E_VERBNF, ("Error:  \"Weapon\" " + tostr(weapon)) + " does not define a :used verb.");
endtry
this:do_attack_notification(target, weapon);
is_player(target) || target:set_last_attacker(by);
weap_slow = weapon:slowness(by, target);
weapon:announce_swing(by, target);
if (i_msg = doll:incorporeal(weapon))
  return rpg:say_action(i_msg, target, weapon, by.location);
endif
"if (doll.end <= 0)";
"  if ((!(m_msg = weapon.materialize)) || rpg:trusted_property(target, \"incorporeal\"))";
"    i_msg = (typeof(i = `target.incorporeal ! E_PROPNF, E_PERM => 0') == STR) ? i | \"The blow passes through %n without visible effect\";";
"    return rpg:say_action(i_msg, target, weapon, by);";
"  else";
"    m_msg = (typeof(m_msg) == STR) ? m_msg | \"The %t forces %n to become tangible!\";";
"    rpg:say_action(m_msg, target, weapon, by);";
"  endif";
"endif";
weap_dam = weapon:dam(by, target);
attack_bonus = weapon:attack(by, target);
mods = ((bmods + use) + attack_bonus) - (length(weapons) * 10);
str_bonus = (weapon:max_str_bonus(by, target) * this.str) / 25;
squeeze = `max(0, weapon:length(by, target) - location.space) ! ANY => 0';
mods = mods - (3 * squeeze);
damage = ((2 * weap_dam) + str_bonus) - squeeze;
if (((squeeze > 3) && is_player(by)) && (random(25) == 1))
  rpg:playertell(by, "The room is a little small to ", weapon.swing, " your ", weapon.name, " properly.");
endif
"Damage will never be greater than 3 * base.";
"damage = min(damage, 3 * weap_dam)";
"'y' is encumbrance penalty of the weapon.  Wielding a heavy weapon with more than one hand reduces the total penalty.";
"A weak person wielding a heavy weapon will be greatly penalized.";
y = str_bonus - ((3 * (weap_enc = weapon:encumbrance(this))) / (1 + weapon:hands(by, target)));
"y = str_bonus - 3 * (weap_enc = weapon:encumbrance(this)) / 5 / (1 + weapon:hands(by, target));";
if (y < 0)
  mods = mods + (5 * y);
  damage = damage + (y / 2);
endif
foes = 0;
for foe in (this.aggressor)
  if (foe.location == location)
    foes = foes + 1;
  endif
endfor
mods = mods - (max(0, foes - 1) * 15);
connects = (qual = weapon:skill(by, target):resolve(by, mods)) > -25;
if (connects)
  doll:respond(by, qual, damage, weapon, barea);
  if (this:read_misc_note("backfire"))
    rpg.magic_db.bf_combat_effect:offensive_effect(by, target, weapon, barea, qual);
  endif
elseif (!weapon:critical_miss(by, target, qual))
  "No critical miss.";
  weapon:announce_miss(by, target, qual);
endif
this:check_fatigue(weap_enc);
weapons = listdelete(weapons, 1);
"If we attack and die for some reason during the attack, no invitation will take place.  Everybody stands around shocked.  If the target dies, everything is fine.";
if (`by.alive ! E_PROPNF' || (is_player(by) && valid(rpg:get_doll(by))))
  this.aggressor = setadd(this.aggressor, target);
  if (weapons)
    this.act = this.act - weap_slow;
    this:attack(target, weapons, bmods - 10, barea, 1);
  endif
  if (valid(doll) && (parent(doll) != $garbage))
    doll.aggressor = setadd(doll.aggressor, by);
  endif
  if (!no_invite)
    this:invite(this.slowness, {1, by, target, weapon});
  endif
endif
"Quinn 05-JAN-93: Use verbs for referencing weapon properties.";
"Quinn 28-FEB-93: Won't fork notification if a fork is already pending.";
"Quinn 11-AUG-93: Big re-write.  Created :legal_target and :do_attack_notifica ion to do the mundane chore of checking and notifying targets; cut down the unwieldly verb size; added some minor comments.";
"Quinn 11-AUG-93: Send weapon to :respond.";
"Quinn 22-OCT-93: Added combat_effect.";
"Quinn 19-NOV-93: Added body area support, :critical_miss.  New args to :Respond.";
"Ogwul 01-JAN-94: Allowed :attack to be called without adding the target to the aggressor list and without :invite via the no_invite parameter.";
"Profane 29-DEC-95 1451EST -- Added windwall check thing.";
"Irin Sun Nov  3 10:49:52 1996 PST -- Bwahaha, attack penalty if fighting more than one foe.";
"Irin Thursday, January 30 -- Rewrite of the :invite section to correspond to changes.  Don't use rpg:trusted.";
"Profane 31-MAR-97 1341PST -- s/valid/$recycler:valid/85";
"Profane 15-APR-97 1518PST -- TRY statement for weapon:used";
"THX 18-May-1997  -- Fixed so that foe.location checks against by.location rather than this.location (one's doll is never in the same place as one's attackers).  Added a weeny fatigue check after the (connects) conditional.";
"Profane 3-OCT-97 -- added check for non-gm owned weapons.";
"Mooshie (#106469) - Fri Feb 27, 1998 - Change how we handle .end == 0. Gory details found in `help incorporeality'.";
"THX (#105941) - Wed Apr  8, 1998 - Removed this.paralyzed and entangled checks.  this:legal_target now does the checks via this:may_not_move().  subst this.character -> by from line 4 onwards and changed the \"I'm still alive.\" check before the invite call since critters always return a valid doll.";
"Profane (#30788) - Sun May  2, 1999 - add inelegant backfire check circa 90-92";
"Profane (#30788) - Sun May 23, 1999 - windwall moved off into armour_effect";
.

@verb #74:"incorporeal(obsolete)" this none this
@program #74:incorporeal(obsolete)
{weapon} = args;
if (is_player(char = this.character) && (!this.end))
  return "The blow passes through %n without visible effect.";
elseif ((char.owner in $local.rpg.gms) && (intangible = `char:incorporeal(weapon) ! ANY => ""'))
  return intangible;
else
  return "";
endif
.

@verb #74:"drew_first_blood" this none this
@program #74:drew_first_blood
":drew_first_blood(OBJ Check_Player) => 1 or 0; did THIS.CHARACTER draw first blood on Check_Player first within the last 9999 seconds? Returns false if either party is an NPC.";
{check} = args;
result = 0;
if ((note = this:read_misc_note("first_blood")) != E_PROPNF)
  $local.rpg:secure();
  for t in (note)
    {who, time} = t;
    ((time > time()) && (check == who)) && (result = 1);
  endfor
endif
return result;
"THX (#105941) - Mon Feb 12, 2001 - Fixed logic.  time must be GREATER THAN time() or the first_blooding has elapsed.";
.

@verb #74:"get_schedule(old)" this none this
@program #74:get_schedule(old)
$local.rpg:secure();
if (args == {})
  return this.schedule;
else
  r = {};
  for w in (this.schedule)
    if (w[1] == args[1])
      r = listappend(r, w);
    endif
  endfor
  return r;
endif
.

@verb #74:"match_body_area(old)" this none this
@program #74:match_body_area(old)
":match_body_area(STR name) -> OBJ body-area-object";
{namestr} = args;
a = $string_utils:match(namestr, this.body_areas, "aliases");
if (valid(a))
  return a;
else
  if ($local.rpg:trusted_property(who = this.character, "locations"))
    if (x = namestr in who.locations)
      return this.body_areas[x];
    else
      return a;
    endif
  endif
endif
"Quinn 18-NOV-93 0201: Added.";
"Irin Wed Sep 25 21:05:33 1996 PDT -- Added possibility of matching different body area names than those from the aliases.";
.

@verb #74:"get_unmod_atts" this none this
@program #74:get_unmod_atts
":get_unmod_atts({STR att1, STR att2, ...}) ==> {NUM val1, NUM val2, ...}";
"just like :get_unmod_att() only returns multiple unmodified atts.";
$local.rpg:secure();
stuff = {};
for stat in (args)
  mod = this.(stat);
  try
    for line in (this.att_schedule)
      {what, dummy, plus, @rest} = line;
      if (what == stat)
        mod = mod - plus;
      endif
    endfor
    stuff = {@stuff, mod};
  except (ANY)
    stuff = {@stuff, mod};
  endtry
endfor
return stuff;
"Prui (#90864) - 02/15/2000 - Wrote from :get_unmod_att";
.

@verb #74:"detect_magic_msg(old)" this none this
@program #74:detect_magic_msg(old)
"Copied from Perceiving the Powers of Magic (#476):detect_magic_msg(doll) by Dred (#49925) Tue Feb 16 06:13:53 1999 PST";
"The default doll detect magic should just look at magic_effects and report anything that has a valid detect_magic_msg.";
{bydoll, qual} = args;
(rpg = $local.rpg):secure();
msg = {};
for d in ($set_utils:diff(this.magic_effects, $list_utils:slice(callers(), 1)))
  if ($object_utils:has_callable_verb(d, "detect_magic_msg"))
    msg = {@msg, @(typeof(f = d:detect_magic_msg(bydoll, qual)) == LIST) ? f | {f}};
  endif
endfor
"Could possibly scroll through doll's players contents for rpg objects, but that would be costly, plus we're not intending to have a target for Perceiving the Powers of Magic, so it would be painful to figure out if this is the caster's doll. Just make them drop stuff on the ground before casting detect!";
return msg;
"Dred 2-12-99: Created new spell result.";
"THX (#105941) - Sun Jan  2, 2000 - Changed to remove caller from the for-loop ... then had to remove all callers from the for-loop.  Should be robust now.";
.

@verb #74:"track_callers" this none this
@program #74:track_callers
if (caller.owner in (rpg = $local.rpg).gms)
  {att, self, callers} = args;
  term = callers[1 + self][2..4];
  if (term in rpg.both_callers)
    return;
  endif
  rpg.(att + "_callers") = setadd(rpg.(att + "_callers"), term);
  if ((term in rpg.ins_callers) && (term in rpg.fat_callers))
    rpg.both_callers = setadd(rpg.both_callers, term);
    rpg.ins_callers = setremove(rpg.ins_callers, term);
    rpg.fat_callers = setremove(rpg.fat_callers, term);
  endif
endif
.

@verb #74:"act(old)" this none this
@program #74:act(old)
(rpg = $local.rpg):secure();
if (this:cannot_cast())
  "Assume that the monster would hit somebody if they can't cast.";
  "This could be wrong, but monsters don't yet check if they can cast?  Maybe after talking with various monster owners?  Things that block casting, but not attacking seem rare at the moment...";
  if (this:cannot_attack())
    return;
  endif
  return this:hit_aggressor(@args);
endif
if (is_player(who = this.character))
  this:hit_aggressor(@args);
else
  $command_utils:suspend_if_needed(1);
  "Assume that their act verb is trusted, if they're not a player and have a doll.";
  who:act(@args);
endif
.

@verb #74:"teammates" this none this
@program #74:teammates
":teammates() => dolls of this doll's teammates";
(rpg = $local.rpg):secure();
teammates = {};
for t in (this.armour_effects)
  (t in rpg.dolls) && (teammates = setadd(teammates, t));
endfor
return teammates;
"THX (#105941) - Wed Oct  4, 2000 - Added.";
.

@verb #74:"invite(old)" this none this
@program #74:invite(old)
":invite(NUM slowness[LIST {NUM respond, OBJ attacker, OBJ target, OBJ weapon} which hasn't been used in a long time.]) => All dolls in this doll's character's location, + the target of the attack (wherever they are) get a chance to act.";
(rpg = $local.rpg):secure();
if (!valid(where = (by = this.character).location))
  return this.act = 0;
elseif (`where.peaceful ! ANY => 0')
  if (is_player(by))
    setremove(this.aggressor, by) && rpg:log_error(((#20:nn(this.character.location) + " (current character location) ") + (((((((#20:nn(this) + ":invite() called in a peaceful room with argstr: ") + argstr) + " ... aggressors: ") + #20:nn(this.aggressor)) + " ... and callers: ") + toliteral(callers(1))) + " ... by player: ")) + #20:nn(player));
  else
    return;
  endif
endif
{slow, @extra} = args;
"Hey, we're actually going to try to use those extra args";
{?respond = 1, ?attacker = this, ?target = #-1, ?weapon = #-1} = `extra[1] ! E_RANGE => extra';
"If invite has already been called, we just decrement .act and return.  The first call to invite will take it from there.  This saves repeating many tasks for each separate invite.";
for c in (callers())
  if ((c[2] == "invite") && (c[4] == rpg.doll))
    this.act = this.act - slow;
    return;
  endif
endfor
dolls = rpg:find_chars(where)[2];
"In case target is in a different room from the attacker.  This could conceivably allow the target free attacks if they're involved in melee, but hopefully getting shot at will negate the advantage here.";
if ((valid(target) && valid(d = rpg:get_doll(target))) && (!`target.location.peaceful ! ANY => 0'))
  dolls = setadd(dolls, d);
endif
"Check schedules, and increment .act.  Dolls that had left over act will get a bit of a bonus, dolls that were in the negative will lose some of that.";
cp = connected_players();
for doll in (dolls)
  try
    if (is_player(char = doll.character) && ((!(char in cp)) || (idle_seconds(char) > rpg.idle_threshold)))
      doll.aggressor = {};
      dolls = setremove(dolls, doll);
    else
      doll.act = ((doll.act / 2) + 60) - ((char == attacker) ? doll.slowness | 0);
      doll:check_schedule();
    endif
  except (E_PROPNF, E_INVIND)
    dolls = setremove(dolls, doll);
  endtry
endfor
this.act = this.act - slow;
if ((!respond) || (length(dolls) <= 1))
  return;
endif
acts = short_of_time = 0;
"Find the doll with the highest .act.  Decrement the .act.  call eir :act verb.  Repeat until we run out of time, or all have acts of less than 0.  If we run out of time, before at least 3 have acted, suspend, instead of quitting.";
while (dolls && ((!short_of_time) || (acts < 3)))
  max = -1;
  fast_dolls = {};
  for doll in (dolls)
    if (`doll.aggressor ! ANY => {}')
      "Took out the 2 * doll.slowness for testing purposes ... put the 2 * back in if you like. - THX";
      doll_act = doll.act = doll.act - doll.slowness;
      if (doll_act >= 0)
        if (doll_act > max)
          fast_dolls = {doll};
          max = doll_act;
        elseif (doll_act == max)
          fast_dolls = setadd(fast_dolls, doll);
        endif
      else
        dolls = setremove(dolls, doll);
      endif
    else
      dolls = setremove(dolls, doll);
    endif
  endfor
  for doll in (fast_dolls)
    if (parent(doll) == $garbage)
      return 0;
    endif
    doll.act = doll.act - doll.slowness;
    doll:act();
    acts = acts + 1;
  endfor
  if ((short_of_time = (ticks_left() < 8000) || (seconds_left() < 2)) && (acts < 3))
    rpg:s_i_n();
  endif
endwhile
return 0;
"Irin Thursday, Jan 30, 1997 -- complete rewrite and redesign.  I think this is vaguely what it was supposed to do, before it got broken.";
"Probably not the same implementation though.  Now, the acting doll calls it's :invite(), rather than inviting the target.";
"Profane 2-MAY-97 1539PDT -- $recycler:valid check  line 54";
"Profane 7-MAY-97 1714PDT -- removed valid check, fixed scattering of extra, s/target/d/21";
"THX - 7-May-1997 -- De-ticked.  Gave :invite() a way out if there is only one doll going into the while loop.  Set idle check to rpg.idle_threshold.  Stopped doing doll:schedule calls on idle/disconnected players.  Removed dolls with no aggressors from the big while-loop ... it's up to hit aggressor to tell the damn doll that it's under attack.  Made the first for-loop in the while-loop a little smarter thus reducing the number of iterations of the second for-loop.   Seems to be working fine now.";
"Profane 10-JUN-97 1855PDT -- Try statement in schedule checking loop, line 24-34";
"THX 17-Jul-1997  -- Put error-catching in doll.aggressor to catch recycled dolls without TBing.  Not that it would happen alot, but it'll save G_M from having to look for an error (only happens with gursts).";
"THX (#105941) - Mon Jun  5, 2000 - Added a check for room.peaceful to terminate the invitation.  Will just log for now.";
.

@verb #74:"death_effects(old)" this none this rx
@program #74:death_effects(old)
":death_effect([killer])";
"Give the attacker's location, and any objects affecting em, a chance to influence eir death.";
"The `death effects' property should be a list of objects whose :death_effect verb will be called with the args (corpse, killer).  Killer may, in some cases, be $no_one.";
"If a non-Numeric value is returned by any object, the death is cancelled.  Any other value is, for now, discarded.  It may apply to a shock check later, in case you'd like to return something.";
"Keep in mind that the `%N has killed %d!' message has already been printed by the time the effect is called.  Returning a non-numeric will prevent the recycling of the doll, but you'll have to announce your own explanations.";
(rpg = $local.rpg):secure();
{?killer = $no_one} = args;
corpse = this.character;
{death_db, trash, gms} = {rpg.death_db, $garbage, rpg.gms};
if (rpg:trusted_verb(corpse.location, "death_effect") && (typeof(effect = corpse.location:death_effect(corpse, killer)) != NUM))
  "Room stopped the death.";
  return effect;
endif
effect = 0;
for effobj in (this.death_effects)
  if ((!valid(effobj)) || (parent(effobj) == trash))
    this.death_effects = setremove(this.death_effects, effobj);
    death_db.ok_death_effects = setremove(death_db.ok_death_effects, effobj);
  elseif ((effobj in death_db.ok_death_effects) || (corpse in gms))
    "... do nothing ...";
  else
    this.death_effects = setremove(this.death_effects, effobj);
  endif
endfor
for effobj in (this.death_effects)
  if (typeof(e = effobj:death_effect(corpse, killer)) != NUM)
    return e;
  else
    effect = effect + e;
  endif
endfor
return effect;
.

@verb #74:"potential_total(old)" this none this
@program #74:potential_total(old)
":potential_total(skill) -> Total (percentage-based) possible rank for the given skill.";
"Governing attributes are totaled and added to the raw rank for 'skill', then governing skills are each :potential_totaled in turn and the final sum is returned.";
if (!(caller_perms() in $local.rpg.gms))
  return E_PERM;
endif
att_obj = $local.rpg:match_skill(args[1]);
skill = 25;
deps = att_obj.dependant;
for dep_att in (deps[1])
  skill = skill + 25;
endfor
for dep_att in (deps[2])
  skill = skill + this:potential_total(dep_att);
endfor
return skill;
.

@verb #74:"stymied" this none this
@program #74:stymied
who = this.character;
if (is_player(who))
  (rpg = $local.rpg):secure();
  if (`who.sessile ! ANY => 1')
    return 1;
  endif
  trusted = {who, $sysobj.owner, @rpg.gms};
  n = 1;
  for t in (who.refused_origins)
    if ((t in trusted) && ("move" in who.refused_actions[n]))
      return n;
    endif
    n = n + 1;
  endfor
  ou = $object_utils;
  for t in (rpg.epermed_hazards)
    if ((where = ou:has_verb(who, t)) && (`verb_code(where[1], t) ! E_PERM => E_PERM' == E_PERM))
      return 1;
    endif
  endfor
endif
return 0;
.

@verb #74:"lumens" this none this
@program #74:lumens
lumens = infra = 0;
loc = this.character.location;
for t in (this.lights)
  lumens = lumens + t:lumens(loc);
  infra = infra + `t:infrared(loc) ! E_VERBNF => 0';
endfor
this.illuminance = infra;
return lumens;
"THX (#105941) - Sat Mar 31, 2001 - Added.";
"THX (#105941) - Thu Jun 28, 2001 - Added doll.illuminance to track \"light sources\" that are usable only by the doll (defined by :infrared() on the light object).";
.

@verb #74:"relight" this none this
@program #74:relight
{what, add} = args;
(rpg = $local.rpg):secure();
if (add)
  if (what in this.lights)
    return;
  else
    this.lights = setadd(this.lights, what);
  endif
  `this.character.location.lights = setadd(this.character.location.lights, this) ! ANY';
elseif (what in this.lights)
  this.lights = setremove(this.lights, what);
else
  return;
endif
`this.character.location:recalc_illum() ! ANY';
`this.character.location:announce_light(what, add) ! ANY';
"THX (#105941) - Sat Mar 31, 2001 - Added.";
.

@verb #74:"mod_anima" this none this
@program #74:mod_anima
":mod_anima(NUM amount, [STR message]) ==> Adds the amt argument to the player's current .anima.  Maximum of 1500; minimum of -1500.  All alterations are logged.  If supplied, and is a STRing, the message argument is broadcast to the player connected with the doll.";
(rpg = $local.rpg):secure();
{amt, ?msg = 0} = args;
max_log = 99;
max_change = (caller_perms() in rpg.grand_masters) ? 1500 | 49;
amt = min(max(amt, -max_change), max_change);
this.anima = min(max(this.anima + amt, -1500), 1500);
info = {this, amt, this.anima, time(), callers()[1][1..2]};
al = rpg.anima_log;
rpg.anima_log = {info, @al}[1..min(length(al) + 1, max_log)];
return (typeof(msg) == STR) ? rpg:playertell(this.character, msg) | 0;
.

@verb #74:"murder_anima" this none this
@program #74:murder_anima
":murder_anima(OBJ killer) ==> Modifies the killer's .Anima by a modifier based on the current standing of the anima of both killer and victim.  Generally speaking, killing `evil' slides you towards `good', and killing `good' slides you towards `evil'.";
(rpg = $local.rpg):secure();
{killer} = args;
if (!valid(kdoll = rpg:get_doll(killer)))
  return;
endif
victim = this.character;
if ((this == rpg.object_db.suit_doll) || (kdoll == rpg.object_db.suit_doll))
  return;
endif
va = this.anima;
ka = kdoll.anima;
mod = 0;
mod = this:murder_anima_mod(ka, va);
if ((is_player(victim) && ((vexp = rpg:exp(victim)) <= 5000)) && (vexp < rpg:exp(killer)))
  "Killing a 5000 or less EXP newbie is `evil', but only if you're more experienced than they are.";
  mod = mod - 10;
endif
if ($object_utils:isa(victim, $guest))
  "Guests don't count.  You miserable, cheating scum.";
  mod = 0;
elseif (is_player(victim) && this:drew_first_blood(killer))
  "His Eminence considers Revenge a neutral act; your Anima won't change for it.";
  mod = 0;
elseif (victim in rpg.death_db.temple:mark(kdoll.character))
  "Assassinations are not precisely neutral, but the targets are essentially voluntary, so it doesn't count.";
  mod = 0;
endif
if ((((goody = 1000 - ka) < 0) && (is_player(victim) || (!victim.animal))) && ((ka + va) > (goody / 10)))
  "Once you're really `good', killing simply doesn't cut it.  Save your animus for the really evil";
  mod = goody / 20;
endif
if (mod && (random(25) == 1))
  msg = (mod < 0) ? rpg.death_db.anima_slip_msgs[2] | rpg.death_db.anima_slip_msgs[1];
else
  msg = 0;
endif
kdoll:mod_anima(mod, msg);
"Hydros (#106189) - Sun Oct 10, 2004 - Added as a prototype of the new Good<->Evil system.";
"Hydros (#106189) - Sun Oct 21, 2004 - Tacked on the lengthy middle splurge to take into account -both- killer and victim's Anima.";
"Hydros (#106189) - Sun Nov 07, 2004 - Removed the aforementioned splurge in favour of reasonable, efficient code.";
"Hydros (#106189) - Fri Nov  26, 2004 - Added an occasional message to let the player know to what side of the Force they're headed.";
"Hydros (#106189) - Wed Dec 15, 2004 - Centralized the crucial formula in here to this:murder_anima_mod(), so if we need to call it elsewhere (like on #9334), the formula remains consistent.";
"Hydros (#106189) - Sun Oct 23, 2005 - The Suit of Armour's doll doesn't count.";
.

@verb #74:"murder_anima(old)" this none this
@program #74:murder_anima(old)
":murder_anima(OBJ killer) ==> Modifies the killer's .Anima by a modifier based on the current standing of the anima of both killer and victim.  Generally speaking, killing `evil' slides you towards `good', and killing `good' slides you towards `evil'.";
(rpg = $local.rpg):secure();
{killer} = args;
kdoll = rpg:get_doll(killer);
victim = this.character;
va = this.anima;
mod = va / 75;
if (killer in rpg.user_utils.hydros.anima_testers)
  if ((is_player(victim) && ((vexp = rpg:exp(victim)) <= 5000)) && (vexp < rpg:exp(killer)))
    "Killing a 5000 or less EXP newbie is `evil', but only if you're more experienced than they are.";
    mod = mod - 10;
  endif
  "We reverse negatives to positives and vice versa, so killing `evil' is `good' and killing `good' is `evil'.";
  mod = -mod;
  ka = kdoll.anima;
  ex = 0;
  if ((ka >= -250) && (ka <= 250))
    if (va > 250)
      ex = -5;
    elseif (va < -250)
      ex = 5;
    elseif ((va < 250) && (va > -250))
      ex = 0;
    endif
  elseif (ka > 250)
    if (va > 250)
      ex = -20;
    elseif (va < -250)
      ex = 10;
    elseif ((va <= 250) && (va >= -250))
      ex = -10;
    endif
  elseif (ka < -250)
    if (va > 250)
      ex = -5;
    elseif (va < -250)
      ex = 20;
    elseif ((va < 250) && (va > -250))
      ex = 5;
    endif
  endif
  mod = mod + ex;
  if ($object_utils:isa(victim, $guest))
    "Guests don't count.  You miserable, cheating scum.";
    mod = 0;
  elseif (is_player(victim) && this:drew_first_blood(killer))
    "His Eminence considers Revenge a neutral act; your Anima won't change for it.";
    mod = 0;
  elseif (victim in rpg.death_db.temple:mark(kdoll.character))
    "Assassinations are not precisely neutral, but the targets are essentially voluntary, so it doesn't count.";
    mod = 0;
  endif
  if (kdoll.anima > 1100)
    "Once you're really `good', killing simply doesn't cut it.  It can only bring you down.  Even if it's a guest, or a newbie, or revenge, or Assassination.";
    mod = -100;
  endif
  kdoll:mod_anima(mod);
endif
"Hydros (#106189) - Sun Oct 10, 2004 - Added as a prototype of the new Good<->Evil system.";
"Hydros (#106189) - Sun Oct 21, 2004 - Tacked on the lengthy middle splurge to take into account -both- killer and victim's Anima.";
.

@verb #74:"murder_anima_mod" this none this
@program #74:murder_anima_mod
"The formula for :murder_anima, herein centralized so that when it's called elsewhere, (like #9334), it remains consistent.";
(rpg = $local.rpg):secure();
{killer_anima, victim_anima} = args;
"Copied from Kagan (#102427):anima at Sun Nov  7 23:41:52 2004 PST, then reformatted by Corwin_MacGregor (#122047).";
return `(-victim_anima) / abs(victim_anima) ! E_DIV => 0' * (6 - ((abs(killer_anima) - abs(victim_anima)) / 250));
.

"***finished***