SmaugWizard/Backup/
SmaugWizard/Backup/L/
SmaugWizard/Boards/
SmaugWizard/Building/
SmaugWizard/Corpses/
SmaugWizard/Councils/
SmaugWizard/Deity/
SmaugWizard/Gods/
SmaugWizard/MudProgs/
SmaugWizard/Player/L/
SmaugWizard/Src/
SmaugWizard/Src/res/
/****************************************************************************
 * [S]imulated [M]edieval [A]dventure multi[U]ser [G]ame      |				*
 * -----------------------------------------------------------|   \\._.//	*
 * SmaugWiz (C) 1998 by Russ Pillsbury (Windows NT version)   |   (0...0)	*
 * -----------------------------------------------------------|    ).:.(	*
 * SMAUG (C) 1994, 1995, 1996 by Derek Snider                 |    {o o}	*
 * -----------------------------------------------------------|   / ' ' \	*
 * SMAUG code team: Thoric, Altrag, Blodkai, Narn, Haus,      |~'~.VxvxV.~'~*
 * Scryn, Swordbearer, Rennard, Tricops, and Gorog.           |				*
 * ------------------------------------------------------------------------ *
 * Merc 2.1 Diku Mud improvments copyright (C) 1992, 1993 by Michael        *
 * Chastain, Michael Quan, and Mitchell Tse.                                *
 * Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,          *
 * Michael Seifert, Hans Henrik Staerfeldt, Tom Madsen, and Katja Nyboe.    *
 ****************************************************************************
 *  The MUDprograms are heavily based on the original MOBprogram code that  *
 *  was written by N'Atas-ha.											    *
 *  Much has been added, including the capability to put a "program" on     *
 *  rooms and objects, not to mention many more triggers and ifchecks, as   *
 *  well as "script" support.											    *
 *                                                                          *
 *  Error reporting has been changed to specify whether the offending       *
 *  program is on a mob, a room or and object, along with the vnum.         *
 *                                                                          *
 *  Mudprog parsing has been rewritten (in mprog_driver). Mprog_process_if  *
 *  and mprog_process_cmnd have been removed, mprog_do_command is new.      *
 *  Full support for nested ifs is in.                                      *
 ****************************************************************************/

#include	"stdafx.h"
#include	"smaug.h"
#include	"mobiles.h"
#include	"objects.h"
#include	"rooms.h"
#include	"deity.h"
#include	"area.h"
#include	"races.h"
#include	"class.h"
#include	"descriptor.h"
#include	"character.h"
#include	"sysdata.h"

/* Defines by Narn for new mudprog parsing, used as 
   return values from mprog_do_command. */
#define COMMANDOK    1
#define IFTRUE       2
#define IFFALSE      3
#define ORTRUE       4
#define ORFALSE      5
#define FOUNDELSE    6
#define FOUNDENDIF   7
#define IFIGNORED    8
#define ORIGNORED    9

/* Ifstate defines, used to create and access ifstate array
   in mprog_driver. */
#define MAX_IFS     20		/* should always be generous */
#define IN_IF        0
#define IN_ELSE      1
#define DO_IF        2
#define DO_ELSE      3

#define MAX_PROG_NEST   20

int mprog_do_command (char *cmnd, CCharacter *mob, CCharacter *actor, 
                      CObjData *obj, void *vo, CCharacter *rndm, 
                      BOOL ignore, BOOL ignore_ors);

//  Mudprogram additions
CCharacter		*supermob;
CActProgData	*room_act_list;
CActProgData	*obj_act_list;
CActProgData	*mob_act_list;

// Local function prototypes
char	*mprog_next_command (char* clist);
BOOL	mprog_seval (const char* lhs, const char* opr, const char* rhs,
			CCharacter *mob);
BOOL	mprog_veval (int lhs, char* opr, int rhs, CCharacter *mob);
int		mprog_do_ifcheck (char* ifcheck, CCharacter* mob, CCharacter* actor,
			CObjData* obj, void* vo, CCharacter* rndm);
void	mprog_translate (char ch, char* t, CCharacter* mob,
			CCharacter* actor, CObjData* obj, void* vo, CCharacter* rndm);
void	mprog_driver (char* com_list, CCharacter* mob, CCharacter* actor,
			CObjData* obj, void* vo, BOOL single_step);

BOOL	mprog_keyword_check (const char *argu, const char *argl);


void	oprog_wordlist_check (char *arg, CCharacter *mob, CCharacter *actor,
			CObjData *obj, void *vo, int type, CObjData *iobj);
void	set_supermob (CObjData *obj);
BOOL	oprog_percent_check (CCharacter *mob, CCharacter *actor,
			CObjData *obj, void *vo, int type);
void	rprog_percent_check (CCharacter *mob, CCharacter *actor,
			CObjData *obj, void *vo, int type);
void	rprog_wordlist_check (char *arg, CCharacter *mob, CCharacter *actor,
			CObjData *obj, void *vo, int type, CRoomIndexData *room);


CMobProgActList::~CMobProgActList ()
{
	delete buf;
}


/***************************************************************************
 * Local function code and brief comments.
 */

/* if you dont have these functions, you damn well should... */

#ifdef DUNNO_STRSTR
char * strstr (s1,s2) const char *s1; const char *s2;
{
  char *cp;
  int i,j=strlen (s1)-strlen (s2),k=strlen (s2);
  if (j<0)
    return NULL;
  for (i=0; i<=j && strncmp (s1++,s2, k)!=0; i++);
  return (i>j) ? NULL : (s1-1);
}
#endif


void init_supermob ()
{
	CRoomIndexData *office;

	supermob = create_mobile (MobTable.GetMob (3));
	office = RoomTable.GetRoom (SysData.m_RoomPoly);
	supermob->SendToRoom (office);

#ifdef NOTDEFD
	supermob = new CCharacter;
	supermob->InitChar ();

	SET_BIT (supermob->act,ACT_IS_NPC);
	supermob->GetName () 		= STRALLOC ("supermob");
	supermob->GetShortDescr () 	= STRALLOC ("supermob");
	supermob->long_descr 	= STRALLOC ("supermob is here");

	supermob_index = new CMobIndexData;
#endif
}


// Used to get sequential lines of a multi line string (separated by "\n\r"
// or "\r\n").  Thus it's like one_argument (), but a trifle different.
// It is destructive to the multi line string argument, and thus clist
// must not be shared.
char *mprog_next_command (char* clist)
{
	char	*ptr = clist;

	while (*ptr != '\n' && *ptr != '\r' && *ptr != '\0')
		ptr++;

	while (*ptr == '\r' || *ptr == '\n')
		*ptr++ = 0;

	return (ptr);
}


// These two functions do the basic evaluation of ifcheck operators.
//  It is important to note that the string operations are not what
//  you probably expect.  Equality is exact and division is substring.
//  remember that lhs has been stripped of leading space, but can
//  still have trailing spaces so be careful when editing since:
//  "guard" and "guard " are not equal.
BOOL mprog_seval (const char *lhs, const char *opr, const char *rhs,
				  CCharacter *mob)
{
	if (! str_cmp (opr, "=="))
		return (BOOL) (!str_cmp (lhs, rhs));
	if (! str_cmp (opr, "!="))
		return (BOOL) (str_cmp (lhs, rhs));
	if (! str_cmp (opr, "/"))
		return (BOOL) (!str_infix (rhs, lhs));
	if (! str_cmp (opr, "!/"))
		return (BOOL) (str_infix (rhs, lhs));

	sprintf (log_buf, "Improper MOBprog operator '%s'", opr);
	progbug (log_buf, mob);
	return 0;
}


BOOL mprog_veval (int lhs, char *opr, int rhs, CCharacter *mob)
{

  if (!str_cmp (opr, "=="))
    return (lhs == rhs);
  if (!str_cmp (opr, "!="))
    return (lhs != rhs);
  if (!str_cmp (opr, ">"))
    return (lhs > rhs);
  if (!str_cmp (opr, "<"))
    return (lhs < rhs);
  if (!str_cmp (opr, "<="))
    return (lhs <= rhs);
  if (!str_cmp (opr, ">="))
    return (lhs >= rhs);
  if (!str_cmp (opr, "&"))
    return (lhs & rhs);
  if (!str_cmp (opr, "|"))
    return (lhs | rhs);

  sprintf (log_buf, "Improper MOBprog operator '%s'", opr);
  progbug (log_buf, mob);

  return 0;

}

/* This function performs the evaluation of the if checks.  It is
 * here that you can add any ifchecks which you so desire. Hopefully
 * it is clear from what follows how one would go about adding your
 * own. The syntax for an if check is: ifcheck (arg) [opr val]
 * where the parenthesis are required and the opr and val fields are
 * optional but if one is there then both must be. The spaces are all
 * optional. The evaluation of the opr expressions is farmed out
 * to reduce the redundancy of the mammoth if statement list.
 * If there are errors, then return BERR otherwise return BOOLean 1,0
 * Redone by Altrag.. kill all that big copy-code that performs the
 * same action on each variable..
 */
int mprog_do_ifcheck (char *ifcheck, CCharacter *mob, CCharacter *actor,
		      CObjData *obj, void *vo, CCharacter *rndm)
{
  char cvar[MAX_INPUT_LENGTH];
  char chck[MAX_INPUT_LENGTH];
  char opr[MAX_INPUT_LENGTH];
  char rval[MAX_INPUT_LENGTH];
  char *point = ifcheck;
  char *pchck = chck;
  CCharacter *chkchar = NULL;
  CObjData *chkobj = NULL;
  int lhsvl, rhsvl;
  
  if (!*point)
  {
    progbug ("Null ifcheck", mob);
    return BERR;
  }
  while (*point == ' ')
    point++;
  while (*point != '(')
    if (*point == '\0')
    {
      progbug ("Ifcheck syntax error", mob);
      return BERR;
    }
    else if (*point == ' ')
      point++;
    else
      *pchck++ = *point++;
  *pchck = '\0';
  point++;
  pchck = cvar;
  while (*point != ')')
    if (*point == '\0')
    {
      progbug ("Ifcheck syntax error", mob);
      return BERR;
    }
    else if (*point == ' ')
      point++;
    else
      *pchck++ = *point++;
  point++;
  
  while (*point == ' ')
    point++;
  if (!*point)
  {
    opr[0] = '\0';
    rval[0] = '\0';
  }
  else
  {
    pchck = opr;
    while (*point != ' ' && !isalnum (*point))
      if (*point == '\0')
      {
        progbug ("Ifcheck operator without value", mob);
        return BERR;
      }
      else
        *pchck++ = *point++;
    *pchck = '\0';
    
    while (*point == ' ')
      point++;
    pchck = rval;
    while (*point != '\0' && *point != '\0')
      *pchck++ = *point++;
    *pchck = '\0';
  }
  
  /* chck contains check, cvar is the variable in the (), opr is the
   * operator if there is one, and rval is the value if there was an
   * operator.
   */
  if (cvar[0] == '$')
  {
    switch (cvar[1])
    {
	case 'i':	chkchar = mob;			break;
	case 'n':	chkchar = actor;		break;
	case 't':	chkchar = (CCharacter *)vo;	break;
	case 'r':	chkchar = rndm;			break;
	case 'o':	chkobj = obj;			break;
	case 'p':	chkobj = (CObjData *)vo;	break;
	default:
	  sprintf (rval, "Bad argument '%c' to '%s'", cvar[0], chck);
	  progbug (rval, mob);
	  return BERR;
    }
    if (!chkchar && !chkobj)
	return BERR;
  }
  if (!str_cmp (chck, "rand"))
  {
    return (number_percent () <= atoi (cvar));
  }
  if (!str_cmp (chck, "economy"))
  {
    int idx = atoi (cvar);
    CRoomIndexData *room;
    
    if (!idx)
    {
      if (!mob->GetInRoom ())
      {
        progbug ("'economy' ifcheck: mob in NULL room with no room vnum "
            "argument", mob);
        return BERR;
      }
      room = mob->GetInRoom ();
    }
    else
      room = RoomTable.GetRoom (idx);
    if (!room)
    {
      progbug ("Bad room vnum passed to 'economy'", mob);
      return BERR;
    }
    return mprog_veval (( (room->GetArea ()->high_economy > 0) ? 1000000000 : 0)
			+ room->GetArea ()->low_economy, opr, atoi (rval), mob);
  }
  if (!str_cmp (chck, "mobinroom"))
  {
    int vnum = atoi (cvar);
    int lhsvl;
    CCharacter *oMob;
    
    if (vnum < 1 || vnum > 2097152000)		// was 32767
    {
      progbug ("Bad vnum to 'mobinroom'", mob);
      return BERR;
    }
    lhsvl = 0;
    for (oMob = mob->GetInRoom ()->first_person; oMob;
          oMob = oMob->GetNextInRoom ())
      if (oMob->IsNpc () && oMob->GetMobIndex ()->vnum == vnum)
        lhsvl++;
    rhsvl = atoi (rval);
    if (rhsvl < 1) rhsvl = 1;
    if (!*opr)
      strcpy (opr, "==");
    return mprog_veval (lhsvl, opr, rhsvl, mob);
  }
  if (!str_cmp (chck, "timeskilled"))
  {
    CMobIndexData *pMob;
    
    if (chkchar)
      pMob = chkchar->GetMobIndex ();
    else if (! (pMob = MobTable.GetMob (atoi (cvar))))
    {
      progbug ("TimesKilled ifcheck: bad vnum", mob);
      return BERR;
    }
    return mprog_veval (pMob->killed, opr, atoi (rval), mob);
  }
  if (!str_cmp (chck, "ovnumhere"))
  {
	CObjData *pObj;
	int vnum = atoi (cvar);
    
	if (vnum < 1 || vnum > 2097152000) {		// was 32767
      progbug ("OvnumHere: bad vnum", mob);
      return BERR;
	}
	lhsvl = 0;
	POSITION	Cpos = mob->GetHeadCarryPos ();
	while (pObj = mob->GetNextCarrying (Cpos))
		if (can_see_obj (mob, *pObj) && pObj->GetIndex ()->vnum == vnum)
			lhsvl++;

	Cpos = mob->GetInRoom ()->GetHeadContentPos ();
	while (pObj = mob->GetInRoom ()->GetNextContent (Cpos))
		if (can_see_obj (mob, *pObj) && pObj->GetIndex ()->vnum == vnum)
			lhsvl++;

	rhsvl = is_number (rval) ? atoi (rval) : -1;
    if (rhsvl < 1)
      rhsvl = 1;
    if (!*opr)
      strcpy (opr, "==");
    return mprog_veval (lhsvl, opr, rhsvl, mob);
  }
  if (!str_cmp (chck, "otypehere"))
  {
    CObjData *pObj;
    int type;
    
    if (is_number (cvar))
      type = atoi (cvar);
    else
      type = get_otype (cvar);
    if (type < 0 || type > MAX_ITEM_TYPE)
    {
      progbug ("OtypeHere: bad type", mob);
      return BERR;
    }
    lhsvl = 0;
	POSITION	Cpos = mob->GetHeadCarryPos ();
	while (pObj = mob->GetNextCarrying (Cpos))
		if (can_see_obj (mob, *pObj) && pObj->item_type == type)
			lhsvl++;

	Cpos = mob->GetInRoom ()->GetHeadContentPos ();
	while (pObj = mob->GetInRoom ()->GetNextContent (Cpos))
		if (can_see_obj (mob, *pObj) && pObj->item_type == type)
			lhsvl++;

	rhsvl = is_number (rval) ? atoi (rval) : -1;
    if (rhsvl < 1)
      rhsvl = 1;
    if (!*opr)
      strcpy (opr, "==");
    return mprog_veval (lhsvl, opr, rhsvl, mob);
  }
  if (!str_cmp (chck, "ovnumroom"))
  {
    CObjData *pObj;
    int vnum = atoi (cvar);
    
    if (vnum < 1 || vnum > 2097152000)	// was 32767
    {
      progbug ("OvnumRoom: bad vnum", mob);
      return BERR;
    }
    lhsvl = 0;
	POSITION	Cpos = mob->GetInRoom ()->GetHeadContentPos ();
	while (pObj = mob->GetInRoom ()->GetNextContent (Cpos))
		if (can_see_obj (mob, *pObj) && pObj->GetIndex ()->vnum == vnum)
			lhsvl++;

	rhsvl = is_number (rval) ? atoi (rval) : -1;
    if (rhsvl < 1)
      rhsvl = 1;
    if (!*opr)
      strcpy (opr, "==");
    return mprog_veval (lhsvl, opr, rhsvl, mob);
  }
  if (!str_cmp (chck, "otyperoom"))
  {
    CObjData *pObj;
    int type;
    
    if (is_number (cvar))
      type = atoi (cvar);
    else
      type = get_otype (cvar);
    if (type < 0 || type > MAX_ITEM_TYPE)
    {
      progbug ("OtypeRoom: bad type", mob);
      return BERR;
    }
    lhsvl = 0;
	POSITION	Cpos = mob->GetInRoom ()->GetHeadContentPos ();
	while (pObj = mob->GetInRoom ()->GetNextContent (Cpos))
		if (can_see_obj (mob, *pObj) && pObj->item_type == type)
			lhsvl++;

	rhsvl = is_number (rval) ? atoi (rval) : -1;
    if (rhsvl < 1)
      rhsvl = 1;
    if (!*opr)
      strcpy (opr, "==");
    return mprog_veval (lhsvl, opr, rhsvl, mob);
  }
  if (!str_cmp (chck, "ovnumcarry"))
  {
    CObjData *pObj;
    int vnum = atoi (cvar);
    
    if (vnum < 1 || vnum > 2097152000)	// was 32767
    {
      progbug ("OvnumCarry: bad vnum", mob);
      return BERR;
    }
    lhsvl = 0;
	POSITION	Cpos = mob->GetHeadCarryPos ();
	while (pObj = mob->GetNextCarrying (Cpos))
		if (can_see_obj (mob, *pObj) && pObj->GetIndex ()->vnum == vnum)
			lhsvl++;

	rhsvl = is_number (rval) ? atoi (rval) : -1;
    if (rhsvl < 1)
      rhsvl = 1;
    if (!*opr)
      strcpy (opr, "==");
    return mprog_veval (lhsvl, opr, rhsvl, mob);
  }
  if (!str_cmp (chck, "otypecarry"))
  {
    CObjData *pObj;
    int type;
    
    if (is_number (cvar))
      type = atoi (cvar);
    else
      type = get_otype (cvar);
    if (type < 0 || type > MAX_ITEM_TYPE)
    {
      progbug ("OtypeCarry: bad type", mob);
      return BERR;
    }
    lhsvl = 0;
	POSITION	Cpos = mob->GetHeadCarryPos ();
	while (pObj = mob->GetNextCarrying (Cpos))
		if (can_see_obj (mob, *pObj) && pObj->item_type == type)
			lhsvl++;

	rhsvl = is_number (rval) ? atoi (rval) : -1;
    if (rhsvl < 1)
      rhsvl = 1;
    if (!*opr)
      strcpy (opr, "==");
    return mprog_veval (lhsvl, opr, rhsvl, mob);
  }
  if (!str_cmp (chck, "ovnumwear"))
  {
    CObjData *pObj;
    int vnum = atoi (cvar);
    
    if (vnum < 1 || vnum > 2097152000)	// was 32767
    {
      progbug ("OvnumWear: bad vnum", mob);
      return BERR;
    }
    lhsvl = 0;
	POSITION	Cpos = mob->GetHeadCarryPos ();
	while (pObj = mob->GetNextCarrying (Cpos))
		if (pObj->wear_loc != WEAR_NONE && can_see_obj (mob, *pObj) &&
			pObj->GetIndex ()->vnum == vnum)
				lhsvl++;

	rhsvl = is_number (rval) ? atoi (rval) : -1;
    if (rhsvl < 1)
      rhsvl = 1;
    if (!*opr)
      strcpy (opr, "==");
    return mprog_veval (lhsvl, opr, rhsvl, mob);
  }
  if (!str_cmp (chck, "otypewear"))
  {
    CObjData *pObj;
    int type;
    
    if (is_number (cvar))
      type = atoi (cvar);
    else
      type = get_otype (cvar);
    if (type < 0 || type > MAX_ITEM_TYPE)
    {
      progbug ("OtypeWear: bad type", mob);
      return BERR;
    }
    lhsvl = 0;
	POSITION	Cpos = mob->GetHeadCarryPos ();
	while (pObj = mob->GetNextCarrying (Cpos))
		if (pObj->wear_loc != WEAR_NONE && can_see_obj (mob, *pObj) &&
			pObj->item_type == type)
				lhsvl++;

	rhsvl = is_number (rval) ? atoi (rval) : -1;
    if (rhsvl < 1)
      rhsvl = 1;
    if (!*opr)
      strcpy (opr, "==");
    return mprog_veval (lhsvl, opr, rhsvl, mob);
  }
  if (!str_cmp (chck, "ovnuminv"))
  {
    CObjData *pObj;
    int vnum = atoi (cvar);
    
    if (vnum < 1 || vnum > 2097152000)	// was 32767
    {
      progbug ("OvnumInv: bad vnum", mob);
      return BERR;
    }
    lhsvl = 0;
	POSITION	Cpos = mob->GetHeadCarryPos ();
	while (pObj = mob->GetNextCarrying (Cpos))
		if (pObj->wear_loc == WEAR_NONE && can_see_obj (mob, *pObj) &&
			pObj->GetIndex ()->vnum == vnum)
				lhsvl++;

	rhsvl = is_number (rval) ? atoi (rval) : -1;
    if (rhsvl < 1)
      rhsvl = 1;
    if (!*opr)
      strcpy (opr, "==");
    return mprog_veval (lhsvl, opr, rhsvl, mob);
  }
  if (!str_cmp (chck, "otypeinv"))
  {
    CObjData *pObj;
    int type;
    
    if (is_number (cvar))
      type = atoi (cvar);
    else
      type = get_otype (cvar);
    if (type < 0 || type > MAX_ITEM_TYPE)
    {
      progbug ("OtypeInv: bad type", mob);
      return BERR;
    }
    lhsvl = 0;
	POSITION	Cpos = mob->GetHeadCarryPos ();
	while (pObj = mob->GetNextCarrying (Cpos))
		if (pObj->wear_loc == WEAR_NONE && can_see_obj (mob, *pObj) &&
			pObj->item_type == type)
				lhsvl++;

	rhsvl = is_number (rval) ? atoi (rval) : -1;
    if (rhsvl < 1)
      rhsvl = 1;
    if (!*opr)
      strcpy (opr, "==");
    return mprog_veval (lhsvl, opr, rhsvl, mob);
  }
  if (chkchar)
  {
    if (!str_cmp (chck, "ismobinvis"))
    {
      return (chkchar->IsNpc () && chkchar->IsAction (ACT_MOBINVIS));
    }
    if (!str_cmp (chck, "mobinvislevel"))
    {
      return (chkchar->IsNpc () ?
          mprog_veval (chkchar->GetMobInvisLevel (), opr, atoi (rval), mob) : FALSE);
    }
    if (!str_cmp (chck, "ispc"))
    {
      return chkchar->IsNpc () ? FALSE : TRUE;
    }
    if (!str_cmp (chck, "isnpc"))
    {
      return chkchar->IsNpc () ? TRUE : FALSE;
    }
    if (!str_cmp (chck, "ispkill"))
    {
      return chkchar->IsPkiller ();
    }
    if (!str_cmp (chck, "isdevoted"))
    {
      return chkchar->IsDevoted ();
    }
    if (!str_cmp (chck, "canpkill"))
    {
      return chkchar->CanPkill ();
    }
    if (!str_cmp (chck, "ismounted"))
    {
      return (chkchar->GetPosition () == POS_MOUNTED);
    }
    if (!str_cmp (chck, "isgood"))
    {
      return chkchar->IsGood ();
    }
    if (!str_cmp (chck, "isneutral"))
    {
      return chkchar->IsNeutral ();
    }
    if (!str_cmp (chck, "isevil"))
    {
      return chkchar->IsEvil ();
    }
    if (!str_cmp (chck, "isfight"))
    {
      return chkchar->GetFightWho () ? TRUE : FALSE;
    }
    if (!str_cmp (chck, "isimmort"))
    {
      return (chkchar->GetTrustLevel () >= LEVEL_IMMORTAL);
    }
    if (!str_cmp (chck, "ischarmed"))
    {
      return chkchar->IsCharmed ();
    }
    if (!str_cmp (chck, "isfollow"))
    {
      return (chkchar->GetMaster () != NULL &&
          chkchar->GetMaster ()->GetInRoom () == chkchar->GetInRoom ());
    }
    if (!str_cmp (chck, "isaffected"))
    {
	int value = get_aflag (rval);
      
	if (value < 0 || value >= MAX_AFFECTED_BY)
	{
	    progbug ("Unknown affect being checked", mob);
	    return BERR;
	}
	return chkchar->IsAffected (value);
    }
    if (!str_cmp (chck, "hitprcnt"))
    {
      return mprog_veval (chkchar->GetHp ()/chkchar->GetMaxHp (), opr, atoi (rval), mob);
    }
    if (!str_cmp (chck, "inroom"))
    {
      return mprog_veval (chkchar->GetInRoom ()->vnum, opr, atoi (rval), mob);
    }
    if (!str_cmp (chck, "wasinroom"))
    {
      return mprog_veval (chkchar->GetWasInRoom ()->vnum, opr, atoi (rval), mob);
    }
    if (!str_cmp (chck, "norecall"))
    {
		return chkchar->GetInRoom ()->IsNoRecall ();
    }
    if (!str_cmp (chck, "sex"))
    {
      return mprog_veval (chkchar->GetSex (), opr, atoi (rval), mob);
    }
    if (!str_cmp (chck, "position"))
    {
      return mprog_veval (chkchar->GetPosition (), opr, atoi (rval), mob);
    }
    if (!str_cmp (chck, "doingquest"))
    {
      return actor->IsNpc () ? FALSE :
          mprog_veval (chkchar->GetPcData ()->quest_number, opr, atoi (rval), mob);
    }
    if (!str_cmp (chck, "ishelled"))
    {
      return actor->IsNpc () ? FALSE :
          mprog_veval (chkchar->GetPcData ()->release_date.GetTotalSeconds (),
			opr, atoi (rval), mob);  
    }

    if (!str_cmp (chck, "level"))
    {
      return mprog_veval (chkchar->GetTrustLevel (), opr, atoi (rval), mob);
    }
    if (!str_cmp (chck, "goldamt"))
    {
      return mprog_veval (chkchar->GetGold (), opr, atoi (rval), mob);
    }

	if (! str_cmp (chck, "class")) {
		if (chkchar->IsNpc ())
			return mprog_seval (ClassTable.GetNpcClassName (chkchar->GetClass ()), opr, rval, mob);
		return mprog_seval (ClassTable.GetName (chkchar->GetClass ()), opr,
			rval, mob);
	}

	if (! str_cmp (chck, "race"))
		return mprog_seval (chkchar->GetRaceName (), opr, rval, mob);

    if (!str_cmp (chck, "clan"))
    {
      if (chkchar->IsNpc () || !chkchar->GetPcData ()->GetClan ())
        return FALSE;
      return mprog_seval (chkchar->GetPcData ()->GetClan ()->GetName (), opr, rval, mob);
    }
    if (!str_cmp (chck, "deity"))
    {
      if (chkchar->IsNpc () || !chkchar->GetPcData ()->deity)
	return FALSE;
      return mprog_seval (chkchar->GetPcData ()->deity->GetName (), opr, rval, mob);
    }
    if (!str_cmp (chck, "guild"))
    {
      if (chkchar->IsNpc () || ! chkchar->IsGuilded ())
        return FALSE;
      return mprog_seval (chkchar->GetPcData ()->GetClan ()->GetName (), opr, rval, mob);
    }
    if (!str_cmp (chck, "clantype"))
    {
      if (chkchar->IsNpc () || !chkchar->GetPcData ()->GetClan ())
        return FALSE;
      return mprog_veval (chkchar->GetPcData ()->GetClan ()->GetType (), opr, atoi (rval),
          mob);
    }
    if (!str_cmp (chck, "favor"))
    {
      return mprog_veval (chkchar->GetPcData ()->favor, opr, atoi (rval), mob);
    }
    if (!str_cmp (chck, "str"))
    {
      return mprog_veval (chkchar->GetCurrentStrength (), opr, atoi (rval), mob);
    }
    if (!str_cmp (chck, "wis"))
    {
      return mprog_veval (chkchar->GetWisdom (), opr, atoi (rval), mob);
    }
    if (!str_cmp (chck, "int"))
    {
      return mprog_veval (chkchar->GetIntelligence (), opr, atoi (rval), mob);
    }
    if (!str_cmp (chck, "dex"))
    {
      return mprog_veval (chkchar->GetDexterity (), opr, atoi (rval), mob);
    }
    if (!str_cmp (chck, "con"))
    {
      return mprog_veval (chkchar->GetConstitution (), opr, atoi (rval), mob);
    }
    if (!str_cmp (chck, "cha"))
    {
      return mprog_veval (get_curr_cha (chkchar), opr, atoi (rval), mob);
    }
    if (!str_cmp (chck, "lck"))
    {
      return mprog_veval (get_curr_lck (chkchar), opr, atoi (rval), mob);
    }
  }
  if (chkobj)
  {
    if (!str_cmp (chck, "objtype"))
    {
      return mprog_veval (chkobj->item_type, opr, atoi (rval), mob);
    }
    if (!str_cmp (chck, "objval0"))
    {
      return mprog_veval (chkobj->value[0], opr, atoi (rval), mob);
    }
    if (!str_cmp (chck, "objval1"))
    {
      return mprog_veval (chkobj->value[1], opr, atoi (rval), mob);
    }
    if (!str_cmp (chck, "objval2"))
    {
      return mprog_veval (chkobj->value[2], opr, atoi (rval), mob);
    }
    if (!str_cmp (chck, "objval3"))
    {
      return mprog_veval (chkobj->value[3], opr, atoi (rval), mob);
    }
    if (!str_cmp (chck, "objval4"))
    {
      return mprog_veval (chkobj->value[4], opr, atoi (rval), mob);
    }
    if (!str_cmp (chck, "objval5"))
    {
      return mprog_veval (chkobj->value[5], opr, atoi (rval), mob);
    }
  }
  /* The following checks depend on the fact that cval[1] can only contain
     one character, and that NULL checks were made previously. */
  if (!str_cmp (chck, "number"))
  {
    if (chkchar)
    {
      if (!chkchar->IsNpc ())
        return FALSE;
      lhsvl = (chkchar == mob) ? chkchar->GetGold () : chkchar->GetMobIndex ()->vnum;
      return mprog_veval (lhsvl, opr, atoi (rval), mob);
    }
    return mprog_veval (chkobj->GetIndex ()->vnum, opr, atoi (rval), mob);
  }
  if (!str_cmp (chck, "name"))
  {
    if (chkchar)
      return mprog_seval (chkchar->GetName (), opr, rval, mob);
    return mprog_seval (chkobj->GetName (), opr, rval, mob);
  }
  
  /* Ok... all the ifchecks are done, so if we didnt find ours then something
   * odd happened.  So report the bug and abort the MUDprogram (return error)
   */
  progbug ("Unknown ifcheck", mob);
  return BERR;
}


/* This routine handles the variables for command expansion.
 * If you want to add any go right ahead, it should be fairly
 * clear how it is done and they are quite easy to do, so you
 * can be as creative as you want. The only catch is to check
 * that your variables exist before you use them. At the moment,
 * using $t when the secondary target refers to an object 
 * i.e. >prog_act drops~<nl>if ispc ($t)<nl>sigh<nl>endif<nl>~<nl>
 * probably makes the mud crash (vice versa as well) The cure
 * would be to change act () so that vo becomes vict & v_obj.
 * but this would require a lot of small changes all over the code.
 */

/*
 *  There's no reason to make the mud crash when a variable's
 *  fubared.  I added some ifs.  I'm willing to trade some 
 *  performance for stability. -Haus
 *
 *  Narn's fubar ***ANNIHILATES*** you!  Hmm, could we add that
 *  as a weapon type? -Narn
 *
 *  Added char_died and obj_extracted checks	-Thoric
 */
void mprog_translate (char ch, char *t, CCharacter *mob, CCharacter *actor,
                    CObjData *obj, void *vo, CCharacter *rndm)
{
 static char *he_she        [] = { "it",  "he",  "she" };
 static char *him_her       [] = { "it",  "him", "her" };
 static char *his_her       [] = { "its", "his", "her" };
 CCharacter   *vict             = (CCharacter *) vo;
 CObjData    *v_obj            = (CObjData  *) vo;

 *t = '\0';
 switch (ch) {
     case 'i':
	 if (mob && !char_died (mob))
	 {
	   if (mob->GetName ())
              one_argument (mob->GetName (), t);
         } else
	    strcpy (t, "someone");
      break;

     case 'I':
	 if (mob && !char_died (mob))
	 {
	   if (mob->GetShortDescr ())
	   {
              strcpy (t, mob->GetShortDescr ());
           } else {
	      strcpy (t, "someone");
	   }
         } else
	    strcpy (t, "someone");
      break;

     case 'n':
         if (actor && !char_died (actor))
	 {
	   if (can_see (mob,actor))
	     one_argument (actor->GetName (), t);
           if (!actor->IsNpc ())
  	     *t = UPPER (*t);
         }
	 else
	      strcpy (t, "someone");
         break;

     case 'N':
         if (actor && !char_died (actor)) 
	 {
            if (can_see (mob, actor))
	       if (actor->IsNpc ())
		 strcpy (t, actor->GetShortDescr ());
	       else
	       {
		   strcpy (t, actor->GetName ());
		   strcat (t, actor->GetPcData ()->GetTitle ());
	       }
	    else
	      strcpy (t, "someone");
         } 
	 else
	      strcpy (t, "someone");
	 break;

     case 't':
         if (vict && !char_died (vict))
	 {
	   if (can_see (mob, vict))
	     one_argument (vict->GetName (), t);
           if (!vict->IsNpc ())
	     *t = UPPER (*t);
         } 
	 else 
	      strcpy (t, "someone");

	 break;

     case 'T':
         if (vict && !char_died (vict)) 
	 {
            if (can_see (mob, vict))
	       if (vict->IsNpc ())
		 strcpy (t, vict->GetShortDescr ());
	       else
	       {
		 strcpy (t, vict->GetName ());
		 strcat (t, " ");
		 strcat (t, vict->GetPcData ()->GetTitle ());
	       }
	    else
	      strcpy (t, "someone");
         }
	 else 
	      strcpy (t, "someone");
	 break;
     
     case 'r':             
         if (rndm && !char_died (rndm))
	 {
	   if (can_see (mob, rndm))
	   {
	     one_argument (rndm->GetName (), t);
           }
           if (!rndm->IsNpc ())
	   {
	     *t = UPPER (*t);
           }
	 }  
	 else
	   strcpy (t, "someone");
      break;

     case 'R':
	 if (rndm && !char_died (rndm))
	 {
            if (can_see (mob, rndm))
	       if (rndm->IsNpc ())
		 strcpy (t,rndm->GetShortDescr ());
	       else
	       {
		 strcpy (t, rndm->GetName ());
		 strcat (t, " ");
		 strcat (t, rndm->GetPcData ()->GetTitle ());
	       }
	    else
	      strcpy (t, "someone");
         }
	 else 
	      strcpy (t, "someone");
	 break;

     case 'e':
         if (actor && !char_died (actor))
	 {
	   can_see (mob, actor) ? strcpy (t, he_she[ actor->GetSex () ])
	                         : strcpy (t, "someone");
         } 
	 else
	      strcpy (t, "it");
	 break;
  
     case 'm':
         if (actor && !char_died (actor))
	 {
	   can_see (mob, actor) ? strcpy (t, him_her[ actor->GetSex () ])
                                 : strcpy (t, "someone");
         }
	 else
	      strcpy (t, "it");
	 break;
  
     case 's':
         if (actor && !char_died (actor))
	 {
	   can_see (mob, actor) ? strcpy (t, his_her[ actor->GetSex () ])
	                         : strcpy (t, "someone's");
         }
	 else
	      strcpy (t, "its'");
	 break;
     
     case 'E':
         if (vict && !char_died (vict))
	 {
	   can_see (mob, vict) ? strcpy (t, he_she[ vict->GetSex () ])
                                : strcpy (t, "someone");
         }
	 else
	      strcpy (t, "it");
	 break;
  
     case 'M':
         if (vict && !char_died (vict))
	 {
	   can_see (mob, vict) ? strcpy (t, him_her[ vict->GetSex () ])
                                : strcpy (t, "someone");
         }
	 else
	      strcpy (t, "it");
	 break;
  
     case 'S':
         if (vict && !char_died (vict))
	 {
	   can_see (mob, vict) ? strcpy (t, his_her[ vict->GetSex () ])
                                : strcpy (t, "someone's"); 
         }
	 else
	      strcpy (t, "its'");
	 break;

     case 'j':
	 if (mob && !char_died (mob))
	 {
	    strcpy (t, he_she[ mob->GetSex () ]);
         } else {
	    strcpy (t, "it");
	 }
	 break;
  
     case 'k':
	 if (mob && !char_died (mob))
	 {
	   strcpy (t, him_her[ mob->GetSex () ]);
         } else {
	    strcpy (t, "it");
	 }
	 break;
  
     case 'l':
	 if (mob && !char_died (mob))
	 {
	   strcpy (t, his_her[ mob->GetSex () ]);
         } else {
	    strcpy (t, "it");
	 }
	 break;

     case 'J':
         if (rndm && !char_died (rndm))
	 {
	   can_see (mob, rndm) ? strcpy (t, he_she[ rndm->GetSex () ])
	                        : strcpy (t, "someone");
         }
	 else
	      strcpy (t, "it");
	 break;
  
     case 'K':
         if (rndm && !char_died (rndm))
	 {
	   can_see (mob, rndm) ? strcpy (t, him_her[ rndm->GetSex () ])
                                : strcpy (t, "someone's");
         }
	 else
	      strcpy (t, "its'");
	 break;
  
     case 'L':
         if (rndm && !char_died (rndm))
	 {
	   can_see (mob, rndm) ? strcpy (t, his_her[ rndm->GetSex () ])
	                        : strcpy (t, "someone");
         }
	 else
	      strcpy (t, "its");
	 break;

     case 'o':
         if (obj && !obj_extracted (obj))
	 {
	   can_see_obj (mob, *obj) ? one_argument (obj->GetName (), t)
                                   : strcpy (t, "something");
         }
	 else
	      strcpy (t, "something");
	 break;

     case 'O':
         if (obj && !obj_extracted (obj))
	 {
	   can_see_obj (mob, *obj) ? strcpy (t, obj->GetShortDescr ())
                                   : strcpy (t, "something");
         }
	 else
	      strcpy (t, "something");
	 break;

     case 'p':
         if (v_obj && !obj_extracted (v_obj))
	 {
	   can_see_obj (mob, *v_obj) ? one_argument (v_obj->GetName (), t)
                                     : strcpy (t, "something");
         }
	 else
	      strcpy (t, "something");
	 break;

     case 'P':
         if (v_obj && !obj_extracted (v_obj))
	 {
	   can_see_obj (mob, *v_obj) ? strcpy (t, v_obj->GetShortDescr ())
                                     : strcpy (t, "something");
         }
	 else
	      strcpy (t, "something");
      break;

     case 'a':
         if (obj && !obj_extracted (obj)) 
	 {
	    strcpy (t, aoran (obj->GetName ()));
/*
          switch (* (obj->GetName ()))
	  {
	    case 'a': case 'e': case 'i':
            case 'o': case 'u': strcpy (t, "an");
	      break;
            default: strcpy (t, "a");
          }
*/
         }
	 else
	      strcpy (t, "a");
	 break;

     case 'A':
         if (v_obj && !obj_extracted (v_obj))
	 {
	      strcpy (t, aoran (v_obj->GetName ()));
         }
	 else
	      strcpy (t, "a");
	 break;

     case '$':
         strcpy (t, "$");
	 break;

     default:
         progbug ("Bad $var", mob);
	 break;
       }

 return;

}

// The main focus of the MOBprograms.  This routine is called 
// whenever a trigger is successful.  It is responsible for parsing
// the command list and figuring out what to do. However, like all
// complex procedures, everything is farmed out to the other guys.
// 
// This function rewritten by Narn for Realms of Despair, Dec/95.
void mprog_driver (char *com_list, CCharacter *mob, CCharacter *actor,
		   CObjData *obj, void *vo, BOOL single_step)
{
	char		tmpcmndlst [MAX_STRING_LENGTH];
	char		*command_list;
	char		*cmnd;
	CCharacter	*rndm = NULL;
	int			count = 0;
	int			ignorelevel = 0;
	int			iflevel, result;
	BOOL		ifstate [MAX_IFS][DO_ELSE + 1];
	static int	prog_nest;

	if (mob->IsCharmed ())
		return;

	// Next couple of checks stop program looping. -- Altrag
	if (mob == actor) {
		progbug ("triggering oneself.", mob);
		return;
	}

	if (++prog_nest > MAX_PROG_NEST) {
		progbug ("max_prog_nest exceeded.", mob);
		--prog_nest;
		return;
	}

	// Make sure all ifstate BOOLs are set to FALSE
	for (iflevel = 0; iflevel < MAX_IFS; ++iflevel) {
		for (count = 0; count < DO_ELSE; count++)
			ifstate [iflevel][count] = FALSE;
	}

	iflevel = 0;

	// get a random visible player who is in the room with the mob.
	//
	// If there isn't a random player in the room, rndm stays NULL.
	// If you do a $r, $R, $j, or $k with rndm = NULL, you'll crash
	// in mprog_translate.
	//
	// Adding appropriate error checking in mprog_translate.
	//   -Haus
	//
	// This used to ignore players MAX_LEVEL - 3 and higher (standard
	// Merc has 4 immlevels).  Thought about changing it to ignore all
	// imms, but decided to just take it out.  If the mob can see you, 
	// you may be chosen as the random player. -Narn
	count = 0;
	CCharacter	*vch = mob->GetInRoom ()->first_person;

	for (; vch; vch = vch->GetNextInRoom ())
		if (! vch->IsNpc () && can_see (mob, vch)) {
			if (number_range (0, count) == 0)
			rndm = vch;
			++count;
		}

	strcpy (tmpcmndlst, com_list);
	command_list = tmpcmndlst;

	if (single_step) {
		if (mob->mpscriptpos > (int) strlen (tmpcmndlst))
			mob->mpscriptpos = 0;
		else
			command_list += mob->mpscriptpos;
		if (*command_list == '\0') {
			mob->mpscriptpos = 0;
			return;
		}
	}

	// From here on down, the function is all mine.  The original code
	// did not support nested ifs, so it had to be redone.  The max 
	// logiclevel (MAX_IFS) is defined at the beginning of this file, 
	// use it to increase/decrease max allowed nesting.  -Narn 

	while (TRUE) {
		// With these two lines, cmnd becomes the current line from the prog,
		// and command_list becomes everything after that line.
		cmnd = command_list;
		command_list = mprog_next_command (command_list);

		// Are we at the end?
		if (cmnd [0] == '\0') {
			if (ifstate [iflevel][IN_IF] || ifstate [iflevel][IN_ELSE])
				progbug ("Missing endif", mob);
			--prog_nest;
			return;
		}

		// Evaluate/execute the command, check what happened.
		result = mprog_do_command (cmnd, mob, actor, obj, vo, rndm, 
			(ifstate[iflevel][IN_IF] && !ifstate[iflevel][DO_IF])
			|| (ifstate[iflevel][IN_ELSE] && !ifstate[iflevel][DO_ELSE]),
			(ignorelevel > 0));

		// Script prog support  -Thoric
		if (single_step) {
			mob->mpscriptpos = command_list - tmpcmndlst;
			--prog_nest;
			return;
		}

		// This is the complicated part.  Act on the returned value from
		// mprog_do_command according to the current logic state.
		switch (result) {
		  case COMMANDOK:
			#ifdef DEBUG
				gpDoc->LogString ("COMMANDOK");
			#endif
			// Ok, this one's a no-brainer.
			continue;

		  case IFTRUE:
			#ifdef DEBUG
				gpDoc->LogString ("IFTRUE");
			#endif
			// An if was evaluated and found true.  Note that we are in an
			// if section and that we want to execute it.
			++iflevel;
			if (iflevel == MAX_IFS) {
				progbug ("Maximum nested ifs exceeded", mob);
				--prog_nest;
				return;
			}

			ifstate [iflevel][IN_IF] = TRUE; 
			ifstate [iflevel][DO_IF] = TRUE;
			break;

		  case IFFALSE:
			#ifdef DEBUG
				gpDoc->LogString ("IFFALSE");
			#endif
			// An if was evaluated and found false.  Note that we are in an
			// if section and that we don't want to execute it unless we find
			// an or that evaluates to true.
			++iflevel;
			if (iflevel == MAX_IFS) {
				progbug ("Maximum nested ifs exceeded", mob);
				--prog_nest;
				return;
			}
			ifstate [iflevel][IN_IF] = TRUE; 
			ifstate [iflevel][DO_IF] = FALSE;
			break;

		  case ORTRUE:
			#ifdef DEBUG
				gpDoc->LogString ("ORTRUE");
			#endif
			// An or was evaluated and found true.  We should already be in an
			// if section, so note that we want to execute it.
			if (! ifstate [iflevel][IN_IF]) {
				progbug ("Unmatched or", mob);
				--prog_nest;
				return;
			}
			ifstate [iflevel][DO_IF] = TRUE;
			break;

		  case ORFALSE:
			#ifdef DEBUG
				gpDoc->LogString ("ORFALSE");
			#endif
			// An or was evaluated and found false.  We should already be
			// in an if section, and we don't need to do much.  If the
			// if was true or there were/will be other ors that evaluate(d)
			// to true, they'll set do_if to true.
			if (! ifstate [iflevel][IN_IF]) {
				progbug ("Unmatched or", mob);
				--prog_nest;
				return;
			}
			continue;

		  case FOUNDELSE:
			#ifdef DEBUG
				gpDoc->LogString ("FOUNDELSE");
			#endif
			// Found an else.  Make sure we're in an if section, bug out
			// if not.  If this else is not one that we wish to ignore,
			// note that we're now in an else section, and look at whether
			// or not we executed the if section to decide whether to
			// execute the else section.  Ca marche bien.
			if (ignorelevel > 0)
				continue;

			if (ifstate [iflevel][IN_ELSE]) {
				progbug ("Found else in an else section", mob);
				--prog_nest;
				return;
			}
			if (! ifstate [iflevel][IN_IF]) {
				progbug ("Unmatched else", mob);
				--prog_nest;
				return;
			}

			ifstate [iflevel][IN_ELSE] = TRUE;
			ifstate [iflevel][DO_ELSE] = !ifstate [iflevel][DO_IF];
			ifstate [iflevel][IN_IF]   = FALSE;
			ifstate [iflevel][DO_IF]   = FALSE;

			break;

		  case FOUNDENDIF:
			#ifdef DEBUG
				gpDoc->LogString ("FOUNDENDIF");
			#endif
			// Hmm, let's see... FOUNDENDIF must mean that we found an
			// endif.  So let's make sure we were expecting one, return
			// if not.  If this endif matches the if or else that we're
			// executing, note that we are now no longer executing an if.
			// If not, keep track of what we're ignoring.
			if (! (ifstate [iflevel][IN_IF] || ifstate [iflevel][IN_ELSE])) {
				progbug ("Unmatched endif", mob);
				--prog_nest;
				return;
			}

			if (ignorelevel > 0) {
				--ignorelevel;
				continue;
			}

			ifstate [iflevel][IN_IF]   = FALSE;
			ifstate [iflevel][DO_IF]   = FALSE;
			ifstate [iflevel][IN_ELSE] = FALSE;
			ifstate [iflevel][DO_ELSE] = FALSE;

			--iflevel;
			break;

		  case IFIGNORED:
			#ifdef DEBUG
				gpDoc->LogString ("IFIGNORED");
			#endif
			if (! (ifstate [iflevel][IN_IF] || ifstate [iflevel][IN_ELSE])) {
				progbug ("Parse error, ignoring if while not in if or else",
					mob);
				--prog_nest;
				return;
			}
			++ignorelevel;
			break;

		  case ORIGNORED:
			#ifdef DEBUG
				gpDoc->LogString ("ORIGNORED");
			#endif
			if (! (ifstate [iflevel][IN_IF] || ifstate [iflevel][IN_ELSE])) {
				progbug ("Unmatched or", mob);
				--prog_nest;
				return;
			}
			if (ignorelevel == 0) {
				progbug ("Parse error, mistakenly ignoring or", mob);
				--prog_nest;
				return;
			}

			break;

		  case BERR:
			#ifdef DEBUG
				gpDoc->LogString ("BERR");
			#endif
			--prog_nest;
			return;
		}
	}
	--prog_nest;
}


/* This function replaces mprog_process_cmnd.  It is called from 
 * mprog_driver, once for each line in a mud prog.  This function
 * checks what the line is, executes if/or checks and calls interpret
 * to perform the the commands.  Written by Narn, Dec 95.
 */
int mprog_do_command (char *cmnd, CCharacter *mob, CCharacter *actor, 
                      CObjData *obj, void *vo, CCharacter *rndm, 
                      BOOL ignore, BOOL ignore_ors)
{
  char firstword[MAX_INPUT_LENGTH];
  char *ifcheck;
  char buf[ MAX_INPUT_LENGTH ];
  char tmp[ MAX_INPUT_LENGTH ];
  char *point, *str, *i;
  int validif, vnum;

  /* Isolate the first word of the line, it gives us a clue what
     we want to do. */
  ifcheck = one_argument (cmnd, firstword);

  if (!str_cmp (firstword, "if"))
  {
    /* Ok, we found an if.  According to the BOOLean 'ignore', either
       ignore the ifcheck and report that back to mprog_driver or do
       the ifcheck and report whether it was successful. */
    if (ignore)
      return IFIGNORED;
    else
      validif = mprog_do_ifcheck (ifcheck, mob, actor, obj, vo, rndm);

    if (validif == 1)
      return IFTRUE;

    if (validif == 0)
      return IFFALSE;

    return BERR;
  }

  if (!str_cmp (firstword, "or"))
  {
    /* Same behavior as with ifs, but use the BOOLean 'ignore_ors' to
       decide which way to go. */
    if (ignore_ors)
      return ORIGNORED;
    else
      validif = mprog_do_ifcheck (ifcheck, mob, actor, obj, vo, rndm);

    if (validif == 1)
      return ORTRUE;

    if (validif == 0)
      return ORFALSE;

    return BERR;
  }

  /* For else and endif, just report back what we found.  Mprog_driver
     keeps track of logiclevels. */
  if (!str_cmp (firstword, "else"))
  {
    return FOUNDELSE;
  }

  if (!str_cmp (firstword, "endif"))
  {
    return FOUNDENDIF;
  }

  /* Ok, didn't find an if, an or, an else or an endif.  
     If the command is in an if or else section that is not to be 
     performed, the BOOLean 'ignore' is set to true and we just 
     return.  If not, we try to execute the command. */

  if (ignore)
    return COMMANDOK;

  /* If the command is 'break', that's all folks. */
  if (!str_cmp (firstword, "break"))
    return BERR;

  vnum = mob->GetMobIndex ()->vnum;
  point   = buf;
  str     = cmnd;

  /* This chunk of code taken from mprog_process_cmnd. */
  while (*str != '\0')
  {
    if (*str != '$')
    {
      *point++ = *str++;
      continue;
    }
    str++;
    mprog_translate (*str, tmp, mob, actor, obj, vo, rndm);
    i = tmp;
    ++str;
    while ((*point = *i) != '\0')
      ++point, ++i;
  }
  *point = '\0';

  interpret (mob, buf);  

  /* If the mob is mentally unstable and does things like fireball
     itself, let's make sure it's still alive. */
  if (char_died (mob))
  {
    bug ("Mob died while executing program, vnum %d.", vnum);
    return BERR;
  }

  return COMMANDOK;
}

/***************************************************************************
 * Global function code and brief comments.
 */

BOOL mprog_keyword_check (const char *argu, const char *argl)
{
    char	word [MAX_INPUT_LENGTH];
    char	arg1 [MAX_INPUT_LENGTH];
    char	arg2 [MAX_INPUT_LENGTH];
    char	*arg, *arglist;
    char	*start, *end;

    strcpy (arg1, strlower (argu));
    arg = arg1;
    strcpy (arg2, strlower (argl));
    arglist = arg2;

	if ((arglist[0] == 'p') && (arglist[1] == ' ')) {
		arglist += 2;
		while ((start = strstr (arg, arglist)))
			if ((start == arg || * (start-1) == ' ')
				&& (* (end = start + strlen (arglist)) == ' '
				||   *end == '\n'
				||   *end == '\r'
				||   *end == '\0'))
					return TRUE;
			else
				arg = start+1;
	} else {
		arglist = one_argument (arglist, word);
		for (; word[0] != '\0'; arglist = one_argument (arglist, word))
			while ((start = strstr (arg, word)))
				if ((start == arg || * (start-1) == ' ')
					&& (* (end = start + strlen (word)) == ' '
					||   *end == '\n'
					||   *end == '\r'
					||   *end == '\0'))
						return TRUE;
				else
					arg = start +1;
    }
/*    bug ("don't match"); */
    return FALSE;
}


// The next two routines are the basic trigger types. Either trigger
// on a certain percent, or trigger on a keyword or word phrase.
// To see how this works, look at the various trigger routines..
void mprog_wordlist_check (char *arg, CCharacter *mob, CCharacter *actor,
			  CObjData *obj, void *vo, int type)
{
	char	temp1 [MAX_STRING_LENGTH];
	char	temp2 [MAX_INPUT_LENGTH];
	char	word [MAX_INPUT_LENGTH];
	char	*list;
	char	*start;
	char	*dupl;
	char	*end;
	int		i;

	CPtrList	&PList = mob->GetMobIndex ()->MobPrgList;
	POSITION	pos = PList.GetHeadPosition ();
	while (pos) {
		CMobProgData	&Mprg = *(CMobProgData*) PList.GetNext (pos);
		if (Mprg.type == type) {
			strcpy (temp1, Mprg.arglist);
			list = temp1;
			for (i = 0; i < (int) strlen (list); i++)
				list [i] = LOWER (list [i]);
			strcpy (temp2, arg);
			dupl = temp2;
			for (i = 0; i < (int) strlen (dupl); i++)
				dupl [i] = LOWER (dupl [i]);

			if ((list [0] == 'p') && (list [1] == ' ')) {
				list += 2;
				while ((start = strstr (dupl, list)))
					if ((start == dupl || * (start-1) == ' ')
					  && (* (end = start + strlen (list)) == ' '
					  || *end == '\n'
					  || *end == '\r'
					  || *end == '\0')) {
						mprog_driver (Mprg.comlist, mob, actor, obj,
							vo, FALSE);
						break;
					}
					else dupl = start + 1;
			} else {
				list = one_argument (list, word);
				for (; word [0] != '\0'; list = one_argument (list, word))
					while ((start = strstr (dupl, word)))
						if ((start == dupl || * (start-1) == ' ')
						  && (* (end = start + strlen (word)) == ' '
						  || *end == '\n'
						  || *end == '\r'
						  || *end == '\0')) {
							mprog_driver (Mprg.comlist, mob, actor, obj,
								vo, FALSE);
							break;
						}
						else dupl = start + 1;
			}
		}
	}
}


void mprog_percent_check (CCharacter *mob, CCharacter *actor, CObjData *obj,
			 void *vo, int type)
{
	CPtrList	&PList = mob->GetMobIndex ()->MobPrgList;
	POSITION	pos = PList.GetHeadPosition ();
	while (pos) {
		CMobProgData	&Mprg = *(CMobProgData*) PList.GetNext (pos);
		if ((Mprg.type == type) && (number_percent () <= atoi (Mprg.arglist))) {
			mprog_driver (Mprg.comlist, mob, actor, obj, vo, FALSE);
			if (type != GREET_PROG && type != ALL_GREET_PROG)
				break;
		}
	}
}


void mprog_time_check (CCharacter *mob, CCharacter *actor, CObjData *obj,
                         void *vo, int type)
{
	BOOL	bTriggerTime;

	CPtrList	&PList = mob->GetMobIndex ()->MobPrgList;
	POSITION	pos = PList.GetHeadPosition ();
	while (pos) {
		CMobProgData	&Mprg = *(CMobProgData*) PList.GetNext (pos);
		bTriggerTime = (time_info.hour == atoi (Mprg.arglist));     

		if (! bTriggerTime) {
			if (Mprg.triggered)
				Mprg.triggered = FALSE;
			continue;
		}

		if ((Mprg.type == type) && ((! Mprg.triggered)
		  || (Mprg.type == HOUR_PROG))) {
			Mprg.triggered = TRUE;
			mprog_driver (Mprg.comlist, mob, actor, obj, vo, FALSE);
		}
	}
}


void mob_act_add (CCharacter *mob)
{
	CActProgData	*runner;

	for (runner = mob_act_list; runner; runner = runner->GetNext ())
		if (runner->vo == mob)
			return;
	runner = new CActProgData;
	runner->vo = mob;
	runner->SetNext (mob_act_list);
	mob_act_list = runner;
}


// The triggers.. These are really basic, and since most appear only
// once in the code (hmm. i think they all do) it would be more efficient
// to substitute the code in and make the mprog_xxx_check routines global.
// However, they are all here in one nice place at the moment to make it
// easier to see what they look like. If you do substitute them back in,
// make sure you remember to modify the variable names to the ones in the
// trigger calls.
void mprog_act_trigger (char *buf, CCharacter *mob, CCharacter *ch,
		       CObjData *obj, void *vo)
{
	CMobProgActList	*tmp_act;
	BOOL			found = FALSE;

	if (mob->IsNpc ()
       && mob->GetMobIndex ()->m_Progtypes.IsSet (ACT_PROG)) {
		// Don't let a mob trigger itself, nor one instance of a mob
		// trigger another instance.
		if (ch->IsNpc () && ch->GetMobIndex () == mob->GetMobIndex ())
			return;

		// make sure this is a matching trigger
		CPtrList	&PList = mob->GetMobIndex ()->MobPrgList;
		POSITION	pos = PList.GetHeadPosition ();
		while (pos) {
			CMobProgData	&Mprg = *(CMobProgData*) PList.GetNext (pos);
			if (Mprg.type == ACT_PROG
			  && mprog_keyword_check (buf, Mprg.arglist)) {
				found = TRUE;
				break;
			}
		}

		if (! found)
			return;

		tmp_act = new CMobProgActList;
		if (mob->GetMobPactnum () > 0)
			tmp_act->SetNext (mob->GetMobPact ());
		else
			tmp_act->SetNext (NULL);

		mob->SetMobPact (tmp_act);
		mob->GetMobPact ()->buf = str_dup (buf);  
		mob->GetMobPact ()->ch  = ch;
		mob->GetMobPact ()->obj = obj;
		mob->GetMobPact ()->vo  = vo; 
		mob->AddMobPactnum (1);
		mob_act_add (mob);
	}
}


void mprog_bribe_trigger (CCharacter *mob, CCharacter *ch, int amount)
{

	char        buf [ MAX_STRING_LENGTH ];
	CObjData   *obj;

	if (mob->IsNpc () && can_see (mob, ch)
	  && mob->GetMobIndex ()->m_Progtypes.IsSet (BRIBE_PROG)) {

		// Don't let a mob trigger itself, nor one instance of a mob
		// trigger another instance.
		if (ch->IsNpc () && ch->GetMobIndex () == mob->GetMobIndex ())
			return;

		obj = create_object (OIdxTable.GetObj (OBJ_VNUM_MONEY_SOME), 0);
		sprintf (buf, obj->GetShortDescr (), amount);
		obj->SetShortDescr (buf);
		obj->value [0] = amount;
		obj = obj_to_char (obj, mob);
		mob->AddGold (-amount);

		CPtrList	&PList = mob->GetMobIndex ()->MobPrgList;
		POSITION	pos = PList.GetHeadPosition ();
		while (pos) {
			CMobProgData	&Mprg = *(CMobProgData*) PList.GetNext (pos);
			if ((Mprg.type == BRIBE_PROG) && (amount >= atoi (Mprg.arglist))) {
				mprog_driver (Mprg.comlist, mob, ch, obj, NULL, FALSE);
				break;
			}
		}
	}
}


void mprog_death_trigger (CCharacter *killer, CCharacter *mob)
{
 if (mob->IsNpc () && killer != mob
     && mob->GetMobIndex ()->m_Progtypes.IsSet (DEATH_PROG))
   {
     mprog_percent_check (mob, killer, NULL, NULL, DEATH_PROG);
   }
 death_cry (mob);
 return;
}

void mprog_entry_trigger (CCharacter *mob)
{

 if (mob->IsNpc ()
     && mob->GetMobIndex ()->m_Progtypes.IsSet (ENTRY_PROG))
   mprog_percent_check (mob, NULL, NULL, NULL, ENTRY_PROG);

 return;

}

void mprog_fight_trigger (CCharacter *mob, CCharacter *ch)
{

 if (mob->IsNpc ()
     && mob->GetMobIndex ()->m_Progtypes.IsSet (FIGHT_PROG))
   mprog_percent_check (mob, ch, NULL, NULL, FIGHT_PROG);

 return;

}


void mprog_give_trigger (CCharacter *mob, CCharacter *ch, CObjData *obj)
{
	char	buf [MAX_INPUT_LENGTH];

	if (mob->IsNpc () && can_see (mob, ch)
	  && mob->GetMobIndex ()->m_Progtypes.IsSet (GIVE_PROG)) {
		// Don't let a mob trigger itself, nor one instance of a mob
		// trigger another instance.
		if (ch->IsNpc () && ch->GetMobIndex () == mob->GetMobIndex ())
			return; 

		CPtrList	&PList = mob->GetMobIndex ()->MobPrgList;
		POSITION	pos = PList.GetHeadPosition ();
		while (pos) {
			CMobProgData	&Mprg = *(CMobProgData*) PList.GetNext (pos);
			one_argument (Mprg.arglist, buf);

			if ((Mprg.type == GIVE_PROG)
			  && ((! str_cmp (obj->GetName (), Mprg.arglist))
			  || (! str_cmp ("all", buf)))) {
				mprog_driver (Mprg.comlist, mob, ch, obj, NULL, FALSE);
				break;
			}
		}
	}
}


void mprog_greet_trigger (CCharacter *ch)
{
 CCharacter *vmob, *vmob_next;

#ifdef DEBUG
 char buf[MAX_STRING_LENGTH];
 sprintf (buf, "mprog_greet_trigger -> %s", ch->GetName ());
 gpDoc->LogString (buf);
#endif

 for (vmob = ch->GetInRoom ()->first_person; vmob; vmob = vmob_next)
 {
   vmob_next = vmob->GetNextInRoom ();
   if (! vmob->IsNpc ()
        || !can_see (vmob, ch)
        || vmob->GetFightData ()
        || ! vmob->IsAwake ())
     continue;

  /* Don't let a mob trigger itself, nor one instance of a mob
     trigger another instance. */
  if (ch->IsNpc () && ch->GetMobIndex () == vmob->GetMobIndex ())
    continue;

   if (vmob->GetMobIndex ()->m_Progtypes.IsSet (GREET_PROG))
     mprog_percent_check (vmob, ch, NULL, NULL, GREET_PROG);
   else if (vmob->GetMobIndex ()->m_Progtypes.IsSet (ALL_GREET_PROG))
     mprog_percent_check (vmob,ch,NULL,NULL,ALL_GREET_PROG);
 }
 return;

}


void mprog_hitprcnt_trigger (CCharacter *mob, CCharacter *ch)
{
	if (mob->IsNpc ()
	  && mob->GetMobIndex ()->m_Progtypes.IsSet (HITPRCNT_PROG)) {
		CPtrList	&PList = mob->GetMobIndex ()->MobPrgList;
		POSITION	pos = PList.GetHeadPosition ();
		while (pos) {
			CMobProgData	&Mprg = *(CMobProgData*) PList.GetNext (pos);

			if ((Mprg.type == HITPRCNT_PROG)
			  && ((100*mob->GetHp () / mob->GetMaxHp ()) <
			  atoi (Mprg.arglist))) {
				mprog_driver (Mprg.comlist, mob, ch, NULL, NULL, FALSE);
				break;
			}
		}
	}
}


void mprog_random_trigger (CCharacter *mob)
{
  if (mob->GetMobIndex ()->m_Progtypes.IsSet (RAND_PROG))
    mprog_percent_check (mob,NULL,NULL,NULL,RAND_PROG);

  return;
}

void mprog_time_trigger (CCharacter *mob)
{
  if (mob->GetMobIndex ()->m_Progtypes.IsSet (TIME_PROG))
       mprog_time_check (mob,NULL,NULL,NULL,TIME_PROG); 
  return;
}

void mprog_hour_trigger (CCharacter *mob)
{
  if (mob->GetMobIndex ()->m_Progtypes.IsSet (HOUR_PROG))
       mprog_time_check (mob,NULL,NULL,NULL,HOUR_PROG); 
  return;
}

void mprog_speech_trigger (char *txt, CCharacter *actor)
{

  CCharacter *vmob;

  for (vmob = actor->GetInRoom ()->first_person; vmob; vmob = vmob->GetNextInRoom ())
  {
    if (vmob->IsNpc ()
	  && vmob->GetMobIndex ()->m_Progtypes.IsSet (SPEECH_PROG))
    {
      if (actor->IsNpc () && actor->GetMobIndex () == vmob->GetMobIndex ())
        continue;
      mprog_wordlist_check (txt, vmob, actor, NULL, NULL, SPEECH_PROG);
    }
  }
  return;

}


BOOL mprog_script_trigger (CCharacter *mob)
{
	BOOL	bDoingScript = FALSE;

	if (mob->GetMobIndex ()->m_Progtypes.IsSet (SCRIPT_PROG)) {
		CPtrList	&PList = mob->GetMobIndex ()->MobPrgList;
		POSITION	pos = PList.GetHeadPosition ();
		while (pos) {
			CMobProgData	&Mprg = *(CMobProgData*) PList.GetNext (pos);
			if ((Mprg.type == SCRIPT_PROG)) {
				if (Mprg.arglist [0] == '\0' || mob->mpscriptpos != 0
				  || atoi (Mprg.arglist) == time_info.hour) {
					mprog_driver (Mprg.comlist, mob, NULL,NULL,NULL,TRUE);
					if (mob->mpscriptpos != 0)
						bDoingScript = TRUE;
				}
			}
		}
	}
	return bDoingScript;
}


void oprog_script_trigger (CObjData *obj)
{
	if (obj->GetIndex ()->HasScriptProg ()) {
		CPtrList	&PList = obj->GetIndex ()->ObjPrgList;
		POSITION	pos = PList.GetHeadPosition ();
		while (pos) {
			CMobProgData	&Oprg = *(CMobProgData*) PList.GetNext (pos);
			if ((Oprg.type == SCRIPT_PROG)) {
				if (Oprg.arglist [0] == '\0'
				  ||   obj->mpscriptpos != 0
				  ||   atoi (Oprg.arglist) == time_info.hour) {
					set_supermob (obj);
					mprog_driver (Oprg.comlist, supermob, NULL,NULL,NULL,TRUE);
					obj->mpscriptpos = supermob->mpscriptpos;
					release_supermob ();
				}
			}
		}
	}
}


void rprog_script_trigger (CRoomIndexData *room)
{
	if (room->m_Progtypes.IsSet (SCRIPT_PROG)) {
		CPtrList	&PList = room->RoomPrgList;
		POSITION	pos = PList.GetHeadPosition ();
		while (pos) {
			CMobProgData	&Rprg = *(CMobProgData*) PList.GetNext (pos);
			if ((Rprg.type == SCRIPT_PROG)) {
				if (Rprg.arglist [0] == '\0'
				  || room->mpscriptpos != 0
				  || atoi (Rprg.arglist) == time_info.hour) {
					rset_supermob (room);
					mprog_driver (Rprg.comlist, supermob, NULL,NULL,NULL,TRUE);
					room->mpscriptpos = supermob->mpscriptpos;
					release_supermob ();
				}
			}
		}
	}
}


//  Mudprogram additions begin here
void set_supermob (CObjData *obj)
{
	CRoomIndexData	*room;
	CObjData		*in_obj;
	CCharacter		*mob;
	char			buf [200];

	if (! supermob)
		supermob = create_mobile (MobTable.GetMob (3));

	mob = supermob;			// debugging

	if (! obj)
		return;

	for (in_obj = obj; in_obj->in_obj; in_obj = in_obj->in_obj)
	;

	if (in_obj->carried_by)
		room = in_obj->carried_by->GetInRoom ();
	else
		room = obj->GetInRoom ();

	if (room) {
		supermob->SetShortDescr (obj->GetShortDescr ());
		supermob->mpscriptpos = obj->mpscriptpos;

		/* Added by Jenny to allow bug messages to show the vnum
		of the object, and not just supermob's vnum */
		sprintf (buf, "Object #%d", obj->GetIndex ()->vnum);
		supermob->SetDescription (buf);

		supermob->RemoveFromRoom ();
		supermob->SendToRoom (room); 
	}
}


void release_supermob ()
{
  supermob->RemoveFromRoom ();
  supermob->SendToRoom (RoomTable.GetRoom (SysData.m_RoomPoly));
}


BOOL oprog_percent_check (CCharacter *mob, CCharacter *actor, CObjData *obj,
			 void *vo, int type)
{
	BOOL executed = FALSE;

	CPtrList	&PList = obj->GetIndex ()->ObjPrgList;
	POSITION	pos = PList.GetHeadPosition ();
	while (pos) {
		CMobProgData	&Oprg = *(CMobProgData*) PList.GetNext (pos);
		if ((Oprg.type == type)
		  && (number_percent () <= atoi (Oprg.arglist))) {
			executed = TRUE;
			mprog_driver (Oprg.comlist, mob, actor, obj, vo, FALSE);
			if (type != GREET_PROG)
				break;
		}
	}
	return executed;
}


/*
 * Triggers follow
 */


/*
 *  Hold on this
 *
void oprog_act_trigger (CCharacter *ch, CObjData *obj)
{
   set_supermob (obj);
   if (obj->GetMobIndex ()->progtypes & ACT_PROG) 
     oprog_percent_check (supermob, ch, obj, NULL, ACT_PROG);

 release_supermob ();
 return;
}
 *
 *
 */

void oprog_greet_trigger (CCharacter *ch)
{
	CObjData	*vobj;

	POSITION	pos = ch->GetInRoom ()->GetHeadContentPos ();
	while (vobj = ch->GetInRoom ()->GetNextContent (pos))
		if (vobj->GetIndex ()->HasGreetProg ()) {
			set_supermob (vobj);			// not very efficient to do here
			oprog_percent_check (supermob, ch, vobj, NULL, GREET_PROG);
			release_supermob ();
		}
}


void oprog_speech_trigger (char *txt, CCharacter *ch)
{
	CObjData	*vobj;

	// supermob is set and released in oprog_wordlist_check
	POSITION	pos = ch->GetInRoom ()->GetHeadContentPos ();
	while (vobj = ch->GetInRoom ()->GetNextContent (pos))
		if (vobj->GetIndex ()->HasSpeechProg ()) 
			oprog_wordlist_check (txt, supermob, ch, vobj, NULL,
				SPEECH_PROG, vobj);
}


/*
 * Called at top of obj_update
 * make sure to put an if (!obj) continue
 * after it
 */
void oprog_random_trigger (CObjData *obj)
{

  if (obj->GetIndex ()->HasRandProg ())
  {
     set_supermob (obj);
     oprog_percent_check (supermob,NULL,obj,NULL,RAND_PROG);
     release_supermob ();
  }
  return;
}

/*
 * in wear_obj, between each successful equip_char 
 * the subsequent return
 */
void oprog_wear_trigger (CCharacter *ch, CObjData *obj)
{
   if (obj->GetIndex ()->HasWearProg ()) 
   {
      set_supermob (obj);
      oprog_percent_check (supermob, ch, obj, NULL, WEAR_PROG);
      release_supermob ();
   }
   return;
}

BOOL oprog_use_trigger (CCharacter *ch, CObjData *obj, CCharacter *vict,
                        CObjData *targ, void *vo)
{
   BOOL executed = FALSE;

   if (obj->GetIndex ()->HasUseProg ()) 
   {
      set_supermob (obj);
      if (obj->item_type == ITEM_STAFF)
      {
        if (vict)
          executed = oprog_percent_check (supermob, ch, obj, vict, USE_PROG);
        else
          executed = oprog_percent_check (supermob, ch, obj, targ, USE_PROG);
      }
      else
      {
        executed = oprog_percent_check (supermob, ch, obj, NULL, USE_PROG);
      }
      release_supermob ();
   }
   return executed;
}

/*
 * call in remove_obj, right after unequip_char   
 * do a if (!ch) return right after, and return TRUE (?)
 * if !ch
 */
void oprog_remove_trigger (CCharacter *ch, CObjData *obj)
{
   if (obj->GetIndex ()->HasRemoveProg ()) 
   {
     set_supermob (obj);
     oprog_percent_check (supermob, ch, obj, NULL, REMOVE_PROG);
     release_supermob ();
   }
   return;
}


/*
 * call in do_sac, right before extract_obj
 */
void oprog_sac_trigger (CCharacter *ch, CObjData *obj)
{
   if (obj->GetIndex ()->HasSacProg ()) 
   {
      set_supermob (obj);
      oprog_percent_check (supermob, ch, obj, NULL, SAC_PROG);
      release_supermob ();
   }
   return;
}

/*
 * call in do_get, right before check_for_trap
 * do a if (!ch) return right after
 */
void oprog_get_trigger (CCharacter *ch, CObjData *obj)
{
   if (obj->GetIndex ()->HasGetProg ()) 
   {
      set_supermob (obj);
      oprog_percent_check (supermob, ch, obj, NULL, GET_PROG);
      release_supermob ();
   }
   return;
}

/*
 * called in damage_obj in act_obj.c
 */
void oprog_damage_trigger (CCharacter *ch, CObjData *obj)
{
   if (obj->GetIndex ()->HasDamageProg ()) 
   {
      set_supermob (obj);
      oprog_percent_check (supermob, ch, obj, NULL, DAMAGE_PROG);
      release_supermob ();
   }
   return;
}

/*
 * called in do_repair in shops.c
 */
void oprog_repair_trigger (CCharacter *ch, CObjData *obj)
{

   if (obj->GetIndex ()->HasRepairProg ()) 
   {
      set_supermob (obj);
      oprog_percent_check (supermob, ch, obj, NULL, REPAIR_PROG);
      release_supermob ();
   }
   return;
}

/*
 * call twice in do_drop, right after the act (AT_ACTION,...)
 * do a if (!ch) return right after
 */
void oprog_drop_trigger (CCharacter *ch, CObjData *obj)
{
   if (obj->GetIndex ()->HasDropProg ()) 
   {
      set_supermob (obj);
      oprog_percent_check (supermob, ch, obj, NULL, DROP_PROG);
      release_supermob ();
   }
   return;
}

/*
 * call towards end of do_examine, right before check_for_trap
 */
void oprog_examine_trigger (CCharacter *ch, CObjData *obj)
{
   if (obj->GetIndex ()->HasExamineProg ()) 
   {
      set_supermob (obj);
      oprog_percent_check (supermob, ch, obj, NULL, EXA_PROG);
      release_supermob ();
   }
   return;
}


/*
 * call in fight.c, group_gain, after (?) the obj_to_room
 */
void oprog_zap_trigger (CCharacter *ch, CObjData *obj)
{
   if (obj->GetIndex ()->HasZapProg ()) 
   {
      set_supermob (obj);
      oprog_percent_check (supermob, ch, obj, NULL, ZAP_PROG);
      release_supermob ();
   }
   return;
}

/*
 * call in levers.c, towards top of do_push_or_pull
 *  see note there 
 */
void oprog_pull_trigger (CCharacter *ch, CObjData *obj)
{
   if (obj->GetIndex ()->HasPullProg ()) 
   {
     set_supermob (obj);
     oprog_percent_check (supermob, ch, obj, NULL, PULL_PROG);
     release_supermob ();
   }
   return;
}

/*
 * call in levers.c, towards top of do_push_or_pull
 *  see note there 
 */
void oprog_push_trigger (CCharacter *ch, CObjData *obj)
{
   if (obj->GetIndex ()->HasPushProg ()) 
   {
      set_supermob (obj);
      oprog_percent_check (supermob, ch, obj, NULL, PUSH_PROG);
      release_supermob ();
   }
   return;
}

void obj_act_add (CObjData *obj);
void oprog_act_trigger (char *buf, CObjData *mobj, CCharacter *ch,
			CObjData *obj, void *vo)
{
   if (mobj->GetIndex ()->HasActProg ()) 
   {
      CMobProgActList *tmp_act;
      
      tmp_act = new CMobProgActList;
      if (mobj->mpactnum > 0)
        tmp_act->SetNext (mobj->mpact);
      else
        tmp_act->SetNext (NULL);
      
      mobj->mpact = tmp_act;
      mobj->mpact->buf = str_dup (buf);
      mobj->mpact->ch = ch;
      mobj->mpact->obj = obj;
      mobj->mpact->vo = vo;
      mobj->mpactnum++;
      obj_act_add (mobj);
   }
   return;
}

void oprog_wordlist_check (char *arg, CCharacter *mob, CCharacter *actor,
			  CObjData *obj, void *vo, int type, CObjData *iobj)
{

	char	temp1 [MAX_STRING_LENGTH];
	char	temp2 [MAX_INPUT_LENGTH];
	char	word [MAX_INPUT_LENGTH];
	char	*list;
	char	*start;
	char	*dupl;
	char	*end;
	int		i;

	CPtrList	&PList = iobj->GetIndex ()->ObjPrgList;
	POSITION	pos = PList.GetHeadPosition ();
	while (pos) {
		CMobProgData	&Oprg = *(CMobProgData*) PList.GetNext (pos);
		if (Oprg.type == type) {
			strcpy (temp1, Oprg.arglist);
			list = temp1;
			for (i = 0; i < (int) strlen (list); i++)
				list [i] = LOWER (list [i]);
			strcpy (temp2, arg);
			dupl = temp2;
			for (i = 0; i < (int) strlen (dupl); i++)
				dupl [i] = LOWER (dupl [i]);

			if ((list [0] == 'p') && (list [1] == ' ')) {
				list += 2;
				while ((start = strstr (dupl, list)))
					if ((start == dupl || * (start-1) == ' ')
					  && (* (end = start + strlen (list)) == ' '
					  || *end == '\n'
					  || *end == '\r'
					  || *end == '\0')) {
						set_supermob (iobj);
						mprog_driver (Oprg.comlist, mob, actor, obj, vo, FALSE);
						release_supermob () ;
						break;
					}
					else dupl = start+1;
			} else {
				list = one_argument (list, word);
				for (; word [0] != '\0'; list = one_argument (list, word))
					while ((start = strstr (dupl, word)))
						if ((start == dupl || * (start-1) == ' ')
						  && (* (end = start + strlen (word)) == ' '
						  || *end == '\n'
						  || *end == '\r'
						  || *end == '\0')) {
							set_supermob (iobj);
							mprog_driver (Oprg.comlist, mob, actor, obj,
								vo, FALSE);
							release_supermob ();
							break;
						}
						else dupl = start+1;
			}
		}
	}
}



/*
 *  room_prog support starts here
 *
 *
 */

void rset_supermob (CRoomIndexData *room)
{
  char buf[200];

  if (room)
  {
    supermob->SetShortDescr (room->GetName ());
    supermob->SetName (room->GetName ());
    supermob->mpscriptpos = room->mpscriptpos;

    /* Added by Jenny to allow bug messages to show the vnum
       of the room, and not just supermob's vnum */
    sprintf (buf, "Room #%d", room->vnum);
    supermob->SetDescription (buf);

    supermob->RemoveFromRoom ();
    supermob->SendToRoom (room); 
  }
}


void rprog_percent_check (CCharacter *mob, CCharacter *actor, CObjData *obj,
			 void *vo, int type)
{
	if (! mob->GetInRoom ())
		return;

	CPtrList	&PList = mob->GetInRoom ()->RoomPrgList;
	POSITION	pos = PList.GetHeadPosition ();
	while (pos) {
		CMobProgData	&Rprg = *(CMobProgData*) PList.GetNext (pos);
		if ((Rprg.type == type)
		  && (number_percent () <= atoi (Rprg.arglist))) {
			mprog_driver (Rprg.comlist, mob, actor, obj, vo, FALSE);
			if (type != ENTRY_PROG)
				break;
		}
	}
}


/*
 * Triggers follow
 */


/*
 *  Hold on this
 * Unhold. -- Alty
 */
void room_act_add (CRoomIndexData *room);
void rprog_act_trigger (char *buf, CRoomIndexData *room, CCharacter *ch,
			CObjData *obj, void *vo)
{
   if (room->m_Progtypes.IsSet (ACT_PROG))
   {
      CMobProgActList *tmp_act;
      
      tmp_act = new CMobProgActList;
      if (room->mpactnum > 0)
        tmp_act->SetNext (room->mpact);
      else
        tmp_act->SetNext (NULL);
      
      room->mpact = tmp_act;
      room->mpact->buf = str_dup (buf);
      room->mpact->ch = ch;
      room->mpact->obj = obj;
      room->mpact->vo = vo;
      room->mpactnum++;
      room_act_add (room);
   }
   return;
}
/*
 *
 */


void rprog_leave_trigger (CCharacter *ch)
{
  if (ch->GetInRoom ()->m_Progtypes.IsSet (LEAVE_PROG))
  {
    rset_supermob (ch->GetInRoom ());
    rprog_percent_check (supermob, ch, NULL, NULL, LEAVE_PROG);
    release_supermob ();
  }
  return;
}

void rprog_enter_trigger (CCharacter *ch)
{
  if (ch->GetInRoom ()->m_Progtypes.IsSet (ENTRY_PROG))
  {
    rset_supermob (ch->GetInRoom ());
    rprog_percent_check (supermob, ch, NULL, NULL, ENTRY_PROG);
    release_supermob ();
  }
  return;
}

void rprog_sleep_trigger (CCharacter *ch)
{
  if (ch->GetInRoom ()->m_Progtypes.IsSet (SLEEP_PROG)) {
    rset_supermob (ch->GetInRoom ());
    rprog_percent_check (supermob, ch, NULL, NULL, SLEEP_PROG);
    release_supermob ();
  }
  return;
}

void rprog_rest_trigger (CCharacter *ch)
{
  if (ch->GetInRoom ()->m_Progtypes.IsSet (REST_PROG)) {
    rset_supermob (ch->GetInRoom ());
    rprog_percent_check (supermob, ch, NULL, NULL, REST_PROG);
    release_supermob ();
  }
  return;
}

void rprog_rfight_trigger (CCharacter *ch)
{
  if (ch->GetInRoom ()->m_Progtypes.IsSet (FIGHT_PROG)) {
    rset_supermob (ch->GetInRoom ());
    rprog_percent_check (supermob, ch, NULL, NULL, FIGHT_PROG);
    release_supermob ();
  }
  return;
}

void rprog_death_trigger (CCharacter *killer, CCharacter *ch)
{
  if (ch->GetInRoom ()->m_Progtypes.IsSet (DEATH_PROG)) {
    rset_supermob (ch->GetInRoom ());
    rprog_percent_check (supermob, ch, NULL, NULL, DEATH_PROG);
    release_supermob ();
  }
  return;
}

void rprog_speech_trigger (char *txt, CCharacter *ch)
{
  if (ch->GetInRoom ()->m_Progtypes.IsSet (SPEECH_PROG))
  {
    /* supermob is set and released in rprog_wordlist_check */
    rprog_wordlist_check (txt, supermob, ch, NULL, NULL, SPEECH_PROG, ch->GetInRoom ());
  }
 return;
}

void rprog_random_trigger (CCharacter *ch)
{

  if (ch->GetInRoom ()->m_Progtypes.IsSet (RAND_PROG))
  {
    rset_supermob (ch->GetInRoom ());
    rprog_percent_check (supermob,ch,NULL,NULL,RAND_PROG);
    release_supermob ();
  }
  return;
}


void rprog_wordlist_check (char *arg, CCharacter *mob, CCharacter *actor,
			  CObjData *obj, void *vo, int type, CRoomIndexData *room)
{
	char	temp1 [MAX_STRING_LENGTH];
	char	temp2 [MAX_INPUT_LENGTH];
	char	word [MAX_INPUT_LENGTH];
	char	*list;
	char	*start;
	char	*dupl;
	char	*end;
	int		i;

	if (actor && ! char_died (actor) && actor->GetInRoom ())
		room = actor->GetInRoom ();

	CPtrList	&PList = room->RoomPrgList;
	POSITION	pos = PList.GetHeadPosition ();
	while (pos) {
		CMobProgData	&Rprg = *(CMobProgData*) PList.GetNext (pos);
		if (Rprg.type == type) {
			strcpy (temp1, Rprg.arglist);
			list = temp1;
			for (i = 0; i < (int) strlen (list); i++)
				list [i] = LOWER (list [i]);
			strcpy (temp2, arg);
			dupl = temp2;
			for (i = 0; i < (int) strlen (dupl); i++)
				dupl [i] = LOWER (dupl [i]);

			if ((list [0] == 'p') && (list [1] == ' ')) {
				list += 2;
				while ((start = strstr (dupl, list)))
					if ((start == dupl || * (start-1) == ' ')
					  && (* (end = start + strlen (list)) == ' '
					  || *end == '\n'
					  || *end == '\r'
					  || *end == '\0')) {
						rset_supermob (room);
						mprog_driver (Rprg.comlist, mob, actor, obj, vo, FALSE);
						release_supermob () ;
						break;
					}
					else dupl = start+1;
			} else {
				list = one_argument (list, word);
				for (; word [0] != '\0'; list = one_argument (list, word))
					while ((start = strstr (dupl, word)))
						if ((start == dupl || * (start-1) == ' ')
						  && (* (end = start + strlen (word)) == ' '
						  || *end == '\n'
						  || *end == '\r'
						  || *end == '\0')) {
							rset_supermob (room);
							mprog_driver (Rprg.comlist, mob, actor, obj,
								vo, FALSE);
							release_supermob ();
							break;
						}
						else dupl = start+1;
			}
		}
	}
}


void rprog_time_check (CCharacter *mob, CCharacter *actor, CObjData *obj,
			void *vo, int type)
{
	CRoomIndexData	*room = (CRoomIndexData*) vo;
	BOOL			bTriggerTime;

	CPtrList	&PList = room->RoomPrgList;
	POSITION	pos = PList.GetHeadPosition ();
	while (pos) {
		CMobProgData	&Rprg = *(CMobProgData*) PList.GetNext (pos);
		bTriggerTime = (time_info.hour == atoi (Rprg.arglist));

		if (! bTriggerTime) {
			if (Rprg.triggered)
				Rprg.triggered = FALSE;
			continue;
		}

		if ((Rprg.type == type)
		  && ((! Rprg.triggered) || (Rprg.type == HOUR_PROG))) {
			Rprg.triggered = TRUE;
			mprog_driver (Rprg.comlist, mob, actor, obj, vo, FALSE);
		}
	}
}


void rprog_time_trigger (CCharacter *ch)
{
  if (ch->GetInRoom ()->m_Progtypes.IsSet (TIME_PROG))
  {
    rset_supermob (ch->GetInRoom ());
    rprog_time_check (supermob, NULL, NULL, ch->GetInRoom (), TIME_PROG);
    release_supermob ();
  }
  return;
}

void rprog_hour_trigger (CCharacter *ch)
{
  if (ch->GetInRoom ()->m_Progtypes.IsSet (HOUR_PROG))
  {
    rset_supermob (ch->GetInRoom ());
    rprog_time_check (supermob, NULL, NULL, ch->GetInRoom (), HOUR_PROG);
    release_supermob ();
  }
  return;
}


// Written by Jenny, Nov 29/95
void progbug (const char *str, CCharacter *mob)
{
	char	buf [MAX_STRING_LENGTH];

	// Check if we're dealing with supermob, which means the bug occurred
	// in a room or obj prog.
	if (mob->GetMobIndex ()->vnum == 3) {
		// It's supermob.  In set_supermob and rset_supermob, the description
		// was set to indicate the object or room, so we just need to show
		// the description in the bug message.
		sprintf (buf, "%s, %s.", str, 
			mob->GetDescription () == NULL ? "(unknown)" : mob->GetDescription ());
	}
	else
		sprintf (buf, "%s, Mob #%d.", str, mob->GetMobIndex ()->vnum);

	bug (buf);
}


/* Room act prog updates.  Use a separate list cuz we dont really wanna go
   thru 5-10000 rooms every pulse.. can we say lag? -- Alty */

void room_act_add (CRoomIndexData *room)
{
	CActProgData	*runner;

	for (runner = room_act_list; runner; runner = runner->GetNext ())
		if (runner->vo == room)
			return;

	runner = new CActProgData;
	runner->vo = room;
	runner->SetNext (room_act_list);
	room_act_list = runner;
}


void room_act_update (void)
{
	CActProgData	*runner;
	CMobProgActList	*mpact;

	while ((runner = room_act_list) != NULL) {
		CRoomIndexData	*room = (CRoomIndexData*) runner->vo;

		while ((mpact = room->mpact) != NULL) {
			if (mpact->ch->GetInRoom () == room)
				rprog_wordlist_check (mpact->buf, supermob, mpact->ch,
					mpact->obj, mpact->vo, ACT_PROG, room);
			room->mpact = mpact->GetNext ();
			delete mpact;
		}
		room->mpact = NULL;
		room->mpactnum = 0;
		room_act_list = runner->GetNext ();
		delete runner;
	}
}


void obj_act_add (CObjData *obj)
{
	CActProgData	*runner;

	for (runner = obj_act_list; runner; runner = runner->GetNext ())
		if (runner->vo == obj)
		return;

	runner = new CActProgData;
	runner->vo = obj;
	runner->SetNext (obj_act_list);
	obj_act_list = runner;
}


void obj_act_update ()
{
	CActProgData	*runner;
	CMobProgActList	*mpact;

	while ((runner = obj_act_list) != NULL) {
		CObjData	*obj = (CObjData*) runner->vo;

		while ((mpact = obj->mpact) != NULL) {
			oprog_wordlist_check (mpact->buf, supermob, mpact->ch,
				mpact->obj, mpact->vo, ACT_PROG, obj);
			obj->mpact = mpact->GetNext ();
			delete mpact->buf;
			delete mpact;
		}
		obj->mpact = NULL;
		obj->mpactnum = 0;
		obj_act_list = runner->GetNext ();
		delete runner;
	}
}