dotd-2.3.7/area/
dotd-2.3.7/clans/
dotd-2.3.7/classes/
dotd-2.3.7/councils/
dotd-2.3.7/deity/
dotd-2.3.7/dict/
dotd-2.3.7/doc/mudprogs/
dotd-2.3.7/player/a/
dotd-2.3.7/player/g/
/****************************************************************************
 * [S]imulated [M]edieval [A]dventure multi[U]ser [G]ame      |   \\._.//   *
 * -----------------------------------------------------------|   (0...0)   *
 * SMAUG 1.0 (C) 1994, 1995, 1996 by Derek Snider             |    ).:.(    *
 * -----------------------------------------------------------|    {o o}    *
 * SMAUG code team: Thoric, Altrag, Blodkai, Narn, Haus,      |   / ' ' \   *
 * Scryn, Rennard, Swordbearer, Gorog, Grishnakh and Tricops  |~'~.VxvxV.~'~*
 ****************************************************************************
 *  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.                                      *
 ****************************************************************************/

/*static char rcsid[] = "$Id: mud_prog.c,v 1.45 2003/12/21 17:20:55 dotd Exp $";*/

#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "mud.h"


extern ROOM_INDEX_DATA *room_index_hash[MAX_KEY_HASH];
extern int top_obj_vnum;

/* 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
#define RETURNTRUE   10
#define RETURNFALSE  11*/
typedef enum
{
    COMMANDOK = 1, IFTRUE, IFFALSE, ORTRUE, ORFALSE, FOUNDELSE,
    FOUNDENDIF, IFIGNORED, ORIGNORED, RETURNTRUE, RETURNFALSE,
    CMDBERR
} prog_cmd_ret;

/* Ifstate defines, used to create and access ifstate array
 in mprog_driver. */
#define MAX_IFS     50		/* 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

sh_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;

/*
 * 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 ) );
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 ) );
bool	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, prog_types 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, prog_types type);
void rprog_percent_check( CHAR_DATA *mob, CHAR_DATA *actor, OBJ_DATA *obj, void *vo, prog_types type);
bool rprog_wordlist_check( char *arg, CHAR_DATA *mob, CHAR_DATA *actor,
                           OBJ_DATA *obj, void *vo, prog_types 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(void)
{
    RID *office;

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

#ifdef NOTDEFD
    supermob = new_char();
    clear_char( supermob );

    SET_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;

}

/* 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 cvar[MAX_INPUT_LENGTH];
    char chck[MAX_INPUT_LENGTH];
    char opr[MAX_INPUT_LENGTH];
    char rval[MAX_INPUT_LENGTH];
    char rcvar[MAX_INPUT_LENGTH];
    char *point = ifcheck;
    char *pchck = chck;
    CHAR_DATA *chkchar = NULL, *rchkchar = NULL;
    OBJ_DATA *chkobj = NULL, *rchkobj = NULL;
    int lhsvl, rhsvl;
    cvar[0] = '\0';
    rcvar[0] = '\0';

    if ( !*point )
    {
        progbug( "Null ifcheck", mob );
        return CMDBERR;
    }
    while ( *point == ' ' )
        point++;

    while ( *point != '(' )
        if ( *point == '\0' )
        {
            progbug( "Ifcheck syntax error", mob );
            return CMDBERR;
        }
        else if ( *point == ' ' )
            point++;
        else
            *pchck++ = *point++;
    *pchck = '\0';
    point++;
    pchck = cvar;
    while ( *point != ')' )
        if ( *point == '\0' )
        {
            progbug( "Ifcheck syntax error", mob );
            return CMDBERR;
        }
        else if ( *point == ' ' )
            point++;
        else
            *pchck++ = *point++;
    point++;

    while ( *point == ' ' )
        point++;
    if ( !*point )
    {
        opr[0] = '\0';
        rval[0] = '\0';
    }
    else
    {
        pchck = opr;
        while ( *point != ' ' && !isalnum(*point) )
            if ( *point == '\0' )
            {
                progbug( "Ifcheck operator without value", mob );
                return CMDBERR;
            }
            else
                *pchck++ = *point++;
        *pchck = '\0';

        while ( *point == ' ' )
            point++;
        pchck = rval;
        while ( *point != '(' && *point != '\0' )
            *pchck++ = *point++;
        *pchck = '\0';

        pchck = rcvar;
        while ( *point != ')' && *point != '\0' )
            *pchck++ = *point++;
        *pchck = '\0';
    }

    /* chck contains check, cvar is the variable in the (), opr is the
     * operator if there is one, and rval is the value if there was an
     * operator, rcvar is variable in () of rval
     */
    if ( cvar[0] == '$' )
    {
        switch(cvar[1])
        {
        case 'i':
            if ( !mob )
            {
                sprintf(rval, "$i without mob");
                progbug(rval, mob);
                return CMDBERR;
            }
            chkchar = mob;
            break;
        case 'n':
            if ( !actor )
            {
                sprintf(rval, "$n without actor");
                progbug(rval, mob);
                return CMDBERR;
            }
            chkchar = actor;
            break;
        case 't':
            if ( !vo )
            {
                sprintf(rval, "$t without vo");
                progbug(rval, mob);
                return CMDBERR;
            }
            chkchar = (CHAR_DATA *)vo;
            break;
        case 'r':
            if ( !rndm )
            {
/*                sprintf(rval, "$r without rndm");
                progbug(rval, mob);*/
                return CMDBERR;
            }
            chkchar = rndm;
            break;
        case 'o':
            if ( !obj )
            {
                sprintf(rval, "$o without obj");
                progbug(rval, mob);
                return CMDBERR;
            }
            chkobj = obj;
            break;
        case 'p':
            if ( !vo )
            {
                sprintf(rval, "$p without vo");
                progbug(rval, mob);
                return CMDBERR;
            }
            chkobj = (OBJ_DATA *)vo;
            break;
        default:
            sprintf(rval, "Bad argument '%c' to '%s'", cvar[0], chck);
            progbug(rval, mob);
            return CMDBERR;
        }
        if ( !chkchar && !chkobj )
            return CMDBERR;
        rchkchar = chkchar;
        rchkobj = chkobj;
    }
   if ( rcvar[0] == '$' )
    {
        switch(rcvar[1])
        {
        case 'i':
            if ( !mob )
            {
                sprintf(rval, "$i without mob");
                progbug(rval, mob);
                return CMDBERR;
            }
            rchkchar = mob;
            break;
        case 'n':
            if ( !actor )
            {
                sprintf(rval, "$n without actor");
                progbug(rval, mob);
                return CMDBERR;
            }
            rchkchar = actor;
            break;
        case 't':
            if ( !vo )
            {
                sprintf(rval, "$t without vo");
                progbug(rval, mob);
                return CMDBERR;
            }
            rchkchar = (CHAR_DATA *)vo;
            break;
        case 'r':
            if ( !rndm )
            {
/*                sprintf(rval, "$r without rndm");
                progbug(rval, mob);*/
                return CMDBERR;
            }
            rchkchar = rndm;
            break;
        case 'o':
            if ( !obj )
            {
                sprintf(rval, "$o without obj");
                progbug(rval, mob);
                return CMDBERR;
            }
            rchkobj = obj;
            break;
        case 'p':
            if ( !vo )
            {
                sprintf(rval, "$p without vo");
                progbug(rval, mob);
                return CMDBERR;
            }
            rchkobj = (OBJ_DATA *)vo;
            break;
        default:
            sprintf(rval, "Bad argument '%c' to '%s'", cvar[0], chck);
            progbug(rval, mob);
            return CMDBERR;
        }
        if ( !rchkchar && !rchkobj )
            return CMDBERR;
    }

    /* return variable */
    if (rval[0] == '%' || rval[0] == '#')
    {
        char *val;

        if (rchkchar)
            val = get_var_val_ch(rchkchar, &rval[1]);
        else
            val = get_var_val_obj(rchkobj, &rval[1]);
        if (val)
            strcpy(rval, val);
        else
            rval[0] = '\0';
    }

    if (chck[0] == '%')
    {
        char *val;

        if (chkchar)
            val = get_var_val_ch(chkchar, &chck[1]);
        else
            val = get_var_val_obj(chkobj, &chck[1]);
        if (!val)
            val = "";

        return mprog_seval(val, opr, rval, mob);
    }
    if (chck[0] == '#')
    {
        if (chkchar)
            lhsvl = get_var_val_int_ch(chkchar, &chck[1]);
        else
            lhsvl = get_var_val_int_obj(chkobj, &chck[1]);

        rhsvl = atoi(rval);

        if ( !*opr )
            strcpy( opr, "==" );

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

    if ( !str_cmp(chck, "varexists") )
    {
        if (!chkchar && !chkobj)
            return FALSE;
        return (get_var(chkchar?chkchar->vars:chkobj->vars, rval)?TRUE:FALSE);
    }

    if ( !str_cmp(chck, "rand") )
    {
        return (number_percent() <= atoi(cvar));
    }
    if ( !str_cmp(chck, "numprcnt") )
    {
        return mprog_veval(number_percent(), opr, atoi(rval), mob);
    }
    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 CMDBERR;
            }
            room = mob->in_room;
        }
        else
            room = get_room_index(idx);
        if ( !room )
        {
            progbug( "Bad room vnum passed to 'economy'", mob );
            return CMDBERR;
        }
        return mprog_veval( ((room->area->high_economy[DEFAULT_CURR] > 0) ? 1000000000 : 0)
                            + room->area->low_economy[DEFAULT_CURR], opr, atoi(rval), mob );
    }
    if ( !str_cmp(chck, "mobinroom") )
    {
        int vnum = atoi(cvar);
        int lhsvl;
        CHAR_DATA *oMob;

        if ( vnum < 1 || vnum > 100000 )
        {
            progbug( "Bad vnum to 'mobinroom'", mob );
            return CMDBERR;
        }
        lhsvl = 0;
        for ( oMob = mob->in_room->first_person; oMob;
              oMob = oMob->next_in_room )
            if ( IS_NPC(oMob) && oMob->vnum == vnum )
                lhsvl++;
        rhsvl = atoi(rval);
        if ( rhsvl < 1 ) rhsvl = 1;
        if ( !*opr )
            strcpy( opr, "==" );
        return mprog_veval(lhsvl, opr, rhsvl, mob);
    }
    if ( !str_cmp(chck, "master") )
    {
        int vnum = atoi(cvar);
        int lhsvl;
        CHAR_DATA *oMob;

        if ( vnum < 1 || vnum > 100000 )
        {
            progbug( "Bad vnum to 'master'", mob );
            return CMDBERR;
        }
        lhsvl = 0;
        for ( oMob = mob->in_room->first_person; oMob; oMob = oMob->next_in_room )
            if ( IS_NPC(oMob) &&
                 oMob->vnum == vnum &&
                 oMob->master == chkchar &&
                 IS_AFFECTED(oMob, AFF_CHARM) )
                return TRUE;
        return FALSE;
    }
    if ( !str_cmp(chck, "timeskilled") )
    {
        MOB_INDEX_DATA *pMob;

        if ( chkchar && !IS_NPC(chkchar) )
            return mprog_veval(chkchar->pcdata->mkills, opr, atoi(rval), mob);

        if ( chkchar )
            pMob = chkchar->pIndexData;
        else if ( !(pMob = get_mob_index(atoi(cvar))) )
        {
            progbug("TimesKilled ifcheck: bad vnum", mob);
            return CMDBERR;
        }
        return mprog_veval(pMob->killed, opr, atoi(rval), mob);
    }
    if ( !str_cmp(chck, "mobskilled") )
    {
        if ( chkchar && !IS_NPC(chkchar) )
            return mprog_veval(chkchar->pcdata->mdeaths, opr, atoi(rval), mob);

        return mprog_veval(0, opr, atoi(rval), mob);
    }
    if ( !str_cmp(chck, "oexists") )
    {
       OBJ_INDEX_DATA *pObjIndx;
       int vnum = atoi(cvar);

       if (!obj_exists_index(vnum))
       {
          progbug("Oexists: bad vnum", mob);
          return CMDBERR;
       }

       if (!is_number(rval))
       {
          progbug("Oexists: bad expression", mob);
          return CMDBERR;
       }

       pObjIndx = get_obj_index(vnum);

       return mprog_veval(pObjIndx->count, opr, atoi(rval), mob);
    }
    if ( !str_cmp(chck, "mexists") )
    {
       MOB_INDEX_DATA *pMobIndx;
       int vnum = atoi(cvar);

       if (!obj_exists_index(vnum))
       {
          progbug("Mexists: bad vnum", mob);
          return CMDBERR;
       }

       if (!is_number(rval))
       {
          progbug("Mexists: bad expression", mob);
          return CMDBERR;
       }

       pMobIndx = get_mob_index(vnum);

       return mprog_veval(pMobIndx->count, opr, atoi(rval), mob);
    }
    if ( !str_cmp(chck, "ovnumhere") )
    {
        OBJ_DATA *pObj;
        int vnum = atoi(cvar);

        if ( vnum < 1 || vnum > top_obj_vnum )
        {
            progbug("OvnumHere: bad vnum", mob);
            return CMDBERR;
        }
        lhsvl = 0;
        for ( pObj = mob->first_carrying; pObj; pObj = pObj->next_content )
            if ( can_see_obj(mob, pObj) && pObj->vnum == vnum )
                lhsvl++;
        for ( pObj = mob->in_room->first_content; pObj;
              pObj = pObj->next_content )
            if ( can_see_obj(mob, pObj) && pObj->vnum == vnum )
                lhsvl++;
        rhsvl = is_number(rval) ? atoi(rval) : -1;
        if ( rhsvl < 1 )
            rhsvl = 1;
        if ( !*opr )
            strcpy(opr, "==");
        return mprog_veval(lhsvl, opr, rhsvl, mob);
    }
    if ( !str_cmp(chck, "otypehere") )
    {
        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 CMDBERR;
        }
        lhsvl = 0;
        for ( pObj = mob->first_carrying; pObj; pObj = pObj->next_content )
            if ( can_see_obj(mob, pObj) && pObj->item_type == type )
                lhsvl++;
        for ( pObj = mob->in_room->first_content; pObj;
              pObj = pObj->next_content )
            if ( can_see_obj(mob, pObj) && pObj->item_type == type )
                lhsvl++;
        rhsvl = is_number(rval) ? atoi(rval) : -1;
        if ( rhsvl < 1 )
            rhsvl = 1;
        if ( !*opr )
            strcpy(opr, "==");
        return mprog_veval(lhsvl, opr, rhsvl, mob);
    }
    if ( !str_cmp(chck, "ovnumroom") )
    {
        OBJ_DATA *pObj;
        int vnum = atoi(cvar);

        if ( vnum < 1 || vnum > top_obj_vnum )
        {
            progbug("OvnumRoom: bad vnum", mob);
            return CMDBERR;
        }
        lhsvl = 0;
        for ( pObj = mob->in_room->first_content; pObj;
              pObj = pObj->next_content )
            if ( can_see_obj(mob, pObj) && pObj->vnum == vnum )
                lhsvl++;
        rhsvl = is_number(rval) ? atoi(rval) : -1;
        if ( rhsvl < 1 )
            rhsvl = 1;
        if ( !*opr )
            strcpy(opr, "==");
        return mprog_veval(lhsvl, opr, rhsvl, mob);
    }
    if ( !str_cmp(chck, "otyperoom") )
    {
        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 CMDBERR;
        }
        lhsvl = 0;
        for ( pObj = mob->in_room->first_content; pObj;
              pObj = pObj->next_content )
            if ( can_see_obj(mob, pObj) && pObj->item_type == type )
                lhsvl++;
        rhsvl = is_number(rval) ? atoi(rval) : -1;
        if ( rhsvl < 1 )
            rhsvl = 1;
        if ( !*opr )
            strcpy(opr, "==");
        return mprog_veval(lhsvl, opr, rhsvl, mob);
    }
    if ( !str_cmp(chck, "ovnumcarry") )
    {
        OBJ_DATA *pObj;
        int vnum = atoi(cvar);

        if ( vnum < 1 || vnum > top_obj_vnum )
        {
            progbug("OvnumCarry: bad vnum", mob);
            return CMDBERR;
        }
        lhsvl = 0;
        for ( pObj = mob->first_carrying; pObj; pObj = pObj->next_content )
            if ( can_see_obj(mob, pObj) && pObj->vnum == vnum )
                lhsvl++;
        rhsvl = is_number(rval) ? atoi(rval) : -1;
        if ( rhsvl < 1 )
            rhsvl = 1;
        if ( !*opr )
            strcpy(opr, "==");
        return mprog_veval(lhsvl, opr, rhsvl, mob);
    }
    if ( !str_cmp(chck, "otypecarry") )
    {
        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 CMDBERR;
        }
        lhsvl = 0;
        for ( pObj = mob->first_carrying; pObj; pObj = pObj->next_content )
            if ( can_see_obj(mob, pObj) && pObj->item_type == type )
                lhsvl++;
        rhsvl = is_number(rval) ? atoi(rval) : -1;
        if ( rhsvl < 1 )
            rhsvl = 1;
        if ( !*opr )
            strcpy(opr, "==");
        return mprog_veval(lhsvl, opr, rhsvl, mob);
    }
    if ( !str_cmp(chck, "ovnumwear") )
    {
        OBJ_DATA *pObj;
        int vnum = atoi(cvar);

        if ( vnum < 1 || vnum > top_obj_vnum )
        {
            progbug("OvnumWear: bad vnum", mob);
            return CMDBERR;
        }
        lhsvl = 0;
        for ( pObj = mob->first_carrying; pObj; pObj = pObj->next_content )
            if ( pObj->wear_loc != WEAR_NONE && can_see_obj(mob, pObj) &&
                 pObj->vnum == vnum )
                lhsvl++;
        rhsvl = is_number(rval) ? atoi(rval) : -1;
        if ( rhsvl < 1 )
            rhsvl = 1;
        if ( !*opr )
            strcpy(opr, "==");
        return mprog_veval(lhsvl, opr, rhsvl, mob);
    }
    if ( !str_cmp(chck, "iswearing") ) /* if iswearing($n) in (vnum) */
    {
        OBJ_DATA *pObj;
        int vnum = atoi(rval);

        if ( vnum < 1 || vnum > top_obj_vnum )
        {
            progbug("Iswearing: bad vnum", mob);
            return CMDBERR;
        }
        if ( str_cmp(opr, "==") )
        {
            progbug("Iswearing: bad operator", mob);
            return CMDBERR;
        }

        for ( pObj = chkchar->first_carrying; pObj; pObj = pObj->next_content )
            if ( pObj->wear_loc != WEAR_NONE &&
                 pObj->vnum == vnum )
            return TRUE;
        return FALSE;
    }
    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 CMDBERR;
        }
        lhsvl = 0;
        for ( pObj = mob->first_carrying; pObj; pObj = pObj->next_content )
            if ( pObj->wear_loc != WEAR_NONE && can_see_obj(mob, pObj) &&
                 pObj->item_type == type )
                lhsvl++;
        rhsvl = is_number(rval) ? atoi(rval) : -1;
        if ( rhsvl < 1 )
            rhsvl = 1;
        if ( !*opr )
            strcpy(opr, "==");
        return mprog_veval(lhsvl, opr, rhsvl, mob);
    }
    if ( !str_cmp(chck, "ovnuminv") )
    {
        OBJ_DATA *pObj;
        int vnum = atoi(cvar);

        if ( vnum < 1 || vnum > top_obj_vnum )
        {
            progbug("OvnumInv: bad vnum", mob);
            return CMDBERR;
        }
        lhsvl = 0;
        for ( pObj = mob->first_carrying; pObj; pObj = pObj->next_content )
            if ( pObj->wear_loc == WEAR_NONE && can_see_obj(mob, pObj) &&
                 pObj->vnum == vnum )
                lhsvl++;
        rhsvl = is_number(rval) ? atoi(rval) : -1;
        if ( rhsvl < 1 )
            rhsvl = 1;
        if ( !*opr )
            strcpy(opr, "==");
        return mprog_veval(lhsvl, opr, rhsvl, mob);
    }
    if ( !str_cmp(chck, "otypeinv") )
    {
        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 CMDBERR;
        }
        lhsvl = 0;
        for ( pObj = mob->first_carrying; pObj; pObj = pObj->next_content )
            if ( pObj->wear_loc == WEAR_NONE && can_see_obj(mob, pObj) &&
                 pObj->item_type == type )
                lhsvl++;
        rhsvl = is_number(rval) ? atoi(rval) : -1;
        if ( rhsvl < 1 )
            rhsvl = 1;
        if ( !*opr )
            strcpy(opr, "==");
        return mprog_veval(lhsvl, opr, rhsvl, mob);
    }
    if ( chkchar )
    {
        if ( !str_cmp(chck, "recallpoint" ) )
        {
            return mprog_veval(chkchar->pcdata->home, opr, atoi(rval), mob);
        }
        if ( !str_cmp(chck, "ismobinvis") )
        {
            return (IS_NPC(chkchar) && IS_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, "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, "isgood") )
        {
            return IS_GOOD(chkchar) ? TRUE : FALSE;
        }
        if ( !str_cmp(chck, "isgoodrace") )
        {
            return IsGoodSide(chkchar) ? TRUE : FALSE;
        }
        if ( !str_cmp(chck, "isneutral") )
        {
            return IS_NEUTRAL(chkchar) ? TRUE : FALSE;
        }
        if ( !str_cmp(chck, "isneutralrace") )
        {
            return IsNeutralSide(chkchar) ? TRUE : FALSE;
        }
        if ( !str_cmp(chck, "isevil") )
        {
            return IS_EVIL(chkchar) ? TRUE : FALSE;
        }
        if ( !str_cmp(chck, "isevilrace") )
        {
            return IsBadSide(chkchar) ? TRUE : FALSE;
        }
        if ( !str_cmp(chck, "isundead") )
        {
            return IsUndead(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, "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 > 31 )
            {
                progbug("Unknown affect being checked", mob);
                return CMDBERR;
            }
            return IS_AFFECTED(chkchar, 1 << value) ? TRUE : FALSE;
        }
        if ( !str_cmp(chck, "hitprcnt") )
        {
            return mprog_veval(100*GET_HIT(chkchar)/GET_MAX_HIT(chkchar), 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") )
        {
            return mprog_veval(chkchar->was_in_room->vnum, opr, atoi(rval), mob);
        }
        if ( !str_cmp(chck, "norecall") )
        {
            return IS_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(actor) ? FALSE :
                mprog_veval(chkchar->pcdata->quest_number, opr, atoi(rval), mob);
        }
        if ( !str_cmp(chck, "ishelled") )
        {
            return IS_NPC(actor) ? FALSE :
                mprog_veval(chkchar->pcdata->release_date, 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(GET_MONEY(chkchar,DEFAULT_CURR), opr, atoi(rval), mob);
        }
        if ( !str_cmp(chck, "class") )
        {
            return mprog_seval(GetClassString(chkchar), opr, rval, mob);
        }
        if ( !str_cmp(chck, "race") )
        {
            return mprog_seval(race_table[GET_RACE(chkchar)].race_name, opr, rval, mob);
        }
        if ( !str_cmp(chck, "clan") )
        {
            if ( IS_NPC(chkchar) || !chkchar->pcdata->clan )
                return FALSE;
            return mprog_seval(chkchar->pcdata->clan->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, "favor") )
        {
            return mprog_veval(chkchar->pcdata->favor, opr, atoi(rval), mob);
        }
        if ( !str_cmp(chck, "str") )
        {
            return mprog_veval(get_curr_str(chkchar), opr, atoi(rval), mob);
        }
        if ( !str_cmp(chck, "wis") )
        {
            return mprog_veval(get_curr_wis(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, "con") )
        {
            return mprog_veval(get_curr_con(chkchar), opr, atoi(rval), mob);
        }
        if ( !str_cmp(chck, "cha") )
        {
            return mprog_veval(get_curr_cha(chkchar), opr, atoi(rval), mob);
        }
        if ( !str_cmp(chck, "lck") )
        {
            return mprog_veval(get_curr_lck(chkchar), opr, atoi(rval), mob);
        }
        if ( !str_cmp(chck, "drunk") )
        {
            return mprog_veval(GET_COND(chkchar, COND_DRUNK), opr, atoi(rval), mob);
        }
    }
    if ( chkobj )
    {
        if ( !str_cmp(chck, "objtype") )
        {
            return mprog_veval(chkobj->item_type, opr, atoi(rval), mob);
        }
        if ( !str_cmp(chck, "objval0") )
        {
            return mprog_veval(chkobj->value[0], opr, atoi(rval), mob);
        }
        if ( !str_cmp(chck, "objval1") )
        {
            return mprog_veval(chkobj->value[1], opr, atoi(rval), mob);
        }
        if ( !str_cmp(chck, "objval2") )
        {
            return mprog_veval(chkobj->value[2], opr, atoi(rval), mob);
        }
        if ( !str_cmp(chck, "objval3") )
        {
            return mprog_veval(chkobj->value[3], opr, atoi(rval), mob);
        }
        if ( !str_cmp(chck, "objval4") )
        {
            return mprog_veval(chkobj->value[4], opr, atoi(rval), mob);
        }
        if ( !str_cmp(chck, "objval5") )
        {
            return mprog_veval(chkobj->value[5], opr, atoi(rval), mob);
        }
    }
    /* The following checks depend on the fact that cval[1] can only contain
     one character, and that NULL checks were made previously. */
    if ( !str_cmp(chck, "number") )
    {
        if ( chkchar )
        {
            if ( !IS_NPC(chkchar) )
                return FALSE;
            lhsvl = (chkchar == mob) ? GET_MONEY(chkchar,DEFAULT_CURR) : chkchar->vnum;
            return mprog_veval(lhsvl, opr, atoi(rval), mob);
        }
        return mprog_veval(chkobj->vnum, opr, atoi(rval), mob);
    }
    if ( !str_cmp(chck, "name") )
    {
        if ( chkchar )
            return mprog_seval(chkchar->name, opr, rval, mob);
        if ( chkobj )
            return mprog_seval(chkobj->name, opr, rval, mob);
        return 0;
    }

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


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

/*
 *  There's no reason to make the mud crash when a variable's
 *  fubared.  I added some ifs.  I'm willing to trade some
 *  performance for stability. -Haus
 *
 *  Narn's fubar ***ANNIHILATES*** you!  Hmm, could we add that
 *  as a weapon type? -Narn
 *
 *  Added char_died and obj_extracted checks	-Thoric
 */
void mprog_translate( char ch, char *t, 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;

    *t = '\0';
    switch ( ch ) {
    case 'c':
        strcpy( t, "$c" );
        break;

    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.
 *
 */
bool 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 ignorelevel  = 0;
    int iflevel;
    sh_int result;
    bool ifstate[MAX_IFS][ DO_ELSE + 1 ];
    static int prog_nest;

/*    if IS_AFFECTED( mob, AFF_CHARM )
        return;*/

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

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

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

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

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

        case IFTRUE:
#ifdef DEBUG
            log_string_plus( "IFTRUE", LOG_DEBUG, LEVEL_LOG_CSET, SEV_INFO );
#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 FALSE;
            }

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

        case IFFALSE:
#ifdef DEBUG
            log_string_plus( "IFFALSE", LOG_DEBUG, LEVEL_LOG_CSET, SEV_INFO );
#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 FALSE;
            }
            ifstate[iflevel][IN_IF] = TRUE;
            ifstate[iflevel][DO_IF] = FALSE;
            break;

        case ORTRUE:
#ifdef DEBUG
            log_string_plus( "ORTRUE", LOG_DEBUG, LEVEL_LOG_CSET, SEV_INFO );
#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 FALSE;
            }
            ifstate[iflevel][DO_IF] = TRUE;
            break;

        case ORFALSE:
#ifdef DEBUG
            log_string_plus( "ORFALSE", LOG_DEBUG, LEVEL_LOG_CSET, SEV_INFO );
#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 FALSE;
            }
            continue;
            break;

        case FOUNDELSE:
#ifdef DEBUG
            log_string_plus( "FOUNDELSE", LOG_DEBUG, LEVEL_LOG_CSET, SEV_INFO );
#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 FALSE;
            }
            if ( !ifstate[iflevel][IN_IF] )
            {
                progbug( "Unmatched else", mob );
                --prog_nest;
                return FALSE;
            }

            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_plus( "FOUNDENDIF", LOG_DEBUG, LEVEL_LOG_CSET, SEV_INFO );
#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 FALSE;
            }

            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_plus( "IFIGNORED", LOG_DEBUG, LEVEL_LOG_CSET, SEV_INFO );
#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 FALSE;
            }
            ignorelevel++;
            break;

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

            break;

        case CMDBERR:
#ifdef DEBUG
            log_string_plus( "CMDBERR", LOG_DEBUG, LEVEL_LOG_CSET, SEV_INFO );
#endif
            --prog_nest;
            return FALSE;
            break;

        case RETURNTRUE:
#ifdef DEBUG
            log_string_plus( "RETURNTRUE", LOG_DEBUG, LEVEL_LOG_CSET, SEV_INFO );
#endif
            --prog_nest;
            return TRUE;
            break;

        case RETURNFALSE:
#ifdef DEBUG
            log_string_plus( "RETURNFALSE", LOG_DEBUG, LEVEL_LOG_CSET, SEV_INFO );
#endif
            --prog_nest;
            return FALSE;
            break;
        }
    }
    --prog_nest;
    return FALSE;
}

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

    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 CMDBERR;
    }

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

    if ( !str_cmp( firstword, "return" ) )
        return atoi(ifcheck)>0?RETURNTRUE:RETURNFALSE;

    vnum = mob->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';

    i = expand_variables(buf, mob);

    interpret( mob, i );

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

    return COMMANDOK;
}

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

bool mprog_keyword_check( const char *argu, const char *argl )
{
    char word[MAX_INPUT_LENGTH];
    char arg1[MAX_INPUT_LENGTH];
    char arg2[MAX_INPUT_LENGTH];
    unsigned 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..
 */
bool mprog_wordlist_check( char *arg, CHAR_DATA *mob, CHAR_DATA *actor,
                           OBJ_DATA *obj, void *vo, sh_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;
    unsigned int i;

    for ( mprg = mob->pIndexData->mudprogs; mprg; mprg = mprg->next )
        if ( IS_PROG_TYPE(mprg, 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' ) )
                    {
                        return mprog_driver( mprg->comlist, mob, actor, obj, vo, FALSE );
                    }
                    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' ) )
                        {
                            return mprog_driver( mprg->comlist, mob, actor, obj, vo, FALSE );
                        }
                        else
                            dupl = start+1;
            }
        }

    return FALSE;
}

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

    for ( mprg = mob->pIndexData->mudprogs; mprg; mprg = mprg->next )
        if ( ( IS_PROG_TYPE(mprg, 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, prog_types 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 ( ( IS_PROG_TYPE(mprg, type) )
             && ( ( !mprg->triggered ) || ( IS_PROG_TYPE(mprg, 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 (!mob)
        return;

    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 ( IS_PROG_TYPE(mprg, 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[128];
    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_money(amount, DEFAULT_CURR);
        snprintf(buf, 127, "bribe_prog %d %s", ch->vnum, ch->name);
        obj->last_carried_by = str_dup(buf);
        obj = obj_to_char( obj, mob );

        for ( mprg = mob->pIndexData->mudprogs; mprg; mprg = mprg->next )
            if ( ( IS_PROG_TYPE(mprg, 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) )
    {
        mprog_percent_check( mob, killer, NULL, NULL, DEATH_PROG );
    }
    death_cry( mob );
    return;
}

void mprog_birth_trigger( CHAR_DATA *maker, CHAR_DATA *mob )
{
    if ( IS_NPC( mob )
         && HAS_PROG(mob->pIndexData, BIRTH_PROG) )
    {
        mprog_percent_check( mob,maker, NULL, NULL, BIRTH_PROG );
    }
    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)) )
        return;

    /* 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 )
    {
        if (!IS_PROG_TYPE(mprg, GIVE_PROG))
            continue;

        one_argument( mprg->arglist, buf );

        if ( !str_cmp( obj->name, mprg->arglist ) ||
             !str_cmp( "all", buf ) ||
             ( obj->pIndexData && obj->vnum == atoi(buf) ) )
        {

            mprog_driver( mprg->comlist, mob, ch, obj, NULL, FALSE );
            break;
        }
    }
}

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_plus( buf, LOG_DEBUG, LEVEL_LOG_CSET, SEV_INFO );
#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 ( ( IS_PROG_TYPE(mprg, HITPRCNT_PROG) )
                 && ( ( 100*GET_HIT(mob) / GET_MAX_HIT(mob) ) < 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);

    return;
}

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

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

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 );
        }
    }
    return;

}
void mprog_leave_trigger( 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, LEAVE_PROG) )
        {
            if ( IS_NPC( actor ) && actor->pIndexData == vmob->pIndexData )
                continue;
            mprog_percent_check(vmob,actor,NULL,NULL,LEAVE_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 ( IS_PROG_TYPE(mprg, SCRIPT_PROG) )
            {
                if ( mprg->arglist[0] == '\0'
                     ||   mob->mpscriptpos != 0
                     ||   atoi( mprg->arglist ) == time_info.hour )
                    mprog_driver( mprg->comlist, mob, NULL, NULL, NULL, TRUE );
            }
    return;
}

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 ( IS_PROG_TYPE(mprg, 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 ( IS_PROG_TYPE(mprg, 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( 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;
        /* so mposet works, kinda scary -Garil 10/16/2002 */
        supermob->first_carrying = in_obj->carried_by->first_carrying;
        supermob->last_carrying = in_obj->carried_by->last_carrying;
    }
    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->vnum );
    STRFREE( supermob->description );
    supermob->description = STRALLOC( buf );

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

void release_supermob( )
{
    supermob->first_carrying = NULL;
    supermob->last_carrying = NULL;
    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, prog_types type)
{
    MPROG_DATA * mprg;
    bool executed = FALSE;

    for ( mprg = obj->pIndexData->mudprogs; mprg; mprg = mprg->next )
        if ( ( IS_PROG_TYPE(mprg, 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();
        }

    return;
}

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 )
{
    if ( HAS_PROG(obj->pIndexData, RAND_PROG) )
    {
        set_supermob( obj );
        oprog_percent_check(supermob,NULL,obj,NULL,RAND_PROG);
        release_supermob();
    }
    return;
}

/*
 * in wear_obj, between each successful equip_char
 * the subsequent return
 */
void oprog_wear_trigger( 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();
    }
    return;
}

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


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

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

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

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

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

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


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

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

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

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);
    }
    return;
}

void oprog_wordlist_check( char *arg, CHAR_DATA *mob, CHAR_DATA *actor,
                           OBJ_DATA *obj, void *vo, prog_types 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;
    unsigned int i;

    for ( mprg = iobj->pIndexData->mudprogs; mprg; mprg = mprg->next )
        if ( IS_PROG_TYPE(mprg, 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, prog_types type)
{
    MPROG_DATA * mprg;

    if(!mob->in_room)
        return;

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

    return;
}

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

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

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();
    }
    return;
}

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();
    }
    return;
}

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();
    }
    return;
}

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();
    }
    return;
}

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();
    }
    return;
}

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();
    }
    return;
}

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 );
    }
    return;
}

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();
    }
    return;
}

bool rprog_wordlist_check( char *arg, CHAR_DATA *mob, CHAR_DATA *actor,
                           OBJ_DATA *obj, void *vo, prog_types 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;
    unsigned int i;

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

    for ( mprg = room->mudprogs; mprg; mprg = mprg->next )
        if ( IS_PROG_TYPE(mprg, type) )
        {
            if ((!arg || !*arg) && (!mprg->arglist || !*mprg->arglist))
            {
                rset_supermob( room );
                mprog_driver( mprg->comlist, mob, actor, obj, vo, FALSE );
                release_supermob();
                return TRUE;
            }

            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() ;
                        return TRUE;
                    }
                    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();
                            return TRUE;
                        }
                        else
                            dupl = start+1;
            }
        }
    return FALSE;
}

void rprog_time_check( CHAR_DATA *mob, CHAR_DATA *actor, OBJ_DATA *obj,
                       void *vo, prog_types 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 ( ( IS_PROG_TYPE(mprg, type) )
             && ( ( !mprg->triggered ) || ( IS_PROG_TYPE(mprg, 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();
    }
    return;
}

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();
    }
    return;
}

/* Written by Jenny, Nov 29/95 */
void progbug( char *str, CHAR_DATA *mob )
{
    /* Check if we're dealing with supermob, which means the bug occurred
     in a room or obj prog. */
    if ( mob->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. */
        log_printf_plus(LOG_BUILD, LEVEL_IMMORTAL, SEV_DEBUG,
                        "%s, %s.", str,
                        mob->description?mob->description:"(unknown)" );
        return;
    }

    log_printf_plus(LOG_BUILD, LEVEL_IMMORTAL, SEV_DEBUG,
                    "%s, Mob v%d, u%d.", str, mob->vnum, mob->unum );
}


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

    while (( runner = room_act_list ))
    {
	room_act_list = runner->next;
        DISPOSE(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 = (ROOM_INDEX_DATA *)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 = (OBJ_DATA *)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;
}


void rprog_quest_trigger( CHAR_DATA *mob )
{
    if ( HAS_PROG(mob->in_room, QUEST_PROG) )
    {
        rset_supermob( mob->in_room );
        rprog_percent_check(supermob, mob, NULL, NULL, QUEST_PROG);
        release_supermob();
    }
}

void mprog_quest_trigger( CHAR_DATA *mob, CHAR_DATA *ch )
{
    if ( IS_NPC(mob) && HAS_PROG(mob->pIndexData, QUEST_PROG) )
        mprog_percent_check( mob, ch, NULL, NULL, QUEST_PROG );
}

void oprog_quest_trigger( CHAR_DATA *ch, OBJ_DATA *obj )
{
    if ( HAS_PROG(obj->pIndexData, QUEST_PROG) )
    {
        set_supermob( obj );
        oprog_percent_check( supermob, ch, obj, NULL, QUEST_PROG );
        release_supermob();
    }
}

void oprog_fight_trigger( CHAR_DATA *ch, OBJ_DATA *weapon, CHAR_DATA *vch )
{
    if (HAS_PROG(weapon->pIndexData, FIGHT_PROG) )
    {
        set_supermob( weapon );
        oprog_percent_check( supermob, ch, weapon, vch, FIGHT_PROG );
        release_supermob();
    }
    return;
}


bool mprog_command_trigger( char *txt, CHAR_DATA *mob, 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, COMMAND_PROG) )
        {
            if ( IS_NPC( actor ) && actor->pIndexData == vmob->pIndexData )
                continue;
            if ( mprog_wordlist_check( txt, vmob, actor, NULL, NULL, COMMAND_PROG ) )
                return TRUE;
        }
    }
    return FALSE;
}

bool rprog_command_trigger( char *txt, CHAR_DATA *ch )
{
    if ( HAS_PROG(ch->in_room, COMMAND_PROG) )
    {
        if ( rprog_wordlist_check( txt, supermob, ch, NULL, NULL, COMMAND_PROG, ch->in_room ) )
            return TRUE;
    }

    return FALSE;
}

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

    if ( !HAS_PROG(room, AREA_RESET_PROG) )
        return;

    for ( mprg = room->mudprogs; mprg; mprg = mprg->next )
        if ( IS_PROG_TYPE(mprg, AREA_RESET_PROG) )
        {
            rset_supermob( room );
            mprog_driver( mprg->comlist, supermob, NULL, NULL, NULL, TRUE );
            release_supermob();
        }
}

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

    if ( !HAS_PROG(room, AREA_INIT_PROG) )
        return;

    for ( mprg = room->mudprogs; mprg; mprg = mprg->next )
        if ( IS_PROG_TYPE(mprg, AREA_INIT_PROG) )
        {
            rset_supermob( room );
            mprog_driver( mprg->comlist, supermob, NULL, NULL, NULL, TRUE );
            release_supermob();
        }
}

void aprog_init_trigger( AREA_DATA *area )
{
    ROOM_INDEX_DATA *pRoomIndex;
    int hash;

    for ( hash = 0; hash < MAX_KEY_HASH; hash++ )
        for ( pRoomIndex = room_index_hash[hash]; pRoomIndex; pRoomIndex = pRoomIndex->next )
            if (pRoomIndex->area == area)
                rprog_area_init_trigger(pRoomIndex);
}

void aprog_reset_trigger( AREA_DATA *area )
{
    ROOM_INDEX_DATA *pRoomIndex;
    int hash;

    for ( hash = 0; hash < MAX_KEY_HASH; hash++ )
        for ( pRoomIndex = room_index_hash[hash]; pRoomIndex; pRoomIndex = pRoomIndex->next )
            if (pRoomIndex->area == area)
                rprog_area_reset_trigger(pRoomIndex);
}