dbna/clans/
dbna/councils/
dbna/deity/
dbna/gods/
dbna/houses/
dbna/space/
/****************************************************************************
 * [S]imulated [M]edieval [A]dventure multi[U]ser [G]ame      |   \\._.//   *
 * -----------------------------------------------------------|   (0...0)   *
 * SMAUG 1.4 (C) 1994, 1995, 1996, 1998  by Derek Snider      |    ).:.(    *
 * -----------------------------------------------------------|    {o o}    *
 * SMAUG code team: Thoric, Altrag, Blodkai, Narn, Haus,      |   / ' ' \   *
 * Scryn, Rennard, Swordbearer, Gorog, Grishnakh, Nivek,      |~'~.VxvxV.~'~*
 * Tricops and Fireblade                                      |             *
 ****************************************************************************
 *  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 <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "mud.h"

/*
 * Recursive function used by the carryingvnum ifcheck.
 * It loops thru all objects belonging to a char (in nested containers)
 * and returns TRUE if it finds a matching vnum.
 * I declared it static to limit its scope to this file.  --Gorog
 *
 * This recursive function works by using the following method for
 * traversing the nodes in a binary tree:
 *
 *    Start at the root node
 *    if there is a child then visit the child
 *       if there is a sibling then visit the sibling
 *    else
 *    if there is a sibling then visit the sibling
 */
static bool carryingvnum_visit( CHAR_DATA * ch, OBJ_DATA * obj, int vnum )
{
/*
   pager_printf(ch, "***obj=%s vnum=%d\n\r", obj->name, obj->pIndexData->vnum );
*/
  if( obj->wear_loc == -1 && obj->pIndexData->vnum == vnum )
    return TRUE;
  if( obj->first_content )  /* node has a child? */
  {
    if( carryingvnum_visit( ch, obj->first_content, vnum ) )
      return TRUE;
    if( obj->next_content ) /* node has a sibling? */
      if( carryingvnum_visit( ch, obj->next_content, vnum ) )
        return TRUE;
  }
  else if( obj->next_content )  /* node has a sibling? */
    if( carryingvnum_visit( ch, obj->next_content, vnum ) )
      return TRUE;
  return FALSE;
}

/*  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
 *  all moved to mud.h
 */

int mprog_do_command( char *cmnd, CHAR_DATA * mob, CHAR_DATA * actor,
                      OBJ_DATA * obj, void *vo, CHAR_DATA * rndm, bool ignore, bool ignore_ors );

/*
 *  Mudprogram additions
 */
CHAR_DATA *supermob;
struct act_prog_data *room_act_list;
struct act_prog_data *obj_act_list;
struct act_prog_data *mob_act_list;

/*
* Global variables to handle sleeping mud progs.
*/
MPSLEEP_DATA *first_mpsleep = NULL;
MPSLEEP_DATA *last_mpsleep = NULL;
MPSLEEP_DATA *current_mpsleep = NULL;


/*
 * Local function prototypes
 */

char *mprog_next_command args( ( char *clist ) );
bool mprog_seval args( ( char *lhs, char *opr, char *rhs, CHAR_DATA * mob ) );
bool mprog_veval args( ( int lhs, char *opr, int rhs, CHAR_DATA * mob ) );
bool mprog_vevall args( ( long double lhs, char *opr, long double rhs, CHAR_DATA * mob ) );
int mprog_do_ifcheck args( ( char *ifcheck, CHAR_DATA * mob,
                             CHAR_DATA * actor, OBJ_DATA * obj, void *vo, CHAR_DATA * rndm ) );
void mprog_translate args( ( char ch, char *t, CHAR_DATA * mob,
                             CHAR_DATA * actor, OBJ_DATA * obj, void *vo, CHAR_DATA * rndm ) );
void mprog_driver args( ( char *com_list, CHAR_DATA * mob, CHAR_DATA * actor, OBJ_DATA * obj, void *vo, bool single_step ) );

bool mprog_keyword_check args( ( const char *argu, const char *argl ) );


void oprog_wordlist_check( char *arg, CHAR_DATA * mob, CHAR_DATA * actor, OBJ_DATA * obj, void *vo, int type,
                           OBJ_DATA * iobj );
void set_supermob( OBJ_DATA * obj );
bool oprog_percent_check( CHAR_DATA * mob, CHAR_DATA * actor, OBJ_DATA * obj, void *vo, int type );
void rprog_percent_check( CHAR_DATA * mob, CHAR_DATA * actor, OBJ_DATA * obj, void *vo, int type );
void rprog_wordlist_check( char *arg, CHAR_DATA * mob, CHAR_DATA * actor,
                           OBJ_DATA * obj, void *vo, int type, ROOM_INDEX_DATA * room );

/***************************************************************************
 * 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

#define RID ROOM_INDEX_DATA

void init_supermob(  )
{
  RID *office;

  supermob = create_mobile( get_mob_index( 3 ) );
  office = get_room_index( 3 );
  char_to_room( supermob, office );

#ifdef NOTDEFD
  CREATE( supermob, CHAR_DATA, 1 );
  clear_char( supermob );

  xSET_BIT( supermob->act, ACT_IS_NPC );
  supermob->name = STRALLOC( "supermob" );
  supermob->short_descr = STRALLOC( "supermob" );
  supermob->long_descr = STRALLOC( "supermob is here" );

  CREATE( supermob_index, MOB_INDEX_DATA, 1 )
#endif
}


#undef RID


/* Used to get sequential lines of a multi line string (separated by "\n\r")
 * Thus its 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 *pointer = clist;

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

  return ( pointer );

}

/* 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( char *lhs, char *opr, char *rhs, CHAR_DATA * 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, CHAR_DATA * 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;

}

bool mprog_vevall( long double lhs, char *opr, long double rhs, CHAR_DATA * 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 ( ( int )lhs & ( int )rhs );
  if( !str_cmp( opr, "|" ) )
    return ( ( int )lhs | ( int )rhs );

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

  return 0;

}

#define isoperator(c) ((c)=='='||(c)=='<'||(c)=='>'||(c)=='!'||(c)=='&'||(c)=='|')
#define MAX_IF_ARGS 6
/*
 * 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, CHAR_DATA * mob, CHAR_DATA * actor, OBJ_DATA * obj, void *vo, CHAR_DATA * rndm )
{
  char buf[MAX_STRING_LENGTH];
  char opr[MAX_INPUT_LENGTH];
  char *chck, *cvar;
  char *argv[MAX_IF_ARGS];
  char *rval = "";
  char *q, *p = buf;
  int argc = 0;
  CHAR_DATA *chkchar = NULL;
  OBJ_DATA *chkobj = NULL;
  int lhsvl, rhsvl = 0;

  if( !*ifcheck )
  {
    progbug( "Null ifcheck", mob );
    return BERR;
  }

  /*
   * New parsing by Thoric to allow for multiple arguments inside the
   * brackets, ie: if leveldiff($n, $i) > 10
   * It's also smaller, cleaner and probably faster
   */
  strcpy( buf, ifcheck );
  opr[0] = '\0';
  while( isspace( *p ) )
    ++p;
  argv[argc++] = p;
  while( isalnum( *p ) )
    ++p;
  while( isspace( *p ) )
    *p++ = '\0';
  if( *p != '(' )
  {
    progbug( "Ifcheck Syntax error (missing left bracket)", mob );
    return BERR;
  }

  *p++ = '\0';
  /*
   * Need to check for spaces or if name( $n ) isn't legal --Shaddai 
   */
  while( isspace( *p ) )
    *p++ = '\0';
  for( ;; )
  {
    argv[argc++] = p;
    while( *p == '$' || isalnum( *p ) )
      ++p;
    while( isspace( *p ) )
      *p++ = '\0';
    switch ( *p )
    {
      case ',':
        *p++ = '\0';
        while( isspace( *p ) )
          *p++ = '\0';
        if( argc >= MAX_IF_ARGS )
        {
          while( *p && *p != ')' )
            ++p;
          if( *p )
            *p++ = '\0';
          while( isspace( *p ) )
            *p++ = '\0';
          goto doneargs;
        }
        break;
      case ')':
        *p++ = '\0';
        while( isspace( *p ) )
          *p++ = '\0';
        goto doneargs;
        break;
      default:
        progbug( "Ifcheck Syntax warning (missing right bracket)", mob );
        goto doneargs;
        break;
    }
  }
doneargs:
  q = p;
  while( isoperator( *p ) )
    ++p;
  strncpy( opr, q, p - q );
  opr[p - q] = '\0';
  while( isspace( *p ) )
    *p++ = '\0';
  rval = p;
  /*
   * while ( *p && !isspace(*p) ) ++p;
   */
  while( *p )
    ++p;
  *p = '\0';

  chck = argv[0] ? argv[0] : "";
  cvar = argv[1] ? argv[1] : "";

  /*
   * 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 = ( CHAR_DATA * ) vo;
        break;
      case 'r':
        chkchar = rndm;
        break;
      case 'o':
        chkobj = obj;
        break;
      case 'p':
        chkobj = ( OBJ_DATA * ) 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 );
    ROOM_INDEX_DATA *room;

    if( !idx )
    {
      if( !mob->in_room )
      {
        progbug( "'economy' ifcheck: mob in NULL room with no room vnum " "argument", mob );
        return BERR;
      }
      room = mob->in_room;
    }
    else
      room = get_room_index( idx );
    if( !room )
    {
      progbug( "Bad room vnum passed to 'economy'", mob );
      return BERR;
    }
    return mprog_veval( room->area->economy, opr, atoi( rval ), mob );
  }
  if( !str_cmp( chck, "mobinarea" ) )
  {
    int vnum = atoi( cvar );
    int lhsvl;
    int world_count;
    int found_count;
    CHAR_DATA *tmob;
    MOB_INDEX_DATA *m_index;

    if( vnum < 1 || vnum > MAX_VNUMS )
    {
      progbug( "Bad vnum to 'mobinarea'", mob );
      return BERR;
    }

    m_index = get_mob_index( vnum );

    if( !m_index )
      world_count = 0;
    else
      world_count = m_index->count;

    lhsvl = 0;
    found_count = 0;

    for( tmob = first_char; tmob && found_count != world_count; tmob = tmob->next )
    {
      if( IS_NPC( tmob ) && tmob->pIndexData->vnum == vnum )
      {
        found_count++;

        if( tmob->in_room->area == mob->in_room->area )
          lhsvl++;
      }
    }
    rhsvl = atoi( rval );

    /*
     * Changed below from 1 to 0 
     */
    if( rhsvl < 0 )
      rhsvl = 0;
    if( !*opr )
      strcpy( opr, "==" );

    return mprog_veval( lhsvl, opr, rhsvl, mob );
  }

  if( !str_cmp( chck, "mobinroom" ) )
  {
    int vnum = atoi( cvar );
    int lhsvl;
    CHAR_DATA *oMob;

    if( vnum < 1 || vnum > MAX_VNUMS )
    {
      progbug( "Bad vnum to 'mobinroom'", mob );
      return BERR;
    }
    lhsvl = 0;
    for( oMob = mob->in_room->first_person; oMob; oMob = oMob->next_in_room )
      if( IS_NPC( oMob ) && oMob->pIndexData->vnum == vnum )
        lhsvl++;
    rhsvl = atoi( rval );
    /*
     * Changed below from 1 to 0 
     */
    if( rhsvl < 0 )
      rhsvl = 0;
    if( !*opr )
      strcpy( opr, "==" );
    return mprog_veval( lhsvl, opr, rhsvl, mob );
  }

  if( !str_cmp( chck, "mobinworld" ) )
  {
    int vnum = atoi( cvar );
    int lhsvl;
    MOB_INDEX_DATA *m_index;

    if( vnum < 1 || vnum > MAX_VNUMS )
    {
      progbug( "Bad vnum to 'mobinworld'", mob );
      return BERR;
    }

    m_index = get_mob_index( vnum );

    if( !m_index )
      lhsvl = 0;
    else
      lhsvl = m_index->count;

    rhsvl = atoi( rval );
    /*
     * Changed below from 1 to 0 
     */

    if( rhsvl < 0 )
      rhsvl = 0;
    if( !*opr )
      strcpy( opr, "==" );

    return mprog_veval( lhsvl, opr, rhsvl, mob );
  }
  if( !str_cmp( chck, "timeskilled" ) )
  {
    MOB_INDEX_DATA *pMob;

    if( chkchar )
      pMob = chkchar->pIndexData;
    else if( !( pMob = get_mob_index( atoi( cvar ) ) ) )
    {
      progbug( "TimesKilled ifcheck: bad vnum", mob );
      return BERR;
    }
    return mprog_veval( pMob->killed, opr, atoi( rval ), mob );
  }
  if( !str_cmp( chck, "ovnumhere" ) )
  {
    OBJ_DATA *pObj;
    int vnum = atoi( cvar );

    if( vnum < 1 || vnum > MAX_VNUMS )
    {
      progbug( "OvnumHere: bad vnum", mob );
      return BERR;
    }
    lhsvl = 0;

    for( pObj = mob->first_carrying; pObj; pObj = pObj->next_content )
      if( pObj->pIndexData->vnum == vnum )
        lhsvl += pObj->count;
    for( pObj = mob->in_room->first_content; pObj; pObj = pObj->next_content )
      if( pObj->pIndexData->vnum == vnum )
        lhsvl += pObj->count;
    rhsvl = is_number( rval ) ? atoi( rval ) : -1;
    /*
     * Changed from 1 to 0 
     */
    if( rhsvl < 0 )
      rhsvl = 0;
    if( !*opr )
      strcpy( opr, "==" );
    return mprog_veval( lhsvl, opr, rhsvl, mob );
  }
  if( !str_cmp( chck, "otypehere" ) )
  {
    OBJ_DATA *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;

    for( pObj = mob->first_carrying; pObj; pObj = pObj->next_content )
      if( pObj->item_type == type )
        lhsvl += pObj->count;
    for( pObj = mob->in_room->first_content; pObj; pObj = pObj->next_content )
      if( pObj->item_type == type )
        lhsvl += pObj->count;
    rhsvl = is_number( rval ) ? atoi( rval ) : -1;
    /*
     * Change below from 1 to 0 
     */
    if( rhsvl < 0 )
      rhsvl = 0;
    if( !*opr )
      strcpy( opr, "==" );
    return mprog_veval( lhsvl, opr, rhsvl, mob );
  }
  if( !str_cmp( chck, "ovnumroom" ) )
  {
    OBJ_DATA *pObj;
    int vnum = atoi( cvar );

    if( vnum < 1 || vnum > MAX_VNUMS )
    {
      progbug( "OvnumRoom: bad vnum", mob );
      return BERR;
    }
    lhsvl = 0;
    for( pObj = mob->in_room->first_content; pObj; pObj = pObj->next_content )
      if( pObj->pIndexData->vnum == vnum )
        lhsvl += pObj->count;
    rhsvl = is_number( rval ) ? atoi( rval ) : -1;
    /*
     * Changed below from 1 to 0 so can check for == no items Shaddai 
     */
    if( rhsvl < 0 )
      rhsvl = 0;
    if( !*opr )
      strcpy( opr, "==" );
    return mprog_veval( lhsvl, opr, rhsvl, mob );
  }
  if( !str_cmp( chck, "otyperoom" ) )
  {
    OBJ_DATA *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;

    for( pObj = mob->in_room->first_content; pObj; pObj = pObj->next_content )
      if( pObj->item_type == type )
        lhsvl += pObj->count;
    rhsvl = is_number( rval ) ? atoi( rval ) : -1;
    /*
     * Changed below from 1 to 0 
     */
    if( rhsvl < 0 )
      rhsvl = 0;
    if( !*opr )
      strcpy( opr, "==" );
    return mprog_veval( lhsvl, opr, rhsvl, mob );
  }
  if( !str_cmp( chck, "ovnumcarry" ) )
  {
    OBJ_DATA *pObj;
    int vnum = atoi( cvar );

    if( vnum < 1 || vnum > MAX_VNUMS )
    {
      progbug( "OvnumCarry: bad vnum", mob );
      return BERR;
    }
    lhsvl = 0;

    for( pObj = mob->first_carrying; pObj; pObj = pObj->next_content )
      if( pObj->pIndexData->vnum == vnum )
        lhsvl += pObj->count;
    rhsvl = is_number( rval ) ? atoi( rval ) : -1;

    if( rhsvl < 0 )
      rhsvl = 0;
    if( !*opr )
      strcpy( opr, "==" );
    return mprog_veval( lhsvl, opr, rhsvl, mob );
  }
  if( !str_cmp( chck, "otypecarry" ) )
  {
    OBJ_DATA *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;
    for( pObj = mob->first_carrying; pObj; pObj = pObj->next_content )
      if( pObj->item_type == type )
        lhsvl += pObj->count;
    rhsvl = is_number( rval ) ? atoi( rval ) : -1;
    /*
     * Changed below from 1 to 0 Shaddai 
     */
    if( rhsvl < 0 )
      rhsvl = 0;
    if( !*opr )
      strcpy( opr, "==" );
    return mprog_veval( lhsvl, opr, rhsvl, mob );
  }
  if( !str_cmp( chck, "ovnumwear" ) )
  {
    OBJ_DATA *pObj;
    int vnum = atoi( cvar );

    if( vnum < 1 || vnum > MAX_VNUMS )
    {
      progbug( "OvnumWear: bad vnum", mob );
      return BERR;
    }
    lhsvl = 0;
    for( pObj = mob->first_carrying; pObj; pObj = pObj->next_content )
      if( pObj->wear_loc != WEAR_NONE && pObj->pIndexData->vnum == vnum )
        lhsvl += pObj->count;
    rhsvl = is_number( rval ) ? atoi( rval ) : -1;
    /*
     * Changed below from 1 to 0 
     */
    if( rhsvl < 0 )
      rhsvl = 0;
    if( !*opr )
      strcpy( opr, "==" );
    return mprog_veval( lhsvl, opr, rhsvl, mob );
  }
  if( !str_cmp( chck, "otypewear" ) )
  {
    OBJ_DATA *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;
    for( pObj = mob->first_carrying; pObj; pObj = pObj->next_content )
      if( pObj->wear_loc != WEAR_NONE && pObj->item_type == type )
        lhsvl += pObj->count;
    rhsvl = is_number( rval ) ? atoi( rval ) : -1;
    /*
     * Changed below from 1 to 0 so can have == 0 Shaddai 
     */
    if( rhsvl < 0 )
      rhsvl = 0;
    if( !*opr )
      strcpy( opr, "==" );
    return mprog_veval( lhsvl, opr, rhsvl, mob );
  }
  if( !str_cmp( chck, "ovnuminv" ) )
  {
    OBJ_DATA *pObj;
    int vnum = atoi( cvar );

    if( vnum < 1 || vnum > MAX_VNUMS )
    {
      progbug( "OvnumInv: bad vnum", mob );
      return BERR;
    }
    lhsvl = 0;

    for( pObj = mob->first_carrying; pObj; pObj = pObj->next_content )
      if( pObj->wear_loc == WEAR_NONE && pObj->pIndexData->vnum == vnum )
        lhsvl += pObj->count;
    rhsvl = is_number( rval ) ? atoi( rval ) : -1;
    /*
     * Changed 1 to 0 so can have == 0 
     */
    if( rhsvl < 0 )
      rhsvl = 0;
    if( !*opr )
      strcpy( opr, "==" );
    return mprog_veval( lhsvl, opr, rhsvl, mob );
  }
  if( !str_cmp( chck, "otypeinv" ) )
  {
    OBJ_DATA *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;
    for( pObj = mob->first_carrying; pObj; pObj = pObj->next_content )
      if( pObj->wear_loc == WEAR_NONE && pObj->item_type == type )
        lhsvl += pObj->count;
    rhsvl = is_number( rval ) ? atoi( rval ) : -1;
    /*
     * Changed below from 1 to 0 for == 0 Shaddai 
     */
    if( rhsvl < 0 )
      rhsvl = 0;
    if( !*opr )
      strcpy( opr, "==" );
    return mprog_veval( lhsvl, opr, rhsvl, mob );
  }
  if( chkchar )
  {
    if( !str_cmp( chck, "ispacifist" ) )
    {
      return ( IS_NPC( chkchar ) && xIS_SET( chkchar->act, ACT_PACIFIST ) );
    }
    if( !str_cmp( chck, "isdead" ) )
    {
      return ( xIS_SET( chkchar->affected_by, AFF_DEAD ) );
    }
    if( !str_cmp( chck, "ismobinvis" ) )
    {
      return ( IS_NPC( chkchar ) && xIS_SET( chkchar->act, ACT_MOBINVIS ) );
    }
    if( !str_cmp( chck, "mobinvislevel" ) )
    {
      return ( IS_NPC( chkchar ) ? mprog_veval( chkchar->mobinvis, opr, atoi( rval ), mob ) : FALSE );
    }
    if( !str_cmp( chck, "ispc" ) )
    {
      return IS_NPC( chkchar ) ? FALSE : TRUE;
    }
    if( !str_cmp( chck, "isnpc" ) )
    {
      return IS_NPC( chkchar ) ? TRUE : FALSE;
    }
    if( !str_cmp( chck, "cansee" ) )
    {
      return can_see( mob, chkchar );
    }
    if( !str_cmp( chck, "ispassage" ) )
    {
      if( find_door( chkchar, rval, TRUE ) == NULL )
        return FALSE;
      else
        return TRUE;
    }
    if( !str_cmp( chck, "isopen" ) )
    {
      EXIT_DATA *pexit;

      if( ( pexit = find_door( chkchar, rval, TRUE ) ) == NULL )
        return FALSE;
      if( !IS_SET( pexit->exit_info, EX_CLOSED ) )
        return TRUE;
      return FALSE;
    }
    if( !str_cmp( chck, "islocked" ) )
    {
      EXIT_DATA *pexit;

      if( ( pexit = find_door( chkchar, rval, TRUE ) ) == NULL )
        return FALSE;
      if( IS_SET( pexit->exit_info, EX_LOCKED ) )
        return TRUE;
      return FALSE;
    }
    if( !str_cmp( chck, "ispkill" ) )
    {
      return IS_PKILL( chkchar ) ? TRUE : FALSE;
    }
    if( !str_cmp( chck, "isdevoted" ) )
    {
      return IS_DEVOTED( chkchar ) ? TRUE : FALSE;
    }
    if( !str_cmp( chck, "canpkill" ) )
    {
      return CAN_PKILL( chkchar ) ? TRUE : FALSE;
    }
    if( !str_cmp( chck, "ismounted" ) )
    {
      return ( chkchar->position == POS_MOUNTED );
    }
    if( !str_cmp( chck, "ismorphed" ) )
    {
      return ( chkchar->morph != NULL ) ? TRUE : FALSE;
    }
    if( !str_cmp( chck, "isnuisance" ) )
    {
      return ( !IS_NPC( chkchar ) ? chkchar->pcdata->nuisance ? TRUE : FALSE : FALSE );
    }
    if( !str_cmp( chck, "isgood" ) )
    {
      return IS_GOOD( chkchar ) ? TRUE : FALSE;
    }
    if( !str_cmp( chck, "isneutral" ) )
    {
      return IS_NEUTRAL( chkchar ) ? TRUE : FALSE;
    }
    if( !str_cmp( chck, "isevil" ) )
    {
      return IS_EVIL( chkchar ) ? TRUE : FALSE;
    }
    if( !str_cmp( chck, "isfight" ) )
    {
      return who_fighting( chkchar ) ? TRUE : FALSE;
    }
    if( !str_cmp( chck, "isimmort" ) )
    {
      return ( get_trust( chkchar ) >= LEVEL_IMMORTAL );
    }
    if( !str_cmp( chck, "ischarmed" ) )
    {
      return IS_AFFECTED( chkchar, AFF_CHARM ) ? TRUE : FALSE;
    }
    if( !str_cmp( chck, "isflying" ) )
    {
      return IS_AFFECTED( chkchar, AFF_FLYING ) ? TRUE : FALSE;
    }
    if( !str_cmp( chck, "isthief" ) )
    {
      return ( !IS_NPC( chkchar ) && xIS_SET( chkchar->act, PLR_THIEF ) );
    }
    if( !str_cmp( chck, "isattacker" ) )
    {
      return ( !IS_NPC( chkchar ) && xIS_SET( chkchar->act, PLR_ATTACKER ) );
    }
    if( !str_cmp( chck, "iskiller" ) )
    {
      return ( !IS_NPC( chkchar ) && xIS_SET( chkchar->act, PLR_KILLER ) );
    }
    if( !str_cmp( chck, "isfollow" ) )
    {
      return ( chkchar->master != NULL && chkchar->master->in_room == chkchar->in_room );
    }
    if( !str_cmp( chck, "isaffected" ) )
    {
      int value = get_aflag( rval );

      if( value < 0 || value > MAX_BITS )
      {
        progbug( "Unknown affect being checked", mob );
        return BERR;
      }
      return IS_AFFECTED( chkchar, value ) ? TRUE : FALSE;
    }
    if( !str_cmp( chck, "numfighting" ) )
    {
      return mprog_veval( chkchar->num_fighting - 1, opr, atoi( rval ), mob );
    }
    if( !str_cmp( chck, "hitprcnt" ) )
    {
      return mprog_veval( chkchar->hit / chkchar->max_hit, opr, atoi( rval ), mob );
    }
    if( !str_cmp( chck, "inroom" ) )
    {
      return mprog_veval( chkchar->in_room->vnum, opr, atoi( rval ), mob );
    }
    if( !str_cmp( chck, "wasinroom" ) )
    {
      if( !chkchar->was_in_room )
        return FALSE;
      return mprog_veval( chkchar->was_in_room->vnum, opr, atoi( rval ), mob );
    }
    if( !str_cmp( chck, "norecall" ) )
    {
      return xIS_SET( chkchar->in_room->room_flags, ROOM_NO_RECALL ) ? TRUE : FALSE;
    }
    if( !str_cmp( chck, "sex" ) )
    {
      return mprog_veval( chkchar->sex, opr, atoi( rval ), mob );
    }
    if( !str_cmp( chck, "position" ) )
    {
      return mprog_veval( chkchar->position, opr, atoi( rval ), mob );
    }
    if( !str_cmp( chck, "doingquest" ) )
    {
      return IS_NPC( chkchar ) ? FALSE : mprog_veval( chkchar->pcdata->quest_number, opr, atoi( rval ), mob );
    }
    if( !str_cmp( chck, "ishelled" ) )
    {
      return IS_NPC( chkchar ) ? FALSE : mprog_veval( chkchar->pcdata->release_date, opr, atoi( rval ), mob );
    }
    if( !str_cmp( chck, "age" ) )
    {
      return mprog_veval( get_newage( chkchar ), opr, atoi( rval ), mob );
    }
    if( !str_cmp( chck, "level" ) )
    {
      return mprog_veval( get_trust( chkchar ), opr, atoi( rval ), mob );
    }
    if( !str_cmp( chck, "goldamt" ) )
    {
      return mprog_veval( chkchar->gold, opr, atoi( rval ), mob );
    }
    if( !str_cmp( chck, "class" ) )
    {
      if( IS_NPC( chkchar ) )
        return mprog_seval( npc_class[chkchar->class], opr, rval, mob );
      return mprog_seval( ( char * )class_table[chkchar->class]->who_name, opr, rval, mob );
    }
    if( !str_cmp( chck, "weight" ) )
    {
      return mprog_veval( chkchar->carry_weight, opr, atoi( rval ), mob );
    }
    if( !str_cmp( chck, "hostdesc" ) )
    {
      if( IS_NPC( chkchar ) || !chkchar->desc->host )
        return FALSE;
      return mprog_seval( chkchar->desc->host, opr, rval, mob );
    }
    if( !str_cmp( chck, "multi" ) )
    {
      CHAR_DATA *ch;
      int lhsvl = 0;

      for( ch = first_char; ch; ch = ch->next )
        if( !IS_NPC( chkchar ) && !IS_NPC( ch )
            && ch->desc && chkchar->desc && QUICKMATCH( ch->desc->host, chkchar->desc->host ) )
          lhsvl++;
      rhsvl = atoi( rval );
      if( rhsvl < 0 )
        rhsvl = 0;
      if( !*opr )
        strcpy( opr, "==" );
      return mprog_veval( lhsvl, opr, rhsvl, mob );
    }
    if( !str_cmp( chck, "race" ) )
    {
      if( IS_NPC( chkchar ) )
        return mprog_seval( npc_race[chkchar->race], opr, rval, mob );

      if( is_android( chkchar ) || wearing_sentient_chip( chkchar ) )
        return mprog_seval( "android", opr, rval, mob );
      else if( is_saiyan( chkchar ) )
        return mprog_seval( "saiyan", opr, rval, mob );
      else if( is_human( chkchar ) )
        return mprog_seval( "human", opr, rval, mob );
      else if( is_namek( chkchar ) )
        return mprog_seval( "namek", opr, rval, mob );
      else if( is_hb( chkchar ) )
        return mprog_seval( "halfbreed", opr, rval, mob );
      else
        return mprog_seval( ( char * )race_table[chkchar->race]->race_name, opr, rval, mob );
    }
    if( !str_cmp( chck, "morph" ) )
    {
      if( chkchar->morph == NULL )
        return FALSE;
      if( chkchar->morph->morph == NULL )
        return FALSE;
      return mprog_veval( chkchar->morph->morph->vnum, opr, rhsvl, mob );
    }
    if( !str_cmp( chck, "nuisance" ) )
    {
      if( IS_NPC( chkchar ) || !chkchar->pcdata->nuisance )
        return FALSE;
      return mprog_veval( chkchar->pcdata->nuisance->flags, opr, rhsvl, mob );
    }
    if( !str_cmp( chck, "clan" ) )
    {
      if( IS_NPC( chkchar ) || !chkchar->pcdata->clan )
        return FALSE;
      return mprog_seval( chkchar->pcdata->clan->short_name, opr, rval, mob );
    }
    /*
     * Check added to see if the person isleader of == clan Shaddai 
     */
    if( !str_cmp( chck, "isleader" ) )
    {
      CLAN_DATA *temp;
      if( IS_NPC( chkchar ) )
        return FALSE;
      if( ( temp = get_clan( rval ) ) == NULL )
        return FALSE;
      if( is_leader( chkchar ) )
        return TRUE;
      else
        return FALSE;
    }

    /*
     * Is char wearing some eq on a specific wear loc?  -- Gorog 
     */
    if( !str_cmp( chck, "wearing" ) )
    {
      OBJ_DATA *obj;
      int i = 0;
      for( obj = chkchar->first_carrying; obj; obj = obj->next_content )
      {
        i++;
/*
		if ( chkchar==obj->carried_by )
		    pager_printf(chkchar, "count=%d obj name=%s\n\r", i, obj->name);
*/
        if( chkchar == obj->carried_by && obj->wear_loc > -1 && !str_cmp( rval, item_w_flags[obj->wear_loc] ) )
          return TRUE;
      }
      return FALSE;
    }
    /*
     * Is char wearing some specific vnum?  -- Gorog 
     */
    if( !str_cmp( chck, "wearingvnum" ) )
    {
      OBJ_DATA *obj;

      if( !is_number( rval ) )
        return FALSE;
      for( obj = chkchar->first_carrying; obj; obj = obj->next_content )
      {
        if( chkchar == obj->carried_by && obj->wear_loc > -1 && obj->pIndexData->vnum == atoi( rval ) )
          return TRUE;
      }
      return FALSE;
    }

    /*
     * Is char carrying a specific piece of eq?  -- Gorog 
     */
    if( !str_cmp( chck, "carryingvnum" ) )
    {
      int vnum;

      if( !is_number( rval ) )
        return FALSE;
      vnum = atoi( rval );
      if( !chkchar->first_carrying )
        return FALSE;
      return ( carryingvnum_visit( chkchar, chkchar->first_carrying, vnum ) );
    }

    /*
     * Check added to see if the person isleader of == clan Gorog 
     */
    if( !str_cmp( chck, "isclanleader" ) )
    {
      CLAN_DATA *temp;
      if( IS_NPC( chkchar ) )
        return FALSE;
      if( ( temp = get_clan( rval ) ) == NULL )
        return FALSE;
      if( is_leader( chkchar ) )
        return TRUE;
      else
        return FALSE;
    }
    if( !str_cmp( chck, "isclan1" ) )
    {
      CLAN_DATA *temp;
      if( IS_NPC( chkchar ) )
        return FALSE;
      if( ( temp = get_clan( rval ) ) == NULL )
        return FALSE;
      if( is_leader( chkchar ) )
        return TRUE;
      else
        return FALSE;
    }
    if( !str_cmp( chck, "isclan2" ) )
    {
      CLAN_DATA *temp;
      if( IS_NPC( chkchar ) )
        return FALSE;
      if( ( temp = get_clan( rval ) ) == NULL )
        return FALSE;
      if( is_leader( chkchar ) )
        return TRUE;
      else
        return FALSE;
    }
    if( !str_cmp( chck, "council" ) )
    {
      if( IS_NPC( chkchar ) || !chkchar->pcdata->council )
        return FALSE;
      return mprog_seval( chkchar->pcdata->council->name, opr, rval, mob );
    }
    if( !str_cmp( chck, "deity" ) )
    {
      if( IS_NPC( chkchar ) || !chkchar->pcdata->deity )
        return FALSE;
      return mprog_seval( chkchar->pcdata->deity->name, opr, rval, mob );
    }
    if( !str_cmp( chck, "guild" ) )
    {
      if( IS_NPC( chkchar ) || !IS_GUILDED( chkchar ) )
        return FALSE;
      return mprog_seval( chkchar->pcdata->clan->name, opr, rval, mob );
    }
    if( !str_cmp( chck, "clantype" ) )
    {
      if( IS_NPC( chkchar ) || !chkchar->pcdata->clan )
        return FALSE;
      return mprog_veval( chkchar->pcdata->clan->clan_type, opr, atoi( rval ), mob );
    }
    if( !str_cmp( chck, "waitstate" ) )
    {
      if( IS_NPC( chkchar ) || !chkchar->wait )
        return FALSE;
      return mprog_veval( chkchar->wait, opr, atoi( rval ), mob );
    }
    if( !str_cmp( chck, "asupressed" ) )
    {
      return mprog_veval( get_timer( chkchar, TIMER_ASUPRESSED ), opr, atoi( rval ), mob );
    }
    if( !str_cmp( chck, "favor" ) )
    {
      if( IS_NPC( chkchar ) || !chkchar->pcdata->favor )
        return FALSE;
      return mprog_veval( chkchar->pcdata->favor, opr, atoi( rval ), mob );
    }
    if( !str_cmp( chck, "hps" ) )
    {
      return mprog_veval( chkchar->hit, opr, atoi( rval ), mob );
    }
    if( !str_cmp( chck, "split" ) )
    {
      if( is_split( chkchar ) || is_splitformed( chkchar ) )
        return TRUE;
      else
        return FALSE;
    }
    if( !str_cmp( chck, "mana" ) )
    {
      return mprog_veval( chkchar->mana, opr, atoi( rval ), mob );
    }
    if( !str_cmp( chck, "str" ) )
    {
      return mprog_veval( get_curr_str( chkchar ), opr, atoi( rval ), mob );
    }
    if( !str_cmp( chck, "int" ) )
    {
      return mprog_veval( get_curr_int( chkchar ), opr, atoi( rval ), mob );
    }
    if( !str_cmp( chck, "dex" ) )
    {
      return mprog_veval( get_curr_dex( chkchar ), opr, atoi( rval ), mob );
    }
    if( !str_cmp( chck, "spd" ) )
    {
      return mprog_veval( get_curr_dex( chkchar ), opr, atoi( rval ), mob );
    }
    if( !str_cmp( chck, "con" ) )
    {
      return mprog_veval( get_curr_con( chkchar ), opr, atoi( rval ), mob );
    }
    if( !str_cmp( chck, "lck" ) )
    {
      return mprog_veval( get_curr_lck( chkchar ), opr, atoi( rval ), mob );
    }
    if( !str_cmp( chck, "pl" ) )
    {
      if( IS_NPC( chkchar ) )
        return mprog_vevall( chkchar->exp, opr, atoll( rval ), mob );
      else
        return mprog_vevall( chkchar->pl, opr, atoll( rval ), mob );
    }
  }
  if( chkobj )
  {
    if( !str_cmp( chck, "objtype" ) )
    {
      return mprog_veval( chkobj->item_type, opr, atoi( rval ), mob );
    }
    if( !str_cmp( chck, "leverpos" ) )
    {
      int isup = FALSE, wantsup = FALSE;
      if( chkobj->item_type != ITEM_SWITCH || chkobj->item_type != ITEM_LEVER || chkobj->item_type != ITEM_PULLCHAIN )
        return FALSE;

      if( IS_SET( obj->value[0], TRIG_UP ) )
        isup = TRUE;
      if( !str_cmp( rval, "up" ) )
        wantsup = TRUE;
      return mprog_veval( wantsup, opr, isup, 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( !IS_NPC( chkchar ) )
        return FALSE;
      lhsvl = ( chkchar == mob ) ? chkchar->gold : chkchar->pIndexData->vnum;
      return mprog_veval( lhsvl, opr, atoi( rval ), mob );
    }
    return mprog_veval( chkobj->pIndexData->vnum, opr, atoi( rval ), mob );
  }
  if( !str_cmp( chck, "time" ) )
  {
    return mprog_veval( time_info.hour, opr, atoi( rval ), mob );
  }
  if( !str_cmp( chck, "name" ) )
  {
    if( chkchar )
      return mprog_seval( chkchar->name, opr, rval, mob );
    return mprog_seval( chkobj->name, opr, rval, mob );
  }

  if( !str_cmp( chck, "rank" ) )  /* Shaddai */
  {
    if( chkchar && !IS_NPC( chkchar ) )
      return mprog_seval( chkchar->pcdata->pretitle, opr, rval, mob );
    return FALSE;
  }

  if( !str_cmp( chck, "mortinworld" ) ) /* -- Gorog */
  {
    DESCRIPTOR_DATA *d;
    for( d = first_descriptor; d; d = d->next )
      if( d->connected == CON_PLAYING
          && d->character && get_trust( d->character ) < LEVEL_IMMORTAL && nifty_is_name( d->character->name, cvar ) )
        return TRUE;
    return FALSE;
  }

  if( !str_cmp( chck, "mortinroom" ) )  /* -- Gorog */
  {
    CHAR_DATA *ch;
    for( ch = mob->in_room->first_person; ch; ch = ch->next_in_room )
      if( ( !IS_NPC( ch ) ) && get_trust( ch ) < LEVEL_IMMORTAL && nifty_is_name( ch->name, cvar ) )
        return TRUE;
    return FALSE;
  }

  if( !str_cmp( chck, "mortinarea" ) )  /* -- Gorog */
  {
    CHAR_DATA *ch;
    for( ch = first_char; ch; ch = ch->next )
      if( ( !IS_NPC( ch ) )
          && ch->in_room->area == mob->in_room->area && get_trust( ch ) < LEVEL_IMMORTAL && nifty_is_name( ch->name, cvar ) )
        return TRUE;
    return FALSE;
  }


  if( !str_cmp( chck, "mortcount" ) ) /* -- Gorog */
  {
    CHAR_DATA *tch;
    ROOM_INDEX_DATA *room;
    int count = 0;
    int rvnum = atoi( cvar );

    room = get_room_index( rvnum ? rvnum : mob->in_room->vnum );

    for( tch = room ? room->first_person : NULL; tch; tch = tch->next_in_room )
      if( ( !IS_NPC( tch ) ) && get_trust( tch ) < LEVEL_IMMORTAL )
        count++;
    return mprog_veval( count, opr, atoi( rval ), mob );
  }


  if( !str_cmp( chck, "mobcount" ) )  /* -- Gorog */
  {
    CHAR_DATA *tch;
    ROOM_INDEX_DATA *room;
    int count = -1;
    int rvnum = atoi( cvar );

    room = get_room_index( rvnum ? rvnum : mob->in_room->vnum );

    for( tch = room ? room->first_person : NULL; tch; tch = tch->next_in_room )
      if( ( IS_NPC( tch ) ) )
        count++;
    return mprog_veval( count, opr, atoi( rval ), mob );
  }


  if( !str_cmp( chck, "charcount" ) ) /* -- Gorog */
  {
    CHAR_DATA *tch;
    ROOM_INDEX_DATA *room;
    int count = -1;
    int rvnum = atoi( cvar );

    room = get_room_index( rvnum ? rvnum : mob->in_room->vnum );

    for( tch = room ? room->first_person : NULL; tch; tch = tch->next_in_room )

      if( ( ( !IS_NPC( tch ) ) && get_trust( tch ) < LEVEL_IMMORTAL ) || IS_NPC( tch ) )  /* mortal or mob */
        count++;
    return mprog_veval( count, opr, atoi( 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;
}

#undef isoperator
#undef MAX_IF_ARGS

/* 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
 *
 *  Added char_died and obj_extracted checks	-Thoric
 */
void mprog_translate( char ch, char *t, CHAR_DATA * mob, CHAR_DATA * actor, OBJ_DATA * obj, void *vo, CHAR_DATA * rndm )
{
  static char *he_she[] = { "it", "he", "she" };
  static char *him_her[] = { "it", "him", "her" };
  static char *his_her[] = { "its", "his", "her" };
  CHAR_DATA *vict = ( CHAR_DATA * ) vo;
  OBJ_DATA *v_obj = ( OBJ_DATA * ) vo;

/* Fix crash bug :)  SHADDAI */
  if( v_obj && v_obj->serial )
    vict = NULL;
  else
    v_obj = NULL;

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

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

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

    case 'N':
      if( actor && !char_died( actor ) )
      {
        if( can_see( mob, actor ) )
          if( IS_NPC( actor ) )
            strcpy( t, actor->short_descr );
          else
          {
            strcpy( t, actor->name );
            strcat( t, actor->pcdata->title );
          }
        else
          strcpy( t, "someone" );
      }
      else
        strcpy( t, "someone" );
      break;

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

      break;

    case 'T':
      if( vict && !char_died( vict ) )
      {
        if( can_see( mob, vict ) )
          if( IS_NPC( vict ) )
            strcpy( t, vict->short_descr );
          else
          {
            strcpy( t, vict->name );
            strcat( t, " " );
            strcat( t, vict->pcdata->title );
          }
        else
          strcpy( t, "someone" );
      }
      else
        strcpy( t, "someone" );
      break;

    case 'r':
      if( rndm && !char_died( rndm ) )
      {
        if( can_see( mob, rndm ) )
        {
          one_argument( rndm->name, t );
        }
        if( !IS_NPC( rndm ) )
        {
          *t = UPPER( *t );
        }
      }
      else
        strcpy( t, "someone" );
      break;

    case 'R':
      if( rndm && !char_died( rndm ) )
      {
        if( can_see( mob, rndm ) )
          if( IS_NPC( rndm ) )
            strcpy( t, rndm->short_descr );
          else
          {
            strcpy( t, rndm->name );
            strcat( t, " " );
            strcat( t, rndm->pcdata->title );
          }
        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->sex] ) : strcpy( t, "someone" );
      }
      else
        strcpy( t, "it" );
      break;

    case 'm':
      if( actor && !char_died( actor ) )
      {
        can_see( mob, actor ) ? strcpy( t, him_her[actor->sex] ) : strcpy( t, "someone" );
      }
      else
        strcpy( t, "it" );
      break;

    case 's':
      if( actor && !char_died( actor ) )
      {
        can_see( mob, actor ) ? strcpy( t, his_her[actor->sex] ) : 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->sex] ) : strcpy( t, "someone" );
      }
      else
        strcpy( t, "it" );
      break;

    case 'M':
      if( vict && !char_died( vict ) )
      {
        can_see( mob, vict ) ? strcpy( t, him_her[vict->sex] ) : strcpy( t, "someone" );
      }
      else
        strcpy( t, "it" );
      break;

    case 'S':
      if( vict && !char_died( vict ) )
      {
        can_see( mob, vict ) ? strcpy( t, his_her[vict->sex] ) : strcpy( t, "someone's" );
      }
      else
        strcpy( t, "its'" );
      break;

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

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

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

    case 'J':
      if( rndm && !char_died( rndm ) )
      {
        can_see( mob, rndm ) ? strcpy( t, he_she[rndm->sex] ) : strcpy( t, "someone" );
      }
      else
        strcpy( t, "it" );
      break;

    case 'K':
      if( rndm && !char_died( rndm ) )
      {
        can_see( mob, rndm ) ? strcpy( t, him_her[rndm->sex] ) : 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->sex] ) : strcpy( t, "someone" );
      }
      else
        strcpy( t, "its" );
      break;

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

    case 'O':
      if( obj && !obj_extracted( obj ) )
      {
        can_see_obj( mob, obj ) ? strcpy( t, obj->short_descr ) : 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->name, 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->short_descr ) : strcpy( t, "something" );
      }
      else
        strcpy( t, "something" );
      break;

    case 'a':
      if( obj && !obj_extracted( obj ) )
      {
        strcpy( t, aoran( obj->name ) );
/*
          switch ( *( obj->name ) )
	  {
	    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->name ) );
      }
      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, CHAR_DATA * mob, CHAR_DATA * actor, OBJ_DATA * obj, void *vo, bool single_step )
{
  char tmpcmndlst[MAX_STRING_LENGTH];
  char *command_list;
  char *cmnd;
  CHAR_DATA *rndm = NULL;
  CHAR_DATA *vch = NULL;
  int count = 0;
  int count2 = 0;
  int ignorelevel = 0;
  int iflevel, result;
  bool ifstate[MAX_IFS][DO_ELSE + 1];
  static int prog_nest;
  MPSLEEP_DATA *mpsleep = NULL;
  char arg[MAX_INPUT_LENGTH];

  if( IS_AFFECTED( mob, AFF_CHARM ) )
    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;
  for( vch = mob->in_room->first_person; vch; vch = vch->next_in_room )
    if( !IS_NPC( vch ) && can_see( mob, vch ) )
    {
      if( number_range( 0, count ) == 0 )
        rndm = vch;
      count++;
    }

  strcpy( tmpcmndlst, com_list );
  command_list = tmpcmndlst;

  /*
   * mpsleep - Restore the environment -rkb 
   */
  if( current_mpsleep )
  {
    ignorelevel = current_mpsleep->ignorelevel;
    iflevel = current_mpsleep->iflevel;

    if( single_step )
      mob->mpscriptpos = 0;

    for( count = 0; count < MAX_IFS; count++ )
    {
      for( count2 = 0; count2 < DO_ELSE; count2++ )
        ifstate[count][count2] = current_mpsleep->ifstate[count][count2];
    }

    current_mpsleep = NULL;
  }

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

  /*
   * 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;
    }

    /*
     * mpsleep - Check if we should sleep -rkb 
     */
    if( !str_prefix( "mpsleep", cmnd ) )
    {
      CREATE( mpsleep, MPSLEEP_DATA, 1 );

      /*
       * State variables 
       */
      mpsleep->ignorelevel = ignorelevel;
      mpsleep->iflevel = iflevel;
      for( count = 0; count < MAX_IFS; count++ )
      {
        for( count2 = 0; count2 < DO_ELSE; count2++ )
        {
          mpsleep->ifstate[count][count2] = ifstate[count][count2];
        }
      }

      /*
       * Driver arguments 
       */
      mpsleep->com_list = STRALLOC( command_list );
      mpsleep->mob = mob;
      mpsleep->actor = actor;
      mpsleep->obj = obj;
      mpsleep->vo = vo;
      mpsleep->single_step = single_step;

      /*
       * Time to sleep 
       */
      cmnd = one_argument( cmnd, arg );
      cmnd = one_argument( cmnd, arg );
      if( arg[0] == '\0' )
        mpsleep->timer = 4;
      else
        mpsleep->timer = atoi( arg );

      if( mpsleep->timer < 1 )
      {
        progbug( "mpsleep - bad arg, using default", mob );
        mpsleep->timer = 4;
      }

      /*
       * Save type of prog, room, object or mob 
       */
      if( mpsleep->mob->pIndexData->vnum == 3 )
      {
        if( !str_prefix( "Room", mpsleep->mob->description ) )
        {
          mpsleep->type = MP_ROOM;
          mpsleep->room = mpsleep->mob->in_room;
        }
        else if( !str_prefix( "Object", mpsleep->mob->description ) )
          mpsleep->type = MP_OBJ;
      }
      else
        mpsleep->type = MP_MOB;

      LINK( mpsleep, first_mpsleep, last_mpsleep, next, prev );

      --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
        log_string( "COMMANDOK" );
#endif
        /*
         * Ok, this one's a no-brainer. 
         */
        continue;
        break;

      case IFTRUE:
#ifdef DEBUG
        log_string( "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
        log_string( "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
        log_string( "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
        log_string( "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;
        break;

      case FOUNDELSE:
#ifdef DEBUG
        log_string( "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
        log_string( "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
        log_string( "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
        log_string( "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
        log_string( "BERR" );
#endif
        --prog_nest;
        return;
        break;
    }
  }
  --prog_nest;
  return;
}

/* 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, CHAR_DATA * mob, CHAR_DATA * actor,
                      OBJ_DATA * obj, void *vo, CHAR_DATA * 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->pIndexData->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.
 */

/* See if there's any mud programs waiting to be continued -rkb */
void mpsleep_update(  )
{
  MPSLEEP_DATA *mpsleep;
  MPSLEEP_DATA *tmpMpsleep;
  bool delete_it;

  mpsleep = first_mpsleep;
  while( mpsleep )
  {
    delete_it = FALSE;

    if( mpsleep->mob )
      delete_it = char_died( mpsleep->mob );

    if( mpsleep->actor && !delete_it )
      delete_it = char_died( mpsleep->actor );

    if( mpsleep->obj && !delete_it )
      delete_it = obj_extracted( mpsleep->obj );

    if( delete_it )
    {
      log_string( "mpsleep_update - Deleting expired prog." );

      tmpMpsleep = mpsleep;
      mpsleep = mpsleep->next;
      STRFREE( tmpMpsleep->com_list );
      UNLINK( tmpMpsleep, first_mpsleep, last_mpsleep, next, prev );
      DISPOSE( tmpMpsleep );

      continue;
    }

    mpsleep = mpsleep->next;
  }

  mpsleep = first_mpsleep;
  while( mpsleep )  /* Find progs to continue */
  {
    if( --mpsleep->timer <= 0 )
    {
      current_mpsleep = mpsleep;

      if( mpsleep->type == MP_ROOM )
        rset_supermob( mpsleep->room );
      else if( mpsleep->type == MP_OBJ )
        set_supermob( mpsleep->obj );

      mprog_driver( mpsleep->com_list, mpsleep->mob, mpsleep->actor, mpsleep->obj, mpsleep->vo, mpsleep->single_step );

      release_supermob(  );

      tmpMpsleep = mpsleep;
      mpsleep = mpsleep->next;
      STRFREE( tmpMpsleep->com_list );
      UNLINK( tmpMpsleep, first_mpsleep, last_mpsleep, next, prev );
      DISPOSE( tmpMpsleep );

      continue;
    }

    mpsleep = mpsleep->next;
  }
}

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];
  int i;
  char *arg, *arglist;
  char *start, *end;

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

  for( i = 0; i < strlen( arglist ); i++ )
    arglist[i] = LOWER( arglist[i] );
  for( i = 0; i < strlen( arg ); i++ )
    arg[i] = LOWER( arg[i] );
  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, CHAR_DATA * mob, CHAR_DATA * actor, OBJ_DATA * obj, void *vo, int type )
{

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

  for( mprg = mob->pIndexData->mudprogs; mprg; mprg = mprg->next )
    if( mprg->type == type )
    {
      strcpy( temp1, mprg->arglist );
      list = temp1;
      for( i = 0; i < strlen( list ); i++ )
        list[i] = LOWER( list[i] );
      strcpy( temp2, arg );
      dupl = temp2;
      for( i = 0; i < 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;
      }
    }

  return;

}

void mprog_percent_check( CHAR_DATA * mob, CHAR_DATA * actor, OBJ_DATA * obj, void *vo, int type )
{
  MPROG_DATA *mprg;

  for( mprg = mob->pIndexData->mudprogs; mprg; mprg = mprg->next )
    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;
    }

  return;

}

void mprog_time_check( CHAR_DATA * mob, CHAR_DATA * actor, OBJ_DATA * obj, void *vo, int type )
{
  MPROG_DATA *mprg;
  bool trigger_time;

  for( mprg = mob->pIndexData->mudprogs; mprg; mprg = mprg->next )
  {
    trigger_time = ( time_info.hour == atoi( mprg->arglist ) );

    if( !trigger_time )
    {
      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 );
    }
  }
  return;
}


void mob_act_add( CHAR_DATA * mob )
{
  struct act_prog_data *runner;

  for( runner = mob_act_list; runner; runner = runner->next )
    if( runner->vo == mob )
      return;
  CREATE( runner, struct act_prog_data, 1 );
  runner->vo = mob;
  runner->next = 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, CHAR_DATA * mob, CHAR_DATA * ch, OBJ_DATA * obj, void *vo )
{
  MPROG_ACT_LIST *tmp_act;
  MPROG_DATA *mprg;
  bool found = FALSE;

  if( IS_NPC( mob ) && HAS_PROG( mob->pIndexData, ACT_PROG ) )
  {
    /*
     * Don't let a mob trigger itself, nor one instance of a mob
     * trigger another instance. 
     */
    if( IS_NPC( ch ) && ch->pIndexData == mob->pIndexData )
      return;

    /*
     * make sure this is a matching trigger 
     */
    for( mprg = mob->pIndexData->mudprogs; mprg; mprg = mprg->next )
      if( mprg->type == ACT_PROG && mprog_keyword_check( buf, mprg->arglist ) )
      {
        found = TRUE;
        break;
      }
    if( !found )
      return;

    CREATE( tmp_act, MPROG_ACT_LIST, 1 );
    if( mob->mpactnum > 0 )
      tmp_act->next = mob->mpact;
    else
      tmp_act->next = NULL;

    mob->mpact = tmp_act;
    mob->mpact->buf = str_dup( buf );
    mob->mpact->ch = ch;
    mob->mpact->obj = obj;
    mob->mpact->vo = vo;
    mob->mpactnum++;
    mob_act_add( mob );
  }
  return;
}

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

  char buf[MAX_STRING_LENGTH];
  MPROG_DATA *mprg;
  OBJ_DATA *obj;

  if( IS_NPC( mob ) && can_see( mob, ch ) && HAS_PROG( mob->pIndexData, BRIBE_PROG ) )
  {
    /*
     * Don't let a mob trigger itself, nor one instance of a mob
     * trigger another instance. 
     */
    if( IS_NPC( ch ) && ch->pIndexData == mob->pIndexData )
      return;

    obj = create_object( get_obj_index( OBJ_VNUM_MONEY_SOME ), 0 );
    sprintf( buf, obj->short_descr, amount );
    STRFREE( obj->short_descr );
    obj->short_descr = STRALLOC( buf );
    obj->value[0] = amount;
    obj = obj_to_char( obj, mob );
    mob->gold -= amount;

    for( mprg = mob->pIndexData->mudprogs; mprg; mprg = mprg->next )
      if( ( mprg->type == BRIBE_PROG ) && ( amount >= atoi( mprg->arglist ) ) )
      {
        mprog_driver( mprg->comlist, mob, ch, obj, NULL, FALSE );
        break;
      }
  }

  return;

}

void mprog_death_trigger( CHAR_DATA * killer, CHAR_DATA * mob )
{
  if( IS_NPC( mob ) && killer != mob && HAS_PROG( mob->pIndexData, DEATH_PROG ) )
  {
    mob->position = POS_STANDING;
    mprog_percent_check( mob, killer, NULL, NULL, DEATH_PROG );
    mob->position = POS_DEAD;
  }
  death_cry( mob );
  return;
}

void mprog_entry_trigger( CHAR_DATA * mob )
{
  if( IS_NPC( mob ) && HAS_PROG( mob->pIndexData, ENTRY_PROG ) )
    mprog_percent_check( mob, NULL, NULL, NULL, ENTRY_PROG );

  return;
}

void mprog_fight_trigger( CHAR_DATA * mob, CHAR_DATA * ch )
{
  if( IS_NPC( mob ) && HAS_PROG( mob->pIndexData, FIGHT_PROG ) )
    mprog_percent_check( mob, ch, NULL, NULL, FIGHT_PROG );

  return;
}

void mprog_give_trigger( CHAR_DATA * mob, CHAR_DATA * ch, OBJ_DATA * obj )
{
  char buf[MAX_INPUT_LENGTH];
  MPROG_DATA *mprg;

  if( IS_NPC( mob ) && can_see( mob, ch ) && HAS_PROG( mob->pIndexData, GIVE_PROG ) )
  {
    /*
     * Don't let a mob trigger itself, nor one instance of a mob
     * trigger another instance. 
     */
    if( IS_NPC( ch ) && ch->pIndexData == mob->pIndexData )
      return;

    for( mprg = mob->pIndexData->mudprogs; mprg; mprg = mprg->next )
    {
      one_argument( mprg->arglist, buf );

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

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

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

  for( vmob = ch->in_room->first_person; vmob; vmob = vmob_next )
  {
    vmob_next = vmob->next_in_room;
    if( !IS_NPC( vmob ) || !can_see( vmob, ch ) || vmob->fighting || !IS_AWAKE( vmob ) )
      continue;

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

    if( HAS_PROG( vmob->pIndexData, GREET_PROG ) )
      mprog_percent_check( vmob, ch, NULL, NULL, GREET_PROG );
    else if( HAS_PROG( vmob->pIndexData, ALL_GREET_PROG ) )
      mprog_percent_check( vmob, ch, NULL, NULL, ALL_GREET_PROG );
  }
  return;

}

void mprog_hitprcnt_trigger( CHAR_DATA * mob, CHAR_DATA * ch )
{
  MPROG_DATA *mprg;

  if( IS_NPC( mob ) && HAS_PROG( mob->pIndexData, HITPRCNT_PROG ) )
  {
    for( mprg = mob->pIndexData->mudprogs; mprg; mprg = mprg->next )
      if( mprg->type == HITPRCNT_PROG && ( 100 * mob->hit / mob->max_hit ) < atoi( mprg->arglist ) )
      {
        mprog_driver( mprg->comlist, mob, ch, NULL, NULL, FALSE );
        break;
      }
  }
  return;
}

void mprog_random_trigger( CHAR_DATA * mob )
{
  if( HAS_PROG( mob->pIndexData, RAND_PROG ) )
    mprog_percent_check( mob, NULL, NULL, NULL, RAND_PROG );
}

void mprog_time_trigger( CHAR_DATA * mob )
{
  if( HAS_PROG( mob->pIndexData, TIME_PROG ) )
    mprog_time_check( mob, NULL, NULL, NULL, TIME_PROG );
}

void mprog_hour_trigger( CHAR_DATA * mob )
{
  if( HAS_PROG( mob->pIndexData, HOUR_PROG ) )
    mprog_time_check( mob, NULL, NULL, NULL, HOUR_PROG );
}

void mprog_speech_trigger( char *txt, CHAR_DATA * actor )
{
  CHAR_DATA *vmob;

  for( vmob = actor->in_room->first_person; vmob; vmob = vmob->next_in_room )
  {
    if( IS_NPC( vmob ) && HAS_PROG( vmob->pIndexData, SPEECH_PROG ) )
    {
      if( IS_NPC( actor ) && actor->pIndexData == vmob->pIndexData )
        continue;
      mprog_wordlist_check( txt, vmob, actor, NULL, NULL, SPEECH_PROG );
    }
  }
}

void mprog_script_trigger( CHAR_DATA * mob )
{
  MPROG_DATA *mprg;

  if( HAS_PROG( mob->pIndexData, SCRIPT_PROG ) )
    for( mprg = mob->pIndexData->mudprogs; mprg; mprg = mprg->next )
      if( mprg->type == SCRIPT_PROG
          && ( mprg->arglist[0] == '\0' || mob->mpscriptpos != 0 || atoi( mprg->arglist ) == time_info.hour ) )
        mprog_driver( mprg->comlist, mob, NULL, NULL, NULL, TRUE );
}

void oprog_script_trigger( OBJ_DATA * obj )
{
  MPROG_DATA *mprg;

  if( HAS_PROG( obj->pIndexData, SCRIPT_PROG ) )
    for( mprg = obj->pIndexData->mudprogs; mprg; mprg = mprg->next )
      if( mprg->type == SCRIPT_PROG )
      {
        if( mprg->arglist[0] == '\0' || obj->mpscriptpos != 0 || atoi( mprg->arglist ) == time_info.hour )
        {
          set_supermob( obj );
          mprog_driver( mprg->comlist, supermob, NULL, NULL, NULL, TRUE );
          obj->mpscriptpos = supermob->mpscriptpos;
          release_supermob(  );
        }
      }
  return;
}

void rprog_script_trigger( ROOM_INDEX_DATA * room )
{
  MPROG_DATA *mprg;

  if( HAS_PROG( room, SCRIPT_PROG ) )
    for( mprg = room->mudprogs; mprg; mprg = mprg->next )
      if( mprg->type == SCRIPT_PROG )
      {
        if( mprg->arglist[0] == '\0' || room->mpscriptpos != 0 || atoi( mprg->arglist ) == time_info.hour )
        {
          rset_supermob( room );
          mprog_driver( mprg->comlist, supermob, NULL, NULL, NULL, TRUE );
          room->mpscriptpos = supermob->mpscriptpos;
          release_supermob(  );
        }
      }
  return;
}


/*
 *  Mudprogram additions begin here
 */
void set_supermob( OBJ_DATA * obj )
{
  ROOM_INDEX_DATA *room;
  OBJ_DATA *in_obj;
  CHAR_DATA *mob;
  char buf[200];

  if( !supermob )
    supermob = create_mobile( get_mob_index( 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->in_room;
  }
  else
  {
    room = obj->in_room;
  }

  if( !room )
    return;

  if( supermob->short_descr )
    STRFREE( supermob->short_descr );

  supermob->short_descr = QUICKLINK( obj->short_descr );
  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->pIndexData->vnum );
  STRFREE( supermob->description );
  supermob->description = STRALLOC( buf );

  if( room != NULL )
  {
    char_from_room( supermob );
    char_to_room( supermob, room );
  }
}

void release_supermob(  )
{
  char_from_room( supermob );
  char_to_room( supermob, get_room_index( 3 ) );
}


bool oprog_percent_check( CHAR_DATA * mob, CHAR_DATA * actor, OBJ_DATA * obj, void *vo, int type )
{
  MPROG_DATA *mprg;
  bool executed = FALSE;

  for( mprg = obj->pIndexData->mudprogs; mprg; mprg = mprg->next )
    if( mprg->type == type && ( number_percent(  ) <= atoi( mprg->arglist ) ) )
    {
      executed = TRUE;
      mprog_driver( mprg->comlist, mob, actor, obj, vo, FALSE );
      if( type != GREET_PROG )
        break;
    }

  return executed;
}

/*
 * Triggers follow
 */


/*
 *  Hold on this
 *
void oprog_act_trigger( CHAR_DATA *ch, OBJ_DATA *obj )
{
    set_supermob( obj );
    if ( HAS_PROG(obj->pIndexData, ACT_PROG) )
	oprog_percent_check( supermob, ch, obj, NULL, ACT_PROG );
    release_supermob();

    return;
}
 *
 *
 */

void oprog_greet_trigger( CHAR_DATA * ch )
{
  OBJ_DATA *vobj;

  for( vobj = ch->in_room->first_content; vobj; vobj = vobj->next_content )
    if( HAS_PROG( vobj->pIndexData, GREET_PROG ) )
    {
      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, CHAR_DATA * ch )
{
  OBJ_DATA *vobj;

  /*
   * supermob is set and released in oprog_wordlist_check 
   */
  for( vobj = ch->in_room->first_content; vobj; vobj = vobj->next_content )
    if( HAS_PROG( vobj->pIndexData, SPEECH_PROG ) )
      oprog_wordlist_check( txt, supermob, ch, vobj, NULL, SPEECH_PROG, vobj );

  return;
}

/*
 * Called at top of obj_update
 * make sure to put an if(!obj) continue
 * after it
 */
void oprog_random_trigger( OBJ_DATA * obj )
{
  CHAR_DATA *ch = NULL;
  if( HAS_PROG( obj->pIndexData, RAND_PROG ) )
  {
    set_supermob( obj );
    if( obj->carried_by )
      if( obj->wear_loc != WEAR_NONE )
        ch = obj->carried_by;
    oprog_percent_check( supermob, ch, obj, NULL, RAND_PROG );
    release_supermob(  );
  }
}

/*
 * in wear_obj, between each successful equip_char
 * the subsequent return
 */
void oprog_wear_trigger( CHAR_DATA * ch, OBJ_DATA * obj )
{
  if( HAS_PROG( obj->pIndexData, WEAR_PROG ) )
  {
    set_supermob( obj );
    oprog_percent_check( supermob, ch, obj, NULL, WEAR_PROG );
    release_supermob(  );
  }
}

bool oprog_use_trigger( CHAR_DATA * ch, OBJ_DATA * obj, CHAR_DATA * vict, OBJ_DATA * targ, void *vo )
{
  bool executed = FALSE;

  if( HAS_PROG( obj->pIndexData, USE_PROG ) )
  {
    set_supermob( obj );
    if( obj->item_type == ITEM_STAFF || obj->item_type == ITEM_WAND || obj->item_type == ITEM_SCROLL )
    {
      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( CHAR_DATA * ch, OBJ_DATA * obj )
{
  if( HAS_PROG( obj->pIndexData, REMOVE_PROG ) )
  {
    set_supermob( obj );
    oprog_percent_check( supermob, ch, obj, NULL, REMOVE_PROG );
    release_supermob(  );
  }
}


/*
 * call in do_sac, right before extract_obj
 */
void oprog_sac_trigger( CHAR_DATA * ch, OBJ_DATA * obj )
{
  if( HAS_PROG( obj->pIndexData, SAC_PROG ) )
  {
    set_supermob( obj );
    oprog_percent_check( supermob, ch, obj, NULL, SAC_PROG );
    release_supermob(  );
  }
}

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

/*
 * called in damage_obj in act_obj.c
 */
void oprog_damage_trigger( CHAR_DATA * ch, OBJ_DATA * obj )
{
  if( HAS_PROG( obj->pIndexData, DAMAGE_PROG ) )
  {
    set_supermob( obj );
    oprog_percent_check( supermob, ch, obj, NULL, DAMAGE_PROG );
    release_supermob(  );
  }
}

/*
 * called in do_repair in shops.c
 */
void oprog_repair_trigger( CHAR_DATA * ch, OBJ_DATA * obj )
{
  if( HAS_PROG( obj->pIndexData, REPAIR_PROG ) )
  {
    set_supermob( obj );
    oprog_percent_check( supermob, ch, obj, NULL, REPAIR_PROG );
    release_supermob(  );
  }
}

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

/*
 * call towards end of do_examine, right before check_for_trap
 */
void oprog_examine_trigger( CHAR_DATA * ch, OBJ_DATA * obj )
{
  if( HAS_PROG( obj->pIndexData, EXA_PROG ) )
  {
    set_supermob( obj );
    oprog_percent_check( supermob, ch, obj, NULL, EXA_PROG );
    release_supermob(  );
  }
}


/*
 * call in fight.c, group_gain, after (?) the obj_to_room
 */
void oprog_zap_trigger( CHAR_DATA * ch, OBJ_DATA * obj )
{
  if( HAS_PROG( obj->pIndexData, ZAP_PROG ) )
  {
    set_supermob( obj );
    oprog_percent_check( supermob, ch, obj, NULL, ZAP_PROG );
    release_supermob(  );
  }
}

/*
 * call in levers.c, towards top of do_push_or_pull
 *  see note there
 */
void oprog_pull_trigger( CHAR_DATA * ch, OBJ_DATA * obj )
{
  if( HAS_PROG( obj->pIndexData, PULL_PROG ) )
  {
    set_supermob( obj );
    oprog_percent_check( supermob, ch, obj, NULL, PULL_PROG );
    release_supermob(  );
  }
}

/*
 * call in levers.c, towards top of do_push_or_pull
 *  see note there
 */
void oprog_push_trigger( CHAR_DATA * ch, OBJ_DATA * obj )
{
  if( HAS_PROG( obj->pIndexData, PUSH_PROG ) )
  {
    set_supermob( obj );
    oprog_percent_check( supermob, ch, obj, NULL, PUSH_PROG );
    release_supermob(  );
  }
}

void obj_act_add( OBJ_DATA * obj );
void oprog_act_trigger( char *buf, OBJ_DATA * mobj, CHAR_DATA * ch, OBJ_DATA * obj, void *vo )
{
  if( HAS_PROG( mobj->pIndexData, ACT_PROG ) )
  {
    MPROG_ACT_LIST *tmp_act;

    CREATE( tmp_act, MPROG_ACT_LIST, 1 );
    if( mobj->mpactnum > 0 )
      tmp_act->next = mobj->mpact;
    else
      tmp_act->next = 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 );
  }
}

void oprog_wordlist_check( char *arg, CHAR_DATA * mob, CHAR_DATA * actor,
                           OBJ_DATA * obj, void *vo, int type, OBJ_DATA * iobj )
{
  char temp1[MAX_STRING_LENGTH];
  char temp2[MAX_INPUT_LENGTH];
  char word[MAX_INPUT_LENGTH];
  MPROG_DATA *mprg;
  char *list;
  char *start;
  char *dupl;
  char *end;
  int i;

  if( !actor )
  {
    if( obj->carried_by )
      if( obj->wear_loc != WEAR_NONE )
        actor = obj->carried_by;
  }

  for( mprg = iobj->pIndexData->mudprogs; mprg; mprg = mprg->next )
    if( mprg->type == type )
    {
      strcpy( temp1, mprg->arglist );
      list = temp1;
      for( i = 0; i < strlen( list ); i++ )
        list[i] = LOWER( list[i] );
      strcpy( temp2, arg );
      dupl = temp2;
      for( i = 0; i < 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( mprg->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( mprg->comlist, mob, actor, obj, vo, FALSE );
              release_supermob(  );
              break;
            }
            else
              dupl = start + 1;
      }
    }

  return;
}



/*
 *  room_prog support starts here
 *
 *
 */

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

  if( room )
  {
    STRFREE( supermob->short_descr );
    supermob->short_descr = QUICKLINK( room->name );
    STRFREE( supermob->name );
    supermob->name = QUICKLINK( room->name );
    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 );
    STRFREE( supermob->description );
    supermob->description = STRALLOC( buf );

    char_from_room( supermob );
    char_to_room( supermob, room );
  }
}


void rprog_percent_check( CHAR_DATA * mob, CHAR_DATA * actor, OBJ_DATA * obj, void *vo, int type )
{
  MPROG_DATA *mprg;

  if( !mob->in_room )
    return;

  for( mprg = mob->in_room->mudprogs; mprg; mprg = mprg->next )
    if( mprg->type == type && number_percent(  ) <= atoi( mprg->arglist ) )
    {
      mprog_driver( mprg->comlist, mob, actor, obj, vo, FALSE );
      if( type != ENTER_PROG )
        break;
    }
}

/*
 * Triggers follow
 */


/*
 *  Hold on this
 * Unhold. -- Alty
 */
void room_act_add( ROOM_INDEX_DATA * room );
void rprog_act_trigger( char *buf, ROOM_INDEX_DATA * room, CHAR_DATA * ch, OBJ_DATA * obj, void *vo )
{
  if( HAS_PROG( room, ACT_PROG ) )
  {
    MPROG_ACT_LIST *tmp_act;

    CREATE( tmp_act, MPROG_ACT_LIST, 1 );
    if( room->mpactnum > 0 )
      tmp_act->next = room->mpact;
    else
      tmp_act->next = 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 );
  }
}

/*
 *
 */


void rprog_leave_trigger( CHAR_DATA * ch )
{
  if( HAS_PROG( ch->in_room, LEAVE_PROG ) )
  {
    rset_supermob( ch->in_room );
    rprog_percent_check( supermob, ch, NULL, NULL, LEAVE_PROG );
    release_supermob(  );
  }
}

void rprog_enter_trigger( CHAR_DATA * ch )
{
  if( HAS_PROG( ch->in_room, ENTER_PROG ) )
  {
    rset_supermob( ch->in_room );
    rprog_percent_check( supermob, ch, NULL, NULL, ENTER_PROG );
    release_supermob(  );
  }
}

void rprog_sleep_trigger( CHAR_DATA * ch )
{
  if( HAS_PROG( ch->in_room, SLEEP_PROG ) )
  {
    rset_supermob( ch->in_room );
    rprog_percent_check( supermob, ch, NULL, NULL, SLEEP_PROG );
    release_supermob(  );
  }
}

void rprog_rest_trigger( CHAR_DATA * ch )
{
  if( HAS_PROG( ch->in_room, REST_PROG ) )
  {
    rset_supermob( ch->in_room );
    rprog_percent_check( supermob, ch, NULL, NULL, REST_PROG );
    release_supermob(  );
  }
}

void rprog_rfight_trigger( CHAR_DATA * ch )
{
  if( HAS_PROG( ch->in_room, RFIGHT_PROG ) )
  {
    rset_supermob( ch->in_room );
    rprog_percent_check( supermob, ch, NULL, NULL, RFIGHT_PROG );
    release_supermob(  );
  }
}

void rprog_death_trigger( CHAR_DATA * killer, CHAR_DATA * ch )
{
  if( HAS_PROG( ch->in_room, RDEATH_PROG ) )
  {
    rset_supermob( ch->in_room );
    rprog_percent_check( supermob, ch, NULL, NULL, RDEATH_PROG );
    release_supermob(  );
  }
}

void rprog_speech_trigger( char *txt, CHAR_DATA * ch )
{
  if( HAS_PROG( ch->in_room, SPEECH_PROG ) )
  {
    /*
     * supermob is set and released in rprog_wordlist_check 
     */
    rprog_wordlist_check( txt, supermob, ch, NULL, NULL, SPEECH_PROG, ch->in_room );
  }
}

void rprog_random_trigger( CHAR_DATA * ch )
{
  if( HAS_PROG( ch->in_room, RAND_PROG ) )
  {
    rset_supermob( ch->in_room );
    rprog_percent_check( supermob, ch, NULL, NULL, RAND_PROG );
    release_supermob(  );
  }
}

void rprog_wordlist_check( char *arg, CHAR_DATA * mob, CHAR_DATA * actor,
                           OBJ_DATA * obj, void *vo, int type, ROOM_INDEX_DATA * room )
{

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

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

  for( mprg = room->mudprogs; mprg; mprg = mprg->next )
    if( mprg->type == type )
    {
      strcpy( temp1, mprg->arglist );
      list = temp1;
      for( i = 0; i < strlen( list ); i++ )
        list[i] = LOWER( list[i] );
      strcpy( temp2, arg );
      dupl = temp2;
      for( i = 0; i < 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( mprg->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( mprg->comlist, mob, actor, obj, vo, FALSE );
              release_supermob(  );
              break;
            }
            else
              dupl = start + 1;
      }
    }
  return;
}

void rprog_time_check( CHAR_DATA * mob, CHAR_DATA * actor, OBJ_DATA * obj, void *vo, int type )
{
  ROOM_INDEX_DATA *room = ( ROOM_INDEX_DATA * ) vo;
  MPROG_DATA *mprg;
  bool trigger_time;

  for( mprg = room->mudprogs; mprg; mprg = mprg->next )
  {
    trigger_time = ( time_info.hour == atoi( mprg->arglist ) );

    if( !trigger_time )
    {
      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 );
    }
  }
  return;
}

void rprog_time_trigger( CHAR_DATA * ch )
{
  if( HAS_PROG( ch->in_room, TIME_PROG ) )
  {
    rset_supermob( ch->in_room );
    rprog_time_check( supermob, NULL, NULL, ch->in_room, TIME_PROG );
    release_supermob(  );
  }
}

void rprog_hour_trigger( CHAR_DATA * ch )
{
  if( HAS_PROG( ch->in_room, HOUR_PROG ) )
  {
    rset_supermob( ch->in_room );
    rprog_time_check( supermob, NULL, NULL, ch->in_room, HOUR_PROG );
    release_supermob(  );
  }
}

/* Written by Jenny, Nov 29/95 */
void progbug( char *str, CHAR_DATA * mob )
{
  char buf[MAX_STRING_LENGTH];
  int vnum = mob->pIndexData ? mob->pIndexData->vnum : 0;

  /*
   * Check if we're dealing with supermob, which means the bug occurred
   * in a room or obj prog.
   */
  if( 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->description == NULL ? "(unknown)" : mob->description );
  }
  else
  {
    sprintf( buf, "%s, Mob #%d.", str, vnum );
  }

  bug( buf, 0 );
  return;
}


/* 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( ROOM_INDEX_DATA * room )
{
  struct act_prog_data *runner;

  for( runner = room_act_list; runner; runner = runner->next )
    if( runner->vo == room )
      return;
  CREATE( runner, struct act_prog_data, 1 );
  runner->vo = room;
  runner->next = room_act_list;
  room_act_list = runner;
}


void room_act_update( void )
{
  struct act_prog_data *runner;
  MPROG_ACT_LIST *mpact;

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

    while( ( mpact = room->mpact ) != NULL )
    {
      if( mpact->ch->in_room == room )
        rprog_wordlist_check( mpact->buf, supermob, mpact->ch, mpact->obj, mpact->vo, ACT_PROG, room );
      room->mpact = mpact->next;
      DISPOSE( mpact->buf );
      DISPOSE( mpact );
    }
    room->mpact = NULL;
    room->mpactnum = 0;
    room_act_list = runner->next;
    DISPOSE( runner );
  }
  return;
}

void obj_act_add( OBJ_DATA * obj )
{
  struct act_prog_data *runner;

  for( runner = obj_act_list; runner; runner = runner->next )
    if( runner->vo == obj )
      return;
  CREATE( runner, struct act_prog_data, 1 );
  runner->vo = obj;
  runner->next = obj_act_list;
  obj_act_list = runner;
}
void obj_act_update( void )
{
  struct act_prog_data *runner;
  MPROG_ACT_LIST *mpact;

  while( ( runner = obj_act_list ) != NULL )
  {
    OBJ_DATA *obj = 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->next;
      DISPOSE( mpact->buf );
      DISPOSE( mpact );
    }
    obj->mpact = NULL;
    obj->mpactnum = 0;
    obj_act_list = runner->next;
    DISPOSE( runner );
  }
  return;
}