/***************************************************************************
 *  Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,        *
 *  Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.   *
 *                                                                         *
 *  Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael          *
 *  Chastain, Michael Quan, and Mitchell Tse.                              *
 *                                                                         *
 *  In order to use any part of this Merc Diku Mud, you must comply with   *
 *  both the original Diku license in 'license.doc' as well the Merc       *
 *  license in 'license.txt'.  In particular, you may not remove either of *
 *  these copyright notices.                                               *
 *                                                                         *
 *  Much time and thought has gone into this software and you are          *
 *  benefitting.  We hope that you share your changes too.  What goes      *
 *  around, comes around.                                                  *
 ***************************************************************************/

/***************************************************************************
 *	ROM 2.4 is copyright 1993-1995 Russ Taylor			   *
 *	ROM has been brought to you by the ROM consortium		   *
 *	    Russ Taylor (rtaylor@pacinfo.com)				   *
 *	    Gabrielle Taylor (gtaylor@pacinfo.com)			   *
 *	    Brian Moore (rom@rom.efn.org)				   *
 *	By using this code, you have agreed to follow the terms of the	   *
 *	ROM license, in the file Rom24/doc/rom.license			   *
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *  MOBprograms for ROM 2.4 v0.98g (C) M.Nylander 1996                     *
 *  Based on MERC 2.2 MOBprograms concept by N'Atas-ha.                    *
 *  Written and adapted to ROM 2.4 by                                      *
 *          Markku Nylander (markku.nylander@uta.fi)                       *
 *  This code may be copied and distributed as per the ROM license.        *
 *                                                                         *
 ***************************************************************************/
/*
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <sys/types.h>
#include <cctype>
*/

#include <iostream>
#include <string>
using namespace std;

#include "merc.h"
#include "tables.h"
#include "lookup.h"

extern int flag_lookup( const char *word, const struct flag_type *flag_table );

/*Local functions*/
PROG_CODE *get_prog_by_vnum (int, int);
PROG_LIST *get_prog_list_by_vnum (int, int, void *);
char *prog_is_cooling (PROG_LIST *);


/*
 * These defines correspond to the entries in fn_keyword[] table.
 * If you add a new if_check, you must also add a #define here.
 */
#define CHK_RAND   	(0)
#define CHK_MOBHERE     (1)
#define CHK_OBJHERE     (2)
#define CHK_MOBEXISTS   (3)
#define CHK_OBJEXISTS   (4)
#define CHK_PEOPLE      (5)
#define CHK_PLAYERS     (6)
#define CHK_MOBS        (7)
#define CHK_CLONES      (8)
#define CHK_ORDER       (9)
#define CHK_HOUR        (10)
#define CHK_ISPC        (11)
#define CHK_ISNPC       (12)
#define CHK_ISGOOD      (13)
#define CHK_ISEVIL      (14)
#define CHK_ISNEUTRAL   (15)
#define CHK_ISIMMORT    (16)
#define CHK_ISCHARM     (17)
#define CHK_ISFOLLOW    (18)
#define CHK_ISACTIVE    (19)
#define CHK_ISDELAY     (20)
#define CHK_ISVISIBLE   (21)
#define CHK_HASTARGET   (22)
#define CHK_ISTARGET    (23)
#define CHK_EXISTS      (24)
#define CHK_AFFECTED    (25)
#define CHK_ACT         (26)
#define CHK_OFF         (27)
#define CHK_IMM         (28)
#define CHK_CARRIES     (29)
#define CHK_WEARS       (30)
#define CHK_HAS         (31)
#define CHK_USES        (32)
#define CHK_NAME        (33)
#define CHK_POS         (34)
#define CHK_CLAN        (35)
#define CHK_RACE        (36)
#define CHK_CLASS       (37)
#define CHK_OBJTYPE     (38)
#define CHK_VNUM        (39)
#define CHK_HPCNT       (40)
#define CHK_ROOM        (41)
#define CHK_SEX         (42)
#define CHK_LEVEL       (43)
#define CHK_ALIGN       (44)
#define CHK_MONEY       (45)
#define CHK_OBJVAL0     (46)
#define CHK_OBJVAL1     (47)
#define CHK_OBJVAL2     (48)
#define CHK_OBJVAL3     (49)
#define CHK_OBJVAL4     (50)
#define CHK_GRPSIZE     (51)
#define CHK_ISINOBJ     (52) //JH: Is the object in another object?

/*
 * These defines correspond to the entries in fn_evals[] table.
 */
#define EVAL_EQ            0
#define EVAL_GE            1
#define EVAL_LE            2
#define EVAL_GT            3
#define EVAL_LT            4
#define EVAL_NE            5

/*
 * if-check keywords:
 */
const char * fn_keyword[] =
{
    "rand",		/* if rand 30		- if random number < 30 */
    "mobhere",		/* if mobhere fido	- is there a 'fido' here */
    "objhere",		/* if objhere bottle	- is there a 'bottle' here */
    /* if mobhere 1233	- is there mob vnum 1233 here */
    /* if objhere 1233	- is there obj vnum 1233 here */
    "mobexists",	/* if mobexists fido	- is there a fido somewhere */
    "objexists",	/* if objexists sword	- is there a sword somewhere */

    "people",		/* if people > 4	- does room contain > 4 people */
    "players",		/* if players > 1	- does room contain > 1 pcs */
    "mobs",		/* if mobs > 2		- does room contain > 2 mobiles */
    "clones",		/* if clones > 3	- are there > 3 mobs of same vnum here */
    "order",		/* if order == 0	- is mob the first in room */
    "hour",		/* if hour > 11		- is the time > 11 o'clock */


    "ispc",		/* if ispc $n 		- is $n a pc */
    "isnpc",		/* if isnpc $n 		- is $n a mobile */
    "isgood",		/* if isgood $n 	- is $n good */
    "isevil",		/* if isevil $n 	- is $n evil */
    "isneutral",	/* if isneutral $n 	- is $n neutral */
    "isimmort",		/* if isimmort $n	- is $n immortal */
    "ischarm",		/* if ischarm $n	- is $n charmed */
    "isfollow",		/* if isfollow $n	- is $n following someone */
    "isactive",		/* if isactive $n	- is $n's position > SLEEPING */
    "isdelay",		/* if isdelay $i	- does $i have mobprog pending */
    "isvisible",	/* if isvisible $n	- can mob see $n */
    "hastarget",	/* if hastarget $i	- does $i have a valid target */
    "istarget",		/* if istarget $n	- is $n mob's target */
    "exists",		/* if exists $n		- does $n exist somewhere */

    "affected",		/* if affected $n blind - is $n affected by blind */
    "act",		/* if act $i sentinel	- is $i flagged sentinel */
    "off",              /* if off $i berserk	- is $i flagged berserk */
    "imm",              /* if imm $i fire	- is $i immune to fire */
    "carries",		/* if carries $n sword	- does $n have a 'sword' */
    /* if carries $n 1233	- does $n have obj vnum 1233 */
    "wears",		/* if wears $n lantern	- is $n wearing a 'lantern' */
    /* if wears $n 1233	- is $n wearing obj vnum 1233 */
    "has",    		/* if has $n weapon	- does $n have obj of type weapon */
    "uses",		/* if uses $n armor	- is $n wearing obj of type armor */
    "name",		/* if name $n puff	- is $n's name 'puff' */
    "pos",		/* if pos $n standing	- is $n standing */
    "clan",		/* if clan $n 'whatever'- does $n belong to clan 'whatever' */
    "race",		/* if race $n dragon	- is $n of 'dragon' race */
    "class",		/* if class $n mage	- is $n's class 'mage' */
    "objtype",		/* if objtype $p scroll	- is $p a scroll */

    "vnum",		/* if vnum $i == 1233  	- virtual number check */
    "hpcnt",		/* if hpcnt $i > 30	- hit point percent check */
    "room",		/* if room $i == 1233	- room virtual number */
    "sex",		/* if sex $i == 0	- sex check */
    "level",		/* if level $n < 5	- level check */
    "align",		/* if align $n < -1000	- alignment check */
    "money",		/* if money $n */
    "objval0",		/* if objval0 > 1000 	- object value[] checks 0..4 */
    "objval1",
    "objval2",
    "objval3",
    "objval4",
    "grpsize",		/* if grpsize $n > 6	- group size check */
    "isinobj",		/* if objhere bottle	- is there a 'bottle' here */
    "\n"		/* Table terminator */
};

const char *fn_evals[] =
{
    "==",
    ">=",
    "<=",
    ">",
    "<",
    "!=",
    "\n"
};


/*
 * Return a valid keyword from a keyword table
 */
int keyword_lookup( const char **table, char *keyword )
{
    register int i;
    for( i = 0; table[i][0] != '\n'; i++ )
        if( !str_cmp( table[i], keyword ) )
            return( i );
    return -1;
}

/*
 * Perform numeric evaluation.
 * Called by cmd_eval()
 */
int num_eval( int lval, int oper, int rval )
{
    switch( oper )
    {
        case EVAL_EQ:
            return ( lval == rval );
        case EVAL_GE:
            return ( lval >= rval );
        case EVAL_LE:
            return ( lval <= rval );
        case EVAL_NE:
            return ( lval != rval );
        case EVAL_GT:
            return ( lval > rval );
        case EVAL_LT:
            return ( lval < rval );
        default:
            bug( "num_eval: invalid oper", 0 );
            return 0;
    }
}

/* JH:
 * Perform string evaluation (case insensitive).
 * Called by new_eval()
 */
int str_eval( char *lval, int oper, char *rval )
{
    switch( oper )
    {
        case EVAL_EQ:
            return ( strcasecmp(lval, rval) + 1 == 1 );
        case EVAL_GE:
            return ( strcasecmp(lval, rval) + 1 >= 1 );
        case EVAL_LE:
            return ( strcasecmp(lval, rval) + 1 <= 1 );
        case EVAL_NE:
            return ( strcasecmp(lval, rval) + 1 != 1 );
        case EVAL_GT:
            return ( strcasecmp(lval, rval) + 1 > 1 );
        case EVAL_LT:
            return ( strcasecmp(lval, rval) + 1 < 1);
        default:
            bug( "str_eval: invalid oper", 0 );
            return 0;
    }
}

// JH Get an prog by its vnum (and type), for 'wait' prog control 2/3/2004 6:22PM
PROG_CODE *get_prog_by_vnum(int vnum, int type)
{
    PROG_CODE *temp;

    switch (type)
    {
        case MOB:
            for(temp = mprog_list; temp != NULL; temp = temp->next)
                if(temp->vnum == vnum)
                    return temp;
            return NULL;
            break;
        case OBJ:
            for(temp = oprog_list; temp != NULL; temp = temp->next)
                if(temp->vnum == vnum)
                    return temp;
            return NULL;
            break;
        default:
            for(temp = rprog_list; temp != NULL; temp = temp->next)
                if(temp->vnum == vnum)
                    return temp;
            return NULL;
            break;
    }

    return NULL;
}

// JH Get an prog_list by its vnum and type for 'cooldown' prog control 2/12/2004 12:28AM
PROG_LIST *get_prog_list_by_vnum(int vnum, int type, void *vpr)
{
    PROG_LIST *temp;
    CHAR_DATA *mob;
    OBJ_DATA *obj;
    ROOM_INDEX_DATA *room;
    switch (type)
    {
        case MOB:
            mob = (CHAR_DATA *)vpr;
            for ( temp = mob->pIndexData->mprogs; temp; temp = temp->next )
            {
                if ( temp->vnum == vnum)
                    return temp;
            }
            break;
        case OBJ:
            obj = (OBJ_DATA *)vpr;
            for ( temp = obj->pIndexData->oprogs; temp; temp = temp->next )
            {
                if ( temp->vnum == vnum)
                    return temp;
            }
            break;
        default:
            room = (ROOM_INDEX_DATA *)vpr;
            for ( temp = room->rprogs; temp; temp = temp->next )
            {
                if ( temp->vnum == vnum)
                    return temp;
            }
            break;
    }

    return NULL;
}

// JH 2/12/2004 12:53AM Find out if a program (an instance of) is cooling down
char *prog_is_cooling (PROG_LIST *prog)
{
    EVENT *ev;
    for (ev = events; ev; ev = ev->next)
    {
        if (ev->type == EVENT_PROG_COOLDOWN && ev->event_args.prog_cooldown.prog_list == prog)
        {
            wiznet(create_string("DEBUG: Program %d is cooling yet", prog->vnum),
                    NULL, NULL, WIZ_DEBUG, 0, MAX_LEVEL);
            return ev->event_args.prog_cooldown.message;
        }

    }

    return NULL;
}


/*
 * ---------------------------------------------------------------------
 * UTILITY FUNCTIONS USED BY CMD_EVAL()
 * ----------------------------------------------------------------------
 */

/*
 * Get a random PC in the room (for $r parameter)
 */
CHAR_DATA *get_random_char( CHAR_DATA *mob, OBJ_DATA *obj, ROOM_INDEX_DATA *room )
{
    CHAR_DATA *vch, *victim = NULL;
    int now = 0, highest = 0;

    if ( (mob && obj) || (mob && room) || (obj && room) )
    {
        bug( "get_random_char received multiple prog types",0);
        return NULL;
    }

    if ( mob )
        vch = mob->in_room->people;
    else if ( obj )
    {
        OBJ_DATA *host = find_container_or_obj(obj);
        if ( host->in_room )
            vch = host->in_room->people;
        else
            vch = host->carried_by->in_room->people;
    }
    else
        vch = room->people;

    for( ; vch; vch = vch->next_in_room )
    {
        if ( mob )
        {
            if ( mob != vch
                &&   !IS_NPC( vch )
                &&   can_see( mob, vch )
                &&   ( now = number_percent() ) > highest )
            {
                victim = vch;
                highest = now;
            }
        }
        else if ( (now = number_percent()) > highest )
        {
            victim = vch;
            highest = now;
        }
    }
    if (victim)
    {
        wiznet(create_string("DEBUG: get_random_char: '%s' chosen", IS_NPC(victim)? victim->short_descr: victim->name),
            NULL, NULL, WIZ_DEBUG, 0, MAX_LEVEL);
    }
    return victim;
}

/*
 * How many other players / mobs are there in the room
 * iFlag: 0: all, 1: players, 2: mobiles 3: mobs w/ same vnum 4: same group
 */
int count_people_room( CHAR_DATA *mob, OBJ_DATA *obj, ROOM_INDEX_DATA *room, int iFlag )
{
    CHAR_DATA *vch;
    int count;

    if ( (mob && obj) || (mob && room) || (obj && room) )
    {
        bug( "count_people_room received multiple prog types",0);
        return 0;
    }

    if ( mob )
        vch = mob->in_room->people;
    else if ( obj )
    {
        if ( obj->in_room )
            vch = obj->in_room->people;
        else
            vch = obj->carried_by->in_room->people;
    }
    else
        vch = room->people;

    for ( count = 0; vch; vch = vch->next_in_room )
    {
        if ( mob )
        {
            if ( mob != vch
                    &&   (iFlag == 0
                        || (iFlag == 1 && !IS_NPC( vch ))
                        || (iFlag == 2 && IS_NPC( vch ))
                        || (iFlag == 3 && IS_NPC( mob ) && IS_NPC( vch )
                            && mob->pIndexData->vnum == vch->pIndexData->vnum )
                        || (iFlag == 4 && is_same_group( mob, vch )) )
                    && can_see( mob, vch ) )
                count++;
        }
        else if ( obj || room )
        {
            if ( iFlag == 0
                    || (iFlag == 1 && !IS_NPC( vch ))
                    || (iFlag == 2 && IS_NPC( vch )))
                count++;
        }
    }

    return ( count );
}

/*
 * Get the order of a mob in the room. Useful when several mobs in
 * a room have the same trigger and you want only the first of them
 * to act
 */
int get_order( CHAR_DATA *ch, OBJ_DATA *obj )
{
    CHAR_DATA *vch;
    OBJ_DATA *vobj;
    int i;

    if ( ch && obj )
    {
        bug( "get_order received multiple prog types",0);
        return 0;
    }

    if ( ch && !IS_NPC(ch) )
        return 0;

    if ( ch )
    {
        vch = ch->in_room->people;
        vobj = NULL;
    }
    else
    {
        vch = NULL;
        if ( obj->in_room )
            vobj = obj->in_room->contents;
        else if ( obj->carried_by->in_room->contents )
            vobj = obj->carried_by->in_room->contents;
        else
            vobj = NULL;
    }

    if ( ch )
        for ( i = 0; vch; vch = vch->next_in_room )
        {
            if ( vch == ch )
                return i;

            if ( IS_NPC(vch)
                    &&   vch->pIndexData->vnum == ch->pIndexData->vnum )
                i++;
        }
    else
        for ( i = 0; vobj; vobj = vobj->next_content )
        {
            if ( vobj == obj )
                return i;

            if ( vobj->pIndexData->vnum == obj->pIndexData->vnum )
                i++;
        }

    return 0;
}

/*
 * Check if ch has a given item or item type
 * vnum: item vnum or -1
 * item_type: item type or -1
 * fWear: TRUE: item must be worn, FALSE: don't care
 */
bool has_item( CHAR_DATA *ch, sh_int vnum, sh_int item_type, bool fWear )
{
    OBJ_DATA *obj;
    for ( obj = ch->carrying; obj; obj = obj->next_content )
        if ( ( vnum < 0 || obj->pIndexData->vnum == vnum )
                &&   ( item_type < 0 || obj->pIndexData->item_type == item_type )
                &&   ( !fWear || obj->wear_loc != WEAR_NONE ) )
            return TRUE;
    return FALSE;
}

/*
 * Check if there's a mob with given vnum in the room
 */
bool get_mob_vnum_room( CHAR_DATA *ch, OBJ_DATA *obj, ROOM_INDEX_DATA *room, sh_int vnum )
{
    CHAR_DATA *mob;

    if ( (ch && obj) || (ch && room) || (obj && room) )
    {
        bug( "get_mob_vnum_room received multiple prog types",0);
        return FALSE;
    }

    if ( ch )
        mob = ch->in_room->people;
    else if ( obj )
    {
        if ( obj->in_room )
            mob = obj->in_room->people;
        else
            mob = obj->carried_by->in_room->people;
    }
    else
        mob = room->people;

    for ( ; mob; mob = mob->next_in_room )
        if ( IS_NPC( mob ) && mob->pIndexData->vnum == vnum )
            return TRUE;
    return FALSE;
}

/*
 * Check if there's an object with given vnum in the room
 */
bool get_obj_vnum_room( CHAR_DATA *ch, OBJ_DATA *obj, ROOM_INDEX_DATA *room, sh_int vnum )
{
    OBJ_DATA *vobj;

    if ( (ch && obj) || (ch && room) || (obj && room) )
    {
        bug( "get_obj_vnum_room received multiple prog types",0);
        return FALSE;
    }

    if ( ch )
        vobj = ch->in_room->contents;
    else if ( obj )
    {
        if ( obj->in_room )
            vobj = obj->in_room->contents;
        else
            vobj = obj->carried_by->in_room->contents;
    }
    else
        vobj = room->contents;

    for ( ; vobj; vobj = vobj->next_content )
        if ( vobj->pIndexData->vnum == vnum )
            return TRUE;
    return FALSE;
}


/* ---------------------------------------------------------------------
 * CMD_EVAL
 * This monster evaluates an if/or/and statement
 * There are five kinds of statement:
 * 1) keyword and value (no $-code)	    if random 30
 * 2) keyword, comparison and value	    if people > 2
 * 3) keyword and actor		    	    if isnpc $n
 * 4) keyword, actor and value		    if carries $n sword
 * 5) keyword, actor, comparison and value  if level $n >= 10
 *
 *----------------------------------------------------------------------
 */
int cmd_eval_mob( sh_int vnum, char *line, int check,
        CHAR_DATA *mob, CHAR_DATA *ch,
        const void *arg1, const void *arg2, CHAR_DATA *rch )
{
    CHAR_DATA *lval_char = mob;
    CHAR_DATA *vch = (CHAR_DATA *) arg2;
    OBJ_DATA *obj1 = (OBJ_DATA  *) arg1;
    OBJ_DATA *obj2 = (OBJ_DATA  *) arg2;
    OBJ_DATA  *lval_obj = NULL;

    char *original, buf[MAX_INPUT_LENGTH], code;
    int lval = 0, oper = 0, rval = -1;

    original = line;
    line = one_argument( line, buf );
    if ( buf[0] == '\0' || mob == NULL )
        return FALSE;

    /*
     * If this mobile has no target, let's assume our victim is the one
     */
    if ( mob->mprog_target == NULL )
        mob->mprog_target = ch;

    switch ( check )
    {
        /*
         * Case 1: keyword and value
         */
        case CHK_RAND:
            return( number_percent() < atoi( buf ));
        case CHK_MOBHERE:
            if ( is_number( buf ) )
                return( get_mob_vnum_room( mob, NULL, NULL, atoi(buf) ) );
            else
                return( (bool) (get_char_room( mob, NULL, buf) != NULL) );
        case CHK_OBJHERE:
            if ( is_number( buf ) )
                return( get_obj_vnum_room( mob, NULL, NULL, atoi(buf) ) );
            else
                return( (bool) (get_obj_here( mob, NULL, buf) != NULL) );
        case CHK_MOBEXISTS:
            return( (bool) (get_char_world( mob, buf) != NULL) );
        case CHK_OBJEXISTS:
            return( (bool) (get_obj_world( mob, buf) != NULL) );
            /*
             * Case 2 begins here: We sneakily use rval to indicate need
             * 		       for numeric eval...
             */
        case CHK_PEOPLE:
            rval = count_people_room( mob, NULL, NULL, 0 ); break;
        case CHK_PLAYERS:
            rval = count_people_room( mob, NULL, NULL, 1 ); break;
        case CHK_MOBS:
            rval = count_people_room( mob, NULL, NULL, 2 ); break;
        case CHK_CLONES:
            rval = count_people_room( mob, NULL, NULL, 3 ); break;
        case CHK_ORDER:
            rval = get_order( mob, NULL ); break;
        case CHK_HOUR:
            rval = time_info.hour; break;
        default:;
    }

    /*
     * Case 2 continued: evaluate expression
     */
    if ( rval >= 0 )
    {
        if ( (oper = keyword_lookup( fn_evals, buf )) < 0 )
        {
            sprintf( buf, "Cmd_eval_mob: prog %d syntax error(2) '%s'",
                    vnum, original );
            bug( buf, 0 );
            return FALSE;
        }
        one_argument( line, buf );
        lval = rval;
        rval = atoi( buf );
        return( num_eval( lval, oper, rval ) );
    }

    /*
     * Case 3,4,5: Grab actors from $* codes
     */
    if ( buf[0] != '$' || buf[1] == '\0' )
    {
        sprintf( buf, "Cmd_eval_mob: prog %d syntax error(3) '%s'",
                vnum, original );
        bug( buf, 0 );
        return FALSE;
    }
    else
        code = buf[1];
    switch( code )
    {
        case 'i':
            lval_char = mob; break;
        case 'n':
            lval_char = ch; break;
        case 't':
            lval_char = vch; break;
        case 'r':
            lval_char = (rch == NULL ? get_random_char( mob, NULL, NULL ) : rch ); break;
        case 'o':
            lval_obj = obj1; break;
        case 'p':
            lval_obj = obj2; break;
        case 'q':
            lval_char = mob->mprog_target; break;
        default:
            sprintf( buf, "Cmd_eval_mob: prog %d syntax error(4) '%s'",
                    vnum, original );
            bug( buf, 0 );
            return FALSE;
    }
    /*
     * From now on, we need an actor, so if none was found, bail out
     */
    if ( lval_char == NULL && lval_obj == NULL )
        return FALSE;

    /*
     * Case 3: Keyword, comparison and value
     */
    switch( check )
    {
        case CHK_ISPC:
            return( lval_char != NULL && !IS_NPC( lval_char ) );
        case CHK_ISNPC:
            return( lval_char != NULL && IS_NPC( lval_char ) );
        case CHK_ISGOOD:
            return( lval_char != NULL && IS_GOOD( lval_char ) );
        case CHK_ISEVIL:
            return( lval_char != NULL && IS_EVIL( lval_char ) );
        case CHK_ISNEUTRAL:
            return( lval_char != NULL && IS_NEUTRAL( lval_char ) );
        case CHK_ISIMMORT:
            return( lval_char != NULL && IS_IMMORTAL( lval_char ) );
        case CHK_ISCHARM: /* A relic from MERC 2.2 MOBprograms */
            return( lval_char != NULL && IS_AFFECTED( lval_char, AFF_CHARM ) );
        case CHK_ISFOLLOW:
            return( lval_char != NULL && lval_char->master != NULL
                    && lval_char->master->in_room == lval_char->in_room );
        case CHK_ISACTIVE:
            return( lval_char != NULL && lval_char->position > POS_SLEEPING );
        case CHK_ISDELAY:
            return( lval_char != NULL && lval_char->mprog_delay > 0 );
        case CHK_ISVISIBLE:
            switch( code )
            {
                default :
                case 'i':
                case 'n':
                case 't':
                case 'r':
                case 'q':
                    return( lval_char != NULL && can_see( mob, lval_char ) );
                case 'o':
                case 'p':
                    return( lval_obj != NULL && can_see_obj( mob, lval_obj ) );
            }
        case CHK_HASTARGET:
            return( lval_char != NULL && lval_char->mprog_target != NULL
                    &&  lval_char->in_room == lval_char->mprog_target->in_room );
        case CHK_ISTARGET:
            return( lval_char != NULL && mob->mprog_target == lval_char );
        default:;
    }

    /*
     * Case 4: Keyword, actor and value
     */
    line = one_argument( line, buf );
    switch( check )
    {
        case CHK_AFFECTED:
            return( lval_char != NULL
                    &&  STR_IS_SET(lval_char->affected_by, flag_lookup(buf, affect_flags)) );
        case CHK_ACT:
            return( lval_char != NULL
                    &&  IS_SET(lval_char->act, flag_lookup(buf, act_flags)) );
        case CHK_IMM:
            return( lval_char != NULL
                    &&  IS_SET(lval_char->imm_flags, flag_lookup(buf, imm_flags)) );
        case CHK_OFF:
            return( lval_char != NULL
                    &&  IS_SET(lval_char->off_flags, flag_lookup(buf, off_flags)) );
        case CHK_CARRIES:
            if ( is_number( buf ) )
                return( lval_char != NULL && has_item( lval_char, atoi(buf), -1, FALSE ) );
            else
                return( lval_char != NULL && (get_obj_carry( lval_char, buf, lval_char ) != NULL) );
        case CHK_WEARS:
            if ( is_number( buf ) )
                return( lval_char != NULL && has_item( lval_char, atoi(buf), -1, TRUE ) );
            else
                return( lval_char != NULL && (get_obj_wear( lval_char, buf, TRUE ) != NULL) );
        case CHK_HAS:
            return( lval_char != NULL && has_item( lval_char, -1, item_lookup(buf), FALSE ) );
        case CHK_USES:
            return( lval_char != NULL && has_item( lval_char, -1, item_lookup(buf), TRUE ) );
        case CHK_NAME:
            switch( code )
            {
                default :
                case 'i':
                case 'n':
                case 't':
                case 'r':
                case 'q':
                    return( lval_char != NULL && is_name( buf, lval_char->name ) );
                case 'o':
                case 'p':
                    return( lval_obj != NULL && is_name( buf, lval_obj->name ) );
            }
        case CHK_POS:
            return( lval_char != NULL && lval_char->position == position_lookup( buf ) );
        case CHK_CLAN:
            return( lval_char != NULL && lval_char->clan == clan_lookup( buf ) );
        case CHK_RACE:
            return( lval_char != NULL && lval_char->race == race_lookup( buf ) );
        case CHK_OBJTYPE:
            return( lval_obj != NULL && lval_obj->item_type == item_lookup( buf ) );
        default:;
    }

    /*
     * Case 5: Keyword, actor, comparison and value
     */
    if ( (oper = keyword_lookup( fn_evals, buf )) < 0 )
    {
        sprintf( buf, "Cmd_eval_mob: prog %d syntax error(5): '%s'",
                vnum, original );
        bug( buf, 0 );
        return FALSE;
    }
    one_argument( line, buf );
    rval = atoi( buf );

    switch( check )
    {
        case CHK_VNUM:
            switch( code )
            {
                default :
                case 'i':
                case 'n':
                case 't':
                case 'r':
                case 'q':
                    if( lval_char != NULL && IS_NPC( lval_char ) )
                        lval = lval_char->pIndexData->vnum;
                    break;
                case 'o':
                case 'p':
                    if ( lval_obj != NULL )
                        lval = lval_obj->pIndexData->vnum;
            }
            break;
        case CHK_HPCNT:
            if ( lval_char != NULL ) lval = (lval_char->hit * 100)/(UMAX(1,lval_char->max_hit)); break;
        case CHK_ROOM:
            if ( lval_char != NULL && lval_char->in_room != NULL )
                lval = lval_char->in_room->vnum; break;
        case CHK_SEX:
            if ( lval_char != NULL ) lval = lval_char->sex; break;
        case CHK_LEVEL:
            if ( lval_char != NULL ) lval = lval_char->level; break;
        case CHK_ALIGN:
            if ( lval_char != NULL ) lval = lval_char->alignment; break;
        case CHK_MONEY:  /* Money is converted to silver... */
            if ( lval_char != NULL )
                lval = lval_char->gold + (lval_char->silver * 10); break;
        case CHK_OBJVAL0:
            if ( lval_obj != NULL ) lval = lval_obj->value[0]; break;
        case CHK_OBJVAL1:
            if ( lval_obj != NULL ) lval = lval_obj->value[1]; break;
        case CHK_OBJVAL2:
            if ( lval_obj != NULL ) lval = lval_obj->value[2]; break;
        case CHK_OBJVAL3:
            if ( lval_obj != NULL ) lval = lval_obj->value[3]; break;
        case CHK_OBJVAL4:
            if ( lval_obj != NULL ) lval = lval_obj->value[4]; break;
        case CHK_GRPSIZE:
            if( lval_char != NULL ) lval = count_people_room( lval_char, NULL, NULL, 4 ); break;
        default:
            return FALSE;
    }
    return( num_eval( lval, oper, rval ) );
}

int cmd_eval_obj( sh_int vnum, char *line, int check,
        OBJ_DATA *obj, CHAR_DATA *ch,
        const void *arg1, const void *arg2, CHAR_DATA *rch )
{
    CHAR_DATA *lval_char = NULL;
    CHAR_DATA *vch = (CHAR_DATA *) arg2;
    OBJ_DATA *obj1 = (OBJ_DATA  *) arg1;
    OBJ_DATA *obj2 = (OBJ_DATA  *) arg2;
    OBJ_DATA *lval_obj = obj;

    char *original, buf[MAX_INPUT_LENGTH], code;
    int lval = 0, oper = 0, rval = -1;

    original = line;
    line = one_argument( line, buf );
    if ( buf[0] == '\0' || obj == NULL )
        return FALSE;

    /*
     * If this object has no target, let's assume our victim is the one
     */
    if ( obj->oprog_target == NULL )
        obj->oprog_target = ch;

    OBJ_DATA *host;
    //find_container_or_obj will return either the container obj is in (allow nesting)
    //or return the obj itself
    host = find_container_or_obj(obj);


    switch ( check )
    {
        /*
         * Case 1: keyword and value
         */
        case CHK_RAND:
            return( number_percent() < atoi( buf ));
        case CHK_MOBHERE:
            if ( is_number( buf ) )
                return( get_mob_vnum_room( NULL, host, NULL,  atoi(buf) ) );
            else
                return( (bool) (get_char_room( NULL, (host->in_room?host->in_room:host->carried_by->in_room), buf) != NULL) );
        case CHK_OBJHERE:
            if ( is_number( buf ) )
                return( get_obj_vnum_room( NULL, host, NULL,  atoi(buf) ) );
            else
                return( (bool) (get_obj_here( NULL, (host->in_room?host->in_room:host->carried_by->in_room), buf) != NULL) );
        case CHK_MOBEXISTS:
            return( (bool) (get_char_world( NULL, buf) != NULL) );
        case CHK_OBJEXISTS:
            return( (bool) (get_obj_world( NULL, buf) != NULL) );
        case CHK_ISINOBJ:
            if (host == obj)
                return FALSE;
            if (!is_number(buf))
                return (is_name(buf, host->name));
            else
                return (host->pIndexData->vnum == atoi(buf));
            /*
             * Case 2 begins here: We sneakily use rval to indicate need
             * 		       for numeric eval...
             */
        case CHK_PEOPLE:
            rval = count_people_room( NULL, host, NULL, 0 ); break;
        case CHK_PLAYERS:
            rval = count_people_room( NULL, host, NULL, 1 ); break;
        case CHK_MOBS:
            rval = count_people_room( NULL, host, NULL, 2 ); break;
        case CHK_CLONES:
            bug( "cmd_eval_obj: received CHK_CLONES.",0); break;
        case CHK_ORDER:
            rval = get_order( NULL, host ); break;
        case CHK_HOUR:
            rval = time_info.hour; break;
        default:;
    }

    /*
     * Case 2 continued: evaluate expression
     */
    if ( rval >= 0 )
    {
        if ( (oper = keyword_lookup( fn_evals, buf )) < 0 )
        {
            sprintf( buf, "Cmd_eval_obj: prog %d syntax error(2) '%s'",
                    vnum, original );
            bug( buf, 0 );
            return FALSE;
        }
        one_argument( line, buf );
        lval = rval;
        rval = atoi( buf );
        return( num_eval( lval, oper, rval ) );
    }

    /*
     * Case 3,4,5: Grab actors from $* codes
     */
    if ( buf[0] != '$' || buf[1] == '\0' )
    {
        sprintf( buf, "Cmd_eval_obj: prog %d syntax error(3) '%s'",
                vnum, original );
        bug( buf, 0 );
        return FALSE;
    }
    else
        code = buf[1];
    switch( code )
    {
        case 'i':
            lval_obj = obj; break;
        case 'n':
            lval_char = ch; break;
        case 't':
            lval_char = vch; break;
        case 'r':
            lval_char = rch == NULL ? get_random_char( NULL, obj, NULL ) : rch ; break;
        case 'o':
            lval_obj = obj1; break;
        case 'p':
            lval_obj = obj2; break;
        case 'q':
            lval_char = obj->oprog_target; break;
        default:
            sprintf( buf, "Cmd_eval_obj: prog %d syntax error(4) '%s'",
                    vnum, original );
            bug( buf, 0 );
            return FALSE;
    }
    /*
     * From now on, we need an actor, so if none was found, bail out
     */
    if ( lval_char == NULL && lval_obj == NULL )
        return FALSE;

    /*
     * Case 3: Keyword, comparison and value
     */
    switch( check )
    {
        case CHK_ISPC:
            return( lval_char != NULL && !IS_NPC( lval_char ) );
        case CHK_ISNPC:
            return( lval_char != NULL && IS_NPC( lval_char ) );
        case CHK_ISGOOD:
            return( lval_char != NULL && IS_GOOD( lval_char ) );
        case CHK_ISEVIL:
            return( lval_char != NULL && IS_EVIL( lval_char ) );
        case CHK_ISNEUTRAL:
            return( lval_char != NULL && IS_NEUTRAL( lval_char ) );
        case CHK_ISIMMORT:
            return( lval_char != NULL && IS_IMMORTAL( lval_char ) );
        case CHK_ISCHARM: /* A relic from MERC 2.2 MOBprograms */
            return( lval_char != NULL && IS_AFFECTED( lval_char, AFF_CHARM ) );
        case CHK_ISFOLLOW:
            return( lval_char != NULL && lval_char->master != NULL
                    && lval_char->master->in_room == lval_char->in_room );
        case CHK_ISACTIVE:
            return( lval_char != NULL && lval_char->position > POS_SLEEPING );
        case CHK_ISDELAY:
            return( lval_char != NULL && lval_char->mprog_delay > 0 );
        case CHK_HASTARGET:
            return( lval_char != NULL && lval_char->mprog_target != NULL
                    &&  lval_char->in_room == lval_char->mprog_target->in_room );
        case CHK_ISTARGET:
            return( lval_char != NULL && obj->oprog_target == lval_char );
        default:;
    }

    /*
     * Case 4: Keyword, actor and value
     */
    line = one_argument( line, buf );
    switch( check )
    {
        case CHK_AFFECTED:
            return( lval_char != NULL
                    &&  STR_IS_SET(lval_char->affected_by, flag_lookup(buf, affect_flags)) );
        case CHK_ACT:
            return( lval_char != NULL
                    &&  IS_SET(lval_char->act, flag_lookup(buf, act_flags)) );
        case CHK_IMM:
            return( lval_char != NULL
                    &&  IS_SET(lval_char->imm_flags, flag_lookup(buf, imm_flags)) );
        case CHK_OFF:
            return( lval_char != NULL
                    &&  IS_SET(lval_char->off_flags, flag_lookup(buf, off_flags)) );
        case CHK_CARRIES:
            if ( is_number( buf ) )
                return( lval_char != NULL && has_item( lval_char, atoi(buf), -1, FALSE ) );
            else
                return( lval_char != NULL && (get_obj_carry( lval_char, buf, lval_char ) != NULL) );
        case CHK_WEARS:
            if ( is_number( buf ) )
                return( lval_char != NULL && has_item( lval_char, atoi(buf), -1, TRUE ) );
            else
                return( lval_char != NULL && (get_obj_wear( lval_char, buf, FALSE ) != NULL) );
        case CHK_HAS:
            return( lval_char != NULL && has_item( lval_char, -1, item_lookup(buf), FALSE ) );
        case CHK_USES:
            return( lval_char != NULL && has_item( lval_char, -1, item_lookup(buf), TRUE ) );
        case CHK_NAME:
            switch( code )
            {
                default :
                case 'n':
                case 't':
                case 'r':
                case 'q':
                    return( lval_char != NULL && is_name( buf, lval_char->name ) );
                case 'i':
                case 'o':
                case 'p':
                    return( lval_obj != NULL && is_name( buf, lval_obj->name ) );
            }
        case CHK_POS:
            return( lval_char != NULL && lval_char->position == position_lookup( buf ) );
        case CHK_CLAN:
            return( lval_char != NULL && lval_char->clan == clan_lookup( buf ) );
        case CHK_RACE:
            return( lval_char != NULL && lval_char->race == race_lookup( buf ) );
        case CHK_OBJTYPE:
            return( lval_obj != NULL && lval_obj->item_type == item_lookup( buf ) );
        default:;
    }

    /*
     * Case 5: Keyword, actor, comparison and value
     */
    if ( (oper = keyword_lookup( fn_evals, buf )) < 0 )
    {
        sprintf( buf, "Cmd_eval_obj: prog %d syntax error(5): '%s'",
                vnum, original );
        bug( buf, 0 );
        return FALSE;
    }
    one_argument( line, buf );
    rval = atoi( buf );

    switch( check )
    {
        case CHK_VNUM:
            switch( code )
            {
                default :
                case 'n':
                case 't':
                case 'r':
                case 'q':
                    if( lval_char != NULL && IS_NPC( lval_char ) )
                        lval = lval_char->pIndexData->vnum;
                    break;
                case 'i':
                case 'o':
                case 'p':
                    if ( lval_obj != NULL )
                        lval = lval_obj->pIndexData->vnum;
            }
            break;
        case CHK_HPCNT:
            if ( lval_char != NULL ) lval = (lval_char->hit * 100)/(UMAX(1,lval_char->max_hit)); break;
        case CHK_ROOM:
            if ( lval_char != NULL && lval_char->in_room != NULL )
                lval = lval_char->in_room->vnum;
            else if ( lval_obj != NULL && (lval_obj->in_room != NULL || lval_obj->carried_by != NULL ))
                lval = lval_obj->in_room?lval_obj->in_room->vnum:lval_obj->carried_by->in_room->vnum;
            break;
        case CHK_SEX:
            if ( lval_char != NULL ) lval = lval_char->sex; break;
        case CHK_LEVEL:
            if ( lval_char != NULL ) lval = lval_char->level; break;
        case CHK_ALIGN:
            if ( lval_char != NULL ) lval = lval_char->alignment; break;
        case CHK_MONEY:  /* Money is converted to silver... */
            if ( lval_char != NULL )
                lval = lval_char->gold + (lval_char->silver * 10); break;
        case CHK_OBJVAL0:
            if ( lval_obj != NULL ) lval = lval_obj->value[0]; break;
        case CHK_OBJVAL1:
            if ( lval_obj != NULL ) lval = lval_obj->value[1]; break;
        case CHK_OBJVAL2:
            if ( lval_obj != NULL ) lval = lval_obj->value[2]; break;
        case CHK_OBJVAL3:
            if ( lval_obj != NULL ) lval = lval_obj->value[3]; break;
        case CHK_OBJVAL4:
            if ( lval_obj != NULL ) lval = lval_obj->value[4]; break;
        case CHK_GRPSIZE:
            if( lval_char != NULL ) lval = count_people_room( lval_char, NULL, NULL, 4 ); break;
        default:
            return FALSE;
    }
    return( num_eval( lval, oper, rval ) );
}

int cmd_eval_room( sh_int vnum, char *line, int check,
        ROOM_INDEX_DATA *room, CHAR_DATA *ch,
        const void *arg1, const void *arg2, CHAR_DATA *rch )
{
    CHAR_DATA *lval_char = NULL;
    CHAR_DATA *vch = (CHAR_DATA *) arg2;
    OBJ_DATA *obj1 = (OBJ_DATA  *) arg1;
    OBJ_DATA *obj2 = (OBJ_DATA  *) arg2;
    OBJ_DATA *lval_obj = NULL;

    char *original, buf[MAX_INPUT_LENGTH], code;
    int lval = 0, oper = 0, rval = -1;

    original = line;
    line = one_argument( line, buf );
    if ( buf[0] == '\0' || room == NULL )
        return FALSE;

    /*
     * If this room has no target, let's assume our victim is the one
     */
    if ( room->rprog_target == NULL )
        room->rprog_target = ch;

    switch ( check )
    {
        /*
         * Case 1: keyword and value
         */
        case CHK_RAND:
            return( number_percent() < atoi( buf ));
        case CHK_MOBHERE:
            if ( is_number( buf ) )
                return( get_mob_vnum_room( NULL, NULL, room,  atoi(buf) ) );
            else
                return( (bool) (get_char_room( NULL, room, buf) != NULL) );
        case CHK_OBJHERE:
            if ( is_number( buf ) )
                return( get_obj_vnum_room( NULL, NULL, room,  atoi(buf) ) );
            else
                return( (bool) (get_obj_here( NULL, room, buf) != NULL) );
        case CHK_MOBEXISTS:
            return( (bool) (get_char_world( NULL, buf) != NULL) );
        case CHK_OBJEXISTS:
            return( (bool) (get_obj_world( NULL, buf) != NULL) );
            /*
             * Case 2 begins here: We sneakily use rval to indicate need
             * 		       for numeric eval...
             */
        case CHK_PEOPLE:
            rval = count_people_room( NULL, NULL, room, 0 ); break;
        case CHK_PLAYERS:
            rval = count_people_room( NULL, NULL, room, 1 ); break;
        case CHK_MOBS:
            rval = count_people_room( NULL, NULL, room, 2 ); break;
        case CHK_CLONES:
            bug( "Cmd_eval_room: received CHK_CLONES.",0); break;
        case CHK_ORDER:
            bug( "Cmd_eval_room: received CHK_ORDER.",0); break;
        case CHK_HOUR:
            rval = time_info.hour; break;
        default:;
    }

    /*
     * Case 2 continued: evaluate expression
     */
    if ( rval >= 0 )
    {
        if ( (oper = keyword_lookup( fn_evals, buf )) < 0 )
        {
            sprintf( buf, "Cmd_eval_room: prog %d syntax error(2) '%s'",
                    vnum, original );
            bug( buf, 0 );
            return FALSE;
        }
        one_argument( line, buf );
        lval = rval;
        rval = atoi( buf );
        return( num_eval( lval, oper, rval ) );
    }

    /*
     * Case 3,4,5: Grab actors from $* codes
     */
    if ( buf[0] != '$' || buf[1] == '\0' )
    {
        sprintf( buf, "Cmd_eval_room: prog %d syntax error(3) '%s'",
                vnum, original );
        bug( buf, 0 );
        return FALSE;
    }
    else
        code = buf[1];
    switch( code )
    {
        case 'i':
            bug( "Cmd_eval_room: received code case 'i'.",0); break;
        case 'n':
            lval_char = ch; break;
        case 't':
            lval_char = vch; break;
        case 'r':
            lval_char = rch == NULL ? get_random_char( NULL, NULL, room ) : rch ; break;
        case 'o':
            lval_obj = obj1; break;
        case 'p':
            lval_obj = obj2; break;
        case 'q':
            lval_char = room->rprog_target; break;
        default:
            sprintf( buf, "Cmd_eval_room: prog %d syntax error(4) '%s'",
                    vnum, original );
            bug( buf, 0 );
            return FALSE;
    }
    /*
     * From now on, we need an actor, so if none was found, bail out
     */
    if ( lval_char == NULL && lval_obj == NULL )
        return FALSE;

    /*
     * Case 3: Keyword, comparison and value
     */
    switch( check )
    {
        case CHK_ISPC:
            return( lval_char != NULL && !IS_NPC( lval_char ) );
        case CHK_ISNPC:
            return( lval_char != NULL && IS_NPC( lval_char ) );
        case CHK_ISGOOD:
            return( lval_char != NULL && IS_GOOD( lval_char ) );
        case CHK_ISEVIL:
            return( lval_char != NULL && IS_EVIL( lval_char ) );
        case CHK_ISNEUTRAL:
            return( lval_char != NULL && IS_NEUTRAL( lval_char ) );
        case CHK_ISIMMORT:
            return( lval_char != NULL && IS_IMMORTAL( lval_char ) );
        case CHK_ISCHARM: /* A relic from MERC 2.2 MOBprograms */
            return( lval_char != NULL && IS_AFFECTED( lval_char, AFF_CHARM ) );
        case CHK_ISFOLLOW:
            return( lval_char != NULL && lval_char->master != NULL
                    && lval_char->master->in_room == lval_char->in_room );
        case CHK_ISACTIVE:
            return( lval_char != NULL && lval_char->position > POS_SLEEPING );
        case CHK_ISDELAY:
            return( lval_char != NULL && lval_char->mprog_delay > 0 );
        case CHK_HASTARGET:
            return( lval_char != NULL && lval_char->mprog_target != NULL
                    &&  lval_char->in_room == lval_char->mprog_target->in_room );
        case CHK_ISTARGET:
            return( lval_char != NULL && room->rprog_target == lval_char );
        default:;
    }

    /*
     * Case 4: Keyword, actor and value
     */
    line = one_argument( line, buf );
    switch( check )
    {
        case CHK_AFFECTED:
            return( lval_char != NULL
                    &&  STR_IS_SET(lval_char->affected_by, flag_lookup(buf, affect_flags)) );
        case CHK_ACT:
            return( lval_char != NULL
                    &&  IS_SET(lval_char->act, flag_lookup(buf, act_flags)) );
        case CHK_IMM:
            return( lval_char != NULL
                    &&  IS_SET(lval_char->imm_flags, flag_lookup(buf, imm_flags)) );
        case CHK_OFF:
            return( lval_char != NULL
                    &&  IS_SET(lval_char->off_flags, flag_lookup(buf, off_flags)) );
        case CHK_CARRIES:
            if ( is_number( buf ) )
                return( lval_char != NULL && has_item( lval_char, atoi(buf), -1, FALSE ) );
            else
                return( lval_char != NULL && (get_obj_carry( lval_char, buf, lval_char ) != NULL) );
        case CHK_WEARS:
            if ( is_number( buf ) )
                return( lval_char != NULL && has_item( lval_char, atoi(buf), -1, TRUE ) );
            else
                return( lval_char != NULL && (get_obj_wear( lval_char, buf, FALSE ) != NULL) );
        case CHK_HAS:
            return( lval_char != NULL && has_item( lval_char, -1, item_lookup(buf), FALSE ) );
        case CHK_USES:
            return( lval_char != NULL && has_item( lval_char, -1, item_lookup(buf), TRUE ) );
        case CHK_NAME:
            switch( code )
            {
                default :
                case 'n':
                case 't':
                case 'r':
                case 'q':
                    return( lval_char != NULL && is_name( buf, lval_char->name ) );
                case 'i':
                    return FALSE;
                case 'o':
                case 'p':
                    return( lval_obj != NULL && is_name( buf, lval_obj->name ) );
            }
        case CHK_POS:
            return( lval_char != NULL && lval_char->position == position_lookup( buf ) );
        case CHK_CLAN:
            return( lval_char != NULL && lval_char->clan == clan_lookup( buf ) );
        case CHK_RACE:
            return( lval_char != NULL && lval_char->race == race_lookup( buf ) );
        case CHK_OBJTYPE:
            return( lval_obj != NULL && lval_obj->item_type == item_lookup( buf ) );
        default:;
    }

    /*
     * Case 5: Keyword, actor, comparison and value
     */
    if ( (oper = keyword_lookup( fn_evals, buf )) < 0 )
    {
        sprintf( buf, "Cmd_eval_room: prog %d syntax error(5): '%s'",
                vnum, original );
        bug( buf, 0 );
        return FALSE;
    }
    one_argument( line, buf );
    rval = atoi( buf );

    switch( check )
    {
        case CHK_VNUM:
            switch( code )
            {
                default :
                case 'n':
                case 't':
                case 'r':
                case 'q':
                    if( lval_char != NULL && IS_NPC( lval_char ) )
                        lval = lval_char->pIndexData->vnum;
                    break;
                case 'i':
                    return FALSE;
                case 'o':
                case 'p':
                    if ( lval_obj != NULL )
                        lval = lval_obj->pIndexData->vnum;
            }
            break;
        case CHK_HPCNT:
            if ( lval_char != NULL ) lval = (lval_char->hit * 100)/(UMAX(1,lval_char->max_hit)); break;
        case CHK_ROOM:
            if ( lval_char != NULL && lval_char->in_room != NULL )
                lval = lval_char->in_room->vnum;
            else if ( lval_obj != NULL && (lval_obj->in_room != NULL || lval_obj->carried_by != NULL ) )
                lval = lval_obj->in_room?lval_obj->in_room->vnum:lval_obj->carried_by->in_room->vnum;
            break;
        case CHK_SEX:
            if ( lval_char != NULL ) lval = lval_char->sex; break;
        case CHK_LEVEL:
            if ( lval_char != NULL ) lval = lval_char->level; break;
        case CHK_ALIGN:
            if ( lval_char != NULL ) lval = lval_char->alignment; break;
        case CHK_MONEY:  /* Money is converted to silver... */
            if ( lval_char != NULL )
                lval = lval_char->gold + (lval_char->silver * 10); break;
        case CHK_OBJVAL0:
            if ( lval_obj != NULL ) lval = lval_obj->value[0]; break;
        case CHK_OBJVAL1:
            if ( lval_obj != NULL ) lval = lval_obj->value[1]; break;
        case CHK_OBJVAL2:
            if ( lval_obj != NULL ) lval = lval_obj->value[2]; break;
        case CHK_OBJVAL3:
            if ( lval_obj != NULL ) lval = lval_obj->value[3]; break;
        case CHK_OBJVAL4:
            if ( lval_obj != NULL ) lval = lval_obj->value[4]; break;
        case CHK_GRPSIZE:
            if( lval_char != NULL ) lval = count_people_room( lval_char, NULL, NULL, 4 ); break;
        default:
            return FALSE;
    }
    return( num_eval( lval, oper, rval ) );
}

/*
 * ------------------------------------------------------------------------
 * EXPAND_ARG
 * These are hacks of act() in comm.c. I've added some safety guards,
 * so that missing or invalid $-codes do not crash the server
 * ------------------------------------------------------------------------
 */
void expand_arg_mob( char *buf,
        const char *format,
        CHAR_DATA *mob, CHAR_DATA *ch,
        const void *arg1, const void *arg2, CHAR_DATA *rch )
{
    static char * const he_she  [] = { "it",  "he",  "she" };
    static char * const him_her [] = { "it",  "him", "her" };
    static char * const his_her [] = { "its", "his", "her" };
    const char *someone = "someone";
    const char *something = "something";
    const char *someones = "someone's";

    char fname[MAX_INPUT_LENGTH];
    CHAR_DATA *vch = (CHAR_DATA *) arg2;
    OBJ_DATA *obj1 = (OBJ_DATA  *) arg1;
    OBJ_DATA *obj2 = (OBJ_DATA  *) arg2;
    const char *str;
    const char *i;
    char *point;

    /*
     * Discard null and zero-length messages.
     */
    if ( format == NULL || format[0] == '\0' )
        return;

    point   = buf;
    str     = format;
    while ( *str != '\0' )
    {
        if ( *str != '$' )
        {
            *point++ = *str++;
            continue;
        }
        ++str;

        switch ( *str )
        {
            default:  bug( "Expand_arg_mob: bad code %d.", *str );
                      i = " <@@@> ";                        break;
            case 'i':
                      one_argument( mob->name, fname );
                      i = fname;                         		break;
            case 'I': i = mob->short_descr;                     break;
            case 'n':
                      i = someone;
                      if ( ch != NULL && can_see( mob, ch ) )
                      {
                          one_argument( ch->name, fname );
                          i = capitalize(fname);
                      }						break;
            case 'N':
                      i = (ch != NULL && can_see( mob, ch ) )
                          ? ( IS_NPC( ch ) ? ch->short_descr : ch->name )
                          : someone;                         		break;
            case 't':
                      i = someone;
                      if ( vch != NULL && can_see( mob, vch ) )
                      {
                          one_argument( vch->name, fname );
                          i = capitalize(fname);
                      }						break;
            case 'T':
                      i = (vch != NULL && can_see( mob, vch ))
                          ? ( IS_NPC( vch ) ? vch->short_descr : vch->name )
                          : someone;                         		break;
            case 'r':
                      if ( rch == NULL )
                          rch = get_random_char( mob, NULL, NULL );
                      i = someone;
                      if( rch != NULL && can_see( mob, rch ) )
                      {
                          one_argument( rch->name, fname );
                          i = capitalize(fname);
                      } 						break;
            case 'R':
                      i = someone;
                      if ( rch == NULL )
                          rch = get_random_char( mob, NULL, NULL );
                      //Expanded below because it confused me. JH 5/23/2004 12:14AM
                      //Also, it had a 'can_see' in there--why? get_random_char checks for can_see.
                      if (rch)
                      {
                          if (IS_NPC(rch))
                              i = rch->short_descr;
                          else
                              i = rch->name;
                      }
                break;
            case 'q':
                      i = someone;
                      if ( mob->mprog_target != NULL && can_see( mob, mob->mprog_target ) )
                      {
                          one_argument( mob->mprog_target->name, fname );
                          i = capitalize( fname );
                      } 						break;
            case 'Q':
                      i = (mob->mprog_target != NULL && can_see( mob, mob->mprog_target ))
                          ? ( IS_NPC( mob->mprog_target ) ? mob->mprog_target->short_descr : mob->mprog_target->name )
                          : someone;                         		break;
            case 'j': i = he_she  [URANGE(0, mob->sex, 2)];     break;
            case 'e':
                      i = (ch != NULL && can_see( mob, ch ))
                          ? he_she  [URANGE(0, ch->sex, 2)]
                          : someone;					break;
            case 'E':
                      i = (vch != NULL && can_see( mob, vch ))
                          ? he_she  [URANGE(0, vch->sex, 2)]
                          : someone;					break;
            case 'J':
                      i = (rch != NULL && can_see( mob, rch ))
                          ? he_she  [URANGE(0, rch->sex, 2)]
                          : someone;					break;
            case 'X':
                      i = (mob->mprog_target != NULL && can_see( mob, mob->mprog_target))
                          ? he_she  [URANGE(0, mob->mprog_target->sex, 2)]
                          : someone;					break;
            case 'k': i = him_her [URANGE(0, mob->sex, 2)];	break;
            case 'm':
                      i = (ch != NULL && can_see( mob, ch ))
                          ? him_her [URANGE(0, ch  ->sex, 2)]
                          : someone;        				break;
            case 'M':
                      i = (vch != NULL && can_see( mob, vch ))
                          ? him_her [URANGE(0, vch ->sex, 2)]
                          : someone;					break;
            case 'K':
                      if ( rch == NULL )
                          rch = get_random_char( mob, NULL, NULL );
                      i = (rch != NULL && can_see( mob, rch ))
                          ? him_her [URANGE(0, rch ->sex, 2)]
                          : someone;					break;
            case 'Y':
                      i = (mob->mprog_target != NULL && can_see( mob, mob->mprog_target ))
                          ? him_her [URANGE(0, mob->mprog_target->sex, 2)]
                          : someone;					break;
            case 'l': i = his_her [URANGE(0, mob ->sex, 2)];    break;
            case 's':
                      i = (ch != NULL && can_see( mob, ch ))
                          ? his_her [URANGE(0, ch ->sex, 2)]
                          : someones;					break;
            case 'S':
                      i = (vch != NULL && can_see( mob, vch ))
                          ? his_her [URANGE(0, vch ->sex, 2)]
                          : someones;					break;
            case 'L':
                      if ( rch == NULL )
                          rch = get_random_char( mob, NULL, NULL );
                      i = ( rch != NULL && can_see( mob, rch ) )
                          ? his_her [URANGE(0, rch ->sex, 2)]
                          : someones;					break;
            case 'Z':
                      i = (mob->mprog_target != NULL && can_see( mob, mob->mprog_target ))
                          ? his_her [URANGE(0, mob->mprog_target->sex, 2)]
                          : someones;					break;
            case 'o':
                      i = something;
                      if ( obj1 != NULL && can_see_obj( mob, obj1 ) )
                      {
                          one_argument( obj1->name, fname );
                          i = fname;
                      } 						break;
            case 'O':
                      i = (obj1 != NULL && can_see_obj( mob, obj1 ))
                          ? obj1->short_descr
                          : something;					break;
            case 'p':
                      i = something;
                      if ( obj2 != NULL && can_see_obj( mob, obj2 ) )
                      {
                          one_argument( obj2->name, fname );
                          i = fname;
                      } 						break;
            case 'P':
                      i = (obj2 != NULL && can_see_obj( mob, obj2 ))
                          ? obj2->short_descr
                          : something;					break;
        }

        ++str;
        while ( ( *point = *i ) != '\0' )
            ++point, ++i;

    }
    *point = '\0';

    return;
}

void expand_arg_other( char *buf,
        const char *format,
        OBJ_DATA *obj, ROOM_INDEX_DATA *room, CHAR_DATA *ch,
        const void *arg1, const void *arg2, CHAR_DATA *rch )
{
    static char * const he_she  [] = { "it",  "he",  "she" };
    static char * const him_her [] = { "it",  "him", "her" };
    static char * const his_her [] = { "its", "his", "her" };
    const char *someone = "someone";
    const char *something = "something";
    const char *someones = "someone's";

    char fname[MAX_INPUT_LENGTH];
    CHAR_DATA *vch = (CHAR_DATA *) arg2;
    OBJ_DATA *obj1 = (OBJ_DATA  *) arg1;
    OBJ_DATA *obj2 = (OBJ_DATA  *) arg2;
    const char *str;
    const char *i;
    char *point;

    if ( obj && room )
    {
        bug( "expand_arg_other received a obj and a room",0);
        return;
    }

    /*
     * Discard null and zero-length messages.
     */
    if ( format == NULL || format[0] == '\0' )
        return;

    point   = buf;
    str     = format;
    while ( *str != '\0' )
    {
        if ( *str != '$' )
        {
            *point++ = *str++;
            continue;
        }
        ++str;

        switch ( *str )
        {
            default:  bug( "Expand_arg: bad code %d.", *str );
                      i = " <@@@> ";                        break;
            case 'i':
                      if ( obj )
                      {
                          one_argument( obj->name, fname );
                          i = fname;
                      }
                      else
                      {
                          bug( "Expand_arg_other: room had an \"i\" case.",0);
                          i = " <@@@> ";
                      }						break;
            case 'I':
                      if ( obj )
                          i = obj->short_descr;
                      else
                      {
                          bug( "Expand_arg_other: room had an \"I\" case.",0);
                          i = " <@@@> ";
                      }						break;
            case 'n':
                      i = someone;
                      if ( ch != NULL )
                      {
                          one_argument( ch->name, fname );
                          i = capitalize(fname);
                      }						break;
            case 'N':
                      i = (ch != NULL )
                          ? ( IS_NPC( ch ) ? ch->short_descr : ch->name )
                          : someone;                         		break;
            case 't':
                      i = someone;
                      if ( vch != NULL )
                      {
                          one_argument( vch->name, fname );
                          i = capitalize(fname);
                      }						break;
            case 'T':
                      i = (vch != NULL )
                          ? ( IS_NPC( vch ) ? vch->short_descr : vch->name )
                          : someone;                         		break;
            case 'r':
                      if ( rch == NULL && obj )
                          rch = get_random_char( NULL, obj, NULL );
                      else if ( rch == NULL && room )
                          rch = get_random_char( NULL, NULL, room );
                      i = someone;
                      if( rch != NULL )
                      {
                          one_argument( rch->name, fname );
                          i = capitalize(fname);
                      } 						break;
            case 'R':
                      if ( rch == NULL && obj )
                          rch = get_random_char( NULL, obj, NULL );
                      else if ( rch == NULL && room )
                          rch = get_random_char( NULL, NULL, room );
                      i  = ( rch != NULL )
                          ? ( IS_NPC( ch ) ? ch->short_descr : ch->name )
                          :someone;					break;
            case 'q':
                      i = someone;
                      if ( obj && obj->oprog_target != NULL )
                      {
                          one_argument( obj->oprog_target->name, fname );
                          i = capitalize( fname );
                      }
                      else if ( room && room->rprog_target != NULL )
                      {
                          one_argument( room->rprog_target->name, fname );
                          i = capitalize( fname );
                      } 						break;
            case 'Q':
                      i = (obj && obj->oprog_target != NULL)
                          ? ( IS_NPC( obj->oprog_target ) ?
                                  obj->oprog_target->short_descr : obj->oprog_target->name )
                          : (room && room->rprog_target != NULL)
                          ? ( IS_NPC( room->rprog_target ) ?
                                  room->rprog_target->short_descr : room->rprog_target->name )
                          : someone;					break;
            case 'j':
                      bug( "Expand_arg_other: Obj/room received case 'j'",0);
                      i = " <@@@> ";     break;
            case 'e':
                      i = (ch != NULL )
                          ? he_she  [URANGE(0, ch->sex, 2)]
                          : someone;					break;
            case 'E':
                      i = (vch != NULL )
                          ? he_she  [URANGE(0, vch->sex, 2)]
                          : someone;					break;
            case 'J':
                      i = (rch != NULL )
                          ? he_she  [URANGE(0, rch->sex, 2)]
                          : someone;					break;
            case 'X':
                      i = (obj && obj->oprog_target != NULL )
                          ? he_she  [URANGE(0, obj->oprog_target->sex, 2)]
                          : (room && room->rprog_target != NULL )
                          ? he_she  [URANGE(0, room->rprog_target->sex, 2)]
                          : someone;					break;
            case 'k':
                      bug( "Expand_arg_other: received case 'k'.",0);
                      i = " <@@@> ";					break;
            case 'm':
                      i = (ch != NULL )
                          ? him_her [URANGE(0, ch->sex, 2)]
                          : someone;        				break;
            case 'M':
                      i = (vch != NULL )
                          ? him_her [URANGE(0, vch->sex, 2)]
                          : someone;					break;
            case 'K':
                      if ( obj && rch == NULL )
                          rch = get_random_char( NULL, obj, NULL );
                      else if ( room && rch == NULL )
                          rch = get_random_char( NULL, NULL, room );
                      i = (rch != NULL)
                          ? him_her [URANGE(0, rch ->sex, 2)]
                          : someone;					break;
            case 'Y':
                      i = (obj && obj->oprog_target != NULL)
                          ? him_her [URANGE(0, obj->oprog_target->sex, 2)]
                          : (room && room->rprog_target != NULL)
                          ? him_her [URANGE(0, room->rprog_target->sex, 2)]
                          : someone;					break;
            case 'l':
                      bug( "Expand_arg_other: received case 'l'.",0);
                      i = " <@@@> ";					break;
            case 's':
                      i = (ch != NULL )
                          ? his_her [URANGE(0, ch ->sex, 2)]
                          : someones;					break;
            case 'S':
                      i = (vch != NULL )
                          ? his_her [URANGE(0, vch ->sex, 2)]
                          : someones;					break;
            case 'L':
                      if ( obj && rch == NULL )
                          rch = get_random_char( NULL, obj, NULL );
                      else if ( room && rch == NULL )
                          rch = get_random_char( NULL, NULL, room );
                      i = ( rch != NULL )
                          ? his_her [URANGE(0, rch ->sex, 2)]
                          : someones;					break;
            case 'Z':
                      i = (obj && obj->oprog_target != NULL)
                          ? his_her [URANGE(0, obj->oprog_target->sex, 2)]
                          : (room && room->rprog_target != NULL)
                          ? his_her [URANGE(0, room->rprog_target->sex, 2)]
                          : someones;					break;
            case 'o':
                      i = something;
                      if ( obj1 != NULL )
                      {
                          one_argument( obj1->name, fname );
                          i = fname;
                      } 						break;
            case 'O':
                      i = (obj1 != NULL)
                          ? obj1->short_descr
                          : something;					break;
            case 'p':
                      i = something;
                      if ( obj2 != NULL )
                      {
                          one_argument( obj2->name, fname );
                          i = fname;
                      } 						break;
            case 'P':
                      i = (obj2 != NULL)
                          ? obj2->short_descr
                          : something;					break;
        }

        ++str;
        while ( ( *point = *i ) != '\0' )
            ++point, ++i;

    }
    *point = '\0';

    return;
}

/*
 * ------------------------------------------------------------------------
 *  PROGRAM_FLOW
 *  This is the program driver. It parses the mob program code lines
 *  and passes "executable" commands to interpret()
 *  Lines beginning with 'mob' are passed to mob_interpret() to handle
 *  special mob commands (in mob_cmds.c)
 *-------------------------------------------------------------------------
 */

#define MAX_NESTED_LEVEL 20 /* Maximum nested if-else-endif's (stack size) */
#define BEGIN_BLOCK       0 /* Flag: Begin of if-else-endif block */
#define IN_BLOCK         -1 /* Flag: Executable statements */
#define END_BLOCK        -2 /* Flag: End of if-else-endif block */
#define MAX_CALL_LEVEL   15 /* Maximum nested calls */

void program_flow (
        sh_int pvnum,  /* For diagnostic purposes */
        char *source,  /* the actual MOBprog code */
        CHAR_DATA *mob,
        OBJ_DATA *obj,
        ROOM_INDEX_DATA *room,
        CHAR_DATA *ch, const void *arg1, const void *arg2, int startat )

{
    CHAR_DATA *rch = NULL;
    char *code, *line;
    char buf[MAX_STRING_LENGTH];
    char control[MAX_INPUT_LENGTH], data[MAX_STRING_LENGTH];
    char bugbuf[MAX_STRING_LENGTH];

    static int call_level; /* Keep track of nested "mpcall"s */

    int level, eval, check;
    int state[MAX_NESTED_LEVEL], /* Block state (BEGIN,IN,END) */
    cond[MAX_NESTED_LEVEL];  /* Boolean value based on the last if-check */
    //JH Below two items used for 'wait' control
    int count = 0;
    bool percent_process = FALSE;

    sh_int mvnum = 0,ovnum = 0,rvnum = 0;

    //ch_printf(ch, "1840: program_flow received startat line of %d.\n\r", startat);

    if ( (mob && obj) || (mob && room) || (obj && room) )
    {
        bug( "PROGs: program_flow received multiple prog types.", 0 );
        return;
    }

    if ( mob )
        mvnum = mob->pIndexData->vnum;
    else if ( obj )
        ovnum = obj->pIndexData->vnum;
    else if ( room )
        rvnum = room->vnum;
    else
    {
        bug( "PROGs: program_flow did not receive a prog type.", 0 );
        return;
    }

    /*JH changed for 'wait'
      if( ++call_level > MAX_CALL_LEVEL )
      {
     */
    if( startat == 1 && ++call_level > MAX_CALL_LEVEL )
    {
        if ( mob )
            sprintf( bugbuf, "Progs: MAX_CALL_LEVEL exceeded, vnum %d, mprog vnum %d", mvnum, pvnum );
        else if ( obj )
            sprintf( bugbuf, "Progs: MAX_CALL_LEVEL exceeded, vnum %d oprog vnum %d.", ovnum, pvnum );
        else
            sprintf( bugbuf, "Progs: MAX_CALL_LEVEL exceeded, vnum %d rprog vnum %d.", rvnum, pvnum );

        bug( bugbuf, 0 );
        call_level--;
        return;
    }

    /*
     * Reset "stack"
     */
    for ( level = 0; level < MAX_NESTED_LEVEL; level++ )
    {
        state[level] = IN_BLOCK;
        cond[level]  = TRUE;
    }
    level = 0;

    code = source;
    /*
     * Parse the Prog code
     */
    while ( *code )
    {
        bool bFirstArg = TRUE;
        char *b = buf, *c = control, *d = data;
        /*
         * Get a command line. We sneakily get both the control word
         * (if/and/or) and the rest of the line in one pass.
         */
        while( isspace( *code ) && *code ) code++;

        while ( *code )
        {
            if ( *code == '\n' || *code == '\r' )
                break;
            else if ( isspace(*code) )
            {
                if ( bFirstArg )
                    bFirstArg = FALSE;
                else
                    *d++ = *code;
            }
            else
            {
                if ( bFirstArg )
                    *c++ = *code;
                else
                    *d++ = *code;
            }

            *b++ = *code++;
        }
        *b = *c = *d = '\0';

        if(++count < startat)
        {
            if ( !str_cmp( control, "if" ) )
            {
                state[level] = BEGIN_BLOCK;
                state[++level] = END_BLOCK;
            }
            else if ( !str_cmp( control, "endif" ) )
            {
                state[level] = IN_BLOCK;
                state[--level] = END_BLOCK;
            }

            continue;
        }

        if ( buf[0] == '\0' )
            break;
        if ( buf[0] == '*' ) /* Comment */
            continue;

        line = data;
        /*
         * Match control words
         */
        //JH New 'wait' control word.. make sure we can wait inside this block
        if ( !str_cmp( control, "wait" ) && cond[level] != FALSE)
        {
            int timer;
            line = one_argument( line, control );
            if(control[0] == '\0')
                timer = 1;
            else if (is_number(control) && atoi(control) > 0)
                timer = atoi(control);
            else
            {
                sprintf( buf, "Prog_flow/Wait: argument NaN, using 1 second as default. prog %d", pvnum );
                bug( buf, 0 );
                timer = 1;
            }
            //ch_printf(ch, "1966: 'wait' control word found on line %d for prog %d.  Creating event.\n\r", startat, pvnum);
            if ( mob )
            {
                PROG_CODE *prog = get_prog_by_vnum(pvnum, MOB);
                prog_event(ch, (void *)mob, MOB, prog, count+1, timer);
                //ch_printf(ch, "1971: Prog_event called for mob: will pick up at line %d.\n\r", count+1);
            }
            else if ( obj )
            {
                PROG_CODE *prog = get_prog_by_vnum(pvnum, OBJ);
                prog_event(ch, (void *)obj, OBJ, prog, count+1, timer);
                //ch_printf(ch, "Prog_event called for obj.\n\r");
            }
            else
            {
                PROG_CODE *prog = get_prog_by_vnum(pvnum, ROOM);
                prog_event(ch, (void *)room, ROOM, prog, count+1, timer);
                //ch_printf(ch, "Prog_event called for room.\n\r");
            }
            return;
        }
        //JH New 'cooldown' control word.. make sure we can do so inside this block
        //Cooldown makes the particular program (on the mob, etc) not trigger again
        //until it it is back to normal.  This is sneakily done by just holding
        //the prog in an event for awhile. 2/12/2004 12:45AM
        if ( (!str_cmp( control, "cooldown" )) && (!level || cond[level] == TRUE) )
        {
            int timer;
            line = one_argument( line, control );
            if(control[0] == '\0')
                timer = 15;
            else if (is_number(control) && atoi(control) > 0)
                timer = atoi(control);
            else
            {
                sprintf( buf, "Prog_flow/Cooldown: argument NaN, using 15 seconds as default. prog %d", pvnum );
                bug( buf, 0 );
                timer = 15;
            }


            wiznet(create_string("DEBUG: program_flow, cooldown: line to be copied: %s", line),
                    NULL, NULL, WIZ_DEBUG, 0, MAX_LEVEL);

            if ( mob )
            {
                PROG_LIST *p_list = get_prog_list_by_vnum(pvnum, MOB, (void *)mob);
                if (p_list && !prog_is_cooling(p_list))
                    prog_cooldown_event (p_list, timer, line);
            }
            else if ( obj )
            {
                PROG_LIST *p_list = get_prog_list_by_vnum(pvnum, OBJ, (void *)obj);
                if (p_list && !prog_is_cooling(p_list))
                    prog_cooldown_event (p_list, timer, line);
            }
            else
            {
                PROG_LIST *p_list = get_prog_list_by_vnum(pvnum, ROOM, (void *)room);
                if (p_list && !prog_is_cooling(p_list));
                prog_cooldown_event (p_list, timer, line);
            }

        }
        else if ( !str_cmp( control, "if" ) )
        {
            if ( state[level] == BEGIN_BLOCK )
            {
                if ( mob )
                    sprintf( buf, "Mobprog: misplaced if statement, mob %d prog %d", mvnum, pvnum );
                else if ( obj )
                    sprintf( buf, "Objprog: misplaced if statement, obj %d prog %d", ovnum, pvnum );
                else
                    sprintf( buf, "Roomprog: misplaced if statement, room %d prog %d", rvnum, pvnum );

                bug( buf, 0 );
                return;
            }
            state[level] = BEGIN_BLOCK;

            if ( ++level >= MAX_NESTED_LEVEL )
            {
                if ( mob )
                    sprintf( buf, "Mobprog: Max nested level exceeded, mob %d prog %d", mvnum, pvnum );
                else if ( obj )
                    sprintf( buf, "Objprog: Max nested level exceeded, obj %d prog %d", ovnum, pvnum );
                else
                    sprintf( buf, "Roomprog: Max nested level exceeded, room %d prog %d", rvnum, pvnum );
                bug( buf, 0 );
                return;
            }
            if ( level && cond[level-1] == FALSE )
            {
                cond[level] = FALSE;
                continue;
            }

            line = percent_argument( line, control ); //jh 5/24/2004 5:07PM
            //check = keyword_lookup( fn_keyword, control );
            check = keyword_lookup( fn_keyword, control );
//            ch_printf(ch, "Line: %s, control: %s\n\r", line, control);
//            return;
            //ch_printf(ch, "Check: %d\n\r", check);
            //ch_printf(ch, "control[0]: %c\n\r", control[0]);

            if (control[0] == '%')
                percent_process = TRUE;

            //ch_printf(ch, "percent_process?: %d\n\r", percent_process);

            if (!percent_process)
            {
                if (check >= 0)
                {
                    if ( mob )
                    {
                        cond[level] = cmd_eval_mob( pvnum, line, check, mob, ch, arg1, arg2, rch );
                    }
                    else if ( obj )
                    {
                        cond[level] = cmd_eval_obj( pvnum, line, check, obj, ch, arg1, arg2, rch );
                    }
                    else if ( room )
                    {
                        cond[level] = cmd_eval_room( pvnum, line, check, room, ch, arg1, arg2, rch );
                    }
                    else
                    {
                        if ( mob )
                            sprintf( buf, "Mobprog: invalid if_check (if), mob %d prog %d", mvnum, pvnum );
                        else if ( obj )
                            sprintf( buf, "Objprog: invalid if_check (if), obj %d prog %d", ovnum, pvnum );
                        else
                            sprintf( buf, "Roomprog: invalid if_check (if), room %d prog %d", rvnum, pvnum );

                        bug( buf, 0 );
                        return;
                    }
                }
                else
                {
                    if ( mob )
                        sprintf( buf, "Mobprog: invalid if_check (if) (check < 0), mob %d prog %d", mvnum, pvnum );
                    else if ( obj )
                        sprintf( buf, "Objprog: invalid if_check (if) (check < 0), obj %d prog %d", ovnum, pvnum );
                    else
                        sprintf( buf, "Roomprog: invalid if_check (if) (check < 0), room %d prog %d", rvnum, pvnum );

                    bug( buf, 0 );
                    return;
                }
            }
            else
            {
                if (mob)
                    cond[level] = new_eval_mob (mvnum, control, line, mob, ch);
                else if (obj)
                    return;
                //crap
                else if (room)
                    return;
                //crap
                else
                    return;
                //bug message;
            }

            state[level] = END_BLOCK;
        }
        else if ( !str_cmp( control, "or" ) )
        {
            if ( !level || state[level-1] != BEGIN_BLOCK )
            {
                if ( mob )
                    sprintf( buf, "Mobprog: or without if, mob %d prog %d", mvnum, pvnum );
                else if ( obj )
                    sprintf( buf, "Objprog: or without if, obj %d prog %d", ovnum, pvnum );
                else
                    sprintf( buf, "Roomprog: or without if, room %d prog %d", rvnum, pvnum );
                bug( buf, 0 );
                return;
            }

            if ( level && cond[level-1] == FALSE )
                continue;

            line = one_argument( line, control );

            if ( mob && ( check = keyword_lookup( fn_keyword, control ) ) >= 0 )
            {
                eval = cmd_eval_mob( pvnum, line, check, mob, ch, arg1, arg2, rch );
            }
            else if ( obj && ( check = keyword_lookup( fn_keyword, control ) ) >= 0 )
            {
                eval = cmd_eval_obj( pvnum, line, check, obj, ch, arg1, arg2, rch );
            }
            else if ( room && ( check = keyword_lookup( fn_keyword, control ) ) >= 0 )
            {
                eval = cmd_eval_room( pvnum, line, check, room, ch, arg1, arg2, rch );
            }
            else
            {
                if ( mob )
                    sprintf( buf, "Mobprog: invalid if_check (or), mob %d prog %d", mvnum, pvnum );
                else if ( obj )
                    sprintf( buf, "Objprog: invalid if_check (or), obj %d prog %d", ovnum, pvnum );
                else
                    sprintf( buf, "Roomprog: invalid if_check (or), room %d prog %d", rvnum, pvnum );
                bug( buf, 0 );
                return;
            }
            cond[level] = (eval == TRUE) ? TRUE : cond[level];
        }
        else if ( !str_cmp( control, "and" ) )
        {
            if ( !level || state[level-1] != BEGIN_BLOCK )
            {
                if ( mob )
                    sprintf( buf, "Mobprog: and without if, mob %d prog %d", mvnum, pvnum );
                else if ( obj )
                    sprintf( buf, "Objprog: and without if, obj %d prog %d", ovnum, pvnum );
                else
                    sprintf( buf, "Roomprog: and without if, room %d prog %d", rvnum, pvnum );

                bug( buf, 0 );
                return;
            }
            if ( level && cond[level-1] == FALSE )
                continue;

            line = one_argument( line, control );

            if ( mob && ( check = keyword_lookup( fn_keyword, control ) ) >= 0 )
            {
                eval = cmd_eval_mob( pvnum, line, check, mob, ch, arg1, arg2, rch );
            }
            else if ( obj && ( check = keyword_lookup( fn_keyword, control ) ) >= 0 )
            {
                eval = cmd_eval_obj( pvnum, line, check, obj, ch, arg1, arg2, rch );
            }
            else if ( room && ( check = keyword_lookup( fn_keyword, control ) ) >= 0 )
            {
                eval = cmd_eval_room( pvnum, line, check, room, ch, arg1, arg2, rch );
            }
            else
            {
                if ( mob )
                    sprintf( buf, "Mobprog: invalid if_check (and), mob %d prog %d", mvnum, pvnum );
                else if ( obj )
                    sprintf( buf, "Objprog: invalid if_check (and), obj %d prog %d", ovnum, pvnum );
                else
                    sprintf( buf, "Roomprog: invalid if_check (and), room %d prog %d", rvnum, pvnum );

                bug( buf, 0 );
                return;
            }

            cond[level] = (cond[level] == TRUE) && (eval == TRUE) ? TRUE : FALSE;
        }
        else if ( !str_cmp( control, "endif" ) )
        {
            if ( !level || state[level-1] != BEGIN_BLOCK )
            {
                if ( mob )
                    sprintf( buf, "Mobprog: endif without if, mob %d prog %d", mvnum, pvnum );
                else if ( obj )
                    sprintf( buf, "Objprog: endif without if, obj %d prog %d", ovnum, pvnum );
                else
                    sprintf( buf, "Roomprog: endif without if, room %d prog %d", rvnum, pvnum );

                bug( buf, 0 );
                call_level--;
                return;
            }
            cond[level] = TRUE;
            state[level] = IN_BLOCK;
            state[--level] = END_BLOCK;
        }
        else if ( !str_cmp( control, "else" ) )
        {
            if ( !level || state[level-1] != BEGIN_BLOCK )
            {
                if ( mob )
                    sprintf( buf, "Mobprog: else without if, mob %d prog %d", mvnum, pvnum );
                else if ( obj )
                    sprintf( buf, "Objprog: else without if, obj %d prog %d", ovnum, pvnum );
                else
                    sprintf( buf, "Roomprog: else without if, room %d prog %d", rvnum, pvnum );
                bug( buf, 0 );
                return;
            }
            if ( level && cond[level-1] == FALSE )
                continue;

            state[level] = IN_BLOCK;
            cond[level] = (cond[level] == TRUE) ? FALSE : TRUE;
        }
        else if ( cond[level] == TRUE
                && ( !str_cmp( control, "break" ) || !str_cmp( control, "end" ) ) )
        {
            call_level--;
            return;
        }
        else if ( (!level || cond[level] == TRUE) && buf[0] != '\0' )
        {
            state[level] = IN_BLOCK;

            if ( mob )
                expand_arg_mob( data, buf, mob, ch, arg1, arg2, rch );
            else if ( obj )
                expand_arg_other( data, buf, obj, NULL, ch, arg1, arg2, rch );
            else
                expand_arg_other( data, buf, NULL, room, ch, arg1, arg2, rch );

            if ( !str_cmp( control, "mob" ) )
            {
                /*
                 * Found a mob restricted command, pass it to mob interpreter
                 */
                line = one_argument( data, control );
                if ( !mob )
                    bug( "mob command in non MOBprog",0);
                else
                    mob_interpret( mob, line );
            }
            else if ( !str_cmp( control, "obj" ) )
            {
                /*
                 * Found an obj restricted command, pass it to obj interpreter
                 */
                line = one_argument( data, control );
                if ( !obj )
                    bug( "obj command in non OBJprog",0);
                else
                    obj_interpret( obj, line );
            }
            else if ( !str_cmp( control, "room" ) )
            {
                /*
                 * Found a room restricted command, pass it to room interpreter
                 */
                line = one_argument( data, control );
                if ( !room )
                    bug( "room command in non ROOMprog", 0 );
                else
                    room_interpret( room, line );
            }
            else
            {
                /*
                 * Found a normal mud command, pass it to interpreter
                 */
                if ( !mob )
                    bug( "Normal MUD command in non-MOBprog, prog vnum %d", pvnum );
                else
                    interpret( mob, data );
            }
        }
    }
    call_level--;
}

/*Locals*/
int get_percent_context (const char *, char *, CHAR_DATA **, CHAR_DATA *, CHAR_DATA *);
int parse_context(sh_int, CHAR_DATA *, char *, int *, char *, bool & );

//JH: 3/18/2004 8:38PM if "%actor.hp%" "< 50"... if "keyword" "line"
int new_eval_mob (sh_int vnum, char *control, char *line, CHAR_DATA *mob, CHAR_DATA *ch)
{
    CHAR_DATA *lhs_ch = NULL;
    CHAR_DATA *rhs_ch = NULL;
    char context[MSL];
    char buf[MSL];
    int lhs_int = -1, rhs_int = -1;
    int parsing_result = 0;
    char lhs_str[MSL], rhs_str[MSL];
    int oper;
    bool stop_parse = false;

    lhs_str[0] = '\0';
    rhs_str[0] = '\0';

    get_percent_context(control, context, &lhs_ch, mob, ch);
//    ch_printf(ch, "result of get_percent_context: lhs_ch->name %s, context %s\n\r", lhs_ch->name, context);
    parsing_result = parse_context(vnum, lhs_ch, context, &lhs_int, lhs_str, stop_parse);

    if (stop_parse) //means parse_context took care of interpretation
    {
        return parsing_result;
    }    

    line = one_argument( line, buf );
    if ((oper = keyword_lookup( fn_evals, buf )) < 0)
    {
        bugf("new_eval_mob: prog %d unknown operand '%s'", vnum, buf );
        return 0;
    }

    line = one_argument(line, buf);

    /*Now check the rhs: if |something| = <rhs>.*/
    if (is_number(buf))
    {
        rhs_int = atoi( buf );
    }
    else if (buf[0] == '%')
    {
        get_percent_context(buf, context, &rhs_ch, mob, ch);
        parse_context(vnum, rhs_ch, context, &rhs_int, rhs_str, stop_parse);
    }
    else
    {
        /*We will assume it's a string*/
        if (buf[0] != '\0')
            sprintf(rhs_str, "%s", buf);
        else
        {
            bugf("new_eval_mob: prog %d no rhs '%s'", vnum, buf );
            return 0;
        }
    }

    //ch_printf(ch, "lhs_int: %d rhs_int: %d lhs_str: %s / rhs_str: %s\n\r",
    //    lhs_int, rhs_int, lhs_str, rhs_str);
    if (lhs_int >= 0 && rhs_int >= 0)
        return( num_eval( lhs_int, oper, rhs_int) );
    else if (rhs_str[0] != '\0' && lhs_str[0] != '\0')
        return( str_eval( lhs_str, oper, rhs_str) );
    else
    {
        bugf("new_eval_mob: program %d no idea how to compare lhs and rhs", vnum);
        return 0;
    }

    return 0;
}

int get_percent_context (const char *keyword, char * context, CHAR_DATA **ref_ch, CHAR_DATA *mob, CHAR_DATA *ch)
{
    int where = -1;

    if (!keyword || !*keyword)
        return 1;

//    ch_printf(ch, "in get_percent_context: keyword is %s\n\r:", keyword);
    string keyw = keyword;
    string left, right;

    //eat first %
    keyw.erase(0, 1);

    //eat last %
    keyw.erase(keyw.length() - 1, 1);

    //find period: X.Y
    where = keyw.find(".");
    //split string: make left X
    left = keyw.substr(0, where);
    //split string: make right Y
    right = keyw.substr(where + 1, keyw.length());

    //X should be something valid
    if (left == "self")
        *ref_ch = mob;
    else if (left == "mob")
        *ref_ch = mob;
    else if (left == "actor")
        *ref_ch = ch;
    else if (left == "target")
    {
        if (mob->mprog_target == NULL)
            mob->mprog_target = ch;

        *ref_ch = mob->mprog_target;
    }
    else
    {
        writelogf("get_percent_operators: unknown lhs '%s'", left.c_str());
        return 1;
    }

    strcpy (context, right.c_str());

    return 0;
}

//Only return anything other than 0 if the prog line is entirely evaluated within here
//ref to say stop processing, then use returned val to return out of get_p_c.
int parse_context(sh_int vnum, CHAR_DATA *vch, char *context, int *int_ref, char *str_ref, bool &stop_parse)
{
    char buf[MSL];

    if (!vch || !*context)
        return 0;

    /**********************Conditions (sort later)**********************/
    if (!str_cmp(context, "name"))
    {
        sprintf(str_ref, "%s", vch->name);
    }
    else if (!str_cmp(context, "short_desc"))
    {
        if (!IS_NPC(vch))
        {
            bugf("parse_context: 'short_desc' context used on PC, prog %d.", vnum);
            wiznet(create_string("DEBUG: parse_context: 'short_desc' context used on PC, prog %d.", vnum),
                    NULL, NULL, WIZ_DEBUG, 0, MAX_LEVEL);
            return FALSE;
        }
        sprintf(str_ref, "%s", vch->short_descr);
    }
    else if (!str_cmp(context, "long_desc"))
    {
        if (!IS_NPC(vch))
        {
            bugf("parse_context: 'long_desc' context used on PC, prog %d.", vnum);
            wiznet(create_string("DEBUG: parse_context: 'long_desc' context used on PC, prog %d.", vnum),
                    NULL, NULL, WIZ_DEBUG, 0, MAX_LEVEL);
            return FALSE;
        }
        sprintf(str_ref, "%s", vch->long_descr);
    }
    else if (!str_cmp(context, "vnum"))
    {
        if (!IS_NPC(vch))
        {
            bugf("parse_context: 'vnum' context used on PC, prog %d.", vnum);
            wiznet(create_string("DEBUG: BUG:parse_context: 'vnum' context used on PC, prog %d.", vnum),
                    NULL, NULL, WIZ_DEBUG, 0, MAX_LEVEL);
            return FALSE;
        }
        *int_ref = vch->pIndexData->vnum;
    }
    else if (!str_cmp(context, "pos")) //standing, etc
    {
        if (vch->position == POS_ENSCONCED)
            sprintf(str_ref, "%s", ensconce_flags[vch->ensconce_type].name);
        else
            sprintf(str_ref, "%s", position_table[vch->position].name);
    }
    else if (!str_cmp(context, "hp"))
        *int_ref = UMAX(vch->hit, 0);
    else if (!str_cmp(context, "sex"))
        *int_ref = vch->sex;
    else if (!str_cmp(context, "str"))
        *int_ref = UMAX(get_curr_stat (vch, STAT_STR), 0);
    else if (!str_cmp(context, "int"))
        *int_ref = UMAX(get_curr_stat (vch, STAT_INT), 0);
    else if (!str_cmp(context, "cha"))
        *int_ref = UMAX(get_curr_stat (vch, STAT_CHA), 0);
    else if (!str_cmp(context, "con"))
        *int_ref = UMAX(get_curr_stat (vch, STAT_CON), 0);
    else if (!str_cmp(context, "dex"))
        *int_ref = UMAX(get_curr_stat (vch, STAT_DEX), 0);
    else if (!str_cmp(context, "perception"))
        *int_ref = UMAX(vch->perception, 0);
    else if (!str_cmp(context, "will"))
        *int_ref = UMAX(vch->will, 0);
    else if (!str_cmp(context, "reflex"))
        *int_ref = UMAX(vch->reflex, 0);
    else if (!str_cmp(context, "gold"))
        *int_ref = UMAX(vch->gold, 0);
    else if (!str_cmp(context, "silver"))
        *int_ref = UMAX(vch->silver, 0);
    else if (!str_cmp(context, "age"))
        *int_ref = (!IS_NPC(vch) ? get_age(vch) : 0);
    else if (!str_cmp(context, "chi"))
        *int_ref = UMAX(vch->magic_points[MAG_CHI], 0);
    else if (!str_cmp(context, "elemental"))
        *int_ref = UMAX(vch->magic_points[MAG_ELE], 0);
    else if (!str_cmp(context, "ley"))
        *int_ref = UMAX(vch->magic_points[MAG_LEY], 0);
    else if (!str_cmp(context, "fae"))
        *int_ref = UMAX(vch->magic_points[MAG_FAE], 0);
    //Special cases
    else if (!str_prefix(context, "isusing"))
    {
        context = one_argument(context, buf);
        context = one_argument(context, buf);
        stop_parse = true;
        if (is_number(buf))
            return has_item( vch, atoi(buf), -1, TRUE );
        else
            return has_item( vch, -1, item_lookup(buf), TRUE );
    }
    else if (!str_prefix("has", context))
    {
        context = one_argument(context, buf); //strip the 'has'
        context = one_argument(context, buf); //get the new word
        stop_parse = true;
        if (is_number(buf))
            return has_item( vch, atoi(buf), -1, FALSE );
        else
            return has_item( vch, -1, item_lookup(buf), FALSE );
    }
    else if (!str_prefix(context, "isfighting"))
    {
        context = one_argument(context, buf);
        context = one_argument(context, buf); //get the new word
        stop_parse = true;
        //dual-purpose:
        //check 1: if %mob.isfighting%
        //check 2 type 1: if %mob.isfighting 1234%
        //check 2 type 3: if %mob.isfighting bob%
        if (buf[0] == '\0')
            return ((int)vch->fighting);
        else
        {
            if (is_number(buf))
            {
                return (vch->fighting && (IS_NPC(vch->fighting) ?
                        atoi(buf) == vch->fighting->pIndexData->vnum : 0));
            }
            else
                return (vch->fighting && (vch->fighting == get_char_room(vch, NULL, buf)));
        }
    }
    else
    {
        bugf("parse_context: Don't understand context of '%s' in prog %d.", context, vnum);
        return FALSE;
    }

    return 0;
}