@create #8868 named generic poison:generic poison,poison
@prop #1760."data" {} r
@prop #1760."task" 1698963992 r
@prop #1760."delayed" 0 rc
@prop #1760."poisoner" "empty slot" rc
@prop #1760."death_msg" "" rc
@prop #1760."stun_msg" "" rc
@prop #1760."damage_msg" "" rc
@prop #1760."ostun_msg" "" rc
@prop #1760."odamage_msg" "" rc
@prop #1760."odeath_msg" "" rc
@prop #1760."cure_msg" "" rc
@prop #1760."ocure_msg" "" rc
;;#1760.("manufactured") = 947275400
;;#1760.("worth") = -1
;;#1760.("condition") = E_NONE
;;#1760.("odrop_failed_msg") = "%<tries> to drop %t but fails!"
;;#1760.("odrop_succeeded_msg") = "%<drops> %t."
;;#1760.("otake_succeeded_msg") = "%<picks> up %t."
"#1760.("key") => E_PERM (Permission denied)
;;#1760.("aliases") = {"generic poison", "poison"}
;;#1760.("description") = {"Ok, so here's how it works:", "", "Design the effect object with combat and/or armour and/or magic effects ", "and make it a child of Generic Armour/Combat/Magic Effect (#8868) as ", "appropriate.", "", "Set the .effects to be {\"magic_effects\", \"combat_effects\"} or whatever ", "you've defined.", "", "Now you can use this:add(<doll>) and this:remove(<doll>) to add/remove ", "this object from the correct effects properties on <doll>.  Whee."}
;;#1760.("object_size") = {11716, 1141286558}

@verb #1760:"effect" this none this
@program #1760:effect
"poison:effect(OBJ doll, OBJ source, INT quality, LIST body areas) => Poison the target with whatever the poison does.";
$local.rpg:secure();
"We need some sort of dollish hook here.  Like maybe the following?";
"This might be able to substitute for monster:receive_damage_poison.";
{doll, source, qual, areas} = args;
if (typeof(effect = doll:chemical_effects(source, this, qual)) != INT)
  return;
endif
qual = qual - effect;
"Such a hook should probably be checked in :delayed_effect too.";
if (this.delayed)
  this:schedule(doll, source, qual, areas);
endif
this:announce_poisoning(doll, source, qual, areas);
this:instant_effect(doll, source, qual, areas);
.

@verb #1760:"schedule" this none this
@program #1760:schedule
":schedule(OBJ doll, OBJ source, INT quality, LIST body areas) => Schedule an even with the root poison task.";
"We're not using doll.schedule, well, because it's icky.";
{doll, source, qual, areas} = args;
$local.rpg:secure();
root = $code_utils:verb_loc();
root.data = listappend(root.data, {this, doll, source, qual, areas});
this:add(doll);
if (!$code_utils:task_valid(root.task))
  fork t (0)
    n = length(root.data);
    while (root.data)
      current = root.data[1];
      if ((!is_player(current[3])) || (`idle_seconds(current[3]) ! E_INVARG' != E_INVARG))
        fork (0)
          current[1]:delayed_effect(@current[2..$]);
          current[1]:remove(current[2]);
        endfork
        root.data = root.data[2..$];
      endif
      n = n - 1;
      suspend(random(20) + 5);
      if (n < 1)
        suspend(200 + random(100));
        n = length(root.data);
      endif
    endwhile
  endfork
  root.task = t;
endif
"THX (#105941) - Wed Oct 13, 1999 - Changed listappend(this.data to listappend(root.data on line 5";
"Profane (#30788) - Wed Feb 14, 2001 - disconnected players get poison saved for them when they come back on.";
.

@verb #1760:"instant_effect" this none this
@program #1760:instant_effect
":instant_effect(OBJ doll, OBJ source, INT quality, LIST body areas) => What happens when the poison first takes effect.";
return 0;
.

@verb #1760:"delayed_effect" this none this
@program #1760:delayed_effect
":delayed_effect(OBJ doll, OBJ source, INT quality, LIST body areas) => What happens when they've been poisoned for a while.";
"This will get called within 5 minutes of the poisoning.  If it wants to continue the effects, it should call :schedule again, presumably with decreased quality (unless the poison is permanent :>).";
return 0;
.

@verb #1760:"announce_poisoning" this none this
@program #1760:announce_poisoning
":announce_poisoning(OBJ doll, OBJ source, INT quality, LIST body areas) => Hi, we'd like you to know that you've been poisoned.";
"If the poison was from a weapon, tell the player, mention what part of them feels queasy, etc, etc.";
return 0;
.

@verb #1760:"damage" this none this
@program #1760:damage
":damage(OBJ doll, OBJ amount, [STR type]) => doll takes damage from poison.  Like :receive_damage, but no critical hits and no armour or other wackiness.  Damage _is_ modified for size.";
"We _may_ want to check some dollish hook, so there is some sort of protection possible against poisons.  At least monsters will be covered in their :receive_damage_poison, though.  I'll stick the hook in as soon as I figure out what it should be.";
"The optional type arg should probably be \"inj\", \"ins\", or \"fat\", though it _could_ be something wacky like pot ;).  Not sure if there's a point to this, because ins and fat don't affect shock rolls anymore, but what the heck.";
(rpg = $local.rpg):secure();
{doll, amount, ?type = "inj"} = args;
who = doll.character;
amount = amount / doll.size;
if (amount < 1)
  ret_val = 0;
  msgname = "cure_msg";
else
  resolve = rpg:resolve(who, "shock", -amount);
  "We do the resolve _first_, to give shock a chance to improve.";
  doll:mod_att(type, amount, 0);
  end = doll.end;
  "You can announce your own messages from the ret_val = value if you feel like it.";
  if (((killer = doll:read_misc_note(this.poisoner)) != E_PROPNF) && (resolve < (-(4 * end))))
    ret_val = -9;
    msgname = "death_msg";
  elseif (resolve < (-end))
    ret_val = -3;
    msgname = "stun_msg";
  else
    ret_val = -2;
    msgname = "damage_msg";
  endif
endif
if (msg = this:(msgname)(who))
  if ((type == "inj") && (ret_val != 0))
    rpg:tell_damage(who, msg);
  else
    rpg:playertell(who, msg);
  endif
endif
if (msg = this:("o" + msgname)(who))
  rpg:rpg_announce_all_but(who.location, {who}, msg);
endif
if (ret_val == -9)
  doll:die(killer, this);
endif
return ret_val;
"Profane (#30788) - Tue Jan  2, 2001 - Move messaging inside this verb to avoid forking off the :die, which was dumb code.";
.

@verb #1760:"make_fertile" this none this
@program #1760:make_fertile
":make_fertile() -- Temporarily toggles the 'f' bit, allowing GMs (and not builders in general) to create a child of adventure gear.  To be used as follows:";
";;#equipment:make_fertile();return player:_create(#equipment)";
"Where '#equipment' is the number of the object you're wanting to create.  The blank object returned will be your child.  Congratulations, Mommy.";
if ($local.rpg:trusted(caller_perms()))
  if (!this.f)
    this.f = 1;
    fork (0)
      this.f = 0;
    endfork
  endif
  return this.f;
endif
"Quinn 09-JUL-93 0343: Changed :trust call to :trusted(caller_perms()); Updated comment lines;  eliminated check for ownership in first 'if'.  Let it crash if a kid not owned by GM uses the parent; Now returns status of f-bit.";
.

@verb #1760:"cancel" this none this
@program #1760:cancel
":cancel(LIST element in this.data) => remove poisoning task from the queue";
$local.rpg:secure();
{elem} = args;
root = $code_utils:verb_loc();
if (elem in root.data)
  root.data = setremove(root.data, elem);
endif
return length(root.data);
.

@verb #1760:"death_effect" this none this
@program #1760:death_effect
{victim, attacker} = args;
if (caller.character != victim)
  return 0;
endif
(rpg = $local.rpg):secure();
for item in (rpg.poison.data)
  if (item[2] == caller)
    rpg.poison.data = setremove(rpg.poison.data, item);
  endif
endfor
this:remove(caller);
return 0;
.

@verb #1760:"schedule(old)" this none this
@program #1760:schedule(old)
":schedule(OBJ doll, OBJ source, INT quality, LIST body areas) => Schedule an even with the root poison task.";
"We're not using doll.schedule, well, because it's icky.";
{doll, source, qual, areas} = args;
$local.rpg:secure();
root = $code_utils:verb_loc();
root.data = listappend(this.data, {this, doll, source, qual, areas});
if (!$code_utils:task_valid(root.task))
  fork t (0)
    n = length(root.data);
    while (root.data)
      current = root.data[1];
      fork (0)
        current[1]:delayed_effect(@current[2..$]);
      endfork
      root.data = root.data[2..$];
      n = n - 1;
      suspend(random(20) + 5);
      if (n < 1)
        suspend(200 + random(100));
        n = length(root.data);
      endif
    endwhile
  endfork
  root.task = t;
endif
.

@verb #1760:"death_msg stun_msg damage_msg odeath_msg ostun_msg odamage_msg cure_msg ocure_msg" this none this
@program #1760:death_msg
msg = this.(verb);
return msg ? $string_utils:pronoun_sub(msg, @args) | "";
.

@verb #1760:"damage(old)" this none this
@program #1760:damage(old)
":damage(OBJ doll, OBJ amount, [STR type]) => doll takes damage from poison.  Like :receive_damage, but no critical hits and no armour or other wackiness.  Damage _is_ modified for size.";
"We _may_ want to check some dollish hook, so there is some sort of protection possible against poisons.  At least monsters will be covered in their :receive_damage_poison, though.  I'll stick the hook in as soon as I figure out what it should be.";
"The optional type arg should probably be \"inj\", \"ins\", or \"fat\", though it _could_ be something wacky like pot ;).  Not sure if there's a point to this, because ins and fat don't affect shock rolls anymore, but what the heck.";
(rpg = $local.rpg):secure();
{doll, amount, ?type = "inj"} = args;
amount = amount / doll.size;
if (amount < 1)
  return 0;
endif
resolve = rpg:resolve(doll.character, "shock", -amount);
"We do the resolve _first_, to give shock a chance to improve.";
doll:mod_att(type, amount, 0);
end = doll.end;
"You can announce your own messages from the return value if you feel like it.";
if (((killer = doll:read_misc_note(this.poisoner)) != E_PROPNF) && (resolve < (-(4 * end))))
  fork (0)
    doll:die(killer, this);
  endfork
  return -9;
elseif (resolve < (-end))
  return -3;
else
  return -2;
endif
.

"***finished***