/*

filename    function
password    mfxmas
changedby   Mesmer
EmailAdd    
request     compile
version     52
END HEADER*/

#include

%zone function
title ""
lifespan 20
reset RESET_NOT
creators {"bakka"}

notes
"&lThis Zone is not a zone at all.  Like basis.zon it is a integral part
of the mud containing nothing but reuseable dil code.  The code comes from
various sources and is intended to allow builders to better utilize already
created dil functions.  After all there is no need to reinvent the wheel.

Changes
12/07/95 - project started
12/07/95 - wander_zones
12/07/95 - scramble
12/07/95 - guard_level
12/10/95 - stealdil
12/10/95 - safe_room
01/06/95 - combat_mag
03/20/96 - added General channel functions.   channel and recieve
03/20/96 - added new timer on blow away function.
04/09/96 - Added teach_q_block and shop_q_block.
04/29/96 - Added direction
05/08/96 - Added shopkeepers
05/11/96 - Added generic 'give' routine which reacts on special players.
15/10/96 - Added fido dil to take over for special
10/23/96 - fixed bug in clan channel
11/28/96 - Made it so that shopkeepers have their money stored on their home
          room to assure a daily maxmoney
          12/21/96 removed old channel1 and channel2 that are no longer used.
02/05/97 - fixed clan channels to deal with new extra format
06/30/97 - Added storage
07/04/97 - Fixed Shopkeepers so they will only work if they can defend
          themselves.
07/08/97 - Fixed the wpntype dil.
07/22/97 - Added aggressive.
09/27/98 - Added guard_way function as an alternative to guard_dir
xx/xx/99 - Around may-june? Storage broken in real commands and put in commands.zon
12/27/99 - Many things added since 98. 26th and 27th, force_move, death_room
          and evaluate, to replace the according dils. Teamwork and rescue
          to be fixed.
01/04/00 - Changed wander_zones, added global_wander
11/19/00 - added vlevel_restrict to allow virtual level restrictions -Ratlin/Darg
"


/* A short description of each function.  See comments next to each dil
  for implementation notes.

wander_zones -  Allows a mob to wander around more than one zone, but not
               all zones.  You specify what zones they mob can wander.
               Has optional intelligence which allows the opening and
               closeing of doors.

scramble     -  Scramble the exits on a room.  Simple way to turn a few
               rooms into a major maze.  Use with extreem care, too much
     causes players to go nuts.

guard_level  -  Guard an exit by a level range.

stealdil     -  Randomly Steal an object from a player


channel and recieve alternative channel updates made to be put on players.
safe_room    -  Make a room safe from all sorts of attacks by pcs
combat_mag   - Replaces the special fun COMBAT_MAGIC and COMBAT_HEAL
direction    - Returns the direction in one of NORTH - DOWN.

aggressive   - make mobs aggressive to certain sex, alignment, race, level,
              etc.
*/


%dil

dilbegin obey();
var
u:unitptr;
arg:string;
code
{
heartbeat:=PULSE_SEC*3;
:start:
wait (SFB_CMD,command("tell"));
arg:=argument;
if (activator!=self.master)
goto start;

u:=findunit(self,arg,FIND_UNIT_HERE,null);
if (u!=self)
goto start;

if (arg =="")
     {
 act("Command $3N to do what?",
     A_ALWAYS,self.master,null,self,TO_CHAR);
   goto start;
   }

block;
 act("You command $2n to '$3t'",
     A_ALWAYS, self.master,self,arg,TO_CHAR);
 act("$1n commands you to '$2t'",
     A_ALWAYS,self.master, arg, self,TO_VICT);
exec (arg,self);
goto start;
}
dilend

dilbegin aware obey_animal();
code
{
heartbeat:=PULSE_SEC*3;
dilcopy ("obey@function()",self);
:start:
wait (SFB_CMD, activator==self);
if (((command ("insult")) or
(command ("contract")) or
(command ("extinguish")) or
(command ("light")) or
(command ("aid")) or
(command ("ventriloquate")) or
(command ("turn")) or
(command ("recite")) or
(command ("use")) or
(command ("backstab")) or
(command ("pick")) or
(command ("steal")) or
(command ("bash")) or
(command ("rescue")) or
(command ("search")) or
(command ("hide")) or
(command ("sneak")) or
(command ("write")) or
(command ("open")) or
(command ("close")) or
(command ("cast")) or
(command ("whisper")) or
(command ("give")) or
(command ("put")) or
(command ("pour")) or
(command ("value")) or
(command ("buy")) or
(command ("sell")) or
(command ("emote")) or
(command ("decapitate")) or
(command ("wear")) or
(command ("wield")) or
(command ("kick")) or
(command ("shout")) or
(command ("say")) or
(command ("ask")) or
(command ("hold")) or
(command ("unlock")) or
(command ("lock"))))
block;

goto start;
}
dilend
/* Evaluate dil, to replace SFUN_EVALUATE, amt is the cost of evaluation
* in iron pieces. Note: not to be confused with evaluate@commands, which
* is a command.
*/
dilbegin aware evaluate (amt: integer);

var u1 : unitptr;
   arg: string;
   buf: string;
   cur: integer;
   craft: integer;
   category: integer;

   pc : unitptr;
   pcn: string;

   arm_text  : stringlist;
   shi_text  : stringlist;
   craft_text: stringlist;
code
{

craft_text:=  {"horrible","very bad","bad","worse than average","average",
 "a little better than average","better than average","good","very good",
 "supreme"};
arm_text:= {"clothes", "leather", "hard leather", "chain", "plate"};
shi_text:= {"small", "medium", "large"};

heartbeat:= PULSE_SEC*3;

:start:
arg:= "";
u1:= null;

wait (SFB_CMD, command("evaluate"));

if (visible(pc, self) == FALSE)
   goto start; // Pc is just trying to eval using the command

block;

pc:= activator;
if (pc.type == UNIT_ST_PC) pcn := pc.name;
else pcn := pc.title;

arg:= argument;

if (visible(self, pc) == FALSE)
   {
   exec ("say I don't do business with people I can't see.", self);
   goto start;
   }

if (arg == "")
   {
   exec ("say Which item do you wish to evaluate, "+pcn+"?", self);
   goto start;
   }

u1:= findunit (pc, arg, FIND_UNIT_IN_ME, null);

if (not u1)
   {
   exec ("say You do not have such an item, "+pcn+".", self);
   goto start;
   }

if ((u1.type != UNIT_ST_OBJ) or ( (u1.objecttype != ITEM_WEAPON) and
   (u1.objecttype != ITEM_ARMOR) and (u1.objecttype != ITEM_SHIELD) ))
   {
   exec ("say The "+u1.name+" is neither a sword, shield nor armor!", self);
   goto start;
   }

// Currency, skip for now

if (not transfermoney (pc, null, amt * IRON_MULT))
   {
   exec ("say The cost is merely "+moneystring(amt*IRON_MULT, TRUE)+
       ", get them first.", self);
   goto start;
   }

category:= u1.value[0];
craft:= u1.value[1] / 5 + 4; // / 5 + 4 is to get corresponding craft_text val

if (craft < 0) craft := 0;  if (craft > 9) craft := 9;

// Change the following to use skill_text(craft) instead of itoa(craft)
if (u1.objecttype == ITEM_WEAPON)
   buf := "say The "+u1.name+" is a "+weapon_name(category)+" of "+
       craft_text.[craft]+" craftmanship and material.";

if (u1.objecttype == ITEM_ARMOR)
   buf := "say The "+u1.name+" is made of "+arm_text.[category]+" and is of "+
       craft_text.[craft]+" craftmanship and material.";

if (u1.objecttype == ITEM_SHIELD)
   buf := "say The "+u1.name+" is a "+shi_text.[category]+" shield of "+
       craft_text.[craft]+" craftmanship and material.";

exec (buf, self);

goto start;

}

dilend


/* Death room dil, to replace SFUN_DEATH_ROOM, tick is in 4th's of seconds
* Damage is damage done per tick, and act_s is string shown to damaged player.
*/
dilbegin death_room(tick: integer, damage: integer, act_s: string);

var ext: extraptr;
   u  : unitptr;
   i  : integer;

code
{

if (tick < 12) tick := 12;

heartbeat:= tick;

if (damage < 0)
   damage := -damage;

if ("$death room for mobs" in self.extra)
   i := UNIT_ST_PC|UNIT_ST_NPC;
else
   i := UNIT_ST_PC;

while (TRUE)
{
wait (SFB_TICK, TRUE);

foreach (i, u)
   {
   if (u.level >= IMMORTAL_LEVEL)
       continue;

   if (("$no death room" in u.extra) and (u.type == UNIT_ST_NPC))
       continue; // Don't allow pcs to get this flag

   if (act_s != "")
       act ("&[hit_me]"+act_s, A_ALWAYS, u, null, null, TO_CHAR);
   else
       act ("&[hit_me]You bleed from your wounds.",
           A_ALWAYS, u, null, null, TO_CHAR);

   u.hp := u.hp - damage;
   position_update (u);
   }

}

}

dilend

/* Force move dil, to replace SFUN_FORCE_MOVE, tick is in 4th's of seconds
* Strings is actually two strings; the idx of the room to force move to,
* and the string shown to the moved one when that happens. Random is a
* boolean whether or not the tick is to be SFB_RANDOM_TIME'd, ie. +/- 50%
* variation.
*/
dilbegin force_move (tick: integer, strings: string, random: integer);

var rmstr: string;
   act_s: string;

   sl   : stringlist;
   i    : integer;
   u    : unitptr;

code
{

if (tick < 12) tick := 12;

heartbeat:= tick;
sl:= split(strings, "!");

rmstr:= sl.[0];

if (sl.[2] != "")
   {
   act_s := "";

   i := 1;
   while (sl.[i] != "")
       {
       act_s := act_s + sl.[i];
       if (sl.[i+1] != "")
           act_s := act_s + "!";

       i := i + 1;
       }
   }

else
   act_s := sl.[1];

if (not findroom(rmstr))
   {
   log ("Could not find room "+rmstr+" (force move)");
   quit;
   }

while (TRUE)
{
if (not random)
   wait (SFB_TICK, TRUE);
else
   wait (SFB_TICK|SFB_RANTIME, TRUE); // pause

foreach (UNIT_ST_PC|UNIT_ST_NPC|UNIT_ST_OBJ, u)
   {
   if ("$no force move" in u.extra)
       continue;

   if (act_s == "")
       act ("You are moved.", A_ALWAYS, u, null, null, TO_CHAR);
   else
       act (act_s, A_ALWAYS, u, null, null, TO_CHAR);

   link (u, findroom(rmstr));
   if (u.type == UNIT_ST_PC) exec ("look", u);

   act ("$1n has arrived.", A_HIDEINV, u, null, null, TO_REST);
   }

}
}

dilend

dilbegin pain_dil (i:integer,s:stringlist);
var
count:integer;
code
{
heartbeat:=PULSE_SEC*i;
:start:
count:=0;
wait (SFB_DONE, TRUE);
count:=length (s);
i:=0;
while (i {
exec (s.[i],self);
pause;
i:=i+1;
}

goto start;
}
dilend



dilbegin tuborg(s:string);
external
sub_drink_info@commands(d:unitptr);
var
 u : unitptr;
code
{
 :start:
 wait(SFB_CMD, ( (command("drink")) or
                (command("sip")) or
         (command("taste")) ) );
u := activator;
secure (u,start);
if (findunit (activator,argument,FIND_UNIT_INVEN|FIND_UNIT_SURRO,null)!=self)
goto start;
if ( command("sip") or command("taste") )
{
  block;
  act("$1n tastes $2n enjoying every drop.", A_HIDEINV, u, self, null,
       TO_ROOM);
   act("The taste of the $2N is nothing less than divine.", A_HIDEINV, u, self,
     null, TO_CHAR);
  goto start;
  }

 if ( u.thirst >20 )
 {
   block;
  act("Your not thirsty.", A_HIDEINV, u, null, null, TO_CHAR);
    goto start;
 }
block;
  act ("You drink $2n and it makes you feel more energetic!", A_HIDEINV, u, self,
      null, TO_CHAR);
  act ("$1n drinks $2n and looks more energetic!", A_HIDEINV, u, self,
      null, TO_ROOM);

 u.thirst := u.thirst + 10;
 u.full := u.full + 10;
 if (u.thirst > 24)
 {
   u.thirst := 24;
 }

 if (u.full > 24)
 {
   u.full := 24;
 }
 u.endurance := u.endurance+50;
 if (u.endurance > u.max_endurance)
 {
   u.endurance := u.max_endurance;
 }
 sub_drink_info@commands(self);
 quit;
}
dilend


dilbegin deputy_check();
code
{
  :loop:
  wait(SFB_CMD, POLICE_ACADEMY in activator.quests);

  /* Lets not check for OUTLAW, since some weapons set it. Only if
     crimes > 0 will the player be banished (meaning that someone
     accused him) */

  if (activator.crimes > 0)
  {
     addextra(activator.quests, {POLICE_BANNED}, "");
     subextra(activator.quests, POLICE_ACADEMY);

     /* Clear all skills & spells taught by the law */

     activator.skills[SKI_CUFF] := 0;

     exec("say "+activator.name+", I am very disappointed in you, you "+
   "can no longer consider yourself a deputy.", self);
  }

  goto loop;
}
dilend



/*
function:  Catchit
descr:
This function is to keep a mob from attacking a non-pk player while controlled
by a player.  This dil should be copied onto the mob
when it is created and the pk value should be set so that it knows if its owner
can pk or not.  For an example of this function see the summon devil spell
in spells.zon
arguments:  pk 1 for can pk 0 for can't
*/
dilbegin aware catchit(pk:integer);
var
pc:unitptr;
code
{
:there:
wait (SFB_PRE,(( command (CMD_AUTO_DAMAGE)) and
(self==activator)));
if (target.type!=UNIT_ST_PC) goto there;
if ((isset (target.pcflags,PC_PK_RELAXED)) and
(pk==1)) goto there;
else
{
 power:=-1;
 block;
 }
 goto there;
}
dilend

/*
function: nokill
descr:
This function is used to give a player pk protection for 10 minutes while
recovering his/her corpse.  The dil should be copied onto the player as
he or she gets killed and it will remove itself after the player quits or
the time expires.
*/
dilbegin aware recall no_kill ();
code
{
interrupt (SFB_MSG,"remove kill prottection"==argument,dil_end);
interrupt (SFB_CMD,(( command ("quit")) and ( self==activator)), dil_end);
interrupt (SFB_CMD,(( command ("remove")) and
("pk protection" ==argument) and
(self==activator)), dil_end);

heartbeat:=PULSE_SEC*600;
if (isset (self.pcflags, PC_PK_RELAXED))
 {
 unset (self.pcflags,PC_PK_RELAXED);
 act ("Due to your death you have been rewarded protection against PK for a short time.",
 A_ALWAYS,self,null,null,TO_CHAR);

 wait (SFB_TICK, TRUE);

 :dil_end:

 set (self.pcflags,PC_PK_RELAXED);
 act ("Your prottection from Pk has just worn off.",
 A_ALWAYS,self,null,null,TO_CHAR);
 quit;
 }
else
 quit;
}
dilend

/*
function carry_n_limit(ch)
descr:
This function returns the amount of items that a person can carry
arguments

ch: a unitptr to the pc your checking.
return:
 returns amount of items.
*/
dilbegin integer carry_n_limit(ch : unitptr);
code
{
  return (10 + (ch.abilities[ABIL_DEX]) / 10);
}
dilend

/*
Function: carry_w_limit(ch)
descr:
This function returns the amount of weight a person
can carry.
argument:
ch : unitptr that you are checking.
return:
 amount of weight player  can carry.
 */
dilbegin integer carry_w_limit(ch : unitptr);
var
  i : integer;
code
{
  i := ch.baseweight / 2;
  if (i < 50)
    i := 50;

  return (50 + i + ch.abilities[ABIL_STR]*2);
}
dilend

/*
function:  Provoked_attack (vict, ch)
descr:
This function returns wether the offensive spell would be one that provokes
the victum to attack or not.  This is usfull in spells like disarm it is
also used in skills like steal.
arguments
vict:  the person you are doing the action on.
ch: the person doing the act.
*/
dilbegin provoked_attack(victim : unitptr, ch : unitptr);
code
{
  if (not (victim.type & (UNIT_ST_NPC|UNIT_ST_PC)))
    return; /* FALSE */

  if (not (ch.type & (UNIT_ST_PC|UNIT_ST_NPC)))
    return; /* FALSE */

  if (victim.level >= 200)
    return; /* FALSE */

  if (ch.level >= 200)
    return; /* FALSE */

  if (not isset(ch.charflags, CHAR_SELF_DEFENCE))
  {
     if ((ch.fighting == null) and
  (not isset(victim.charflags, CHAR_LEGAL_TARGET)))
       set(victim.charflags, CHAR_SELF_DEFENCE);
  }

  /* Test for LEGAL_TARGET bit */
  if (isset(victim.charflags, CHAR_PROTECTED))
  {
      if ((not isset(victim.charflags, CHAR_LEGAL_TARGET)) and
          (not isset(ch.charflags, CHAR_SELF_DEFENCE)))
        set(ch.charflags, CHAR_LEGAL_TARGET);
  }

  if (victim.position <= POSITION_SLEEPING)
    return; /* FALSE */

  if (isset(victim.charflags, CHAR_PEACEFUL))
    return; /* FALSE */

  if (opponent(victim, ch))
    return; /* TRUE */

  set_fighting(victim, ch);

  return; /* TRUE */
}
dilend

/*
function:  hit_limit(i)
descr:
this is used in the heal spells in spells.zon to determin amount healed.

*./
dilbegin integer hit_limit(i : integer);
code
{
  return (i * 3);
}
dilend


/* You can add any number of hitpoints, even negative. Excess are chopped
  off to max_hp, negative may kill because of the position_update */

/*
function:  add_hitpoints
descr:
this function is used in the heal spells to add hit points.
arguments:
ch:  person your adding to.
php:  amount your adding.
*/
dilbegin add_hitpoints(ch : unitptr, php : integer);
code
{
  ch.hp := ch.hp + php;
  if (ch.hp > ch.max_hp)
    ch.hp := ch.max_hp;
  position_update(ch);
  return;
}
dilend

/*
function:  may_tele_away
descr:
This little function is to let you know if the unit is allowed to teleport
from the current location.
argument:
u: unit that is teleporting.
returns:
true if  the unit can teleport.
false if not.
*/
dilbegin integer may_tele_away(u : unitptr);
code
{
  while (u)
  {
     if (u.flags & UNIT_FL_NO_TELEPORT)
       return(FALSE);
     u := u.outside;
  }

  return(TRUE);
}
dilend
/*
function:  unit_room(u)
descr:
This function returns the room the pc is in no matter how many containers
the pc, obj, room, or npc is in.

arguments:
u:  pc to find the room from.
returns:
the unit pointer to the room.
*/
dilbegin unitptr unit_room(u : unitptr);
code
{
  while (u.type != UNIT_ST_ROOM)
    u := u.outside;

  return(u);
}
dilend

/*
function:  unit_char(u)
descr:
this function finds the char which is a pc or npc that the unit is in.
arguments:
u:  unit to find the pc or npc it is in from.
return
unit ptr to the unit npc or pc.
*/
dilbegin unitptr unit_char(u : unitptr);
code
{
  while ((u.type & (UNIT_ST_NPC | UNIT_ST_PC)) == 0)
    u := u.outside;

  return(u);
}
dilend

/*
function skill_resist.
*/
dilbegin integer skillresist(aa : integer, ad : integer,
       sa : integer, sd : integer);
code
{
  return (openroll(100, 5) + aa + sa - ad - sd - 50);
}
dilend

dilbegin integer skill_duration(hm : integer);
code
{
  if (hm < 20)
    return (2);
  else if (hm > 150)
    return (15);
  else
    return (hm / 10);
}
dilend

dilbegin string sizestring(cm : integer);
var
  ftn : integer;
  fts : string;
  inn : integer;
  ins : string;
code
{
  /* One inch equals 2,54 cm. There are 12 inches (30.48 cm) to a foot */
  ftn := cm / 30;
  inn := (10*(cm % 30))/25;
  if (ftn == 1)
    fts := "one foot";
  else if (ftn > 1)
    fts := itoa(ftn)+" feet";
  else
    fts := "";

  if (inn == 1)
    ins := "one inch";
  else if (inn > 1)
    ins := itoa(inn)+" inches";
  else
    ins := "";

  if (fts != "")
  {
     if (ins != "")
       fts := fts + " and ";
  }
  else
  {
     if (inn < 1)
ins := "less than an inch";
  }

  return (fts + ins);
}
dilend


dilbegin string weightstring(p : integer);
var
  s : string;
code
{
  if (p == 1)
    s := "one pound";
  else if (p > 1)
    s := itoa(p)+" pounds";
  else
    s := "less than a pound";

  return (s);
}
dilend







/*
function walk_room
purpose: to allow a mob to walk to a room even if he/she has
to unlock open and close doors.
*/
dilbegin integer walk_room (place:string, spd:integer);
       external
  string dirstring@function (dr:integer);
  integer rev_dir@function (i:integer);
var
rm:unitptr;
rdir:integer;
 dir : integer;
 dir_string : string;
 rdir_string : string;
 dr_name : string;
 heart : integer;
 thing : unitptr;
code
{
  :init:
  on_activation(self.position <= POSITION_SLEEPING, skip);
  log(itoa(spd));
  if(( (spd*4) < 5) )
  {
     heartbeat := 5 * 3;
  } else {
     heartbeat := spd * 3;
  }
  heart := heartbeat;

  :start:
if (self.outside==thing) return (TRUE);
  wait(SFB_TICK, TRUE);

  /* Lets stand up if for some reason we are sitting */
  if ((self.position == POSITION_RESTING) or
      (self.position == POSITION_SITTING))
  {
     exec("stand", self);
  }

  /* if were not standing now then abort! */

  if (self.position != POSITION_STANDING)
    return (FALSE);

  heartbeat := heart;
  thing:=findroom(place);
log(thing.name);

dir:=pathto (self,thing);
if (dir==DIR_IMPOSSIBLE) return (FALSE);
dir_string:=dirstring@function (dir);
rdir:=rev_dir@function(dir);
     rdir_string :=dirstring@function (rdir);

     if (isset (self.outside.exit_info[dir], EX_LOCKED))
goto locked;
     if (isset (self.outside.exit_info[dir], EX_CLOSED))
     {
     exec ("open "+dir_string+" "+self.outside.exit_names[dir].[0],self);
     exec (dir_string,self);
     exec ("close "+rdir_string+" "+self.outside.exit_names[rdir].[0],self);
 goto start;
     }

  :locked:
     if (isset (self.outside.exit_info[dir], EX_LOCKED))
     {
     rm:=self.outside;
 unset(rm.exit_info[dir], EX_LOCKED);
     exec ("open "+dir_string+" "+self.outside.exit_names[dir].[0],self);
     exec (dir_string,self);
     exec ("close "+rdir_string+" "+self.outside.exit_names[rdir].[0],self);
 set(rm.exit_info[dir], EX_LOCKED);
 goto start;
     }
     if (dir== DIR_ENTER)
     exec (dir_string+" "+self.outside.exit_names[dir].[0],self);
else
  exec(dir_string, self);
  goto start;
}
dilend


/*
function dirstring
purpose: use to return the string of a direction when you ahve its number
value.
for example if you have  DIR_NORTH you can use this like
yourstring:=dirstring (DIR_NORTH);
and it will return "north" in yourstring.
*/

dilbegin string dirstring (dr:integer);
var
 dirlist:stringlist;
code
{
dirlist:= {"north","east","south","west","up","down","northeast",
"northwest","southeast","southwest","enter","exit","impossible","here"};
return (dirlist.[dr]);
}
dilend

/*
function rev_dir
purpose This function returns the oposite direction so if you have  DIR_NORTH
it will return the number value of south.
*/

dilbegin integer rev_dir(i : integer);
var
 rev_dir : stringlist;
code
{
  rev_dir := {"2", "3", "0", "1", "5", "4", "9", "8", "7", "6"};
  return (atoi(rev_dir.[i]));
}
dilend

/*
The following are the new restrict functions a doc called restrict.doc
is in the valhalla doc directory for you to download and read to better
understand how to use them.

*/



dilbegin guild_restrict
(guilds:stringlist,damage:integer,percent:integer,action:string);
var
amount:integer;
expd : extraptr;
code
{
if (damage < 0) damage := -damage;
if (percent < 0) percent := -percent;

interrupt (SFB_MSG,argument=="unstore",start);
if ((self.equip) and
(self.outside.zoneidx=="g_q"))
goto nextstart;
:start:
wait (SFB_DONE, (((command ("hold")) or
(command ("wear")) or
(command ("grab")) or
(command ("wield"))) and
(self==medium)));
:nextstart:
if ((activator.type == UNIT_ST_NPC) and not ("$pc obey" in activator.extra)
  and (activator.master.type != UNIT_ST_PC) ) goto start;

if (activator.guild in guilds) goto start;
if (self.outside.level>=IMMORTAL_LEVEL) goto start;

expd:= "$path" in self.outside.quests;
  if (expd != null)
  {
     if ((expd.names.[1] in guilds) or
         (expd.names.[2] in guilds) or
         (expd.names.[3] in guilds))
                  goto start;
  }

if ((not( command ("wear"))) and
(not(command ("wield"))) and
(not(command ("hold")))) goto start;

if (action=="")
 {
act ("Your $2n burns you and you drop it to the ground.",
A_ALWAYS,self.outside,self,null,TO_CHAR);
act ("$1n's $2n burns $1m and $1e drops it to the ground.",
A_SOMEONE,self.outside,self,null,TO_REST);
act ("Being in the right guild might help.",
A_ALWAYS,self.outside,null,null,TO_CHAR);
}
else action ();

if (percent!=0)
 amount:=(self.outside.hp*percent)/100;
if (damage==-1)
 damage:=openroll (100,5);
if (percent!=0)
 if (damage>amount)
   damage:=amount;
 else if (damage==0)
 damage:=amount;


self.outside.hp:=self.outside.hp-damage;
position_update (self.outside);

unequip (self);
link (self,self.outside.outside);
goto start;
}
dilend


dilbegin anti_guild
(guilds:stringlist,damage:integer,percent:integer,action:string);
var
amount:integer;
code
{
heartbeat:=PULSE_SEC*3;
interrupt (SFB_MSG,argument=="unstore",start);
if ((self.equip) and
(self.outside.zoneidx=="g_q"))
goto nextstart;
:start:
wait (SFB_DONE, (((command ("hold")) or
(command ("wear")) or
(command ("grab")) or
(command ("wield"))) and
(self==medium)));
:nextstart:

if (activator.type!=UNIT_ST_PC) goto start;

if (not (activator.guild in guilds)) goto start;

if (self.outside.level>=IMMORTAL_LEVEL) goto start;

if (action =="")
 {
 act ("Your $2n burns you and you drop it to the ground.",
 A_ALWAYS,self.outside,self,null,TO_CHAR);
 act ("$1n's $2n burns $1m and $1e drops it to the ground.",
 A_SOMEONE,self.outside,self,null,TO_REST);
 act ("Not being in this guild might help.",
 A_ALWAYS,self.outside,null,null,TO_CHAR);
 }
else action ();

if (percent!=0)
 amount:=(self.outside.hp*percent)/100;
if (damage==-1)
 damage:=openroll (100,5);

if (percent!=0)
 if (damage>amount)
   damage:=amount;
 else if (damage==0)
 damage:=amount;


self.outside.hp:=self.outside.hp-damage;
position_update (self.outside);

unequip (self);
link (self,self.outside.outside);
goto start;
}
dilend

dilbegin quest_restrict
(quest:string,damage:integer,percent:integer,action:string);
var
amount:integer;
code
{
if (damage < 0) damage := -damage;
if (percent < 0) percent := -percent;

heartbeat:=PULSE_SEC*3;
interrupt (SFB_MSG,argument=="unstore",start);
if ((self.equip) and
(self.outside.zoneidx=="g_q"))
goto nextstart;
:start:
wait (SFB_DONE, (((command ("hold")) or
(command ("wear")) or
(command ("grab")) or
(command ("wield"))) and
(self==medium)));
:nextstart:
if ((activator.type == UNIT_ST_NPC) and not ("$pc obey" in activator.extra)
  and (activator.master.type != UNIT_ST_PC) ) goto start;

if (quest in activator.quests) goto start;

if (self.outside.level>=IMMORTAL_LEVEL) goto start;
if (action=="")
 {
 act ("Your $2n burns you and you drop it to the ground.",
 A_ALWAYS,self.outside,self,null,TO_CHAR);
 act ("$1n's $2n burns $1m and $1e drops it to the ground.",
 A_SOMEONE,self.outside,self,null,TO_REST);
 act ("Doing the right Quest might help.",
 A_ALWAYS,self.outside,null,null,TO_CHAR);
 }
else action ();

if (percent!=0)
 amount:=(self.outside.hp*percent)/100;
if (damage==-1)
 damage:=openroll (100,5);
if (percent!=0)
 if (damage>amount)
   damage:=amount;
 else if (damage==0)
 damage:=amount;

self.outside.hp:=self.outside.hp-damage;
position_update (self.outside);

unequip (self);
link (self,self.outside.outside);
goto start;
}
dilend

dilbegin quests_restrict
(qts:stringlist,damage:integer,percent:integer,action:string);
var
amount:integer;
i:integer;
code
{
if (damage < 0) damage := -damage;
if (percent < 0) percent := -percent;

interrupt (SFB_MSG,argument=="unstore",start);
if ((self.equip) and
(self.outside.zoneidx=="g_q"))
goto nextstart;
:start:
wait (SFB_DONE, (((command ("hold")) or
(command ("wear")) or
(command ("grab")) or
(command ("wield"))) and
(self==medium)));
:nextstart:

if ((activator.type == UNIT_ST_NPC) and not ("$pc obey" in activator.extra)
  and (activator.master.type != UNIT_ST_PC) ) goto start;

if (self.outside.level>=IMMORTAL_LEVEL) goto start;

i:=0;
while (qts.[i]!=null)
 {
 if (qts.[i]==null) break;
 if (qts.[i] in activator.quests) goto start;
 i:=i+1;
 }
if (action=="")
 {
 act ("Your $2n burns you and you drop it to the ground.",
 A_ALWAYS,self.outside,self,null,TO_CHAR);
 act ("$1n's $2n burns $1m and $1e drops it to the ground.",
 A_SOMEONE,self.outside,self,null,TO_REST);
 act ("Doing the right Quest might help.",
 A_ALWAYS,self.outside,null,null,TO_CHAR);
}
else action ();

if (percent!=0)
 amount:=(self.outside.hp*percent)/100;
if (damage==-1)
 damage:=openroll (100,5);
if (percent!=0)
 if (damage>amount)
   damage:=amount;
 else if (damage==0)
 damage:=amount;


self.outside.hp:=self.outside.hp-damage;
position_update (self.outside);

unequip (self);
link (self,self.outside.outside);
goto start;
}
dilend


dilbegin ali_restrict (max_ali:integer,min_ali:integer,
 damage:integer,percent:integer,
 action:string);
 var
 amount:integer;
code
{
if (max_ali  {
 amount:=max_ali;
 max_ali:=min_ali;
 min_ali:=amount;
 }

if (damage < 0) damage := -damage;
if (percent < 0) percent := -percent;

interrupt (SFB_MSG,argument=="unstore",start);
if ((self.equip) and
(self.outside.zoneidx=="g_q"))
goto nextstart;
:start:
wait (SFB_DONE, (((command ("hold")) or
(command ("wear")) or
(command ("grab")) or
(command ("wield"))) and
(self==medium)));
:nextstart:

if ((activator.type == UNIT_ST_NPC) and not ("$pc obey" in activator.extra)
  and (activator.master.type != UNIT_ST_PC) ) goto start;

if ((self.outside.alignment<=max_ali) and
(self.outside.alignment>=min_ali)) goto start;

if (self.outside.level>=IMMORTAL_LEVEL) goto start;

if (action=="")
 {
 act ("Your $2n burns you and you drop it to the ground.",
 A_ALWAYS,self.outside,self,null,TO_CHAR);
 act ("$1n's $2n burns $1m and $1e drops it to the ground.",
 A_SOMEONE,self.outside,self,null,TO_REST);

 if (activator.alignment>max_ali)
   act ("You are to good to use $2n",
   A_ALWAYS,self.outside,self,null,TO_CHAR);
 else
   act ("You are to evil to use $2n",
 A_ALWAYS,self.outside,self,null,TO_CHAR);
 }
else action (max_ali,min_ali);

if (percent!=0)
 amount:=(self.outside.hp*percent)/100;
if (damage==-1)
 damage:=openroll (100,5);

if (percent!=0)
 if (damage>amount)
   damage:=amount;
 else if (damage==0)
 damage:=amount;


self.outside.hp:=self.outside.hp-damage;
position_update (self.outside);

unequip (self);
link (self,self.outside.outside);
goto start;
}
dilend
/*Virtual Level restriction add by Ratlin 11/2000 */
dilbegin vlevel_restrict (lvl:integer,damage:integer,percent:integer,action:string);
var
 amount:integer;

code
{
if (damage < 0) damage := -damage;
if (percent < 0) percent := -percent;

interrupt (SFB_MSG,argument=="unstore",start);
if ((self.equip) and (self.outside.zoneidx=="g_q"))
goto nextstart;
:start:
wait (SFB_DONE, (((command ("hold")) or (command ("wear")) or (command ("grab"))
or (command ("wield"))) and (self==medium)));
:nextstart:

if ((activator.type == UNIT_ST_NPC) and not ("$pc obey" in activator.extra)
  and (activator.master.type != UNIT_ST_PC) ) goto start;

if (activator.vlevel>=lvl) goto start;

if (self.outside.level>=IMMORTAL_LEVEL) goto start;
 amount:=lvl-activator.vlevel;

if (action=="")
 {
act ("Your $2n burns you and you drop it to the ground.",
A_ALWAYS,self.outside,self,null,TO_CHAR);
act ("$1n's $2n burns $1m and $1e drops it to the ground.",
A_SOMEONE,self.outside,self,null,TO_REST);
 act ("You are not high enough level to use $3n. $2t levels needed to use it.",
 A_ALWAYS,self.outside,itoa (amount),self,TO_CHAR);
 }
else action (amount);

if (percent!=0)
 amount:=(self.outside.hp*percent)/100;
if (damage==-1)
 damage:=openroll (100,5);

if (percent!=0)
 if (damage>amount)
   damage:=amount;
 else if (damage==0)
 damage:=amount;


self.outside.hp:=self.outside.hp-damage;
position_update (self.outside);

unequip (self);
link (self,self.outside.outside);
goto start;
}
dilend


dilbegin level_restrict (lvl:integer,damage:integer,percent:integer,
action:string);
var
 amount:integer;

code
{
if (damage < 0) damage := -damage;
if (percent < 0) percent := -percent;

interrupt (SFB_MSG,argument=="unstore",start);
if ((self.equip) and
(self.outside.zoneidx=="g_q"))
goto nextstart;
:start:
wait (SFB_DONE, (((command ("hold")) or (command ("wear")) or (command ("grab"))
or (command ("wield"))) and (self==medium)));
:nextstart:

if ((activator.type == UNIT_ST_NPC) and not ("$pc obey" in activator.extra)
  and (activator.master.type != UNIT_ST_PC) ) goto start;

if (activator.level>=lvl) goto start;

if (self.outside.level>=IMMORTAL_LEVEL) goto start;
 amount:=lvl-activator.level;

if (action=="")
 {
act ("Your $2n burns you and you drop it to the ground.",
A_ALWAYS,self.outside,self,null,TO_CHAR);
act ("$1n's $2n burns $1m and $1e drops it to the ground.",
A_SOMEONE,self.outside,self,null,TO_REST);
 act ("You are not high enough level to use $3n.  $2t levels needed to use
it.",
 A_ALWAYS,self.outside,itoa (amount),self,TO_CHAR);
 }
else action (amount);

if (percent!=0)
 amount:=(self.outside.hp*percent)/100;
if (damage==-1)
 damage:=openroll (100,5);

if (percent!=0)
 if (damage>amount)
   damage:=amount;
 else if (damage==0)
 damage:=amount;


self.outside.hp:=self.outside.hp-damage;
position_update (self.outside);

unequip (self);
link (self,self.outside.outside);
goto start;
}
dilend

dilbegin race_restrict
(rc:integer,damage:integer,percent:integer,action:string);
var
 amount:integer;

code
{
// Reverse damage if negative...
if (damage < 0) damage := -damage;
if (percent < 0) percent := -percent;
interrupt (SFB_MSG,argument=="unstore",start);

if ((self.equip) and
(self.outside.zoneidx=="g_q"))
goto nextstart;
:start:
wait (SFB_DONE, (((command ("hold")) or
(command ("wear")) or
(command ("grab")) or
(command ("wield"))) and
(self==medium)));
:nextstart:

if ((activator.type == UNIT_ST_NPC) and not ("$pc obey" in activator.extra)
  and (activator.master.type != UNIT_ST_PC) ) goto start;

if (activator.race!=rc) goto start;

if (self.outside.level>=IMMORTAL_LEVEL) goto start;
if (action=="")
 {
act ("Your $2n burns you and you drop it to the ground.",
A_ALWAYS,self.outside,self,null,TO_CHAR);
act ("$1n's $2n burns $1m and $1e drops it to the ground.",
A_SOMEONE,self.outside,self,null,TO_REST);
act ("Being the right race might help.",
A_ALWAYS,self.outside,null,null,TO_CHAR);
}
else action ();

if (percent!=0)
 amount:=(self.outside.hp*percent)/100;
if (damage==-1)
 damage:=openroll (100,5);

if (percent!=0)
 if (damage>amount)
   damage:=amount;
 else if (damage==0)
 damage:=amount;


self.outside.hp:=self.outside.hp-damage;
position_update (self.outside);

unequip (self);
link (self,self.outside.outside);
goto start;
}
dilend

dilbegin string abistr (ability:integer);
var
ablst:stringlist;
code
{
ablst:={"magic" , "divinity" , "strength" , "dextarity","constitution" ,
   "charisma" ,"brains" , "hit points"};
return (ablst.[ability]);
   }
   dilend

dilbegin abi_restrict (ab:integer,min_abi:integer,
         damage:integer,percent:integer,
         action:string);
         external
         string abistr (ability:integer);
var
 amount:integer;
 abl:string;
code
{
if (damage < 0) damage := -damage;
if (percent < 0) percent := -percent;

interrupt (SFB_MSG,argument=="unstore",start);
if ((self.equip) and
(self.outside.zoneidx=="g_q"))
goto nextstart;
:start:
wait (SFB_DONE, (((command ("hold")) or
(command ("wear")) or
(command ("grab")) or
(command ("wield"))) and
(self==medium)));
:nextstart:

if ((activator.type == UNIT_ST_NPC) and not ("$pc obey" in activator.extra)
  and (activator.master.type != UNIT_ST_PC) ) goto start;

if (activator.abilities[ab]>=min_abi) goto start;

if (self.outside.level>=IMMORTAL_LEVEL) goto start;

if (action =="")
 {
 act ("Your $2n burns you and you drop it to the ground.",
 A_ALWAYS,self.outside,self,null,TO_CHAR);
 act ("$1n's $2n burns $1m and $1e drops it to the ground.",
 A_SOMEONE,self.outside,self,null,TO_REST);

   amount:=min_abi-activator.abilities[ab];
 abl:=abistr (ab);
 act ("You don't have enough "+abl+" to use $3n.",
 A_ALWAYS,self.outside,itoa (amount),self,TO_CHAR);
act ("$2t more "+abl+" needed to use it.",
 A_ALWAYS,self.outside,itoa (amount),self,TO_CHAR);
 }
else action (min_abi);

if (percent!=0)
 amount:=(self.outside.hp*percent)/100;
if (damage==-1)
 damage:=openroll (100,5);

if (percent!=0)
 if (damage>amount)
   damage:=amount;
 else if (damage==0)
 damage:=amount;


self.outside.hp:=self.outside.hp-damage;
position_update (self.outside);

unequip (self);
link (self,self.outside.outside);
goto start;
}
dilend

dilbegin ski_restrict (ab:integer,min_abi:integer,
         damage:integer,percent:integer,
         action:string);
var
 amount:integer;
 abl:string;
code
{
if (damage < 0) damage := -damage;
if (percent < 0) percent := -percent;

interrupt (SFB_MSG,argument=="unstore",start);
if ((self.equip) and
(self.outside.zoneidx=="g_q"))
goto nextstart;
:start:
wait (SFB_DONE, (((command ("hold")) or
(command ("wear")) or
(command ("grab")) or
(command ("wield"))) and
(self==medium)));
:nextstart:

if ((activator.type == UNIT_ST_NPC) and not ("$pc obey" in activator.extra)
  and (activator.master.type != UNIT_ST_PC) ) goto start;

if (activator.skills[ab]>=min_abi) goto start;

if (self.outside.level>=IMMORTAL_LEVEL) goto start;

if (action =="")
 {
 act ("Your $2n burns you and you drop it to the ground.",
 A_ALWAYS,self.outside,self,null,TO_CHAR);
 act ("$1n's $2n burns $1m and $1e drops it to the ground.",
 A_SOMEONE,self.outside,self,null,TO_REST);

   amount:=min_abi-activator.skills[ab];
 act ("You are not skilled enough to use $3n.  $2t skill needed to use it.",
 A_ALWAYS,self.outside,itoa (amount),self,TO_CHAR);
 }
else action (min_abi);

if (percent!=0)
 amount:=(self.outside.hp*percent)/100;
if (damage==-1)
 damage:=openroll (100,5);

if (percent!=0)
 if (damage>amount)
   damage:=amount;
 else if (damage==0)
 damage:=amount;


self.outside.hp:=self.outside.hp-damage;
position_update (self.outside);

unequip (self);
link (self,self.outside.outside);
goto start;
}
dilend

dilbegin sp_restrict (ab:integer,min_abi:integer,
         damage:integer,percent:integer,
         action:string);
var
 amount:integer;
 abl:string;
code
{
if (damage < 0) damage := -damage;
if (percent < 0) percent := -percent;

interrupt (SFB_MSG,argument=="unstore",start);
if ((self.equip) and
(self.outside.zoneidx=="g_q"))
goto nextstart;
:start:
wait (SFB_DONE, (((command ("hold")) or
(command ("wear")) or
(command ("grab")) or
(command ("wield"))) and
(self==medium)));
:nextstart:

if ((activator.type == UNIT_ST_NPC) and not ("$pc obey" in activator.extra)
  and (activator.master.type != UNIT_ST_PC) ) goto start;

if (activator.spells[ab]>=min_abi) goto start;

if (self.outside.level>=IMMORTAL_LEVEL) goto start;
if (action =="")
 {
 act ("Your $2n burns you and you drop it to the ground.",
 A_ALWAYS,self.outside,self,null,TO_CHAR);
 act ("$1n's $2n burns $1m and $1e drops it to the ground.",
 A_SOMEONE,self.outside,self,null,TO_REST);

   amount:=min_abi-activator.spells[ab];
 act ("You are not skilled enough to use $3n.  $2t skill needed to use it.",
 A_ALWAYS,self.outside,itoa (amount),self,TO_CHAR);
 }
else action (min_abi);

if (percent!=0)
 amount:=(self.outside.hp*percent)/100;
if (damage==-1)
 damage:=openroll (100,5);

if (percent!=0)
 if (damage>amount)
   damage:=amount;
 else if (damage==0)
 damage:=amount;


self.outside.hp:=self.outside.hp-damage;
position_update (self.outside);

unequip (self);
link (self,self.outside.outside);
goto start;
}
dilend

dilbegin weap_restrict (ab:integer,min_abi:integer,
         damage:integer,percent:integer,
         action:string);
var
 amount:integer;
 abl:string;
code
{
if (damage < 0) damage := -damage;
if (percent < 0) percent := -percent;

interrupt (SFB_MSG,argument=="unstore",start);
if ((self.equip) and
(self.outside.zoneidx=="g_q"))
goto nextstart;
:start:
wait (SFB_DONE, (((command ("hold")) or
(command ("wear")) or
(command ("grab")) or
(command ("wield"))) and
(self==medium)));
:nextstart:

if ((activator.type == UNIT_ST_NPC) and not ("$pc obey" in activator.extra)
  and (activator.master.type != UNIT_ST_PC) ) goto start;

if (activator.weapons[ab]>=min_abi) goto start;

if (self.outside.level>=IMMORTAL_LEVEL) goto start;
if (action =="")
 {
 act ("Your $2n burns you and you drop it to the ground.",
 A_ALWAYS,self.outside,self,null,TO_CHAR);
 act ("$1n's $2n burns $1m and $1e drops it to the ground.",
 A_SOMEONE,self.outside,self,null,TO_REST);

   amount:=min_abi-activator.weapons[ab];
 act ("You are not skilled enough to use $3n.  $2t skill needed to use it.",
 A_ALWAYS,self.outside,itoa (amount),self,TO_CHAR);
 }
else action (min_abi);

if (percent!=0)
 amount:=(self.outside.hp*percent)/100;
if (damage==-1)
 damage:=openroll (100,5);

if (percent!=0)
 if (damage>amount)
   damage:=amount;
 else if (damage==0)
 damage:=amount;


self.outside.hp:=self.outside.hp-damage;
position_update (self.outside);

unequip (self);
link (self,self.outside.outside);
goto start;
}
dilend

dilbegin sex_restrict (sx:integer,damage:integer,percent:integer,
action:string);
var
amount:integer;
code
{
if (damage < 0) damage := -damage;
if (percent < 0) percent := -percent;

interrupt (SFB_MSG,argument=="unstore",start);
if ((self.equip) and
(self.outside.zoneidx=="g_q"))
goto nextstart;
:start:
wait (SFB_DONE, (((command ("hold")) or
(command ("wear")) or
(command ("grab")) or
(command ("wield"))) and
(self==medium)));
:nextstart:

if ((activator.type == UNIT_ST_NPC) and not ("$pc obey" in activator.extra)
  and (activator.master.type != UNIT_ST_PC) ) goto start;

if (activator.sex==sx) goto start;

if (self.outside.level>=IMMORTAL_LEVEL) goto start;
if (action=="")
 {
act ("Your $2n burns you and you drop it to the ground.",
A_ALWAYS,self.outside,self,null,TO_CHAR);
act ("$1n's $2n burns $1m and $1e drops it to the ground.",
A_SOMEONE,self.outside,self,null,TO_REST);
 act ("You are not the right sex to use $3n.",
 A_ALWAYS,self.outside,null,self,TO_CHAR);
 }
else action ();

if (percent!=0)
 amount:=(self.outside.hp*percent)/100;
if (damage==-1)
 damage:=openroll (100,5);

if (percent!=0)
 if (damage>amount)
   damage:=amount;
 else if (damage==0)
 damage:=amount;


self.outside.hp:=self.outside.hp-damage;
position_update (self.outside);

unequip (self);
link (self,self.outside.outside);
goto start;
}
dilend
/* dilcopy ("ply_restrict@function("+pc.name+",0,25,\""\ ",item); */
dilbegin ply_restrict
(person:string,damage:integer,percent:integer,action:string);
var
amount:integer;
code
{
if (damage < 0) damage := -damage;
if (percent < 0) percent := -percent;

heartbeat:=PULSE_SEC*3;
interrupt (SFB_MSG,argument=="unstore",start);
if ((self.equip) and
(self.outside.zoneidx=="g_q"))
goto nextstart;
:start:
wait (SFB_DONE, (((command ("hold")) or
(command ("wear")) or
(command ("grab")) or
(command ("wield"))) and
(self==medium)));
:nextstart:

if ((activator.type == UNIT_ST_NPC) and not ("$pc obey" in activator.extra)
  and (activator.master.type != UNIT_ST_PC) ) goto start;

if (person == activator.name) goto start;

if (self.outside.level>=IMMORTAL_LEVEL) goto start;
if (action=="")
 {
 act ("Your $2n burns you and you drop it to the ground.",
 A_ALWAYS,self.outside,self,null,TO_CHAR);
 act ("$1n's $2n burns $1m and $1e drops it to the ground.",
 A_SOMEONE,self.outside,self,null,TO_REST);
 act ("You realy shouldn't try to use someone elses personal item.",
 A_ALWAYS,self.outside,null,null,TO_CHAR);
 }
else action ();

if (percent!=0)
 amount:=(self.outside.hp*percent)/100;
if (damage==-1)
 damage:=openroll (100,5);
if (percent!=0)
 if (damage>amount)
   damage:=amount;
 else if (damage==0)
 damage:=amount;

self.outside.hp:=self.outside.hp-damage;
position_update (self.outside);

unequip (self);
link (self,self.outside.outside);
goto start;
}
dilend

/* This is an enhanced version of the Guard way SFUN. It will allow both
  certain players to enter as well as certain mobs. An optional stopdil
  can be supplied if you wish to do something special. It takes two
  arguments, the activator and the direction.

  Examples:


dilcopy guard_dir@function("east", {"rejji"}, null, null);
dilcopy guard_dir@function("south", {"papi", "whistler"},
                          {"welmar", "guard", "captain", "bipbop@kingc");

  Important: The guard dir is ONLY active in the room in which the
             monster is initially loaded! Thus, if it is summoned
             it will not block directions until it is back where it
             was first created.
*/

dilbegin guard_dir(direction : string,
                  excludepc : stringlist,
                  excludenpc : stringlist,
                  stopdil : string);
var
 place : integer;
code
{
  place := self.outside.idx;

  :start:
  wait(SFB_CMD, command(direction));

  if (self.outside.idx != place)
     goto start;

  if (activator.type == UNIT_ST_PC)
  {
     if (activator.name in excludepc)
goto start;

     goto stopit;
  }

  if (activator.name in excludenpc)
    goto start;

  :stopit:
  block;

  if (stopdil == "")
  {
     act ("$3n stops you and says, 'Members only!'",
   A_ALWAYS,activator,null,self,TO_CHAR);
     act ("$3n stops $1n, and says, 'Members only!'",
   A_SOMEONE,activator,null,self,TO_REST);
  }
  else
  {
     stopdil(activator, direction);
  }
  goto start;
}
dilend

/* guard_way is a simplified version of the above guard_dir.  The difference
being that only one specified pc/npc is allowed to pass (This function is
used primarily with the hedge wall spell; the guard_dir stringlist
variables don't pass very well).  Like guard_dir, it is only effective in
the room the mobile is originally loaded into. */

dilbegin guard_way(direction : integer,
                  excludepc : string);
external
string dirstring(i:integer);
var
 place : integer;
dstr:string;
code
{
  place := self.outside.idx;
 dstr:=dirstring(direction);

  :start:
  wait(SFB_CMD, command(dstr));

  if (self.outside.idx != place)
     goto start;

  if (activator.name in excludepc)
    goto start;

  :stopit:
  block;

if(activator.type == UNIT_ST_NPC)
{
 sendto("guard_block",activator);
}

     act ("$3n prevents you from proceeding.",
   A_ALWAYS,activator,null,self,TO_CHAR);
     act ("$3n prevents $1n from proceeding.",
   A_SOMEONE,activator,null,self,TO_REST);
  goto start;
}
dilend


/* The okdil & notokdil are optional and both take two arguments of
  type unitptr - the activator and the key.

  This DIL can be used for many other things than keys....

Example:

  dilcopy key_give@function({"hugo", "bent"}, "key2@house", "key", "", "");

This will load the "key2@house" when either Bent or Hugo says "key".

*/

dilbegin key_give(pcnames : stringlist, keysym : string, word : string,
                 okdil : string, notokdil : string);
var
 key:unitptr;

code
{
  heartbeat := PULSE_SEC*5;
  :start:
  wait(SFB_DONE, command("say") and (word in argument));

  if (activator.name in pcnames)
  {
     key := load (keysym);
     link (key, activator);

     if (okdil == "")
     {
 act ("$3n gives $1n $2n.",
      A_SOMEONE, activator, key, self, TO_REST);
 act ("$3n gives you $2n.",
      A_SOMEONE,activator,key,self,TO_CHAR);
     }
     else
     {
 okdil(activator, key);
     }
  }
  else
  {
     if (notokdil == "")
     {
 exec("say I don't know you.",self);
     }
     else
     {
 notokdil(activator, key);
     }
  }

  goto start;
}
dilend

/*
The following dils are the shopkeepers in dil.
For more information look at the shopkeeper.doc.
*/
/***************************************************************************
***/
#define STATE_OPEN   0
#define STATE_CLOSED 1
dilbegin integer isopen( times : stringlist );
var
clock:stringlist;
t1:integer;
t2:integer;
i:integer;
ln:integer;
cl:integer;
tempi:stringlist;
b:integer;

code
{
cl:=STATE_CLOSED;
ln:=length(times);
   if(( ln %2) ==1)
    return(STATE_OPEN);
 
 addstring (tempi,times.[0]);
 i:=1;
 while   (i  if (times.[i] in tempi)
                         return(STATE_OPEN);
       i:=i+1;
       }
       
clock:={"0","1","2","3","4","5","6","7","9","10","11","12",
"13","14","15","16","17","18","19","20","21","22","23"};

i:=0;
while(i  t1:=atoi(times.[i]);
 t2:=atoi(times.[i+1]);
if (t1 while (t1<=t2){
clock.[t1]:="cleared";
t1:=t1+1;
}
}
if (t1>t2){
b:=0;
 while (b<=t2){
clock.[b]:="cleared";
b:=b+1;
}
 b:=t1;
 while (b<24){
clock.[b]:="cleared";
b:=b+1;
}
}
i:=i+2;
}

if (itoa(mudhour) in times)
return (STATE_CLOSED);

return (STATE_OPEN);  
}
dilend
/***************************************************************************
***/
dilbegin string wpntype( obj : unitptr );
code{
    if( not( obj.type & UNIT_ST_OBJ ) )
        return("");

    if( obj.objecttype == ITEM_WEAPON )
        on obj.value[0] goto
root,axe,sword,club_mace,polearm,unarmed,special,

battle_axe,hand_axe,war_mattock,war_hammer,great_sword,scimitar,katana,
        falchion,kopesh,broad_sword,long_sword,rapier,short_sword,dagger,
        battle_mace,mace,battle_club,club,morning_star,flail,quarterstaff,
        spear,halberd,bardiche,sickle,scythe,trident,

fist,kick,bite,sting,claw,crush,whip,wakizashi,bow,throw,blank0,blank1,
        blank2,blank3,blank4;
    return("");

  :root:
    return("[Root]");
  :axe:
    return("[Axe/Hammer]");
  :sword:
    return("[Sword]");
  :club_mace:
    return("[Club/Mace]");
  :polearm:
    return("[Polearm]");
  :unarmed:
    return("[Unarmed]");
  :special:
    return("[Special]");
  :battle_axe:
    return("[Battle Axe]");
  :hand_axe:
    return("[Hand Axe]");
  :war_mattock:
    return("[War Mattock]");
  :war_hammer:
    return("[War Hammer]");
  :great_sword:
    return("[Great Sword]");
  :scimitar:
    return("[Scimitar]");
  :katana:
    return("[Katana]");
  :falchion:
    return("[Falchion]");
  :kopesh:
    return("[Kopesh]");
  :broad_sword:
    return("[Broad Sword]");
  :long_sword:
    return("[Long Sword]");
  :rapier:
    return("[Rapier]");
  :short_sword:
    return("[Short Sword]");
  :dagger:
    return("[Dagger]");
  :battle_mace:
    return("[Battle Mace]");
  :mace:
    return("[Mace]");
  :battle_club:
    return("[Battle Club]");
  :club:
    return("[Club]");
  :morning_star:
    return("[Morning Star]");
  :flail:
    return("[Flail]");
  :quarterstaff:
    return("[Staff]");
  :spear:
    return("[Spear]");
  :halberd:
    return("[Halberd]");
  :bardiche:
    return("[Bardiche]");
  :sickle:
    return("[Sickle]");
  :scythe:
    return("[Scythe]");
  :trident:
    return("[Trident]");
  :fist:
    return("[Fist]");
  :kick:
    return("[Kick]");
  :bite:
    return("[Bite]");
  :sting:
    return("[Sting]");
  :claw:
    return("[Claw]");
  :crush:
    return("[Crush]");
  :whip:
    return("[Whip]");
  :wakizashi:
    return("[Wakizashi]");
  :bow:
    return("[Bow]");
  :throw:
    return("[Throw]");
  :blank0:
    return("[Blank0]");
  :blank1:
    return("[Blank1]");
  :blank2:
    return("[Blank2]");
  :blank3:
    return("[Blank3]");
  :blank4:
    return("[Blank4]");

}
dilend
/***************************************************************************
***/
/* Makes sure that the items sold are first in the list. */
dilbegin shop_sort( );
var
item : unitptr;
dest : unitptr;
nxt  : unitptr;
code{
    dest := load( "bag@midgaard" );/* Load something to store in */
    item := self.inside;
    while( item )                  /* Store all items that have a price
tag */
    {
      nxt := item.next;
      if( "$price" in item.extra )
        link( item, dest );
      item := nxt;
    }
    item := dest.inside;
    while( item )                  /* Put all items back inside shopkeeper */
    {
      nxt := item.next;
      link( item, self );
      item := nxt;
    }
    destroy( dest );
    return;
}
dilend
/***************************************************************************
***/
/* This is added to all shopitems so they wont have a pricetag when stolen or
  is some gods forces them to drop the items... */
dilbegin shopown( );
var
owner : unitptr;
code{
    owner := self.outside;
    secure( owner, gone );
    heartbeat:= PULSE_SEC*5;
  :loop:
    wait( SFB_TICK, TRUE );
    if( owner == self.outside ) goto loop;

  :gone:
    subextra( self.extra, "$price" );
    quit;
}
dilend
/***************************************************************************
***/
#define LOG(_CMDSTR) log(self.nameidx+"/"+rom.nameidx+"Shop error - "+_CMDSTR)

#define BUY_PRICE(u) ((u.cost * buyprofit)/100)
#define SELL_PRICE(u) ((u.cost * sellprofit)/100)

dilbegin aware shopkeeper(prod       : stringlist, custom_acts : stringlist,
                         opentimes  : stringlist, itemtype    : string,
                         sellprofit : integer,    buyprofit   : integer,
                         maxcash    : integer,
                         closedil   : string,     dilparams   : string );
external
       integer isopen( opentimes : stringlist );
       integer carry_n_limit( ch : unitptr );
       integer carry_w_limit( ch : unitptr );
        string wpntype( obj : unitptr );
               shop_sort( );
var
temp_nitem : integer;    /* added to fix a bug with act */
sitem     : integer;    /* Selected item */
citem      : integer;    /* Current item */
mitem      : integer;    /* Max item */

iteml      : stringlist; /* Item list */
items      : string;     /* Item symbolic */
itmsl      : stringlist; /* Items symbolc list */
itmsn      : stringlist; /* Itemlist including names */
nitml      : stringlist; /* Current number of items */
mitml      : stringlist; /* Max number of items */
produ      : stringlist; /* Copy of prod, but only functioning items */
cisl       : stringlist; /* Complete item symbolic list */
acts       : stringlist;

arg        : string;     /* Argument */
nitem      : integer;    /* Number of items */
clc        : integer;    /* Calculations integer */

tmps       : string;     /* Crap string */
u          : unitptr;    /* Crap unitptr */
rom        : unitptr;
pc         : unitptr;
tu         : unitptr;    /* Temporary unit */
ts         : stringlist; /* Temporary stringlist */
fitstr     : string;     /* Fit string */
wpnstr     : string;     /* Weapon string */

ti         : integer;    /* Temporary integer */
ts1        : string;     /* Temporary string */
ts2        : string;     /* Temporary string */
ext        : extraptr;   /* Temporary extraptr */
types      : stringlist; /* Extraptr holding item types traded. */


closed     : integer;    /* Open/Closed flag */

tm         : integer;    /* For cost calculation */

cc         : integer;    /* Cost */

openstat   : integer;    /* Current shop status: Open or Closed */
chgtime    : integer;    /* Time for next change of shop status */
choice     : integer;    /* Command choice */
foundany   : integer;    /* If any items was in list */

homes      : string;   /* Symbolic of room where we were first loaded */
home     : unitptr;    /* Home room where we were first loaded */
mys        : string;     /* Symbolic of the shopkeeper */
account    : extraptr;   /* Where we store our money */
noaccount  : integer;    /* Is there a account already? */

code
{

:init:
  /* Everything is handled by a price extra and an item list */
  /* Any item in inv without a pricetag can't be sold! */

  heartbeat := 3;
  rom := self;
  while( not( rom.type & UNIT_ST_ROOM ) )
     rom := rom.outside;

  /* Made it so that the shopkeeper stores the amount of money he has  */
  /* left in an extra on his "home room". This way the shopkeeper does */
  /* not have full money every time he is killed, only refreshed once  */
  /* per day or after a reboot/crash as it should. (Nexus)             */

  homes := rom.nameidx+"@"+rom.zoneidx;
  mys := self.nameidx+"@"+self.zoneidx;
  home := findroom(homes);
  account := "$Shop Account" in home.extra;

  if ((account) and (mys in account.names))
     noaccount := 0;
  else if (account)
  {
     log(self.nameidx+"/"+rom.nameidx+" Shop Error - Found other "+
        "shopkeeper's account");
     noaccount := 0;
  }
  else /* make account */
  {
     ts := {"$Shop Account"};
     addstring(ts,mys);
     addextra(home.extra,ts,"");
     noaccount := 1;
  }

  if( length( custom_acts.[0] ) > 0 )
     addstring( acts, custom_acts.[0] );
  else
     addstring( acts, "$1n says, 'I've got no such item!'" );

  if( length( custom_acts.[1] ) > 0 )
     addstring( acts, custom_acts.[1] );
  else
     addstring( acts, "$1n says, '$3n, you havn't even got it!'" );

  if( length( custom_acts.[2] ) > 0 )
     addstring( acts, custom_acts.[2] );
  else
     addstring( acts, "$1n says, 'I don't trade with things such as $2n'" );

  if( length( custom_acts.[3] ) > 0 )
     addstring( acts, custom_acts.[3] );
  else
     addstring( acts, "$1n says, '$3n, you can't afford $2n'" );

  if( length( custom_acts.[4] ) > 0 )
     addstring( acts, custom_acts.[4] );
  else
     addstring( acts, "$1n says, 'Here is $2n'" );

  if( length( custom_acts.[5] ) > 0 )
     addstring( acts, custom_acts.[5] );
  else
     addstring( acts, "$1n says, 'Thank you for $2n'" );

  if( length( custom_acts.[6] ) > 0 )
     addstring( acts, custom_acts.[6] );
  else
     addstring( acts, "$1n says, 'I don't seem to have that many $2ns in
my stock'" );

  if( length( custom_acts.[7] ) > 0 )
     addstring( acts, custom_acts.[7] );
  else
     addstring( acts, "$1n says, 'The shop is closed, come back later'" );

  if( length( custom_acts.[8] ) > 0 )
     addstring( acts, custom_acts.[8] );
  else
     addstring( acts, "$1n says, '$3n, I have no use for $2n.'" );

  if( length( custom_acts.[9] ) > 0 )
     addstring( acts, custom_acts.[9] );
  else
     addstring( acts, "$1n says, 'It looks like i don't have enough cash
for it.'" );

/* Brain segment added by Eirinn December 7th to counteract stealing. */

  if ((self.level < 10) and (self.abilities[ABIL_BRA] < 50))
     self.abilities[ABIL_BRA] := 50;
  else if ((self.level < 20) and (self.level >= 10) and
     (self.abilities[ABIL_BRA] < 75))
     self.abilities[ABIL_BRA] := 75;
  else if ((self.level < 30) and (self.level >= 20) and
     (self.abilities[ABIL_BRA] < 100))
     self.abilities[ABIL_BRA] := 100;
  else if ((self.level < 40) and (self.level >= 30) and
     (self.abilities[ABIL_BRA] < 125))
     self.abilities[ABIL_BRA] := 125;
  else if ((self.level >=40) and (self.abilities[ABIL_BRA] < 150))
     self.abilities[ABIL_BRA] := 150;

  iteml := prod;                       /* Init. all needed system lists! */
  itmsl := null;
  produ := null;
  itmsn := null;
  mitml := null;
  nitml := null;
  citem := 0;
  tm := 0;
  while( citem < length( iteml ) )
  {
     tmps := iteml.[citem];
     items := getword( tmps );
     ts1 := getword( tmps );
     ts2 := getword( tmps );
     u := load( items );
     secure( u, lost_u );
     if( u )
     {
 if( ( atoi( ts1 ) > 0 ) and ( atoi( ts2 ) > 0 ) )
 {
    if( not( items in itmsl ) )
    {
       if( not( u.name in itmsn ) )
       {
   addstring( produ, iteml.[citem] );
   addstring( itmsl, items         );
   addstring( nitml, ts1     );
   addstring( mitml, ts2           );
   addstring( itmsn, u.name        );
   tmps := moneystring( ( u.cost * sellprofit )/100, 0 );
   addextra( u.extra, {"$price"}, tmps );
       }
       else
       {
   LOG( "2 or more items with the same first name '" + u.name + "'" );
   destroy( u );
       }
    }
    else
    {
       LOG( "Duplicate items in shop, item '" + items + "'" );
       destroy( u );
    }
 }
 else
 {
    LOG( "Illegal unit definition '" + iteml.[citem] + "'" );
    destroy( u );
 }
     }
     else
 LOG( "Error loading item '" + items + "'" );

  :lost_u:
     unsecure( u );
     citem := citem + 1;
     tm := tm + 1;
     if( tm == 5 )
     {
 pause;
 tm := 0;
     }
  }
  cisl := itmsl;

  interrupt( SFB_DEAD, self==activator, destroy_items );

  if( length( dilparams ) < 2 )
     dilparams := "()";
  else if( not ( "(" in dilparams ) ) /* No spaces in parameters.. add
some */
     dilparams := "(" + dilparams + ")";

  types := null;
  ts1   := itemtype;
  while( length( ts1 ) > 0 )
  {
     ts2 := getword( ts1 );
     if( atoi( ts2 ) > 0 )
 addstring( types, ts2 );
  }
  pause;
  heartbeat := PULSE_SEC * 30;

  if (noaccount)
     goto newday;      /* credit account */
  else
     goto no_credit;   /* don't credit account */

:check_open:
  sendto( "closing", self );
  heartbeat := PULSE_SEC * 30;
  if( length( closedil ) > 0 )
     if( not( "(" in closedil or ")" in closedil ) )
        dilcopy( closedil + dilparams, self );

:loop_closed:
  wait( SFB_CMD|SFB_TICK|SFB_DONE, TRUE );
  if ( self.position < POSITION_RESTING) goto loop_closed;

  if(  command( "list"    ) or
       command( "buy"     ) or
       command( "sell"    ) or
       command( "value"   ) or
       command( "request" ) )
  {
     block;
     act( acts.[7], A_SOMEONE, self, u, activator, TO_VICT );
  }
  else if( not( command( CMD_AUTO_TICK ) or command( "give" ) ) )
     goto loop_closed;

  closed := isopen( opentimes );
  if( closed == STATE_OPEN )
  {
     closed := dildestroy( closedil, self );
     closed := STATE_OPEN;
     goto newday;
  }

  if( ( atoi( opentimes.[0] ) - 1 ) == mudhour )
     sendto( "opening", self );

  if( command( "give" ) and medium )
  {
     if( "$price" in medium.extra )
 subextra( medium.extra, "$price" );
  }

  shop_sort();
  goto loop_closed;

:loop:
  closed := isopen( opentimes );
  if( closed ) goto check_open;

  shop_sort();
  wait( SFB_CMD|SFB_TICK|SFB_DONE, TRUE );

  closed := isopen( opentimes );
  if( closed ) goto check_open;
  if( command( CMD_AUTO_TICK ) ) goto loop;

  if( command( "give" ) )
  {
     if( not medium ) goto loop;
     if( "$price" in medium.extra )
 subextra( medium.extra, "$price" );
     goto loop;
  }

  if( ( self.position == POSITION_FIGHTING ) or
      ( self.position < POSITION_RESTING ) )
     goto loop;


  if( command( "list"    ) ) choice := 0;
  else if( command( "buy"     ) ) choice := 1;
  else if( command( "sell"    ) ) choice := 2;
  else if( command( "value"   ) ) choice := 3;
  else if( command( "request" ) ) choice := 4;
  else if( command( "stock" ) and ( activator.level >= IMMORTAL_LEVEL ) )
     choice := 5;
  else
     goto loop;

  pc := activator;
  if( not visible( pc, self ) )
     goto loop;
  block;
  if( not visible( self, pc ) )
  {
     exec( "say I only do business with people i can see.", self );
     goto loop;
  }
  arg := argument;
  secure( pc, lost_pc );

  on choice goto list,buy,sell,check_value,request,stock;

  log( "Something is wrong, shop triggered on an unknown command." );
  unsecure( pc );
  goto loop;

:stock:
  mitem := length( produ );
  citem := 0;
  items := self.nameidx + "@" + self.zoneidx + " current stock:&n";
  while( citem < mitem )
  {
     tmps := produ.[citem];
     items := items + " " + getword( tmps ) + " P: " + getword( tmps ) +
", M: " + mitml.[citem];
     items := items + ", C: " + nitml.[citem] + "&n";
     citem := citem + 1;
  }
  sendtext( items, pc );
  unsecure( pc );
  goto loop;

:newday:   /* New day, increase stock & load stolen/soldout goods */
  home := findroom(homes);
  account := "$Shop Account" in home.extra;
  if ((account) and (mys in account.names))
     account.descr := itoa(maxcash); /* fill daily account */
  else if (account)
     log(self.nameidx+"/"+rom.nameidx+"Shop Error - The account I found is
not mine!");
  else
     log(self.nameidx+"/"+rom.nameidx+"Shop Error - Can't find a shop
account!");

:no_credit:   /* Go here if account should not be credited */
  mitem := length( itmsl );
  citem := 0;
  while( citem < mitem )
  {
     items := produ.[citem];
     tmps := getword( items );
     tmps := itoa( atoi( nitml.[citem] ) + atoi( getword( items ) ) );
     ts1  := getword( items );
     if( atoi( tmps ) > atoi( ts1 ) )
 tmps := ts1;
     nitml.[citem] := tmps;
     u := findunit( self, itmsn.[citem], FIND_UNIT_INVEN, null );
     if( ( u == null ) and ( atoi( tmps ) > 0 ) )
     {
 u := load( itmsl.[citem] );
 tmps := moneystring( ( u.cost * sellprofit ) / 100, 0 );
 addextra( u.extra, {"$price"}, tmps );
     }
     else if( not( "$price" in u.extra ) and ( atoi( tmps ) != 0) )
     {
 if( ( u.nameidx + "@" + u.zoneidx ) != itmsl.[citem] )
    u := load( itmsl.[citem] );
 tmps := moneystring( ( u.cost * sellprofit ) / 100, 0 );
 addextra( u.extra, {"$price"}, tmps );
     }
     citem := citem + 1;
  }
  goto loop;

:request:   /* Price request on item in own inv. */
  u := findunit( self, arg, FIND_UNIT_INVEN, null );
  if( u == null)
  {
     act( acts.[0], A_SOMEONE, self, null, pc, TO_VICT );
  }
  else
  {
     ext := "$price" in u.extra;
     if( ext )
     {
 if( ext.descr == "" )
    act( "$1n says, 'I'll give $2n away for free'", A_SOMEONE, self, u,
pc, TO_VICT);
 else
    act( "$1n says, 'I want "+ext.descr+" for $2n'", A_SOMEONE, self, u,
pc, TO_VICT);
     }
     else
 act( acts.[0], A_SOMEONE, self, u, pc, TO_VICT );
  }
  unsecure( pc );
  goto loop;

:sell:   /* Sell routine for players to sell to shopkeeper */
  u := findunit( pc, arg, FIND_UNIT_INVEN, null );
  if( u == null )
  {
     act( acts.[1], A_SOMEONE, self, null, pc, TO_VICT );
  }
  else
  {
     if( "$no-sell" in u.extra )
     {
        act( acts.[8], A_SOMEONE, self, u, pc, TO_VICT );
        unsecure( pc );
        goto loop;
     }

     if( not( itoa( u.objecttype ) in types ) )
     {
 act( acts.[2], A_SOMEONE, self, u, pc, TO_VICT );
 unsecure( pc );
 goto loop;
     }

     cc := u.cost;
     if( cc < 0 )
 cc := 0;

     tm := ( cc * buyprofit ) / 100;

     home := findroom(homes);
     account := "$Shop Account" in home.extra;
     if ((account) and (mys in account.names)) /* do account stuff */
     {
        ti := atoi(account.descr);
        if (tm > ti)
        {
           act( acts.[9], A_SOMEONE, self, u, pc, TO_VICT );
           unsecure( pc );
           goto loop;
        }
        ti := ti - tm;
        account.descr := itoa(ti);
     }
     else /* if no account or other shopkeeper's, say can't afford */
     {
        act( acts.[9], A_SOMEONE, self, u, pc, TO_VICT );
        unsecure( pc );
        goto loop;
        log(self.nameidx+"/"+rom.nameidx+ "Shop Error - Can't find my "+
           "shop account!");
     }
     log(self.nameidx+"/"+rom.nameidx+ "Shop Error - Can't find my shop
account!");

     citem := transfermoney( null, pc, tm );

     tmps := moneystring( ( cc * sellprofit ) / 100, FALSE );

     if( not( "$price" in u.extra ) )
        addextra( u.extra, {"$price"}, tmps );
/*
     act( acts.[5], A_SOMEONE, self, u, pc, TO_VICT );
*/
     tmps := moneystring( ( cc * buyprofit ) / 100, FALSE );
     act( "$1n says, 'I paid you $2t for that $3n'",
          A_SOMEONE, self, tmps, pc, TO_ALL );

     link( u, self );

     if( ( u.nameidx + "@" + u.zoneidx ) in itmsl )
     {
 mitem := length( itmsl );
 citem := 0;
 sitem := ( u.nameidx + "@" + u.zoneidx ) in itmsl;

 nitml.[sitem - 1] := itoa( atoi( nitml.[sitem - 1] ) + 1 );

 destroy( u );
     }
     else if( ( u.nameidx + "@" + u.zoneidx ) in cisl )/* If already in
store*/
     {                                                 /* destroy new
item. */
 destroy( u );
     }
     else                                  /* If not in store already */
     {                                     /* add to store list */
 if( length( cisl ) >= 24 )         /* Allow only 24 items in list */
    destroy( u );
 else
    addstring( cisl, u.nameidx+"@"+u.zoneidx );
     }
  }
  unsecure( pc );
  goto loop;

:check_value:   /* Check value of item in players inv. on request */
  u := findunit( pc, arg, FIND_UNIT_INVEN, null);

  if (u.type != UNIT_ST_OBJ)
      {
      act ("$1n says, 'Ewww it's alive!'", // Let's assume it's not a room
          A_SOMEONE, self, u, pc, TO_VICT);
      unsecure ( pc );
      goto loop;
      }

  if( "$no-sell" in u.extra )
      {
      act( acts.[8], A_SOMEONE, self, u, pc, TO_VICT );
      unsecure( pc );
      goto loop;
      }

  if( u != null )
  {
     tmps := moneystring( ( u.cost * buyprofit ) / 100, 0 );

     act( "$1n says, 'I'll pay " + tmps + " for the $2N'",
 A_SOMEONE, self, u, pc, TO_VICT );
  }
  else
    act( acts.[1], A_SOMEONE, self, null, pc, TO_VICT );
  unsecure( pc );
  goto loop;

:buy:                /* Buy routine */
  nitem := 0;
  tm := 0;
  nitem := atoi( getword( arg ) );

  if (nitem < 0)
  {
     act("$1n says, 'How are you planning to buy a negative number of
items?'",
 A_SOMEONE, self, null, pc, TO_ALL );
  }
  else if(( nitem > 1 ) and ( arg != "" ))
  {
     /* If bigger than 0, then a multiple number was requested */

     if( nitem > 100 )
     {
 act( "$1n says, 'I don't sell over 100 items at one time'",
    A_SOMEONE, self, null, pc, TO_ALL );
 unsecure( pc );
 goto loop;
     }

     u := findunit( self, arg, FIND_UNIT_INVEN, null ); /*Check inventory */

     if( "$price" in u.extra )     /* If in inventory */
     {
 clc := can_carry( pc, u, nitem );
 if( clc == 1 )
 {
    act("Your hands are full!", A_SOMEONE, self, null, pc, TO_VICT );
    unsecure( pc );
    goto loop;
 }
 else
   if( clc == 2 )
   {
      act("You can't carry the weight!",
  A_SOMEONE, self, null, pc, TO_VICT );
      unsecure( pc );
      goto loop;
   }

 cc := u.cost;                             /* Get item cost */
 items := u.nameidx + "@" + u.zoneidx;     /* Get symbolic name */
 sitem := items in itmsl;

        if( sitem )
    cc := (cc * atoi( mitml.[sitem - 1] )) / atoi( nitml.[sitem - 1] );

 if( pc.level > IMMORTAL_LEVEL )
    tm := 1;
 else
    tm := transfermoney( pc, null, cc * nitem );

        if ( tm )
 {
    if ( items in itmsl )              /* Check if standard item */
    {
       if( nitem <= atoi( nitml.[sitem - 1] ) )
       {
    nitml.[sitem - 1] := itoa(atoi( nitml.[sitem - 1] ) - nitem);
   if( nitml.[sitem - 1] == "0" )
       destroy( u );
   else
   {
      subextra( u.extra, "$price" );
                    tmps := moneystring( ( u.cost * sellprofit * atoi(
mitml.[sitem-1] ) / atoi( nitml.[sitem-1] ) ) / 100, 0 );
      addextra( u.extra, {"$price"}, tmps );
   }
                 temp_nitem:=nitem;
   while( temp_nitem > 0 )      /* Load and link all items */
   {
      tu := load( items );
      tu.height := pc.height;
      link( tu, pc );
      temp_nitem := temp_nitem- 1;
   }
                 tmps := moneystring( (cc * nitem), FALSE );
                 act( "$1n says, 'Those cost you $2t", A_SOMEONE, self,
tmps, pc, TO_ALL );
   act( "$1n gives you " + getword( argument ) + " $2n.",
      A_SOMEONE, self, u, pc, TO_VICT );
   act( "$1n gives $3n some $2ns.",
      A_SOMEONE, self, u, pc, TO_NOTVICT );
              }
       else
       {
   if (transfermoney( null, pc, cc * nitem ) and FALSE)
      exec("grin",self);

   act( acts.[6], A_SOMEONE, self, u, pc, TO_ALL );
       }
    }
    else  /* Not standard item, can't buy multiple.. */
       act( acts.[6], A_SOMEONE, self, u, pc, TO_ALL );
        }
 else
        {
    act( acts.[3], A_SOMEONE, self, u, pc, TO_ALL );
        }
     }
     else
     {
       act( acts.[0], A_SOMEONE, self, null, pc, TO_ALL );
     }
  }
  else
  {
     if( nitem == 1 )
 u := findunit( self, arg,      FIND_UNIT_INVEN, null );
     else
 u := findunit( self, argument, FIND_UNIT_INVEN, null );

     if( "$price" in u.extra )
     {
 clc := can_carry( pc, u, 1 );

 if( clc == 1 )
 {
    act("Your hands are full!", A_SOMEONE, self, null, pc, TO_VICT );
    unsecure( pc );
    goto loop;
 }
 else if( clc == 2 )
 {
    act("You can't carry the weight!",
       A_SOMEONE, self, null, pc, TO_VICT );
    unsecure( pc );
    goto loop;
 }

        cc    := (u.cost * sellprofit) / 100;
 items := u.nameidx + "@" + u.zoneidx;
 sitem := items in itmsl;
 if( sitem )
    cc := (cc * atoi( mitml.[sitem - 1] )) / atoi( nitml.[sitem - 1] );

 /* If buyer is a god, don't take money */
 if( pc.level >= IMMORTAL_LEVEL )
    tm := 1
 else
    tm := transfermoney( pc, null, cc );

        if( tm != 0)
 {
    if( items in itmsl )
    {
       mitem := length(itmsl);
       citem := 0;

       if( atoi( nitml.[sitem - 1] ) > 0 )
       {
   nitml.[sitem-1] := itoa( atoi( nitml.[sitem-1] ) - 1 );

                 ext := "$price" in u.extra;

                 tmps := ext.descr;

                 act( "$1n says, 'That cost you $2t", A_SOMEONE, self,
tmps, pc, TO_ALL );
/*
                 act( acts.[4], A_SOMEONE, self, u, pc, TO_ALL );
*/

                 if( nitml.[sitem-1] == "0" )
      destroy( u );
   else
   {
      subextra( u.extra, "$price" );
      tmps := moneystring( ( u.cost * sellprofit * atoi( mitml.[sitem-1] )
/ atoi( nitml.[sitem-1] ) ) / 100, 0 );
      addextra( u.extra, {"$price"}, tmps );
   }

                 tu := load( items );
   tu.height := pc.height;
   link( tu, pc );
       }
       else
   act( acts.[0], A_SOMEONE, self, null, pc, TO_ALL );
    }
    else
    {
              tmps := moneystring( cc, FALSE );
              act( "$1n says, 'That cost you $2t", A_SOMEONE, self, tmps,
pc, TO_ALL );
/*
              act( acts.[4], A_SOMEONE, self, u, pc, TO_ALL );
*/
       subextra( u.extra, "$price" );
       substring( cisl, u.nameidx+"@"+u.zoneidx );
       link( u, pc );
    }
 }
        else
        {
    act( acts.[3], A_SOMEONE, self, u, pc, TO_ALL );
        }
     }
     else
 act( acts.[0], A_SOMEONE, self, null, pc, TO_ALL );
  }
  unsecure( pc );
  goto loop;

:list:
  act( "Items in store:", A_SOMEONE, self, null, pc, TO_VICT );
  u := self.inside;
  sitem := 0;
  foundany := 0;
  while( u )
  {
     ext := "$price" in u.extra;
     if( ext )
     {
 if( u.minv <= pc.level )
 {
    tmps := ext.descr;
    if ( u.objecttype == ITEM_WEAPON )
    {
       wpnstr := wpntype( u );
       wpnstr := wpnstr + " ";
           }
    else
       wpnstr := "";

    fitstr := fits( pc, u, -1 );
    foundany := 1;

    citem := ( u.nameidx + "@" + u.zoneidx ) in itmsl;

    if ( citem > 0 ) /* ( u.nameidx + "@" + u.zoneidx ) in itmsl )*/
    {
       act(" [" + nitml.[citem - 1] + "] $2N " + wpnstr + "at " + tmps + ".",
   A_ALWAYS, self, u, pc, TO_VICT );
    }
    else
    {
              if (fitstr != "")
                fitstr := fitstr + " ";

       act( " [1] $2N " + wpnstr + "&c+c" + fitstr + "&cwat " + tmps + ".",
   A_ALWAYS, self, u, pc, TO_VICT );
    }
 }
     }

     sitem := sitem + 1;
     if( sitem == 50 ) /* This shouldn't be required, but pause */
     {                 /* to prevent destruction of dil */
 sitem := 0;
 secure( u, lost_unit);
 heartbeat := 2;
 wait(SFB_TICK, TRUE);
 heartbeat := PULSE_SEC * 30;
 unsecure( u );
     }
     u := u.next;
  }
  if( foundany == 0 )
     act( "None!", A_SOMEONE, self, null, pc, TO_VICT );
  unsecure( pc );
  goto loop;

:lost_unit:
  unsecure( u );
  goto loop;

:lost_pc:
  unsecure( pc );
  goto loop;

:destroy_items:    /* Wipe all units that shopkeeper used to sell */
  u := self.inside;
  while(u!=null)
  {
     pc := u.next;
     subextra(u.extra,"$price");
     if(((u.nameidx+"@"+u.zoneidx) in cisl) and (u.equip==0))
  destroy(u);
     u := pc;
  }
  quit;

}
dilend /* shopkeeper */

dilbegin integer direction(dir : string);
code
{
  dir := " " + getword(dir);
  if (dir in " north")
    return (NORTH);
  else if (dir in " east")
    return (EAST);
  else if (dir in " south")
    return (SOUTH);
  else if (dir in " west")
    return (WEST);
  else if (dir in " up")
    return (UP);
  else if (dir in " down")
    return (DOWN);
  else if (dir in " northeast")
    return (NORTHEAST);
  else if (dir in " northwest")
    return (NORTHWEST);
  else if (dir in " southeast")
    return (SOUTHEAST);
  else if (dir in " southwest")
    return (SOUTHWEST);
  return (-1);
}
dilend

dilbegin busy (s:string);
code
{
  :start:
  wait (SFB_DONE, command("give") and
 (self==target));
  exec ("say "+s, self);
  exec ("drop "+medium.name, self);
  goto start;
}
dilend

dilbegin string accept_dil (pc:unitptr,s:string);
var
  i:integer;
code
{
  heartbeat := PULSE_SEC*7;
  secure(pc,losthim);
  exec ("say "+s,self);
  i := 0;
  while (i < 5)
  {
     wait(SFB_DONE, activator == pc);
     if (command("nod"))
return ("yes");
     else if (command("say") and
       (argument =="yes"))
return ("yes");
     else if (command("shake"))
return ("no");
     else if (command("say") and
       (argument =="no"))
return ("no");
     else
     {
 /*  This specifies what happens if the pc does something else*/
 pause;
 exec ("say "+s,self);
     }

     i := i+1;
     pause;
  }
  :losthim:

  return ("no");
}
dilend



/*

  Blocks the buy & sell commands unless he has completed the quest in the
  string 'q'. If not, the string 's' is executed by owner (self).
  For example:

dilcopy teach_q_block@function("$Police Academy", "say Deputies only!");

special SFUN_TEACH_INIT ...

*/

dilbegin shop_q_block(q : string, s : string);
code
{
  :loop:
  wait(SFB_CMD, command("buy") or command("sell"));

  if (not (q in activator.quests))
  {
     exec(s, self);
     block;
  }

  goto loop;
}
dilend


/*

  Blocks the practice command unless he has completed the quest in the
  string 'q'. If not, the string 's' is executed by owner (self).
  For example:

dilcopy teach_q_block@function("Mary's Pot Complete", "say Buggar ye off");

special SFUN_TEACH_INIT ...

*/

dilbegin teach_q_block(q : string, s : string);
code
{
  :loop:
  wait(SFB_CMD, command("practice"));

  if (not (q in activator.quests))
  {
     exec(s, self);
     block;
  }

  goto loop;
}
dilend



/*
function blowaway

used on:  items and even mobs shiver

example:
dilcopy blowaway (1500,"this is the act");
*/

dilbegin blowaway(seconds:integer,s : string);
code
{
  heartbeat := PULSE_SEC *seconds;

  :loop:
  pause;

  if (self.outside.type == UNIT_ST_ROOM)
  {
     if (not isset(self.flags, UNIT_FL_BURIED))
       act(s, A_SOMEONE, self, null, null, TO_ROOM);
     destroy(self);
  }

  goto loop;
}
dilend


/* The climb skill as a special routine to be connected to ROOMS

 Ticks  : None
 Used on: ROOMS
 Syntax : climb(destination:string, difficulty:integer,
         damage : integer, direction : integer);
 Example: dilcopy climb@function("deck@ship", 17, 20, "up");

    This special dil is used for the climb skill and should be set
    on stationary objects (stationary mast, robe, tree, wall, etc).
    The is the skill-amount required to climb. A skill of 100
    would be a 50% chance for the expert thief / climber.
    The is how much damage is given if you fail to climb the
    object. When you fail, you "fall" to the , so you can
    make gravity work correctly.
    The destination can be the same room in which you started.
    The is the direction in which a climb is required
    (most usually it is up, which is macro UP == 5).
*/

dilbegin climb(destination:string, difficulty:integer,
              damage:integer, direction:integer);
external
  integer skillresist(aa : integer, ad : integer,
                            sa : integer, sd : integer);
var
  dest : unitptr;
  skilla : integer;
  hm : integer;
  doorname : string;
code
{
  if (not self.exit_to[direction])
  {
     log("No such exit in climb dil.");
     quit;
  }

  if (not findroom(destination))
  {
     log("No such room in climb dil.");
     quit;
  }

  doorname := self.exit_names[direction].[0];
  /* One could check for an empty name here... */

dilcopy ("climb_cmd@function("+itoa(direction)+")",self);

  :loop:
  wait(SFB_CMD,command(direction));

  if (activator.type == UNIT_ST_PC)
  {
     skilla := activator.skills[SKI_CLIMB];
     if (skilla < 1)
skilla := -25;
  }
  else
    skilla := activator.abilities[ABIL_DEX];

  hm := skillresist(skilla, 0,
      activator.abilities[ABIL_DEX], 0);

  if (hm >= difficulty)
  {
     act("You easily climb the $2t.",
  A_ALWAYS, activator, doorname, null, TO_CHAR);
     act("$1n easily climbs the $2t.",
  A_ALWAYS, activator, doorname, null, TO_ROOM);
     goto loop;
  }

  dest := findroom(destination);

  if (dest != activator.outside)
  {
     act("You fall and hit yourself!",
  A_ALWAYS, activator, doorname, null, TO_CHAR);

     act("$1n failed to climb the $2t and takes a nasty fall...",
  A_ALWAYS, activator, doorname, null, TO_ROOM);

     link(activator, dest);

     act("$1n failed to climb the $2t and lands at your feet...",
  A_ALWAYS, activator, doorname, null, TO_ROOM);
  }
  else
  {
     act("You fail to climb the $2t!",
  A_ALWAYS, activator, doorname, null, TO_CHAR);

     act("$1n failed to climb the $2t!",
  A_ALWAYS, activator, doorname, null, TO_ROOM);
  }

  if (activator.level < IMMORTAL_LEVEL)
  {
     activator.hp := activator.hp - damage;
     position_update(activator);
  }
  block;
  goto loop;
}
dilend

dilbegin climb_cmd (direction:integer);
external
                       string dirstring (dr:integer);
                       var
                        dir:string;
code
{
dir:=dirstring(direction);
:start:
wait (SFB_CMD, command ("climb"));
block;
if (not(argument == dir))
 {
 act ("You can't seem to climb in that direction.",
 A_ALWAYS,activator,null,null,TO_CHAR);
 goto start;
 }

exec (dir,activator);
goto start;
}
dilend



/*
Function:  combat_mag
Descr:     Replaces the special function SFUN_COMBAT_MAGIC and
          SFUN_COMBAT_HEAL that better uses the magic.
use on:    mobiels
arguments: atk_spl : string  (Attack spell ie "fireball" or "" for none)
          def_spl : string  (Defense Spell ie "heal" or "" for none)
    def_pct : integer (At what % of hitpoints defense spell
                        will be cast)
    spd     : integer (speed at which mob will uses its attack magic
                       1 for all at once (every round) to
         5 for every 5 rounds. I suggest 2.)

Defense spells take priority when the hitpoints fall below the % specified,
after (if) the hits have been restored above that number attack magic will
resume.  If def_spl is used, function automaticly makes sure that it retains
enough mana for at least one healing, ie it will attack 4 times if it don't
need a healing.

example:
dilcopy combat_mag@function ("harm", "heal", 25, 2);
*/

dilbegin combat_mag(atk_spl : string, def_spl : string, def_pct : integer,
                   spd: integer);
var
 use_atk : integer;
 use_def : integer;
 can_cast : integer;
 hp_trig : integer;
 i : integer;
 test : string;
code
{
  :init:
use_atk := 0;
use_def := 0;
hp_trig := ((self.max_hp*def_pct)/100);
if (atk_spl != "")
  {
/* Use for compatibility */
  if ("cast" in atk_spl)
  {
    test := getword(atk_spl);
  }
   use_atk := 1;
}
if (def_spl != "")
{
  if ("cast" in def_spl)
  {
    test := getword(def_spl);
  }
    use_def := 1;
}
heartbeat := PULSE_VIOLENCE;

:waitcom:
wait(SFB_COM, self.position == POSITION_FIGHTING);

:def_spl:
heartbeat := 3;
if (use_def != 1)
   goto atk_spl;

if (self.hp <= hp_trig)
{
   exec("cast " + def_spl, self);
 goto waitcom;
}

:atk_spl:
if (use_atk == 1)
  {
 if(use_def == 1)
 {
    i := self.mana -20;
 if (i >= 20) {
   can_cast := 1;
 } else {
   can_cast := 0;
 }
 } else {
 if (self.mana >= 16) {
       can_cast := 1;
 } else {
   can_cast := 0;
     }
 }

 if(can_cast == 1)
  {
     heartbeat := PULSE_VIOLENCE * spd;
     pause; /* The self.fighting.name part was an addition but it causes */
       exec("cast " + atk_spl/* + " " + self.fighting.name */, self);
  }         /* problems when there are many mobs with the same name in a room */
  }

  if (self.mana <10)
{
  wait(SFB_TICK, TRUE);
}
  goto waitcom;
}
dilend

/*
function: fido
descr this replaces the old fido special
arguments:
 txt1 : the text shown when mob finds and eats corpses, default:
  'XXX savagely devours a corpse.'
   will be shown if txt1 is set to "".
   If txt1 is set to "stop", the mob will NOT devour corpses
   (convenient if you want your dogs to only eat food leftovers
   but not corpses).

   txt2 : the text shown when mob finds and eats ITEM_FOOD, default:
  'XXX hungrily devours YYY.'
   will be shown if txt2 is set to "".
   If txt2 is set to "stop", the mob will NOT devour
   ITEM_FOOD (convenient if you want to make a corpse-eating
   ghoul, who'd choke on normal food, etc).

   In both cases $1n is the mob itself, $2n is the title of the
   item devoured.

  Example:

dilcopy fido@function("$1n slowly devours $2n, crunching the bones.",
       "$1n grabs $2n and hungrily munches it.");

*/


dilbegin fido(txt1:string,txt2:string);
var
u : unitptr;
code
{
:start:
heartbeat:=PULSE_SEC*rnd(5,15);
wait(SFB_TICK,TRUE);
wait(SFB_TICK,TRUE);
foreach (UNIT_ST_OBJ,u)
{
if ((u.nameidx=="corpse")and(u.zoneidx=="basis")and
   (u.flags!=UNIT_FL_BURIED)and(visible(self,u))and
   (txt1!="stop") and
 (u.value[2]!=1))
{
if (txt1=="")
  {
 exec("emote savagely devours a corpse.",self);
        }
else
      {
 act(""+txt1,A_SOMEONE,self,u,null,TO_ALL);
 }
while (u.inside) link(u.inside,self.outside);
destroy(u);
goto start;
}
if ((u.objecttype == ITEM_FOOD)and(txt2 != "stop")and
   (u.flags!=UNIT_FL_BURIED)and(visible(self,u)))
  {
if (txt2=="")
  {
 exec("emote hungrily devours "+u.title+".",self);
        }
else
      {
 act(""+txt2,A_SOMEONE,self,u,null,TO_ALL);
 }
destroy(u);
goto start;
}
goto start;
}
goto start;
}
dilend




/*
Function:  safe_room
Descr:     To make a room that disallows all forms of attacks.  Mainly used
          for safehavens like the temple of udgaard.
use on:    rooms
arguments:  NONE

example:

dilcopy safe_room@function();
*/
dilbegin string laststring (s:string );
var
 i:integer;
 s_list:stringlist;
code
{
s_list:=getwords(s);
i:=length(s_list);
if (i==0)
 return ("0");
else
 return (s_list.[(i-1)]);
 }
 dilend

dilbegin safe_room();
external
 string laststring (s:string);/*returns last word in a string.*/
var
 tgt_string:string;
 first_arg:string;
 arg:string;
 tgt:unitptr;
code
{
  :there:
  heartbeat := PULSE_SEC*3;
  wait(SFB_CMD, TRUE and
((activator.type==UNIT_ST_PC) or
 (activator.type==UNIT_ST_NPC)));

  if ((activator.type==UNIT_ST_NPC) and
      (activator.master==null)) goto there;


arg:=argument;
first_arg:=getword(arg);
tgt_string:=laststring (argument);
if (tgt_string=="0") goto there;
tgt:=findunit (activator,tgt_string,FIND_UNIT_SURRO,null);/*should probably
think of a better return*/
if ((tgt==null)and (first_arg!="command"))  goto there;

  if ((POLICE_ACADEMY in activator.quests) and
  ( tgt.type ==UNIT_ST_PC) and
(isset (tgt.charflags,CHAR_OUTLAW))) goto there;

if (tgt==activator.master) goto there;
  if ((command ("steal")) or
      (command ("kill")) or
      (command ("filch")) or
      (command ("pickpocket")) or
      (command ("disarm")) or
      (command ("trip")) or
      (command ("drag")) or
      (command ("backstab")) or
      (command ("hit")) or
      (command ("rescue")) or
      (command ("kick") ) or
      (command ("bash")) or
      (command ("use")) or
      (command ("recite")) or
      (command ("cast")))
  {
     block;
     act ("Spells and attacks are not allowed in safe rooms.",
   A_SOMEONE, activator, null, null, TO_CHAR);
  }

  goto there;
}
dilend


/*
Function:    wander_zones
Description: Random Wander like the special function,
            but allows more than one zone,
            i.e.  Can have a mob only wander two or three zones.

Arguments:
   zones : string  =   A string of zonenames seperated by spaces.
    spd : integer =   The speed (in seconds) at which the mob wanders.
                      Minumum = 5 secs (for process time).
  doors : integer =   Can open/close doors (0 = false, 1 = true)
lckd_doors: integer =   Can open/closed locked doors (0=false, 1=true)

Example:
dilcopy wander_zones@function ("halfzon haon_dor", 5, 1);

Jan 4th 2000: Added @loadzone; iputting @loadzone somewhere in the zones' string
             will replace it by the zone the mob is loaded in.
*/

dilbegin wander_zones(zones : string, spd : integer, doors : integer,
                     lckd_doors : integer);
                     external
                       string dirstring (dr:integer);
                       integer rev_dir (i:integer);
var
i:integer;
 dir : integer;
 x : integer;
 rm : unitptr;
 dir_string : string;
 op_string : string;
 dr_name : string;
 heart : integer;
 zones_lst : stringlist;

 s: string;

code
{
  :init:
  on_activation((self.position <= POSITION_SLEEPING) or
   ("$block wander" in self.extra), skip);

  zones_lst := getwords(zones);

  if ("@loadzone" in zones_lst)
     zones_lst.[("@loadzone" in zones_lst) - 1] := self.zone;

  if(( (spd*PULSE_SEC) < 5) )
  {
     heartbeat := PULSE_SEC*5;
  } else {
     heartbeat := spd * PULSE_SEC;
  }
  heart := heartbeat;

  :start:

  wait(SFB_TICK, TRUE);

  /* Make Sure we are in a legal wander zone.*/
  if (not(self.outside.zoneidx in zones_lst))
    goto start;

  /* Lets stand up if for some reason we are sitting */
  if ((self.position == POSITION_RESTING) or
      (self.position == POSITION_SITTING))
  {
     exec("stand", self);
  }

  /* if were not standing now then abort! */

  if (self.position != POSITION_STANDING)
    goto start;

  /* Get a rnd dir and make sure it exists,
     it is one of the zones we are allowed to wander, and
     it is not a no_mob room  */

  x := 0;  /* initilize loop counter, to prevent dil destroy */
  :get_dir:
  heartbeat := 3;
  dir := rnd(0,5);

  if ( (self.exit_to[dir] == null) or
      (not ((self.outside.exit_to[dir].zoneidx) in zones_lst)) or
      ( isset( self.outside.exit_to[dir].flags, UNIT_FL_NO_MOB)))
  {
     x := x+1;
     if (x > 20)
     {
 x := 0;
 pause;
     }
     goto get_dir;
  }
  heartbeat := heart;

dir_string:=dirstring (dir);
i:=rev_dir(dir);
     op_string :=dirstring (i);

  if (doors == TRUE) {
     if (isset (self.outside.exit_info[dir], EX_LOCKED))
goto locked;
     if (isset (self.outside.exit_info[dir], EX_CLOSED))
     {
     exec ("open "+dir_string+" "+self.outside.exit_names[dir].[0],self);
     exec (dir_string,self);
     dir :=rev_dir (dir);
     dir_string:=dirstring (dir);
     exec ("close "+dir_string+" "+self.outside.exit_names[dir].[0],self);
 goto start;
     }
  }

  :locked:
  if (lckd_doors == TRUE) {
     if (isset (self.outside.exit_info[dir], EX_LOCKED))
     {
 unset(self.outside.exit_info[dir], EX_LOCKED);
     exec ("open "+dir_string+" "+self.outside.exit_names[dir].[0],self);
     exec (dir_string,self);
     dir :=rev_dir (dir);
     dir_string:=dirstring (dir);
     exec ("close "+dir_string+" "+self.outside.exit_names[dir].[0],self);
 set(rm.exit_info[dir], EX_LOCKED);
 goto start;
     }
  }

  exec(dir_string, self);
  goto start;
}
dilend

/* This is similar to wander_zones because it's just a modified version of it,
* which requires no zones argument since it moves in all zones.
*/
// Coder's notes: dirlist should be made intlist

dilbegin global_wander(spd : integer, doors : integer,
                     lckd_doors : integer);

external string dirstring@function (dr:integer);
        integer rev_dir@function (i:integer);
var
 i:integer;
 dir : integer;
 dirlist: stringlist;
 rm : unitptr;
 dir_string : string;
 op_string : string;
 dr_name : string;
 heart : integer;

code
{
  :init:
  on_activation((self.position <= POSITION_SLEEPING) or
   ("$block wander" in self.extra), skip);

  if(( (spd*PULSE_SEC) < 5) )
  {
     heartbeat := PULSE_SEC*5;
  } else {
     heartbeat := spd * PULSE_SEC;
  }
  heart := heartbeat;

  :start:

  wait(SFB_TICK, TRUE);

  /* Lets stand up if for some reason we are sitting */
  if ((self.position == POSITION_RESTING) or
      (self.position == POSITION_SITTING))
  {
     exec("stand", self);
  }

  /* if were not standing now then abort! */

  if (self.position != POSITION_STANDING)
    goto start;

  /* Get a rnd dir and make sure it exists,
     it is one of the zones we are allowed to wander, and
     it is not a no_mob room  */

:get_dir:

rm:= self.outside;

dirlist:= {"abc"};
substring (dirlist, "abc");

dir:= 0;  /* initilize loop counter, to prevent dil destroy */

while (dir <= MAX_EXIT)
   {
   if ( (rm.exit_to[dir] != null) and
      not (isset (rm.exit_to[dir].flags, UNIT_FL_NO_MOB)) )
       addstring (dirlist, itoa(dir));

   dir := dir + 1;
   }

if (length(dirlist) <= 0)
   goto start;

dir:= rnd (0, length(dirlist) - 1);
dir:= atoi(dirlist.[dir]);

dir_string:=dirstring@function (dir);
i:=rev_dir@function(dir);
op_string:=dirstring@function (i);

  if (doors == TRUE) {
     if (isset (self.outside.exit_info[dir], EX_LOCKED))
goto locked;
     if (isset (self.outside.exit_info[dir], EX_CLOSED))
     {
     exec ("open "+dir_string+" "+self.outside.exit_names[dir].[0],self);
     exec (dir_string,self);
     dir :=rev_dir@function (dir);
     dir_string:=dirstring@function (dir);
     exec ("close "+dir_string+" "+self.outside.exit_names[dir].[0],self);
 goto start;
     }
  }

  :locked:
  if (lckd_doors == TRUE) {
     if (isset (self.outside.exit_info[dir], EX_LOCKED))
     {
 unset(self.outside.exit_info[dir], EX_LOCKED);
     exec ("open "+dir_string+" "+self.outside.exit_names[dir].[0],self);
     exec (dir_string,self);
     dir :=rev_dir@function (dir);
     dir_string:=dirstring@function (dir);
     exec ("close "+dir_string+" "+self.outside.exit_names[dir].[0],self);
 set(rm.exit_info[dir], EX_LOCKED);
 goto start;
     }
  }

  exec(dir_string, self);
  goto start;
}
dilend

/* Basic Guard Function.  Hopefully will take care of all of the necessary
  function of a law protecting function.  There are alot of options that
  need to be passed:
                    Type              Example
                    -------------------------------------------------
zones             - string  : halfzon halfzon2
cont_proc         - string  : guard@halfzon     * symbolic main guard *
       prot_prot         - integer : 1                 * 0 = false, 1= true *
       captain           - string  : sherrif@halfzon   * symbolic name of
captain*
*/



/* A guard door function by level, this blocks all previous holes,
  drag, groups, etc. and allows greater flexibility.  Groups are allowed
  to pass with out problem, only holding out those that do not meet the
  level criteria.

    dir          -  WEST, NORTH, EAST, WEST, UP, DOWN
    g_room       -  symbolic name of room for guard function to work.
    l_lvl        -  lowest level you want to allow through.
    m_lvl        -  Max level you want to allow through.
    disp_actions -  A dil function that is called when they fail the criteria
*/


dilbegin guard_level( dir : integer, g_room : string, l_lvl : integer,
      m_lvl : integer,  disp_actions : string );
                     external
                       string dirstring (dr:integer);
                       integer rev_dir (i:integer);

var
  pc : unitptr;
  folwr : unitptr;
  cmd_dir : integer;
  dir_string :string;
  leave_string : string;
  i:integer;
code
{
  /* Ok lets set up the whole mess.   This is only run once when the function
     is copied to the dil.  It does not change.  We have the dir_string and
     leave_strings to help show the right messages for following pcs. */

  on_activation(self.position <= POSITION_SLEEPING, sleep);
  heartbeat := 3;

     cmd_dir := dir;
     dir_string := dirstring(dir);
     i:=rev_dir(dir);
     leave_string :=dirstring (i);
  /* Now that that is all set up we can wait around for commands */

  :start:

  wait(SFB_CMD, ( (command(cmd_dir) or command("drag")) and
   (activator.type == UNIT_ST_PC) and
   (self.outside == findroom(g_room))));

  pc := activator;
  secure (pc, start);
  block;

  if((command("drag") and (dir_string in argument)) )
  {
     act("You stumble and fall down.",
  A_SOMEONE, pc,null, null, TO_CHAR);
     act("$1n stumbles and falls down.",
  A_SOMEONE, pc,null, null, TO_REST);
     pc.position := POSITION_SITTING;
     goto start;
  }

  if( ( (pc.level < l_lvl) or (pc.level > m_lvl) ) )
  {
     disp_actions(self, pc);
     goto start;
  }

  /* Loop through all players and mobs simulating the follow, etc */

  foreach( UNIT_ST_PC|UNIT_ST_NPC, folwr )
  {
     if( ( (folwr.master == pc) and (folwr != pc) ) )
     {
 if ( (folwr.level < l_lvl) or (folwr.level > m_lvl) )
 {
    disp_actions(self, folwr);
 } else {
    act("You follow $2n " + dir_string + ".",
 A_SOMEONE, folwr, pc, null, TO_CHAR);
    if (folwr.minv <= pc.level)
    {
       act("$1n leaves " + dir_string + ".",
    A_SOMEONE, folwr, pc, null, TO_REST);
       act("$2n arrives from " + leave_string + ".",
    A_SOMEONE, pc, folwr, null, TO_CHAR);
    }
    link(folwr, self.outside.exit_to[dir]);
    exec("look", folwr);
 }
     } else if (folwr == pc) {
 act("$1n leaves " + dir_string + ".",
     A_SOMEONE, pc, null, null, TO_REST);
     }

  }
  link(pc, self.outside.exit_to[dir]);
  exec("look", pc);

  goto start;

  :sleep:
  heartbeat := 240; /* approx 1 min max sleeping time */
  pause;
  self.position := POSITION_STANDING;
  exec("yawn", self);
  heartbeat := 3;
  walkto(findroom(g_room));  /* incase he was dragged away while asleep */
  goto start;  /* Back to work */
}
dilend

/*
Function:    scramble
Description: Scramble a rooms exits to make a ever changing maze.

Arguments:
  a_exit  =  string of exits to scramble
           valid are:   north  south  west  east  up  down
Example:

dilcopy scramble@function ("north east south west");
*/

dilbegin scramble(a_exit : string);
var
 dir : integer;
 pc : unitptr;
 fol_pc : unitptr;
 dir_string : string;
 leave_string :string;
code
{
  heartbeat := 3;
  if ("north" in a_exit)
    interrupt(SFB_CMD,( (command("north")) and
  (activator.type == UNIT_ST_PC) and
  (activator.level < IMMORTAL_LEVEL) ), scramble);
  if ("south" in a_exit)
    interrupt(SFB_CMD,( (command("south")) and
  (activator.type == UNIT_ST_PC) and
  (activator.level < IMMORTAL_LEVEL) ), scramble);
  if ("east" in a_exit)
    interrupt(SFB_CMD,( (command("east")) and
  (activator.type == UNIT_ST_PC) and
  (activator.level < IMMORTAL_LEVEL) ), scramble);
  if ("west" in a_exit)
    interrupt(SFB_CMD,( (command("west")) and
  (activator.type == UNIT_ST_PC) and
  (activator.level < IMMORTAL_LEVEL) ), scramble);
  if ("up" in a_exit)
    interrupt(SFB_CMD,( (command("up")) and
  (activator.type == UNIT_ST_PC) and
  (activator.level < IMMORTAL_LEVEL) ), scramble);
  if ("down" in a_exit)
    interrupt(SFB_CMD,( (command("down")) and
  (activator.type == UNIT_ST_PC) and
  (activator.level < IMMORTAL_LEVEL) ), scramble);

  interrupt(SFB_CMD,( (command("drag")) and
       (activator.type == UNIT_ST_PC) and
       (activator.level < IMMORTAL_LEVEL) ), sorry);

  interrupt(SFB_CMD,( (command("ride")) and
       (activator.type == UNIT_ST_PC) and
       (activator.level < IMMORTAL_LEVEL) ), sorry);
  :start:
  wait(SFB_MSG, "DESTROY" in argument );
  quit;
  goto start;

  :sorry:
  act("Sorry you can't do that here!",
      A_SOMEONE, pc, null, null, TO_CHAR);
  goto start;


  :scramble:

  pc := activator;
  secure(pc, start);
  block;

  :get_dir:
  dir := rnd(0,5);

  if (self.exit_to[dir] == null) goto get_dir;

  if (command("north")) {
     dir_string := "north";
     leave_string := "south";
  }

  if (command("south")) {
     dir_string := "south";
     leave_string := "north";
  }

  if (command("east")) {
     dir_string := "east";
     leave_string := "west";
  }

  if (command("west")) {
     dir_string := "west";
     leave_string := "east";
  }

  if (command("up")) {
     dir_string := "up";
     leave_string := "below";
  }

  if (command("down")) {
     dir_string := "down";
     leave_string := "above";
  }

  foreach( UNIT_ST_PC|UNIT_ST_NPC, fol_pc )
  {
     if( (fol_pc.master == pc) and (fol_pc != pc) and
 (fol_pc.position==POSITION_STANDING))
     {
 act("You follow $2n " + dir_string + ".",
     A_SOMEONE, fol_pc, pc, null, TO_CHAR);
 if (fol_pc.minv < pc.level) {
    act("$2n arrives from the " + leave_string + ".",
 A_SOMEONE, pc, fol_pc, null, TO_CHAR);
 }
 link(fol_pc, self.exit_to[dir]);
 exec("look", fol_pc);
     } else if (fol_pc != pc) {
 act("$2n leaves " + dir_string + ".",
     A_SOMEONE, fol_pc, pc, null, TO_CHAR);
     }

  }
  link(pc, self.exit_to[dir]);
  exec("look", pc);
}
dilend

/*
Function:    stealdil
Description: Randomly steal from a player.
Use On:      Mobs
Arguments:
  item  =  string of item to try to steal.
          ie   money  platinum  sword  hammer
  only one item may be passed

Example:

dilcopy stealdil@function("platinum");
*/

dilbegin stealdil(item : string);
var
i : integer;
pc : unitptr;
code
{
heartbeat := PULSE_SEC*5;
:start:
wait (SFB_CMD, (activator.type == UNIT_ST_PC));
pc := activator;
i := rnd(1,10);
if (i>2) goto start;
exec ("steal " + item + " from " + pc.name, self);
exec ("flee", self);
goto start;
}
dilend

/*
function  Channel and recieve to be used together.

used on:  Players

argume nts: none;

example: dilcopy ("channel",pc);
no need to copy recieve channel takes care of the recieve part.
*/
#define MAX_CHAN 25
dilbegin recieve( slActive : stringlist, msg : string , talker : unitptr,
                 jmode : integer, dmode : integer);
var
 nWizinv : integer;
 sChan : string;
 sName : string;
 sKey : string;
 sFirst : string;
 slKeys : stringlist;
 clan_rec:extraptr;
 clan_send:extraptr;
 rclanname:string;
 sclanname:string;
 garbage:string;
code
{
 secure(talker, lost);
 slKeys := {"@", "#", "1join1", "1left1"};
 sKey := "";
 sChan := getword(msg); /*Throw away the CHANNEL*/
 sChan := getword(msg);
 if(not(sChan in slActive))
     return;
if ((sChan=="gchat") and (not (self.guild==talker.guild)))
return;

if(sChan=="clan"){
if(self.type==UNIT_ST_PC)
 clan_rec:=CLAN_NAME in self.extra;

if(clan_rec==null){
 return;
}

if(talker.type==UNIT_ST_PC)
 clan_send:= CLAN_NAME in talker.extra;

if(clan_send==null){
 return;
}

rclanname:=clan_rec.descr;
sclanname:=clan_send.descr;
if(rclanname!=sclanname){
 return;
}
}



 sFirst := getword(msg);
 if ( sFirst in slKeys ) {
   sKey := sFirst;
 } else {
   if (length(msg) > 0) {
     msg := sFirst + " " + msg;
 } else {
   msg := Sfirst;
 }
 }
 if (sKey == "@") {
   if (self.level >= talker.minv) {
     sendtext("&c+w<"+sChan+ ">&cc " +talker.name+ "&c+w "
+msg+"&bn&cw&n",self );
   } else {
     sendtext("&c+w<"+sChan+">&cc Someone &c+w"+msg+"&bn&cw&n",self );
   }
 }
else if (sKey == "#"){
 if (self.minv <= talker.level ) {
  if(self.type==UNIT_ST_PC){
   if ((sChan=="clan") and (clan_send.names.[3]=="Master"))
{
   if (self.level < 200)
   sendtext("  &c+w "+self.name+" "+self.title+ " (Level "+itoa(self.vlevel) +
   ") (alignment/"+itoa(self.alignment)+ ") (guild/"+self.guild+
   ")&bn&cw&n", talker);
   else if (self.level < 251)
   sendtext("  &c+w "+self.name+" "+self.title+ " (Immortal [Level " +
   itoa(self.level) +"]" +
   ") (alignment/"+itoa(self.alignment)+ ") (guild/"+self.guild+
   ")&bn&cw&n", talker);
   else
   sendtext("  &c+w "+self.name+" "+self.title+ " (Admin [Level " +
   itoa(self.level) +"]" +
   ") (alignment/"+itoa(self.alignment)+ ") (guild/"+self.guild+
   ")&bn&cw&n", talker);
}
else
{
if (self.level <200)
sendtext("  &c+w "+self.name+" "+self.title+ " (Level "+itoa(self.vlevel) +
")&bn&cw&n", talker);
   else if (self.level < 251)
   sendtext("  &c+w "+self.name+" "+self.title+ " (Immortal [Level " +
   itoa(self.level) +"]" +
   ")&bn&cw&n", talker);
   else
   sendtext("  &c+w "+self.name+" "+self.title+ " (Admin [Level " +
   itoa(self.level) +"]" +
   ")&bn&cw&n", talker);
}
  }
  else
   sendtext("  &c+w "+self.title+"&bn&cw&n",talker);
 }
} else if (sKey == "1join1" ) {
    if (jmode ==1) {
      if (self.level >= talker.minv ) {
        sendtext("&c+w <"+sChan+">&cc " +talker.name + " has entered the " +
                 "channel.&bn&cw&n",self);
      }
    }
 } else if (sKey == "1left1" ) {
    if (dmode ==1) {
      if (self.level >= talker.minv ) {
        sendtext("&c+w <"+sChan+">&cc " +talker.name + " has left the " +
                 "channel.&bn&cw&n",self);
      }
    }
 } else {
   if (self.level >= talker.minv) {
     sendtext("&c+w <"+sChan+">&cc " +talker.name+" says &c+w'"
+msg+"'&bn&cw&n",self);
   } else {
     sendtext(" &c+w <"+sChan+">&cc Someone says &c+w'" +msg
+"'&bn&cw&n",self);
   }
 }
 :lost:
 return;
}
dilend

#define NOSHOUT_QUEST    "$noshout"
#define  NOTELL_QUEST   "$notell"
#define  NOCOMM_QUEST  "$nocomm"

dilbegin aware recall channel( );
external
 recieve@function( slActive : stringlist, msg : string, talker : unitptr,
               jmode:integer, dmode:integer);
var
time_string:string;
targ:unitptr;
 slActive : stringlist;
 func:string;
 msg : string;
 words : stringlist;
 nChan : integer;
 nEcho : integer;
 x : integer;
 dmode : integer;
 jmode : integer;
code
{
 heartbeat := 3;
 nEcho := 1;
 dmode := 1;
 jmode := 1;
 if (self.level>=200)
 interrupt (SFB_CMD, (command ("nocomm") and
 (self==activator)),nochannel);
 interrupt (SFB_CMD, (command ("noshout") and
 (self==activator)),noshout);
 interrupt (SFB_CMD, (command ("notell") and
 (self==activator)),notell);

interrupt (SFB_DONE,((command ("tell")) and (self==target)),told);

 interrupt (SFB_CMD, (command("comm") and
                     (("add" in argument) or
                     ("del" in argument)) and
       (activator == self)), add_del);
 interrupt (SFB_CMD, (command("comm") and
                     ("echo" in argument) and
       (activator == self)), echo_chan);
 interrupt (SFB_CMD, (command("comm") and
                     ("help" == argument) and
       (activator == self)), help_chan);
 interrupt (SFB_CMD, (command("comm") and
                     ("inform" in argument) and
       (activator == self)), inform);
 interrupt (SFB_CMD, (command("comm") and
                     ("#" == argument) and
                     (activator == self)), show_chan);
 interrupt (SFB_MSG, ("CHANNEL" in argument), recieve);

:start:
 wait (SFB_CMD, (excmdstr in slActive) and (activator == self));
 block;

if (NOCOMM_QUEST in self.quests)
 {
 act ("You have lost the privilege to use communication channels.",
 A_ALWAYS,self,null,null,TO_CHAR);
 goto start;
 }
 /*Channel has been recognized, take argument and send it to rest of dils*/
 if( isset(self.pcflags, PC_NOTELLING)) {
   act("Your telepathic ability has been lost.",
     A_SOMEONE, self, null, null, TO_CHAR);
 goto start;
 }
 msg := argument;
 msg := "CHANNEL " + excmdstr + " " + msg;
 if (argument=="#")
   sendtext ("&n",self);

 sendtoalldil(msg, "channel@function");
 if (nEcho == 1) {
   recieve@function(slActive, msg, self,jmode,dmode);
 }
 goto start;


:told:

subextra (self.quests,"$reply");
addextra (self.quests,{"$reply"},activator.name);
goto start;

:recieve:
if (not (NOCOMM_QUEST in self.quests))
 recieve@function(slActive, argument, activator,jmode, dmode);
 goto start;

:add_del:
func:=argument;
words:=getwords(func);
if ("add" in words.[0])
 goto add_chan;
 else if ("del" in words.[0])
   goto del_chan;
   else goto start;

:add_chan:
 block;
 words := getwords(argument);
 if (("add" in words.[0]) and (words.[1] !=null) ) {
     if ( words.[1] in slActive ) {
    act("Channel "+words.[1] + " Already Active!",
      A_SOMEONE, self, null, null, TO_CHAR);
  goto start;
 }
    addstring(slActive, words.[1]);
  act("Channel "+words.[1]+" activated.",
      A_SOMEONE, self, null, null, TO_CHAR);
    msg := "CHANNEL " + words.[1] + " 1join1";
    sendtoalldil(msg, "channel@function");
    if (nEcho == 1) {
      recieve@function(slActive, msg, self,jmode,dmode);
    }
 } else {
    act("Unknown channel command or incorrect options.",
      A_SOMEONE, self, null, null, TO_CHAR);
 }
 goto start;

:del_chan:
 block;
 words := getwords(argument);
 if (("del" in words.[0]) and (words.[1] in slActive)) {
     if (not(words.[1] in slActive) ) {
    act("Channel "+words.[1] + " is not on!",
      A_SOMEONE, self, null, null, TO_CHAR);
  goto start;
 }
  act("Channel "+words.[1]+" deactivated.",
      A_SOMEONE, self, null, null, TO_CHAR);
    msg := "CHANNEL " + words.[1] + " 1left1";
    sendtoalldil(msg, "channel@function");
    if (nEcho == 1) {
      recieve@function(slActive, msg, self,jmode,dmode);
    }
  substring(slActive, words.[1]);
 } else {
    act("Unknown channel command or incorrect options.",
      A_SOMEONE, self, null, null, TO_CHAR);
 }
 goto start;

:show_chan:
 block;
 nChan := length(slActive);
 x := 0;
 while (x < nChan)
 {
  sendtext("&c+wChannel "+slActive.[x]+" is active.&bn&cw&n",self );
    x:= x+1;
 }
goto start;

:echo_chan:
block;
 if( nEcho == 1) {
   act("Channel echoing in disabled, you will NOT see your own messages.",
      A_SOMEONE, self, null, null, TO_CHAR);
   nEcho := 0;
 } else {
   act("Channel echoing in enabled, you will see your own messages.",
      A_SOMEONE, self, null, null, TO_CHAR);
   nEcho := 1;
 }
goto start;

:help_chan:
block;
sendtext ("&cc To add a channel type:&n"+
"&c+wcomm add &n&n"+
"&cc  To delete a channel type:&n"+
"&c+wcomm delete &n&n"+
"&cc  To see all your active channels type:&n"+
"&c+wcomm #&n&n"+
"&cc  To see who is on a specific channel type:&n"+
"&c+w #&n&n"+
"&cc  To see who joins or departs a channel type:&n"+
"&c+wcomm inform &n&n"+
"&cc  To toggle echoing of your message type:&n"+
"&c+wcomm echo&n"+
"&cc  To do a channel emote type: &n"+
"&c+w @ &n"+
"&cc  The standard guild channel is gchat only members will here you&bn &cw
&n&n",self);

goto start;

:inform:
block;
words:= getwords(argument);
if (words.[1] == "") {
 act("Not enough arguments for inform command try 'chan help'",
     A_SOMEONE, self, null, null, TO_CHAR);
} else if (words.[1] == "on") {
 act("Channel full inform on.",
     A_SOMEONE, self, null, null, TO_CHAR);
 jmode := 1;
 dmode := 1;
} else if (words.[1] == "off") {
 act("Channel inform turned off.",
    A_SOMEONE, self, null, null, TO_CHAR);
 jmode := 0;
 dmode := 0;
} else if (words.[1] == "join" ){
 act ("Channel inform Join Only set.",
    A_SOMEONE, self, null, null, TO_CHAR);
 jmode := 1;
 dmode := 0;
} else if (words.[1] == "depart" ){
 act ("Channel inform Depart Only set.",
    A_SOMEONE, self, null, null, TO_CHAR);
 jmode := 1;
 dmode := 0;
}
goto start;

:noshout:
targ:=findunit (self,argument,FIND_UNIT_WORLD,null);
if (targ==null) goto start;

if (not (NOSHOUT_QUEST in targ.quests))
 {
   time_string:=asctime(realtime);
   time_string:=time_string+" "+self.name;
 addextra (targ.quests,{NOSHOUT_QUEST},time_string);
 }
else
 subextra (targ.quests,NOSHOUT_QUEST);

goto start;

:notell:
targ:=findunit (self,argument,FIND_UNIT_WORLD,null);
if (targ==null) goto start;

if (not (NOTELL_QUEST in targ.quests))
 {
   time_string:=asctime(realtime);
   time_string:=time_string+" "+self.name;
 addextra (targ.quests,{NOTELL_QUEST},time_string);
 }
else
 subextra (targ.quests,NOTELL_QUEST);

goto start;




:nochannel:
block;
targ:=findunit (self,argument,FIND_UNIT_WORLD,null);
if (targ==null)
 {
 act ("No such person found.",
 A_ALWAYS,self,null,null,TO_CHAR);
 goto start;
 }
if (targ.level >=self.level)
 {
 act ("$2n would not like that very much.",
 A_ALWAYS,self,targ,null,TO_CHAR);
 goto start;
 }

 if (not(NOCOMM_QUEST in targ.quests))
   {
   time_string:=asctime(realtime);
   time_string:=time_string+" "+self.name;
 addextra (targ.quests,{NOCOMM_QUEST},time_string);
 act ("You have lost the ability to use communication channels.",
   A_ALWAYS,targ,null,null,TO_CHAR);
 act ("$2n has lost the ability to use communication channels.",
   A_ALWAYS,self,targ,null,TO_CHAR);
 goto start;
 }
else
 {
 subextra (targ.quests,NOCOMM_QUEST);
 act ("You have regained the privilege to use the communication channels.",
   A_ALWAYS,targ,null,null,TO_CHAR);
 act ("$2n can now use the communication channels again.",
 A_ALWAYS,self,targ,null,TO_CHAR);
 goto start;
 }

goto start;

:leave:
 quit;

}
dilend

/* To replace SFUN_TEAMWORK (teamwork) and SFUN_RESCUE (rescue) */

dilbegin aware teamwork(lst: string);

var u     : unitptr;
sl    : stringlist;
i     : integer;
gotcha: integer;

code
{

if (" / " in lst)
   sl := split (lst, " / ");
else
   sl := split (lst, "/");

if (sl.[0] == "")
   {
   log ("Target list is empty");
   quit;
   }

if (not (self.type & (UNIT_ST_PC|UNIT_ST_NPC)) )
   {
   log ("Dil was copied on a non-char");
   quit;
   }

:init:

heartbeat:= PULSE_VIOLENCE;

wait (SFB_TICK, TRUE);

wait (SFB_COM, ((self.position > POSITION_SLEEPING) and
(self.position != POSITION_FIGHTING)) );

if (self.position < POSITION_STANDING) exec ("stand", self);
if (self.position < POSITION_STANDING) goto init;

gotcha:= FALSE;

foreach (/*UNIT_ST_PC|*/UNIT_ST_NPC, u) /* we'll assume npcs only for now */
   {
if (u == self) continue;

if (not visible (self, u)) continue;

   if (isset (u.charflags, CHAR_LEGAL_TARGET) and
       isset (u.charflags, CHAR_PROTECTED))
       continue;

i := 0;
while (i < length(sl))
       {
       if (sl.[i] in u.names)
  {

  if ((u.position == POSITION_FIGHTING) and
      (u.fighting != self))
               {
               gotcha := TRUE;
      break;
      }
           }

 i := i + 1;
 }

if (gotcha == TRUE)
       {
       act("$1n bravely comes to your assistance.",
           A_ALWAYS, self, null, u, TO_VICT);
       act("$1n bravely comes to $3n's assistance.",
           A_ALWAYS, self, null, u, TO_NOTVICT);

       set_fighting(self,u.fighting);
       break;
 }
}

goto init;

}

dilend

dilbegin aware rescue(lst: string);

external
   integer skillresist(aa: integer, ad: integer, sa: integer, sd: integer);
   base_rescue@skills(targ: unitptr);

var u     : unitptr;
sl    : stringlist;
i     : integer;
gotcha: integer;
   evil  : unitptr;

code
{

if (" / " in lst)
   sl := split (lst, " / ");
else
   sl := split (lst, "/");

:init:

heartbeat:= PULSE_VIOLENCE;

wait (SFB_TICK, TRUE);

wait (SFB_COM, (self.position > POSITION_SLEEPING) );

if (self.position < POSITION_STANDING) exec ("stand", self);
if (self.position < POSITION_STANDING) goto init;

gotcha:= FALSE;

foreach (/*UNIT_ST_PC|*/UNIT_ST_NPC, u)
   {
if (u == self) continue;

if (not visible (self, u)) continue;
i := 0;

   if (isset (u.charflags, CHAR_LEGAL_TARGET) and
       isset (u.charflags, CHAR_PROTECTED))
       continue;

while (i < length(sl))
       {

       if (sl.[i] in u.names)
           {

  if ((u.position == POSITION_FIGHTING) and
      (u.fighting != self) and (u.hp <= (u.max_hp / 2)) )
               {

               evil := self.outside.inside;
               while (evil != null)
                   {
                   if (evil.fighting == u) // Find if someone is hitting dude
                       {
                       gotcha := TRUE;
                       break;
                       }

                   evil := evil.next;
                   }

         if (gotcha == TRUE) break;
      }
           }


 i := i + 1;
 }

if (gotcha == TRUE)
       {
       base_rescue@skills(u);
       break;
 }
}

goto init;

}

dilend

/*
Function:    aggressive
Description: Replacement and enhancement of the SFUN_AGGRESSIVE functions.
            Makes a Mob hostile to person(s) in the room with it, under
            certain conditions which are provided as arguments.
            In short, the all-singing, all-dancing aggression dil.
dilbegin aggressive (sx : integer, rce : integer, opp : integer,
                    levl : integer, sanc : integer, tme : integer,
                    tar : integer, align : string, attack : stringlist);

Arguments:
        sx          : integer - NOTE: not the sex values in values.h.
                                This decides the sex of your mob's
                                victim.
                                0 - Sex doesn't matter,
                                1 - Attack opposite sex to self (if not
                                    neutral!),
                                2 - Attack SEX_MALE,
                                3 - Attack SEX_FEMALE,
                                4 - Attack SEX_NEUTRAL.

        rce         : integer - Any of the PC races from 0 to 14. A value
                                of -1 means we don't care about the
                                victim's race.

        opp         : integer - 0 - Non race specific (same as rce := -1)
                                1 - Attack the specified rce,
                                2 - Attack any pc race _but_ the specified
                                    rce.

        levl        : integer - Allow level specific aggression.
                                A value of 30 would make the mob hostile
                                to all pcs level 30 and above.
                                A value of -30 (note the -) would make the
                                mob hostile to all pcs level 30 or below.
                                A value of 0 means level doesn't matter.

        sanc        : integer - Does this mob obey the sanc/soothe rules?
                                (ie, if someone has cast sanctuary on
                                themselves, will this mob recognise it, and
                                not attack, or attack anyway).
                                0 - Doesn't obey sanc or soothe
                                1 - Obeys only sanc
                                2 - Obeys only soothe
                                3 - Obeys both sanc and soothe
                                (SOOTHE is a new spell for ranger's guild)

        tme         : integer - Time in ticks to wait before attacking (is
                                automatically put to RANTIME, ie, time
                                variance of time-time/2 to time+time/2).
                                Values accepted are from 0 to 400 (that's
                                0 - 100 seconds. Can be specified using
                                PULSE_SEC).

        tar         : integer - This is a special value which determines
                                which of the eligible victims we pick.
                                -2 - Last eligible victim to into the room.
                                -1 - Weakest eligible victim in room.
                                0  - Random eligible victim.
                                +1 - Strongest eligible victim in room.
                                +2 - First eligible victim into the room.

        align       : string  - The desired alignment of the victim.
                                "ANY"      - We don't care about the alignment.
                                "GOOD"     - Attack only good alignment.
                                "EVIL"     - Attack only evil alignment.
                                "NEUTRAL " - Attack only neutral alignment.
                                "OPPOSITE" - Attack opposite alignment to self
                                             (provided self isn't neutral).
                                "SALIGN"   - Attack same alignment as self.
                                "DALIGN"   - Attack any alignment different to
                                             self.

        attack      : stringlist - This is a 2 string stringlist. These are
                                   the messages sent to the people in the room
                                   except the victim, and the victim itself,
                                   in that order.
                                   If the second (victim) string is "", the
                                   first string will be shown to the victim,
                                   as if they were anyone else in the room.
                                   You can leave both blank if you wish.
                                   $1n is the mob name (self), $3n is the
                                   victim's name.
                                   NOTE: the $ values only apply if you supply
                                   BOTH string 1 and 2.

Example:
           Let's say our mob is a level 40 Goblin who doesn't like dwarves.
           He's very particular in that he doesn't like evil female dwarves
           who are level 20 and above. He does recognise the sanctuary spell,
           but he does recognise soothe, and he'll wait 10 seconds on average
           before he attacks. The 2 messages sent are:
           "$1n savagely attacks $3n with his big axe!" and "$1n attacks you!"

           Here's what the function call would look like:

dilcopy aggressive (3, 2, 1, 20, 2, PULSE_SEC*10, 0, "EVIL",
                   {"$1n savagely attacks $3n with his big axe!",
                    "$1n attacks you!"});

In this example, 3 (attack females), 2 (attack dwarves), 1 (just dwarves),
                20 (Level 20+ victims), 2 (obey only soothe), PULSE_SEC*10
                (wait around 10 seconds before attacking), EVIL (attack only
                evil), and the strings in the stringlist are displayed to
                the victim and the room, in that order.

A note on the old SFUN_AGGRESSIVE.
If I wanted to translate the following into the new function:
  SFUN_AGGRESSIVE "Big monster attacks!" time PULSE_SEC*20 SFB_RANTIME
it would be:
  dilcopy aggressive@function (0, -1, 0, 0, 3, PULSE_SEC*20, 0, "ANY",
                               {"Big monster attacks!",""};

I hope that explains the function well enough. If you need further
explanation or information on it, contact Eirinn (clarej@tcd.ie)
*/

#define ALOG(_CMDSTR) \
log(self.nameidx+"@"+self.zoneidx+" AGGRO ERROR - "+_CMDSTR)

dilbegin aggressive (sx : integer, rce : integer, opp : integer,
                    levl : integer, sanc : integer, tme : integer,
                    tar : integer, align : string, attack : stringlist);

external
       string find_targ (tar : integer, sx1 : integer, alig : integer,
                         rce : integer, opp : integer, mlev : integer,
                         hlev: integer, sanc : integer);

var
  pc       : unitptr; /* The activator and reference point */
  sucker   : unitptr; /* The eventual victim */
  tempstr  : string;  /* A temporary string */
  des_sx   : integer; /* The desired sex of the victim */
  des_ali  : integer; /* What alignment we're looking for (-1, 0, +1) */
  des_mlv  : integer; /* Minimum level sucker can be */
  des_hlv  : integer; /* Maximum level sucker can be */

  to_wait  : integer; /* Time to wait before attacking */
  ten_time : integer; /* One tenth of tme - used in "randomisation" */
  time_rnd : integer; /* Random part of time */

  i        : integer; /* For interrupt */

code
{
  /* First of all, we must process the arguments as far as possible so that
     we know exactly what kind of victim we want. This will save time later
     when a victim walks into the room (we want it to execute as fast
     as possible). */

:init:

  interrupt(SFB_COM,self.position==POSITION_FIGHTING,lost_pc);
  heartbeat := 1; /* This will be used to wait for all followers (if any),
                     to enter the room before activation */

/* The following part of the routine up to the start label checks the
  arguments passed to the function for validity (and processes them,
  if necessary), before allowing the function to procede. */


:sex_check: /* Process the sex argument (also checking if valid) */

  if (sx == 0) des_sx := 5; /* Just using 5 to mean not sex specific */
  else if (sx == 1)
          {
             if (self.sex == SEX_NEUTRAL)
                {
                   ALOG("You can't have an opposite sex to neutral! *bonk*");
                   quit;
                }
             else if (self.sex == SEX_FEMALE)
                {
                   des_sx := SEX_MALE;
                }
             else if (self.sex == SEX_MALE)
                {
                   des_sx := SEX_FEMALE;
                }
             else
                {
                   ALOG("Eeek - I'm not a valid sex!");
                   quit;
                }
          }
  else if (sx == 2) des_sx := SEX_MALE;
  else if (sx == 3) des_sx := SEX_FEMALE;
  else if (sx == 4) des_sx := SEX_NEUTRAL;
  else
     {
        ALOG("Eeek - Sex argument not valid!");
        quit;
     }


:race_check: /* Check the race/opp arguments' validities */

  if (rce == -1) goto align_check; /* Race doesn't matter */
  else
     {
        if ((rce >= 15) or (rce < 0))
           {
              ALOG("Race argument specifies an invalid pc race!");
              quit;
           }

        if ((opp != 0) and (opp !=1) and (opp != 2))
           {
        /* Do I attack the race or all but race, or do I care? */
              ALOG("Opp argument is not 0, 1, or 2!");
              quit;
           }
     }


:align_check: /* Process the align argument (also checking if valid) */

  if (align == "ANY") des_ali := 2; /* Attack anyone */
  else if (align == "GOOD")
   {
    des_ali := 1; /* Attack only good */
   }
  else if (align == "EVIL") des_ali := -1; /* Attack only evil */
  else if (align == "NEUTRAL") des_ali := 0; /* Attack only neutral */
  else if (align == "OPPOSITE") /* Attack opposite alignment to self */
          {
             if (self.alignment < -349) des_ali := 1;
             else if (self.alignment > 349) des_ali := -1;
             else
                {
                   ALOG("Neutral align has no opposite!");
                   quit;
                }
          }
  else if (align == "SALIGN") /* Attack same alignment as self */
          {
             if (self.alignment < -349) des_ali := -1;
             else if (self.alignment > 349) des_ali := 1;
             else if ((self.alignment > -350) and (self.alignment < 350))
                  des_ali := 0;
          }
  else if (align == "DALIGN") /* Attack any alignment except own */
                              /* 2 : evil and neutral, 3 : evil and good
                                 4 : neutral and good */
          {
             if (self.alignment < -349) des_ali := 4;
             else if (self.alignment > 349) des_ali := 2;
             else if ((self.alignment > -350) and (self.alignment < 350))
                  des_ali := 3;
          }
  else
     {
        ALOG("Align argument specifies an invalid option!");
        quit;
     }


:level_check: /* Process the level argument (also checking if valid) */

  if ((levl < 0) and (levl > (-1 * MORTAL_MAX_LEVEL)))
     {
        des_hlv := (-1 * levl);
        if (des_hlv > MORTAL_MAX_LEVEL) des_hlv := MORTAL_MAX_LEVEL;
        des_mlv := 0;

     }
  else if ((levl > 0) and (levl <= MORTAL_MAX_LEVEL))
     {
        des_hlv := (MORTAL_MAX_LEVEL);
        des_mlv := ( levl);
     }
  else if (levl == 0)
     {
        des_hlv := (MORTAL_MAX_LEVEL);
        des_mlv := 0;
     }
  else
     {
        ALOG("Levl argument is invalid!");
        quit;
     }


:sanc_check: /* Checking if the sanc argument is valid:
                  0 - Doesn't obey sanc or soothe
                  1 - Obeys only sanc
                  2 - Obeys only soothe
                  3 - Obeys both sanc and soothe */

  if ((sanc != 0) and (sanc != 1) and (sanc != 2) and (sanc != 3))
     {
        ALOG("Sanc argument is invalid!");
        quit;
     }
  if (sanc > 1)
     {
        addextra(self.extra, {"$Soothe"}, "");
     }

:time_check: /* Checking if the time argument is valid */

  if (not((tme >=0) and (tme <=400)))
     {
        ALOG("Time argument is out of range!");
        quit;
     }

  /* Process the time argument - see later for comments */

  if (tme == 0) goto get_target; /* Let's not wait then. See if I care :P */
  else

  ten_time := (tme / 10);       /* Divide the original time by 10 */
  tme := tme / 2;               /* This is the new base time value */


:target_check: /* Checking if the tar value is acceptable */

  if (not((tar >= -2) and (tar <=2)))
     {
        ALOG("Target argument is not a valid target type!");
        quit;
     }


:act_check: /* Check the strings for the act are present, if not, provide
              defaults */

  if (length(attack) > 2)
     {
        ALOG("Too many strings in attack stringlist!");
        quit;
     }


:start: /* Phew, we got to the actual aggression part. Thank Heavens for that.
          I still want to know why men have nipples though. Oh well. */

  wait(SFB_DONE, ( (activator.type == UNIT_ST_PC) and
                   (self.position >= POSITION_RESTING) and
                   (self.position != POSITION_FIGHTING) ));
  pc := activator;
  secure(pc, lost_pc);

  pause; /* Wait for 1/4 of a second in case the pc has a group (we want
            them all to be present in the room */

  /*
     Now we must wait the appointed time (randomly chosen between the time
     value which was passed and +/- the time value divided by two and then
     randomly chosen in intervals of 10ths of this). The reason this is
     done before target selection is in case a chosen target leaves the room
     but the original activator of the function doesn't, that way we minimise
     potential misfires. We could of course do it the other way around, but
     why use CPU time for nothing?
  */

:wait_time:

  time_rnd := (rnd(0,10) * ten_time);   /* Now give me a random value from 0 to
                                           the original time value */
  to_wait := tme + time_rnd;            /* Make the tick time a random value
                                           from half the original time value to
                                           1.5 times the original time */

  heartbeat := to_wait;             /* Set the new tick time */
  pause;                            /* Wait the appointed time */
  heartbeat := 1;                   /* Reset the tick time and go get a
                                       target */


  /*
     The following function call is for find_targ - this sorts through
     all of the pcs in the room and returns the one we want according to
     the tar variable which was passed in the original aggressive function
     call.
  */

:get_target:

  tempstr := find_targ (tar, des_sx, des_ali, rce, opp,
                        des_mlv, des_hlv, sanc);

  if (tempstr == "No valid targets in the room") goto lost_pc;

  sucker := findunit(self, tempstr, 0, self.outside.inside);

  if (self.position == POSITION_FIGHTING) goto lost_pc;

  if (self.position < POSITION_RESTING) goto lost_pc; // dude is sleeping?

  if (self.position < POSITION_STANDING)
      exec ("stand", self);

  if (sucker == null) goto lost_pc;

  if (sucker.type != UNIT_ST_PC) goto lost_pc;

  if (attack.[0] == "") goto start_fight;

  if (attack.[1] != "")
     {
        act(attack.[0],
            A_SOMEONE, self, null, sucker, TO_NOTVICT);
        act(attack.[1],
            A_ALWAYS, self, null, sucker, TO_VICT);
     }
  else
     {
        act(attack.[0],
            A_SOMEONE, self, null, null, TO_ALL);
     }

:start_fight:
  if (sucker == self)
  {
     quit;
  }

  set_fighting(self, sucker);
  unsecure(pc);
  goto start;

:lost_pc:
  unsecure(pc);
  goto start;
}
dilend /* aggressive */

/* This dil will rate a pc on their abilities, with different weights on
  the abilities. Gods get a + 10000 headstart. */

dilbegin integer rate_pc (rate_me : unitptr, stre : integer, dext : integer,
                         cons : integer, char : integer, brai : integer,
                         magi : integer, divi : integer, hitp : integer);

var
  rating     : integer; /* The Final Rating of the PC */
  temp_total : integer; /* A Temporary variable */

code
{

heartbeat:=PULSE_SEC;

:start:

  rating := 0;

  if (rate_me.level >= IMMORTAL_LEVEL) rating := rating + 10000;

  if (stre) rating := rating + (10 * rate_me.abilities[ABIL_STR]);

  if (dext) rating := rating + (10 * rate_me.abilities[ABIL_DEX]);

  if (cons) rating := rating + (4 * rate_me.abilities[ABIL_CON]);

  if (char) rating := rating + (7 * rate_me.abilities[ABIL_CHA]);

  if (brai) rating := rating + (8 * rate_me.abilities[ABIL_BRA]);

  if (magi) rating := rating + (6 * rate_me.abilities[ABIL_MAG]);

  if (divi) rating := rating + (6 * rate_me.abilities[ABIL_DIV]);

  if (hitp) rating := rating + (8 * rate_me.abilities[ABIL_HP]);

  return (rating);
}
dilend /* rate_pc */

/* This function does the slog work for aggressive@function. */

dilbegin string find_targ (tar : integer, sx1 : integer, alig : integer,
                          rce : integer, opp : integer, mlev : integer,
                          hlev: integer, sanc : integer);
external
        integer rate_pc (rate_me : unitptr, stre : integer, dext : integer,
                         cons : integer, char : integer, brai : integer,
                         magi : integer, divi : integer, hitp : integer);

var
  pc        : string;     /* The victim's name */
  loseu     : string;     /* In case we lose u, which we do (scream) */

  u         : unitptr;    /* The temporary unitptr */

  rating    : integer;    /* The Rating of the PC in terms of abilities */
  lowrating : integer;    /* The lowest rating encountered so far */
  highrating: integer;    /* The highest rating encountered so far */
  verdict   : integer;    /* Used as pass value for the unitptr test */
  urace     : integer;    /* The PC's race */
  ulevel    : integer;    /* The PC's level */
  sancvar   : integer;    /* Whether the PC has SANCTUARY cast on self */
  soothe    : integer;    /* Whether the PC has SOOTHE cast on self */
  ualign    : integer;    /* The PC's alignment*/
  usex      : integer;    /* The PC's sex */
  count     : integer;    /* The count of elligible PCs checked so far */

/* All the integers are necessary because the foreach seems to lose track of
  the u half-way through. */

code
{
:init:
  heartbeat:=PULSE_SEC*3; /* In case we need it */

  count      := 0; /* Initialise the variable (no real reason) */

  foreach (UNIT_ST_PC, u)
     {
         loseu   := u.name;
         ulevel  := 0;
         urace   := 0;
         sancvar := 0;
         soothe  := 0;

         verdict := 1;  /* Innocent until proven guilty. Ie, it passes the
                           tests unless we prove otherwise. If verdict == 0,
                           this u fails. */

         ulevel  := u.level;
         urace   := u.race;
         ualign  := u.alignment;
         usex    := u.sex;
         sancvar := isaff(u, ID_SANCTUARY);
         if ("$ID_SOOTHE" in u.extra) soothe := 1;
         if (not(visible(self, u))) verdict := 0;

         rating  := rate_pc (u, 1, 1, 0, 0, 1, 1, 1, 1); /* Go get the pc's
                                                            rating */

         /* SEX */
         if (sx1 != 5)
            {
               if (usex != sx1) verdict := 0;
            }


         /* ALIGNMENT */

         if (alig != 2)
            {
               if (alig == -1)
                  {
                     if (ualign > -350) verdict := 0;
                  }
               else if (alig == 0)
                  {
                     if ((ualign < -349) or (ualign > 349)) verdict := 0;
                  }
               else if (alig == 1)
                  {
                     if (ualign < 350) verdict := 0;
                  }
               else verdict := 0;
            }


         /* RACE */
         if ((rce != -1) or (opp != FALSE))
            {
               if (opp == 1)
                  {
                     if (urace!= rce) verdict := 0;
                  }
               if (opp == 2)
                  {
                     if (urace == rce) verdict := 0;
                  }
            }


         /* LEVEL */
         if ((ulevel < mlev) or (ulevel > hlev)) verdict := 0;


         /* SANC */
         if (sanc != 0)
            {
               if (sanc == 1)
                  {
                     if (sancvar == TRUE) verdict := 0;
                  }
               if (sanc == 2)
                  {
                     if (soothe == TRUE) verdict := 0;
                  }
               if (sanc == 3)
                  {
                     if ((sancvar) or (soothe)) verdict := 0;
                  }
            }



     if ( verdict ) count := count + 1; /* Increment the count if passed */

     if ( ( verdict ) and ( tar == FALSE ) ) /* If passes and looking for
                                                random PC. */
        {
           if (rnd(1,count) == 1) pc:=loseu;
        }

     else if ( ( verdict ) and ( tar == -2 ) ) /* If passes and looking for
                                                  last PC to enter. */
        {
           if (count == 1) pc:=loseu;
        }

     else if ( ( verdict ) and ( tar == 2 ) )  /* If passes and looking for
                                                  first PC to enter. */
        {
           pc:=loseu;    /* The last person to pass will be firstpc
                            and we then have the value we want. */
        }

     else if ( ( verdict ) and ( tar == -1 ) ) /* If passes and looking for
                                                  weakest PC to enter. */
        {
           if (count == 1)
              {
                 lowrating := rating;
                 pc        := loseu;
              }

           else if (rating < lowrating)
              {
                 lowrating := rating;
                 pc        := loseu;
              }
        }

     else if ( ( verdict ) and ( tar == 1 ) ) /* If passes and looking for
                                                 strongest PC to enter. */
        {
           if (count == 1)
              {
                 highrating := rating;
                 pc         := loseu;
              }

           else if (rating > highrating)
              {
                 highrating := rating;
                 pc         := loseu;
              }
        }

     } /* END FOREACH */

:pass_back:

  return(pc);
}
dilend /* find_targ */

/*
Function: comb_poison
Usage:
dilcopy comb_poison@function(toact : string, restact : string);

toact is what the victim sees when poisoned.
restact is what everyone else sees when the victim is poisoned.

Example:
dilcopy comb_poison@function("You feel very ill as $1n bites you!",
                            "$1n sinks $1s fangs deep into $3n!");

Notes:
It is recommended that builders set magic and brain abilities on mobiles
using this function for optimal performance.
*/

dilbegin aware comb_poison(toact : string, restact : string);
var
  i : integer;
code
{
  heartbeat := PULSE_VIOLENCE;

  :start:
  wait(SFB_PRE, command(CMD_AUTO_DAMAGE));

  if ((activator == self) and (rnd(1,10) > 5))
  {
     if (activator == target) quit;
     if (power <= 0) goto start;
     i := cast_spell(SPL_POISON, self, self, target, "");
     if (i < 0) i := 0;
     power := power + (i/5);
     act(toact, A_ALWAYS, self, null, target, TO_VICT);
     act(restact, A_SOMEONE, self, null, target, TO_NOTVICT);
  }
  goto start;
}
dilend

dilbegin string coinstring(coins: integer, typ: string, ext: integer);

var
   sing_list: stringlist;
   plur_list: stringlist;
   comp_list: stringlist;

   amt_s: string;
   typ_s: string;
   typ_i: integer;

code
{

sing_list:= {"iron piece", "copper piece", "silver piece", "gold piece",
             "platinum piece"};
plur_list:= {"iron pieces", "copper pieces", "silver pieces", "gold pieces",
             "platinum pieces"};
comp_list:= {"ip", "cp", "sp", "gp", "pp"};

if (typ == IRON_PIECE)
   typ_i := 0;

if (typ == COPPER_PIECE)
   typ_i := 1;

if (typ == SILVER_PIECE)
   typ_i := 2;

if (typ == GOLD_PIECE)
   typ_i := 3;

if (typ == PLATINUM_PIECE)
   typ_i := 4;

if (typ_i > MAX_MONEY)
   return ("nothing");

/* Add more when new currencies come in */

if ((coins <= 1) and (ext))
   {
   typ_s := sing_list.[typ_i];

   if (typ_s.[0] in {"a", "e", "i", "o"})
       amt_s := "an ";
   else
       amt_s := "a ";

   if (coins <= 0)
       amt_s := "no ";
   }

if ((coins > 1) and (ext))
   {
   typ_s := plur_list.[typ_i];
   amt_s := itoa(coins) + " ";
   }

if (not ext)
   {
   typ_s := comp_list.[typ_i];
   amt_s := itoa(coins) + " ";
   }

return (amt_s + typ_s);

}

dilend

dilbegin integer transfercoins (a: unitptr, t: unitptr, coins: integer,
   typ: string);

var cash : unitptr;
   c_fnd: integer;
   cidx : string;
   mult : integer;
   ccost: integer;

   u    : unitptr;
   i    : integer;
   bill : unitptr;
   tcash: unitptr;

code
{

if (coins <= 0) return(TRUE); /* is it possible to fail giving nothing? */

if (coins > 100000) return(FALSE); /* We don't want crashes */

cidx:= "";
if (typ == IRON_PIECE)
   {
   mult := IRON_MULT;
   cidx := "iron_piece@basis";
   }

if (typ == COPPER_PIECE)
   {
   mult := COPPER_MULT;
   cidx := "copper_piece@basis";
   }

if (typ == SILVER_PIECE)
   {
   mult := SILVER_MULT;
   cidx := "silver_piece@basis";
   }

if (typ == GOLD_PIECE)
   {
   mult := GOLD_MULT;
   cidx := "gold_piece@basis";
   }

if (typ == PLATINUM_PIECE)
   {
   mult := PLATINUM_MULT;
   cidx := "platinum_piece@basis";
   }

if (cidx == "")
   return (FALSE);

:giver:
if (a == null) goto receiver;

cash:= findsymbolic(a, cidx, FIND_UNIT_INVEN);

if ((cash.objecttype != ITEM_MONEY) and (cash != null))
   {
   cash := a.inside;
   c_fnd := FALSE;

   while (cash != null)
       {
       if ((cash.type != UNIT_ST_OBJ) or
           (cash.objecttype != ITEM_MONEY) or
           (cash.nameidx+"@"+cash.zoneidx != cidx))
           continue; /* The unit is not what we seek */

       c_fnd := TRUE;
       cash := cash.next;
       }

   if (not c_fnd)
       return(FALSE);
   }

if (cash == null)
   return(FALSE);

if (cash.cost < coins)
   return(FALSE);

cash.cost:= cash.cost - coins;

if (cash.cost <= 0)
   destroy(cash);

tcash:= cash;
ccost:= cash.cost;

bill:= load("moneychanger@function");
link (cash, bill);
i:= transfermoney (bill, a, mult); /* To reset the money vals */
i:= transfermoney (bill, a, (ccost - 1)* mult); /* I hate hacks */
destroy (bill);

:receiver:
if (t == null) goto done;

cash:= findsymbolic(t, cidx, FIND_UNIT_INVEN);

if ((cash.objecttype != ITEM_MONEY) and (cash != null))
   {
   cash := t.inside;
   c_fnd := FALSE;

   while (cash != null)
       {
       if ((cash.type != UNIT_ST_OBJ) or
           (cash.objecttype != ITEM_MONEY) or
           (cash.nameidx+"@"+cash.zoneidx != cidx))
           continue; /* The unit is not what we seek */

       c_fnd := TRUE;
       cash := cash.next;
       }

   if (not c_fnd)
       {
       cash := load(cidx);
       cash.cost := 0;
       link (cash, t);
       }
   }

if (cash == null)
   {
   bill := load("moneychanger@function");

   link(tcash, bill);      /* This is unfortunate but made money disappear */
   cash := load(cidx);     /* if we loaded a brand new cash in someone with */
   link (cash, t);         /* already coins of that type */
   link (tcash, a);

   cash.cost := 0;
   destroy (bill);
   }

cash.cost:= cash.cost + coins;
ccost:= cash.cost;

bill:= load("moneychanger@function");
link (cash, bill);
i:= transfermoney (bill, t, mult);
i:= transfermoney (bill, t, (ccost - 1)* mult);
destroy (bill);

:done:
return (TRUE);

}

dilend

%rooms

dil_chamber
names {"dil"}
title "Dil Chamber."
descr
"Magic of great power swirls and dances about you.  You stand on something
yet nothing.  You feel everything  yet you touch nothing. Your full yet
hungry.
This you realize is where the life of the world is held.  Stand and fly,
feel safe with no fear."

flags {UNIT_FL_NO_TELEPORT}
end

%mobiles

moneychanger
names {"moneyman"}
title "the Wizi Money Man"
descr "Surely noone can see him here."

minv 200

M_SHOP_KEEPER(199, SEX_MALE, RACE_HUMAN)

end

%end