A dump of #259 (Generic Monster)

@create #7680 named generic monster:generic monster,monster
@prop #259."locations" {} rc
;;#259.("locations") = {"head", "left arm", "right arm", "chest", "neck", "abdomen", "right hand", "left hand", "right leg", "left leg", "left foot", "right foot"}
@prop #259."doll" #-1 r
@prop #259."alive" 0 r
@prop #259."body" #-3 r
@prop #259."animal" 0 r
@prop #259."cemetery" #1131 rc
@prop #259."gestate" 1000 rc
@prop #259."die_words" 0 rc
@prop #259."nat_prot" 0 rc
@prop #259."mine" {} r
@prop #259."mine_quality" 100 rc
@prop #259."corpse_msg" "A stinking pile of flesh and bone meal.  Worms crawl in and out of the hollow orbits of %p naked skull, squirming in the goo that was once %p mind." rc
@prop #259."magic_resist" 0 rc
@prop #259."generic_equipment" {} r
@prop #259."special_equipment" {} r
@prop #259."default_cast_msg" "%N %<waves> %p arms and %<mutters> weird syllables." rc
@prop #259."spell_deflection_msg" "%N %<deflects> the spell with an occult gesture." rc
@prop #259."flags" {} r
"#259.("initial_props") => E_PERM (Permission denied)
"#259.("initial_levels") => E_PERM (Permission denied)
@prop #259."last_attacker" #2 r
"#259.("money") => E_PERM (Permission denied)
"#259.("spell_immunities") => E_PERM (Permission denied)
@prop #259."props_to_clear_upon_death" {} r
;;#259.("props_to_clear_upon_death") = {"wielding", "wearing", "aggressor", "misc_notes", "combat_effects", "magic_effects", "connect_effects", "casting_effects", "freeze_effects", "view_effects", "armour_effects", "schedule", "att_schedule", "paralyzed"}
@prop #259."item_get_msg" "You fill the %[dname] with %[tvial_name] from the %t." rc
@prop #259."item_oget_msg" "%N fills the %[dname] with %[tvial_name] from the %t." rc
@prop #259."item_get_empty_msg" "You attempt to fill the %[dname] with %[tvial_name] from %t, but %t holds no more %[tvial_name]." rc
@prop #259."item_oget_empty_msg" "%N %<attempts> to fill the %[dname] with %[tvial_name] from %t, but %t holds no more %[tvial_name]." rc
@prop #259."corpse_name" 0 rc
@prop #259."vial_item" {} rc
;;#259.("vial_item") = {#6468}
;;#259.("seek_failed_msg") = "%n bounces off a topographical barrier."
;;#259.("home2") = #175
;;#259.("home") = #175
;;#259.("brief") = 1
;;#259.("simulating") = 0
;;#259.("listen_actions") = {}
"#259.("key") => E_PERM (Permission denied)
;;#259.("aliases") = {"generic monster", "monster"}
;;#259.("object_size") = {61307, 1141286558}

@verb #259:"act" this none this
@program #259:act
if (this.alive)
  $local.rpg:secure();
  doll = this.doll;
  if ((typeof(doll.aggressor) == LIST) && doll.aggressor)
    return (!doll:cannot_attack()) && doll:hit_aggressor();
  else
    return (!doll:cannot_move()) && this:procrastinate();
  endif
endif
return $nothing;
"THX1138 15-Apr-1997 -- Upgraded security and saved a few ticks.";
"Profane (#30788) - Tue Mar  9, 1999 - added paralysis checks.";
.

@verb #259:"acceptable" this none this
@program #259:acceptable
{what} = args;
rpg = $local.rpg;
return (what in this.mine) || ((caller_perms() in rpg.gms) && rpg.object_db:is_thing(what));
"return (args[1] in this.mine) || ((caller_perms() in $local.rpg.gms) && $object_utils:isa(args[1], $local.rpg.thing));";
"THX (#105941) - Fri Apr 20, 2001 - Upgrade.";
.

@verb #259:"duty" this none none rxd
@program #259:duty
"Duty the monster.";
"- check objects in minelist, returning unique ones and replacing others.";
"- set condition of all weapons in the minelist.";
"- wield any weapons and/or armour in the minelist.";
"- dispose of the corpse (if any).";
"- send the monster to its home.";
(rpg = $local.rpg):secure_x();
if (valid(doll = this.doll) && (parent(doll) != $garbage))
  doll.aggressor = doll.wielding = doll.wearing = {};
  rpg:s_i_n();
  this:check_mine();
  this:prime_mine();
  rpg:s_i_n();
  this:equip_mine();
  this:wealth_mine();
  this:rot_corpse();
  this.alive = 1;
  this:go_home();
  if (!callers())
    player:tell($string_utils:nn(this), " on duty in ", $string_utils:nn(this.location), ".");
  endif
else
  return E_NONE;
endif
.

@verb #259:"look_self" this none this
@program #259:look_self
pass(@args);
if (this.animal)
  this:look_health();
  this:look_gear();
else
  this:tell_contents(this:contents());
  this:look_health();
  this:look_gear();
endif
"DR 04-AUG-94 -- Change in order to make NPCs look more like players.";
.

@verb #259:"armour" this none this
@program #259:armour
":armour(penetration, hit-location[, attacker, weapon-used, damage])";
return (this.nat_prot * args[1]) + this.doll:protection(@args);
"THX1138 15-Apr-1997 -- Copied old verb to armour(old) and sped the thing up.";
.

@verb #259:"abort" this none none rxd
@program #259:abort
"abort <monster> -- Kill the monster's doll.";
rpg = $local.rpg;
who = callers() ? caller_perms() | player;
if (caller == this)
elseif (!(who in {@rpg.grand_masters, this.owner}))
  return E_PERM;
endif
s_u = $string_utils;
if (valid(doll = rpg:get_doll(this)))
  rpg.recycler:_recycle(doll);
  msg = s_u:nn(this) + " is no longer alive.";
else
  msg = s_u:nn(this) + " is not alive.";
endif
if (!callers())
  who:tell(msg);
  check = $object_utils:all_contents(this);
  for t in ({@this.mine, @this.special_equipment})
    check = setadd(check, t);
  endfor
  for t in (check)
    `t.unique ! ANY => 0' || (check = setremove(check, t));
  endfor
  check && who:tell(s_u:nn(this), " has unique items in its possession.  Please check the following carefully: ", s_u:nn(check));
endif
this.doll = $nothing;
this.alive = 0;
.

@verb #259:"recycle" this none this
@program #259:recycle
"Abort a rebirth and recalculate list of monsters.";
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
  return E_PERM;
endif
this:abort();
if (this.owner in $local.rpg.gms)
  fork (0)
    $local.rpg.vacuum:update_monsters();
  endfork
endif
return pass(@args);
.

@verb #259:"moveto" this none this
@program #259:moveto
if ((rpg = $local.rpg):trusted(caller_perms()))
elseif ((caller == this) || $perm_utils:controls(caller_perms(), this))
elseif (rpg:trusted(caller.owner) && (((typeof(source = `caller.source ! E_PROPNF, E_PERM => 0') == OBJ) && valid(source)) ? source == this.location | 1))
else
  return E_PERM;
endif
p = pass(@args);
if (this.alive)
  this:ok_alive_loc(this.location) || this:go_home();
endif
return p;
.

@verb #259:"procrastinate" this none this
@program #259:procrastinate
if ($local.rpg:trusted(caller_perms()) && valid(doll = this.doll))
  doll.act = 0;
endif
return $nothing;
.

@verb #259:"look_gear" this none this
@program #259:look_gear
if ((this.animal in {"phantom", "corpse"}) || (!this.animal))
  $local.rpg:look_gear(this, @args);
endif
.

@verb #259:"initialize" this none this
@program #259:initialize
":initialize() -- Update the monster population in ~vacuum.";
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
  return E_PERM;
endif
rpg = $local.rpg;
if (this.owner in rpg.gms)
  fork (0)
    rpg.vacuum:update_monsters();
  endfork
endif
this.doll = $nothing;
"this.rebirth_task = 0;";
return pass(@args);
"Profane 17-APR-97 -- ensure .doll is invalid";
.

@verb #259:"notify_attack notify_aggression notify_death notify_arrival notify_exit notify_hit notify_miss notify_dodge notify_parry notify_bully notify_salute" this none this
@program #259:notify_attack
":notify_attack(OBJ attacker, OBJ defender[, OBJ weapon])";
"-- Whenever someone is attacked.";
":notify_aggression(OBJ attacker, OBJ defender[, OBJ weapon])";
"-- The first time someone is attacked.";
":notify_death(OBJ victim[, OBJ killer])";
"-- When someone is killed.";
"";
":notify_arrival(OBJ who, OBJ exit)";
":notify_exit(OBJ who, OBJ exit)";
"-- When someone enters or leaves the room.  If they teleported, `exit' will be $nothing.";
"";
":notify_payment(OBJ from, NUM amount, STR denomination, NUM worth)";
"-- When someone gives money to your monster.  `worth' is the unit worth of the given denomination.  Multiple that value by `amount' to get the full worth of the transaction.";
"";
"notify_hit(OBJ attacker, OBJ defender, OBJ weapon, STR location, NUM|STR severity, NUM damage)";
"notify_miss(OBJ attacker, OBJ defender[, OBJ weapon])";
"notify_dodge(OBJ attacker, OBJ defender[, OBJ weapon])";
"notify_parry(OBJ attacker, OBJ defender[, OBJ weapon])";
"notify this monster that one of the above has occurred.";
"notify_bully(OBJ bully, OBJ bully-ee, OBJ weapon)";
"-- When someone uses `bully monster with <weapon>'";
"notify_salute(OBJ saluter, OBJ salutee, OBJ armour)";
"-- When someone salutes this in some way with their headgear.  For practical application, an example might be checking for the default salute_msg, which a monster might recognize as courtesy and react in kind.";
return;
.

@verb #259:"location" this none this
@program #259:location
return this.location;
.

@verb #259:"sub_mine" this none this
@program #259:sub_mine
"HACKED by Quinn (a little too late) 22-APR-93.  No longer needs wizperms.";
if ((caller_perms() == #259.owner) && (p = args[1] in (mine = this.mine)))
  this:set_mine(listset(mine, args[2], p));
endif
.

@verb #259:"is_controllable_by" this none this
@program #259:is_controllable_by
":is_controllable_by(caller_perms())";
return $perm_utils:controls(p = args[1], this) || $local.rpg:is_grandmaster(p);
.

@verb #259:"tell_contents" this none this
@program #259:tell_contents
":tell_contents(list)";
"Called from :look_self()";
c = args[1];
if ($recycler:valid(doll = this.doll))
  "These things have already been shown by 'look_gear'";
  for each_thing in ({@doll:wielding(), @doll.wearing})
    c = setremove(c, each_thing);
  endfor
endif
if (c)
  longear = gear = {};
  width = player:linelen();
  half = width / 2;
  player:tell("Carrying:");
  for thing in (c)
    cx = tostr(" ", thing:title());
    if (length(cx) > half)
      longear = {@longear, cx};
    else
      gear = {@gear, cx};
    endif
  endfor
  player:tell_lines($string_utils:columnize(gear, 2, width));
  player:tell_lines(longear);
endif
"Profane 26-APR-97 1248PDT -- s/valid/$recycler:valid";
.

@verb #259:"get_body_area_name" this none this rx
@program #259:get_body_area_name
":get_body_area_name(OBJ body_area)";
if ((names = this.locations) == $local.rpg.monster.locations)
  return E_NONE;
elseif (i = args[1] in this.doll.body_areas)
  return names[i];
else
  return E_NONE;
endif
.

@verb #259:"go_home" this none this
@program #259:go_home
":go_home() -- Send the monster home.";
"If the home's an exit, :move it through.  Else just move it into the room.";
(rpg = $local.rpg):secure();
if (!valid(home = this.home))
  this.owner:tell(this.name, " (", this, ") is homeless.");
elseif (this.location == home)
  "It's *already* home, no need to do anything.";
  return;
elseif ($object_utils:isa(home, $exit))
  valid(this.location) || this:moveto(rpg.cemetery.turf[1]);
  (this.location == `home.dest ! E_PERM') || home:move(this);
else
  try
    home:acceptable(this) && this:moveto(home);
    if (this.location != home)
      this.owner:tell(this.name, " (", this, "):go_home() fails because its home is not accepting it.  Player: ", #20:nn(player), "  Callers: ", toliteral(callers(1)), "  Current location: ", #20:nn(this.location));
      this:moveto(rpg.cemetery.turf[1]);
    endif
  except e (ANY)
    this.owner:tell(this.name, " (", this, "):go_home() fails with ", e[2]);
    this:moveto(rpg.cemetery.turf[1]);
  endtry
endif
"Hannibal 9/9/99 - Added try statement and home:acceptable(this) check";
.

@verb #259:"equip_mine" this none this
@program #259:equip_mine
":equip_mine()";
"Loop through the monster's mine list, wearing and wielding what it can.";
(rpg = $local.rpg):secure();
hands = (doll = this.doll).hands;
{weapon, armour, hand_and_a_half, natural_weapon, vacuum} = {rpg.weapon, rpg.armour, rpg.hand_and_a_half, rpg.natural_weapon, rpg.vacuum};
size = doll.size;
for foo in (this.mine)
  if (valid(foo) && (parent(foo) != $garbage))
    if (foo.location != this)
      foo:moveto(this);
    endif
    anc = rpg:parents(foo);
    if (hand_and_a_half in anc)
      foo:((hands == 1) ? "one" | "two")();
    elseif ((natural_weapon in anc) && (this:get_iprop("natural_weapons") == E_PROPNF))
      doll.natural_weapons = setremove(doll.natural_weapons, rpg.fists);
      doll.natural_weapons = setadd(doll.natural_weapons, foo);
    endif
    if ((weapon in anc) && (hands >= foo:hands(this, $nothing)))
      hands = hands - foo:hands(this, $nothing);
      doll.wielding = setadd(doll.wielding, foo);
      foo:do_wield(this);
    elseif (armour in anc)
      doll.wearing = setadd(doll.wearing, foo);
      foo:do_wear(this);
      if (size != 10)
        (vacuum:unique(foo) || ((foo.worn_area in {"necklace", "amulet"}) && (size > 5))) || (foo.size = (foo.size * size) / 10);
      endif
    endif
    rpg:s_i_f_i_n();
  else
    this.mine = setremove(this.mine, foo);
  endif
endfor
"Profane (#30788) - Tue Apr 20, 1999 - changed .hands to :hands() line 18-19";
"THX (#105941) - Tue Apr 25, 2000 - Added a hickety hack to size the monster's armour up/down to its size as needed.";
.

@verb #259:"prime_mine" this none this
@program #259:prime_mine
":prime_mine()";
"Prepare this monster's equipment to be readied.  Right now, this just means setting the `condition' of everything to `mine_quality'.";
(rpg = $local.rpg):secure();
qual = this.mine_quality;
for foo in (this.mine)
  if (((typeof(old = `foo.condition ! E_PROPNF => E_PROPNF') == NUM) && (old != qual)) && (!(foo.unique && (foo in this.generic_equipment))))
    foo:set_condition(qual);
  endif
endfor
.

@verb #259:"rot_corpse" this none this rx
@program #259:rot_corpse
":rot_corpse() -- Rot this monster's corpse, if it exists and is a child of $local.rpg.corpse.";
(rpg = $local.rpg):secure();
body = this.body;
if (body in rpg.object_db.generic_corpses)
elseif (!body.is_corpse)
  this:set_body(valid(body) ? rpg.corpse | $failed_match);
  return E_NONE;
else
  body:rot();
  this:set_body($recycler:valid(this.body) ? this.body | rpg.corpse);
endif
"Quinn 01-DEC-93 0244: Added.";
"Quinn 03-DEC-93 0114: Added :set_body call so you get a corpse more than just once.";
"StarDancer 16-DEC-95 added 'nother :set_body call so's a swept corpse 'll get put back too.";
"THX (#105941) - Thu Apr 19, 2001 - Changed 1's to rpg.corpse";
.

@verb #259:"announce_death" this none this
@program #259:announce_death
":announce_death([killer])";
"Announce the die words of this beast to its location.";
"Sub %N for dead monster and %D for killer (if given).";
rpg = $local.rpg;
if ($list_utils:assoc(rpg.magic_db, callers(), 4))
  "Death occured as a result of a damage-dealing spell. These have their own death messages, therefore the monster doesn't need to spew it's own scene.";
  return;
elseif ((elm = $list_utils:assoc("critical_hit", callers(), 2)) && elm[1].handles_death_msg)
  return;
endif
if (msg = this.die_words)
  {?killer = $nothing} = args;
  msg = $string_utils:pronoun_sub(msg, this, this, where = this.location, killer);
  for line in ((typeof(msg) == LIST) ? msg | {msg})
    rpg:rpg_announce_all_but(where, {this}, line);
  endfor
endif
"THX1138 15-Apr-1997 -- Scatter assignment, and saved a few ticks.";
"Profane (#30788) - Sun Mar 28, 1999 - just exit if death occurs from spell. So incinerated monsters don't dance around and expire in a pool of blood, they just incinerate.";
.

@verb #259:"corpse_msg" this none this
@program #259:corpse_msg
":corpse_msg()";
"-> What this monster's spawn of generic corpse should look like.";
return $string_utils:pronoun_sub(this.corpse_msg, this);
.

@verb #259:"get_corpse(19Apr2001)" this none this rx
@program #259:get_corpse(19Apr2001)
":get_corpse([killer])";
"Get a corpse for this monster, dependant on the value of `body'.";
"If body == #x and #x is valid and a child of $local.rpg.corpse, use that custom corpse.";
"If body == 1 (or any true value), create a new corpse and describe it as `corpse_msg'.";
"If a corpse is found, return {#corpse}.  Else, return false.";
"`killer' is who killed the beast.  Provided for future customization.";
{?killer = $nothing} = args;
(rpg = $local.rpg):secure();
body = this.body;
mama_corpse = rpg.corpse;
if (`body.is_corpse ! E_PROPNF => 0')
  "body.time_of_death = time();";
  "return {body};";
elseif ((body != 1) && (!valid(body)))
  return E_NONE;
elseif (typeof(body = rpg.recycler:_create(mama_corpse)) != OBJ)
  return E_QUOTA;
endif
body:prep_corpse(this, killer);
"body:set_name(name = \"corpse of \" + this.name);";
"body:set_aliases({name, \"corpse\", this.name, \"body\", \"cadaver\"});";
"body:set_description(this:corpse_msg());";
"body.victim = this;";
"body.time_of_death = time();";
"body.encumbrance = this.doll.size * 9;";
"if (((\"demon\" in (aliases = this.aliases)) || (\"avatar\" in aliases)) || (\"devil\" in aliases))";
"  body.vial_item = this.fluids[2];";
"  body.vial_name = body.vial_item.name;";
"elseif (\"golem\" in aliases)";
"  body.vial_item = $nothing;";
"endif";
return {this.body = body};
"THX (#105941) - Thu Mar 22, 2001 - Moved configuration to body:prep_corpse(this, killer).";
.

@verb #259:"deposit_remains" this none this
@program #259:deposit_remains
":deposit_remains([killer])";
"If a corpse exists; get it, move all our loot inside it, and move it to this location.";
"If no corpse; move this loot to this location.";
"`killer' is who killed the beast, passed for the benefit of :deposit_loot.";
$local.rpg:secure();
if (corpse = this:get_corpse())
  corpse = corpse[1];
  corpse:moveto(this.location);
  return this:deposit_loot(corpse, @args);
endif
return this:deposit_loot(this.location, @args);
"THX1138 15-Apr-1997 --  Copied old code to #259:deposit_remains(old) and rewrote with faster security and layout.";
.

@verb #259:"deposit_loot" this none this rx
@program #259:deposit_loot
":deposit_loot([location[, killer]])";
"Move all equipment (except those with a false .:keep_at_death) to the given location, which defaults to the monster's location.";
"`killer' is who killed the beast.  Provided for special occasions where, for example, you may want to deprive a dishonorable killer of the post-mortem prize.  Similiarly passed to :keep_at_death.";
$local.rpg:secure();
{?where = this.location, ?killer = $nothing} = args;
for loot in (this:contents())
  if (!loot:keep_at_death(killer))
    loot:moveto(where);
    if ((!(loot in this.special_equipment)) && `loot.condition != E_NONE ! ANY => 0')
      loot.condition = (min(loot.condition, 100) * (random(3) + 5)) / 8;
    endif
  endif
endfor
"THX1138 - 15-Apr-1997 -- Upgraded security.  Scatter assignment.  Copied old verb to #259:deposit_loot(old).";
"dalton (#116601) - Mon Jul  7, 2003 - Dropped loot will be a little degraded.  Make 'em get it repaired.";
.

@verb #259:"set_body set_corpse" this none this rx
@program #259:set_body
":set_body(OBJ corpse)";
"Set the corpse to be deposited when this creature dies.";
"#xxxxx -- If a caller-controlled kid of $local.rpg.corpse, use that object.";
"1      -- Create a generic corpse described as this.corpse_msg.";
"$failed_match -- No corpse.";
{corpse} = args;
rpg = $local.rpg;
if (corpse == 1)
  corpse = rpg.corpse;
elseif (corpse == 0)
  corpse = $failed_match;
endif
cp = caller_perms();
if ((caller != this) && (!this:is_controllable_by(cp)))
  return E_PERM;
elseif (typeof(corpse) != OBJ)
  return E_TYPE;
elseif (corpse == $failed_match)
  return this.body = corpse;
elseif (!corpse.is_corpse)
  return E_INVARG;
elseif ((corpse.owner == cp) || ((corpse.owner in rpg.gms) && (!corpse.is_specific)))
  return this.body = corpse;
else
  return E_NACC;
endif
"THX (#105941) - Thu Apr 19, 2001 - Modified to convert old accepted INT arguments to new object-only arguments.";
.

@verb #259:"description" this none this
@program #259:description
"Runs the description through pronoun_sub.";
rpg = $local.rpg;
if ((this.alive && (callers()[$][2] in {"@exam", "@examine"})) && (!(player in {@rpg.gms, @rpg.wannabees})))
  player:tell("Use 'examine' rather than '@examine' to look at LambdaRPG monsters.");
  kill_task(task_id());
endif
name = this:titlec();
desc = $string_utils:pronoun_sub(this.description, this, this, this.location, player);
desc = (typeof(desc) == LIST) ? desc | {desc};
desc = index(desc[1], name) ? desc | listinsert(desc, name, 1);
(this == #79322) && (desc = desc[1]);
return desc;
"Copied from Almost Human Bastard (#6475):description by Quinn (#19845) Wed Mar 16 16:55:06 1994 PST";
"THX (#105941) - Tue Nov 24, 1998 - Hacked this to prepend the critter's name if it isn't already there.";
"THX (#105941) - Tue Oct  3, 2000 - Hacked for dirt golem until Bartlebooth can fix it.";
.

@verb #259:"magic_effect" this none this rx
@program #259:magic_effect
":magic_effect(OBJ caster doll, OBJ target doll, NUM spell quality, OBJ spell skill) => NUM number directly SUBTRACTED from spell quality. i.e. magic resistance.";
"A simple hack to replace the old direct property ref..";
{caster_doll, target_doll, quality, spell} = args;
this = target_doll.character;
if ((spell in this.spell_immunities) && (caller_perms() in $local.rpg.gms))
  return 999;
elseif (spell.resist)
  return this.doll.(spell.resist);
endif
return this.magic_resist || 0;
"THX (#105941) - Tue Jan  4, 2000 - Added a spell_immunities property so that individual spells can be resisted easily without coding a whole verb.";
.

@verb #259:"die" this none this
@program #259:die
":die([killer]) -- KILL THE BEAST";
"- schedule a task to rebirth";
"- remove monster from aggressors list of all in vicinity";
"- clear monster's doll's wielding, wearing and aggressor lists";
"- announce the death to the corpse location";
"- deposit remains (corpse, loot, burial)";
(rpg = $local.rpg):secure();
cemetery = `this.cemetery ! E_PROPNF => rpg.cemetery';
if ((!this.alive) && (this.location == cemetery))
  return E_NONE;
endif
this.alive = 0;
if (this.gestate > 0)
  rpg.clock:add_death(this);
endif
for who in (this.location:contents())
  if (valid(doll = rpg:get_doll(who)))
    doll.aggressor = setremove(doll.aggressor, this);
  endif
endfor
doll = this.doll;
for property in (this.props_to_clear_upon_death)
  clear_property(doll, property);
endfor
claimed_stuff = doll.claimed_objects;
fork (rpg.gm_lag / 2)
  for item in (claimed_stuff)
    "Remove claim data from objects.";
    rpg:s_i_f_i_n();
    if ((valid(item) && rpg.object_db:is_thing(item)) && (item.claimed_by == this))
      item:unclaim_for(item.claimed_by);
      if (item.claimed_by != this)
        `doll.claimed_objects = setremove(doll.claimed_objects, item) ! ANY';
      endif
    endif
  endfor
endfork
{?killer = $nothing} = args;
rpg:s_i_n();
this:deposit_remains(killer);
this:announce_death(killer);
rpg:s_i_n();
this:moveto(cemetery);
fork (0)
  rpg.death_db.slayer_db:notify_death(killer, this);
endfork
"Profane 17-MAR-95 13:28 -- Added (Well, changed from old :die() to use new resurrection queue)";
"Slither 29-JUN-95 00:16 -- Added erasing of claimed objects.";
"THX1138 15-Apr-1997 -- Upgraded security, scatter assignment, and saved a few ticks.";
"Profane (#30788) - Tue May  8, 2001 - Erm clear out them new effect properties, yah?";
"Profane (#30788) - Sun May 27, 2001 - Move all that resetting props into a for loop with a property reference, what a concept.";
"Hannibal (#104762) - Monday May 13, 2002 - Only remove items from .claimed_objects that are no longer claimed.";
"Hydros (#106189) - Sun Aug 28, 2005 - Added a couple :s_i_n() calls to tackle the `zombified' monster problem we've been having--id est, monsters becoming !.alive, without moving to their proper resting place.";
"Hydros (#106189) - Tue Sep 20, 2005 - Added :notify_death call for Everin's Slayer Quests.";
.

@verb #259:"cancel_rebirth" this none this
@program #259:cancel_rebirth
":cancel_rebirth() -- Kill this monster's rebirth task.";
(rpg = $local.rpg):secure();
rpg.clock:remove_death(this);
"Profane 17-MAR-95 13:30EST - Changed from old :cancel_rebirth to use new resurrection queue";
"THX1138 (#105941) - Mon Nov 16, 1998 - Rewrote with fewer ticks.  Set the verb +d.";
.

@verb #259:"set_generic_equipment" this none this
@program #259:set_generic_equipment
":set_generic_equipment(LIST objects)";
"Set the generic equipment list to a list of objects";
"Each object should be a RPG generic";
"Returns a list of created objects.";
if (!((caller == this) || this:is_controllable_by(caller_perms())))
  return E_PERM;
endif
newequip = args[1];
"Rampant paranoia.  But even paranoids get E_TYPE errors.";
if (typeof(newequip) != LIST)
  return E_INVARG;
endif
for item in (newequip)
  if (typeof(item) != OBJ)
    return E_INVARG;
  endif
  "Seven's a nice number";
  $command_utils:suspend_if_needed(7);
endfor
this.generic_equipment = newequip;
return newequip;
"5/28/95 Slither -- first version";
.

@verb #259:"make_generic_equipment" this none this
@program #259:make_generic_equipment
":make_generic_equipment()";
"Creates clones of generic items based upon the contents of";
"this.generic_equipment.  The items are moved into this, and";
"the list of created items is returned.";
"See the comments in :set_generic_equipment for the data format";
(rpg = $local.rpg):secure();
dispenser = rpg.dispenser;
stuff = {};
for parent in (this.generic_equipment)
  if (valid(parent) && (parent(parent) != $garbage))
    new = dispenser:grab(parent, $nothing);
    if ((typeof(new) == OBJ) && valid(new))
      this.mine = setadd(this.mine, new);
      new.name = parent.name;
      new:moveto(this);
      stuff = {@stuff, new};
    endif
  else
    this.generic_equipment = setremove(this.generic_equipment, parent);
  endif
  rpg:s_i_f_i_n();
endfor
return stuff;
.

@verb #259:"set_special_equipment" this none this
@program #259:set_special_equipment
":set_special_equipment(LIST objects)";
"Set the special equipment list to a list of objects";
if (!((caller == this) || this:is_controllable_by(caller_perms())))
  return E_PERM;
endif
newequip = args[1];
"make sure its really a list of objects.";
if (typeof(newequip) != LIST)
  return E_INVARG;
endif
for item in (newequip)
  if (typeof(item) != OBJ)
    return E_INVARG;
  endif
  $command_utils:suspend_if_needed(5);
endfor
this.special_equipment = newequip;
return newequip;
"5/28/95 Slither -- first version";
.

@verb #259:"make_special_equipment" this none this rx
@program #259:make_special_equipment
":make_special_equipment()";
"Prepares the special equipment that the monster needs, based upon";
"the content's of the monster's .special_equipment property.";
"This amounts to making sure that the equipment exists, and moving";
"it to the monster if necessary.";
"The verb returns a list of the special equipment that was successfully gotten.";
(rpg = $local.rpg):secure();
stuff = {};
trash = $garbage;
for item in (this.special_equipment)
  if (valid(item) && (parent(item) != trash))
    "Good, still exists";
    loc = item.location;
    if ((loc != this) && (!children(item)))
      "Stupid thing has escaped!";
      item:moveto(this);
      (item.location == this) && loc:tell("The ", item.name, " taken from ", this.name, " disappears.");
    elseif (children(item) && (item.owner == rpg.owner))
      item = rpg.recycler:_create(item);
      item:moveto(this);
    endif
    if (item.location == this)
      stuff = {@stuff, item};
    endif
  else
    this.special_equipment = setremove(this.special_equipment, item);
  endif
  rpg:s_i_f_i_n();
endfor
return stuff;
"5/28/95 Slither -- first version";
.

@verb #259:"set_mine" this none this
@program #259:set_mine
":set_mine(LIST objects)";
"Set the RPG equipment this monster will be dutied with.";
"In its present form mainly for compatibility with old code.";
$local.rpg:secure();
return this.mine = args[1];
.

@verb #259:"check_mine" this none this
@program #259:check_mine
":check_mine()";
"Construct the monster's mine list from its components.";
"Basically, mainly here for compatibility with old code.";
"Returns new mine list.";
$local.rpg:secure();
mine = {@this:make_generic_equipment(), @this:make_special_equipment()};
return this:set_mine(mine);
.

@verb #259:"look_health" this none this
@program #259:look_health
$local.rpg:(verb)(this, @args);
.

@verb #259:"set_iprop add_iprop" this none this
@program #259:set_iprop
":add_ivalue(<property>, <value>) => Adds <property> to the list of ivalues on the monster.  These are stored in a list, rather than individual properties, to save db space.  Returns old value, if existed, 0 if none, or E_PROPNF if not on the doll.";
(rpg = $local.rpg):secure();
if ((caller == this) || (caller_perms() in {this.owner, rpg.owner}))
  doll = rpg.doll;
  {prop, value} = args;
  if (length(this.initial_levels) != length(this.initial_props))
    player:tell("Something is amiss.  The .initial_props property is not the same length as the .initial_levels property");
    return E_INVARG;
  elseif ($object_utils:defines_property(doll, prop))
    if (doll.(prop) == value)
      this:remove_iprop(prop);
    elseif (x = prop in this.initial_props)
      old = this.initial_levels[x];
      this.initial_levels[x] = value;
      return old;
    else
      this.initial_levels = {@this.initial_levels, value};
      this.initial_props = {@this.initial_props, prop};
      return 0;
    endif
  else
    return E_PROPNF;
  endif
else
  return E_PERM;
endif
"Mooshie (#106469) - Fri Jun 12, 1998 - Made this puppy +d. Plus changed the check for a valid prop from an error trapping check to an $object_utils:defines_property, that way we can avoid stuff that would bomb the birthing task, like trying to set !c properties defined on ancestors.";
.

@verb #259:"remove_iprop" this none this
@program #259:remove_iprop
":remove_iprop(<prop>) => removes prop from the list of initial values for the monster's doll.  Returns value of item deleted, or E_PROPNF, if it wasn't found.";
(rpg = $local.rpg):secure();
if (caller_perms() in {this.owner, rpg.owner})
  {prop} = args;
  if (x = prop in this.initial_props)
    old = this.initial_levels[x];
    this.initial_props = listdelete(this.initial_props, x);
    this.initial_levels = listdelete(this.initial_levels, x);
    return old;
  else
    return E_PROPNF;
  endif
else
  return E_PERM;
endif
.

@verb #259:"init*ial_levels" this none this
@program #259:initial_levels
(rpg = $local.rpg):secure_x();
{?values_only = 0} = args;
if (values_only)
  return {this.initial_props, this.initial_levels};
endif
items = {};
for n in [1..length(this.initial_props)]
  items = {@items, (this.initial_props[n] + ": ") + $string_utils:from_value(this.initial_levels[n], 1, 10)};
endfor
l = $string_utils:columnize(items, 2, 76);
player:tell($string_utils:center((tostr(this) + "  ") + this.name, 76, "_"));
for x in (l)
  player:tell(x);
endfor
player:tell($string_utils:space(76, "-"));
.

@verb #259:"flatline" this none this
@program #259:flatline
{?state = 0} = args;
$local.rpg:secure();
this.alive = state;
"THX1138 - 23-Apr-1997 --  Added so gms can directly kill their critters at the beginning of the monster:die() that they write.";
.

@verb #259:"pick_aggressor" this none this
@program #259:pick_aggressor
return $nothing;
.

@verb #259:"spell_deflection_msg" this none this
@program #259:spell_deflection_msg
{?targ = this, ?by = player} = args;
return $string_utils:pronoun_sub(this.(verb), targ, dobj = by);
"THX (#105941) - Mon Sep 11, 2000 - Pass 'target' to pronoun_sub as first arg and 'by' as second instead of other way around.  Also set dobj = by for messaging purposes.  Altered all <monster>.spell_deflection_msg properties appropriately.";
.

@verb #259:"whereare" this none none
@program #259:whereare
"Copied from Generic Anklebiter (#8872):whereare by THX1138 (#105941) Wed Apr  9 16:43:57 1997 PDT";
sorted = {};
for t in ($set_utils:intersection(children(this), $local.rpg.pcs))
  sorted = {@sorted, {t, t.name}};
endfor
sorted = $list_utils:sort_alist(sorted, 2);
s_u = $string_utils;
for t in (sorted)
  {critnum, critter} = t;
  player:tell(s_u:left(tostr(critter, " (", critnum, ")"), 32, " "), s_u:left(s_u:nn(critnum.location), 47));
endfor
player:tell("Living monsters: ", length(sorted), "  ... ", this.name, ":", verb, " complete.");
.

@verb #259:"receive_damage_poison" this none this
@program #259:receive_damage_poison
"Copied from $local.rpg.monster (#1920):receive_damage_poison by Irin (#106049) Wed Mar 11 20:56:27 1998 PST";
":receive_damage_poison(OBJ source, OBJ poison, INT quality, LIST body areas)";
$local.rpg:secure();
{source, poison, qual, areas} = args;
if (this.animal in {"golem", "corpse", "demon", "phantom", "plant"})
  return 0;
else
  return poison:effect(this.doll, source, qual, areas);
endif
.

@verb #259:"random_hit_location" this none this
@program #259:random_hit_location
return 0;
.

@verb #259:"set_animal" this none this
@program #259:set_animal
(rpg = $local.rpg):secure();
if (caller_perms() in {rpg.owner, this.owner})
  {?animal = 0} = args;
  if (animal in rpg.object_db.ok_animals)
  elseif (!animal)
    animal = 0;
  else
    return E_INVARG;
  endif
else
  return E_PERM;
endif
if (parent(this).animal == animal)
  clear_property(this, "animal");
else
  this.animal = animal;
endif
return animal;
.

@verb #259:"notify_forgive" this none this
@program #259:notify_forgive
":notify_forgive(OBJ who) => Who wishes to cease combat with the monster.  If monsters wishes to cease combat with who, it should remove who from it's aggressor list, and remove itself from who's aggressor list, then print an appropriate message.";
return 0;
.

@verb #259:"get_iprop" this none this
@program #259:get_iprop
(rpg = $local.rpg):secure();
{prop} = args;
if (slot = prop in this.initial_props)
  return this.initial_levels[slot];
else
  return `rpg.doll.(prop) ! E_PROPNF => E_PROPNF';
endif
.

@verb #259:"removal" this none this
@program #259:removal
for s in (this.command_ok)
  if ((s in #46.gms) || (s == #97170))
  else
    this.command_ok = setremove(this.command_ok, s);
  endif
endfor
.

@verb #259:"get_value" this none this
@program #259:get_value
if ($local.rpg:trusted(caller_perms()))
  return this.(args[1]);
else
  return E_PERM;
endif
.

@verb #259:"set_value" this none this
@program #259:set_value
":set_value(STR property_name, value) -- Sets this.(property_name) to `value'";
{prop, value} = args;
rpg = $local.rpg;
if (rpg:trusted(cp = caller_perms()) && ((cp in {this.owner, rpg.thing.owner}) || (caller == this)))
  return this.(prop) = value;
else
  return E_PERM;
endif
"THX1138 22-MAR-1997 -- Scatter assignment and s /db/rpg/g 1-$ for commonality.";
.

@verb #259:"reinitialize" this none this
@program #259:reinitialize
":reinitialize() triggers a reset of the critters stats to the setting in :init(1).";
if (caller == this)
  doll = this.doll;
  rpg_doll = (rpg = $local.rpg).doll;
  doll_props = setremove(properties(rpg_doll), "character");
  rpg:s_i_n();
  initial_props = this.initial_props;
  initial_levels = this.initial_levels;
  for slot in [1..length(initial_props)]
    att = initial_props[slot];
    stat = initial_levels[slot];
    if (stat != rpg_doll.(att))
      doll.(att) = stat;
      doll_props = setremove(doll_props, att);
    endif
  endfor
  for att in (doll_props)
    clear_property(doll, att);
  endfor
  rpg:s_i_n();
endif
"THX (#105941) - Wed Nov  4, 1998 - Added.";
.

@verb #259:"birth" this none none rxd
@program #259:birth
":birth() -- Make it live!";
"If already alive, reset stats to initial state.";
rpg = $local.rpg;
if (!rpg:trusted((c = callers()) ? caller_perms() | player))
  return E_PERM;
elseif (this.owner in rpg.gms)
  this.doll = doll = rpg:create_pc(this);
  "doll.free_will = 0;";
  this:cancel_rebirth();
  this:reinitialize();
  this:duty();
  if (!c)
    player:tell($string_utils:nn(this), " has been reset with ", $string_utils:nn(doll), " as its doll.");
  endif
  return doll;
else
  return E_NACC;
endif
"Quinn 11-OCT-95 1150: Made verb !d, removed $object_util:has_property() check in att reset loop.";
"Profane 28-JUN-96 2247EDT -- No fork, suspend.";
"Profane 24-MAR-97 1043PST -- 1.8ify and PUT BACK THE DAMN SUSPEND AND YOU BETTER LEAVE IT THAT WAY GODDAMN YOU.";
"THX (#105941) - Sun Jun  7, 1998 - Changed code to use initial_props and initial_levels instead of iprops.  Clear all other props afterwards.";
"THX (#105941) - Tue Nov 3rd, 1998 -- Moved the iprop stuff to :reinitialize().";
"THX (#105941) - Wed Nov  4, 1998 - Added.  Use call to :reinitialize() instead of doing the iprop stuff in here.";
.

@verb #259:"set_last_attacker" this none this
@program #259:set_last_attacker
":set_last_attacker(attacker OBJ) => this.last_attacker   sets the .last_attacker.";
$local.rpg:secure();
return this.last_attacker = args[1];
.

@verb #259:"ok_alive_loc" this none this
@program #259:ok_alive_loc
{where} = args;
return valid(where) && (where.owner in {@$local.rpg.gms, @$local.rpg.object_db.grandparented});
.

@verb #259:"moveto(old)" this none this
@program #259:moveto(old)
rpg = $local.rpg;
(((caller == this) || $perm_utils:controls(caller_perms(), this)) || (rpg:trusted(caller.owner) && (((typeof(source = `caller.source ! E_PROPNF, E_PERM => 0') == OBJ) && valid(source)) ? source == this.location | 1))) || rpg:secure();
p = pass(@args);
if (this.alive)
  this:ok_alive_loc(this.location) || this:go_home();
endif
return p;
"these = {};";
"what = this;";
"monster = (rpg = $local.rpg).monster;";
"while (what != monster)";
"  these = listappend(these, what.owner);";
"  what = parent(what);";
"endwhile";
"p = rpg:trust(@these) ? pass(@args) | E_PERM;";
"Mooshie (#106469) - Thu Dec 24, 1998 - I don't think we need the elaborate :trust call, and the :go_home setup was causing infinite loops.";
"THX1138 (#105941) - Thu Dec 31, 1998 - Re-enabled the :go_home() feature.  Let's fix those infinite loops instead of sweeping them under the carpet.";
.

@verb #259:"do_follow do_unfollow" this none this
@program #259:do_follow
"Copied from Helania (#4777):do_follow by THX1138 (#105941) Wed Sep 29 07:58:24 1999 PDT";
rpg = $local.rpg;
if (rpg:is_grandmaster(caller_perms()))
  {who} = args;
  this:do((verb[4..$] + " ") + tostr(who));
endif
.

@verb #259:"wealth_mine" this none this
@program #259:wealth_mine
":wealth_mine() => treasure list  This verb takes the contents of the monster's .money property and creates the appropriate amount of wealth.  Examples:  {{0, 0}, {6, 1}} would have the critter start with no copper and random(6) silver.  {{0, 0}, {3, 0}, {1, 0}, {4, 0}} would result in 3 silver, 1 gold, and a gem of value 40 gold.";
loot = {};
if (this.money)
  (rpg = $local.rpg):secure();
  baseline = rpg.object_db.obj_money;
  n = 0;
  for t in (this.money)
    {amount, randomize} = t;
    n = n + 1;
    if (amount)
      if (n < 4)
        x = baseline[n]:mint();
        x:set_quantity(randomize ? random(amount) | amount);
      else
        x = rpg.user_utils.thx1138:jewel(amount, $nothing, !randomize);
      endif
      loot = {@loot, x};
    endif
  endfor
  loot && this:set_mine({@this.mine, @loot});
  for t in (loot)
    t:moveto(this);
  endfor
endif
return loot;
"THX (#105941) - Mon Oct 18, 1999 - Added.";
.

@verb #259:"make_fertile" this none this
@program #259:make_fertile
"Usage: make_fertile()  => Makes this temporarily fertile if permitted.";
"       make_fertile(1) => Resets this to unfertile.";
$local.rpg:secure();
if (args)
  this.f = 0;
elseif (this.f)
else
  this.f = 1;
  fork (0)
    this.f = 0;
  endfork
endif
.

@verb #259:"magic_effect(obsolete)" this none this rx
@program #259:magic_effect(obsolete)
":magic_effect(OBJ caster doll, OBJ target doll, NUM spell quality, OBJ spell skill) => NUM number directly SUBTRACTED from spell quality. i.e. magic resistance.";
"A simple hack to replace the old direct property ref..";
{caster_doll, target_doll, quality, spell} = args;
this = target_doll.character;
if ((spell in this.spell_immunities) && (caller_perms() in $local.rpg.gms))
  return 999;
endif
return this.magic_resist || 0;
return this.doll.(spell.resist) || 0;
"THX (#105941) - Tue Jan  4, 2000 - Added a spell_immunities property so that individual spells can be resisted easily without coding a whole verb.";
.

@verb #259:"set_home" this none this
@program #259:set_home
{where} = args;
if (caller == this)
  $local.rpg:secure();
  `this.home = where ! ANY';
endif
.

@verb #259:"hidden_verbs" this none this
@program #259:hidden_verbs
{who} = args;
prev = pass(who);
monster = (rpg = $local.rpg).monster;
if (!rpg:trusted(who))
  ancestors = $object_utils:ancestors(this);
  ancestors = ancestors[1..monster in ancestors];
  for t in (ancestors)
    prev = {@prev, {t, "birth", {"this", "none", "none"}}};
    prev = {@prev, {t, "duty", {"this", "none", "none"}}};
  endfor
  prev = {@prev, {monster, "whereare", {"this", "none", "none"}}};
endif
if ((who != this.owner) && (!rpg:is_grandmaster(who)))
  prev = {@prev, {monster, "abort", {"this", "none", "none"}}};
endif
if (!this:command_ok(who))
  prev = {@prev, {$local.puppet, "com*mand", {"this", "at/to", "any"}}};
endif
if (!this:monitor_ok(who))
  prev = {@prev, {$local.puppet, "mon*itor", {"this", "none", "none"}}};
endif
if (!(who in this.tell_forward))
  prev = {@prev, {$local.puppet, "ign*ore", {"this", "none", "none"}}};
endif
if (who != this.owner)
  prev = {@prev, {$gendered_object, "@gen*der", {"this", "is", "any"}}};
endif
return prev;
.

"#259:65 --- Permission denied";
@verb #259:"notify_iceshell" this none this
@program #259:notify_iceshell
{type, announce} = args;
return;
.

@verb #259:"get_corpse(old)" this none this rx
@program #259:get_corpse(old)
":get_corpse([killer])";
"Get a corpse for this monster, dependant on the value of `body'.";
"If body == #x and #x is valid and a child of $local.rpg.corpse, use that custom corpse.";
"If body == 1 (or any true value), create a new corpse and describe it as `corpse_msg'.";
"If a corpse is found, return {#corpse}.  Else, return false.";
"`killer' is who killed the beast.  Provided for future customization.";
(rpg = $local.rpg):secure();
body = this.body;
mama_corpse = rpg.corpse;
if (`body.is_corpse ! E_PROPNF => 0')
  body.time_of_death = time();
  return {body};
elseif (!body)
  return E_NONE;
elseif (typeof(body = rpg.recycler:_create(mama_corpse)) != OBJ)
  return E_QUOTA;
endif
body:set_name(name = "corpse of " + this.name);
body:set_aliases({name, "corpse", this.name, "body", "cadaver"});
body:set_description(this:corpse_msg());
body.victim = this;
body.time_of_death = time();
body.encumbrance = this.doll.size * 10;
if ((("demon" in (aliases = this.aliases)) || ("avatar" in aliases)) || ("devil" in aliases))
  body.vial_item = this.fluids[2];
  body.vial_name = body.vial_item.name;
elseif ("golem" in aliases)
  body.vial_item = $nothing;
endif
return {this.body = body};
.

@verb #259:"quietbirth" this none none
@program #259:quietbirth
if (caller == $local.rpg.owner)
  this:birth();
endif
"THX (#105941) - Sun Apr 15, 2001 - Had to add this so G_M can force_input the births from the cemetery without getting spammed with afterbirth feedback.";
.

@verb #259:"get_corpse" this none this rx
@program #259:get_corpse
":get_corpse([killer])";
"Get a corpse for this monster, dependant on the value of `body'.";
"If body == #x and #x is valid and #x.is_specific => TRUE, use that custom corpse.";
"If valid(body), create a new corpse and describe it as `corpse_msg'.";
"If a corpse is found, return {#corpse}.  Else, return false.";
"`killer' is who killed the beast.  Provided for future customization.";
{?killer = $nothing} = args;
(rpg = $local.rpg):secure();
body = this.body;
if (`body.is_specific ! ANY => 0')
elseif (valid(body))
  if (typeof(body = rpg.recycler:_create(body)) != OBJ)
    return E_QUOTA;
  endif
else
  return E_NONE;
endif
body:prep_corpse(this, killer);
return {this.body = body};
"THX (#105941) - Thu Mar 22, 2001 - Moved configuration to body:prep_corpse(this, killer).";
.

@verb #259:"choose_weaponry" this none this
@program #259:choose_weaponry
if (caller == this)
  doll = this.doll;
  rpg = $local.rpg;
  rank = weapon = 0;
  for t in ($object_utils:all_contents(this))
    temp = rank;
    rating = `rpg:rate_weapon(t, {this, this}) ! ANY => 0';
    if (rating && t:may_wield(this))
      rank = max(rank, rating * doll:total(t.skill));
    endif
    weapon = (temp == rank) ? weapon | t;
  endfor
  wielded = {rank, weapon};
  for t in (doll.natural_weapons)
    temp = rank;
    rating = `rpg:rate_weapon(t, {this, this}) ! ANY => 0';
    if (rating)
      rank = max(rank, rating * doll:total(t.skill));
    endif
    weapon = (temp == rank) ? weapon | t;
  endfor
  if (rank > wielded[1])
    for t in (doll.wielding)
      this:do(strsub(`this.dispose_weapon ! ANY => "unwield %weapon"', "%weapon", tostr(t)));
    endfor
  else
    this:do(tostr("wield ", wielded[2]));
  endif
endif
.

@verb #259:"purge_AHaB" this none this
@program #259:purge_AHaB
"this:purge_relationships() => delete useless sets of relationship data.";
rpg = $local.rpg;
{doll, o_u, gms} = {rpg.doll, $object_utils, rpg.gms};
for set in (r = this:get_prop("relationships"))
  d = set[1];
  (((o_u:isa(d, doll) && (typeof(char = d.character) == OBJ)) && is_player(char)) && (!(char in gms))) || (r = setremove(r, set));
  rpg:s_i_f_i_n();
endfor
return this:set_prop("relationships", r);
.

"***finished***