src/
/***************************************************************************
 *  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.        *
 *                                                                         *
 ***************************************************************************/
 /***************************************************************************
 *                                 _/                            _/        *
 *      _/_/_/  _/_/      _/_/_/  _/    _/_/    _/    _/    _/_/_/         *
 *     _/    _/    _/  _/        _/  _/    _/  _/    _/  _/    _/          *
 *    _/    _/    _/  _/        _/  _/    _/  _/    _/  _/    _/           *
 *   _/    _/    _/    _/_/_/  _/    _/_/      _/_/_/    _/_/_/            *
 ***************************************************************************
 * Mindcloud Copyright 2001-2003 by Jeff Boschee (Zarius),                 *
 * Additional credits are in the help file CODECREDITS                     *
 * All Rights Reserved.                                                    *
 ***************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <ctype.h>
#include "merc.h"
#include <regex.h>
#include "recycle.h"

/*extern int flag_lookup args((const char *name, const struct flag_type *flag_table));*/

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

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

char      match[3][MAX_INPUT_LENGTH];
SLEEP_DATA *first_sleep = NULL;

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

        "\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;
        }
}

/*
 * ---------------------------------------------------------------------
 * 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)
        {
                if (obj->in_room)
                        vch = obj->in_room->people;
                else
                        vch = obj->carried_by->in_room->people;
        }
        else
                vch = room->people;

        for (; vch; vch = vch->next_in_room)
        {
                if (mob
                    && 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;
                }
        }
        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, int vnum, 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,
                       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,
                       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(int vnum, char *line, int check,
                 CHAR_DATA * mob, CHAR_DATA * ch,
                 void *arg1, 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 (atoi(buf) < number_percent());
        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)
                {
                        xprintf(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')
        {
                xprintf(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:
                xprintf(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
                        && 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_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_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)
        {
                xprintf(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 bones... */
                if (lval_char != NULL)
                        lval = lval_char->bones;
                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(int vnum, char *line, int check,
                 OBJ_DATA * obj, CHAR_DATA * ch,
                 void *arg1, 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;

        switch (check)
        {
                /*
                 * Case 1: keyword and value
                 */
        case CHK_RAND:
                return (atoi(buf) < number_percent());
        case CHK_MOBHERE:
                if (is_number(buf))
                        return (get_mob_vnum_room
                                (NULL, obj, NULL, atoi(buf)));
                else
                        return ((bool)
                                (get_char_room
                                 (NULL,
                                  (obj->in_room ? obj->in_room : obj->
                                   carried_by->in_room), buf) != NULL));
        case CHK_OBJHERE:
                if (is_number(buf))
                        return (get_obj_vnum_room
                                (NULL, obj, NULL, atoi(buf)));
                else
                        return ((bool)
                                (get_obj_here
                                 (NULL,
                                  (obj->in_room ? obj->in_room : obj->
                                   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 2 begins here: We sneakily use rval to indicate need
                 *             for numeric eval...
                 */
        case CHK_PEOPLE:
                rval = count_people_room(NULL, obj, NULL, 0);
                break;
        case CHK_PLAYERS:
                rval = count_people_room(NULL, obj, NULL, 1);
                break;
        case CHK_MOBS:
                rval = count_people_room(NULL, obj, NULL, 2);
                break;
        case CHK_CLONES:
                bug("cmd_eval_obj: received CHK_CLONES.", 0);
                break;
        case CHK_ORDER:
                rval = get_order(NULL, obj);
                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)
                {
                        xprintf(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')
        {
                xprintf(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:
                xprintf(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
                        && 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_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_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)
        {
                xprintf(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 bones... */
                if (lval_char != NULL)
                        lval = lval_char->bones;
                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(int vnum, char *line, int check,
                  ROOM_INDEX_DATA * room, CHAR_DATA * ch,
                  void *arg1, 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 (atoi(buf) < number_percent());
        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)
                {
                        xprintf(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')
        {
                xprintf(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:
                xprintf(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
                        && 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_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_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)
        {
                xprintf(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 bones... */
                if (lval_char != NULL)
                        lval = lval_char->bones;
                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,
                    void *arg1, 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 'a':
                        i = match[0];
                        break;
                case 'b':
                        i = match[1];
                        break;
                case 'c':
                        i = match[2];
                        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':
                        if (rch == NULL)
                                rch = get_random_char(mob, NULL, NULL);
                        i = (rch != NULL && can_see(mob, rch))
                                ? (IS_NPC(ch) ? ch->short_descr : ch->name)
                                : someone;
                        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,
                      void *arg1, 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 12 /* 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    5 /* Maximum nested calls */

void program_flow(int pvnum,    /* For diagnostic purposes */
                  char *source, /* the actual MOBprog code */
                  CHAR_DATA * mob,
                  OBJ_DATA * obj,
                  ROOM_INDEX_DATA * room,
                  CHAR_DATA * ch, void *arg1, void *arg2, int numlines)
{
        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 */
        int       count = 0;
        PROG_CODE *prog = get_mprog_by_vnum(pvnum);

        int       mvnum = 0, ovnum = 0, rvnum = 0;

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

        if (numlines == 1 && ++call_level > MAX_CALL_LEVEL)
        {
                if (mob)
                        xprintf(bugbuf,
                                "Progs: MAX_CALL_LEVEL exceeded, vnum %d, mprog vnum %d",
                                mvnum, pvnum);
                else if (obj)
                        xprintf(bugbuf,
                                "Progs: MAX_CALL_LEVEL exceeded, vnum %d oprog vnum %d.",
                                ovnum, pvnum);
                else
                        xprintf(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      first_carg = 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 (first_carg)
                                        first_carg = FALSE;
                                else
                                        *d++ = *code;
                        }
                        else
                        {
                                if (first_carg)
                                        *c++ = *code;
                                else
                                        *d++ = *code;
                        }
                        *b++ = *code++;
                }
                *b = *c = *d = '\0';
                if (++count < numlines)
                {
                        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
                 */
                if (!str_cmp(control, "sleep"))
                {
                        int       timer;
                        SLEEP_DATA *sd;

                        line = one_argument(line, control);
                        if (control[0] == '\0')
                                timer = 4;
                        else if (is_number(control) && atoi(control) > 0)
                                timer = atoi(control);
                        else
                        {
                                xprintf(buf,
                                        "Mobprog: argument NaN, using 4 as default. mob %d prog %d",
                                        mvnum, pvnum);
                                bug(buf, 0);
                                timer = 4;
                        }

                        sd = new_sleep_data();
                        sd->ch = ch;
                        sd->mob = mob;
                        sd->line = count + 1;
                        sd->vnum = prog->vnum;
                        sd->timer = timer;
                        sd->prog = prog;

                        sd->next = first_sleep;
                        if (first_sleep)
                                first_sleep->prev = sd;
                        first_sleep = sd;
                        return;
                }
                if (!str_cmp(control, "if"))
                {
                        if (state[level] == BEGIN_BLOCK)
                        {
                                if (mob)
                                        xprintf(buf,
                                                "Mobprog: misplaced if statement, mob %d prog %d",
                                                mvnum, pvnum);
                                else if (obj)
                                        xprintf(buf,
                                                "Objprog: misplaced if statement, obj %d prog %d",
                                                ovnum, pvnum);
                                else
                                        xprintf(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)
                                        xprintf(buf,
                                                "Mobprog: Max nested level exceeded, mob %d prog %d",
                                                mvnum, pvnum);
                                else if (obj)
                                        xprintf(buf,
                                                "Objprog: Max nested level exceeded, obj %d prog %d",
                                                ovnum, pvnum);
                                else
                                        xprintf(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 = one_argument(line, control);
                        if (mob
                            && (check =
                                keyword_lookup(fn_keyword, control)) >= 0)
                        {
                                cond[level] =
                                        cmd_eval_mob(pvnum, line, check, mob,
                                                     ch, arg1, arg2, rch);
                        }
                        else if (obj
                                 && (check =
                                     keyword_lookup(fn_keyword,
                                                    control)) >= 0)
                        {
                                cond[level] =
                                        cmd_eval_obj(pvnum, line, check, obj,
                                                     ch, arg1, arg2, rch);
                        }
                        else if (room
                                 && (check =
                                     keyword_lookup(fn_keyword,
                                                    control)) >= 0)
                        {
                                cond[level] =
                                        cmd_eval_room(pvnum, line, check,
                                                      room, ch, arg1, arg2,
                                                      rch);
                        }
                        else
                        {
                                if (mob)
                                        xprintf(buf,
                                                "Mobprog: invalid if_check (if), mob %d prog %d",
                                                mvnum, pvnum);
                                else if (obj)
                                        xprintf(buf,
                                                "Objprog: invalid if_check (if), obj %d prog %d",
                                                ovnum, pvnum);
                                else
                                        xprintf(buf,
                                                "Roomprog: invalid if_check (if), room %d prog %d",
                                                rvnum, pvnum);
                                bug(buf, 0);
                                return;
                        }
                        state[level] = END_BLOCK;
                }
                else if (!str_cmp(control, "or"))
                {
                        if (!level || state[level - 1] != BEGIN_BLOCK)
                        {
                                if (mob)
                                        xprintf(buf,
                                                "Mobprog: or without if, mob %d prog %d",
                                                mvnum, pvnum);
                                else if (obj)
                                        xprintf(buf,
                                                "Objprog: or without if, obj %d prog %d",
                                                ovnum, pvnum);
                                else
                                        xprintf(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)
                                        xprintf(buf,
                                                "Mobprog: invalid if_check (or), mob %d prog %d",
                                                mvnum, pvnum);
                                else if (obj)
                                        xprintf(buf,
                                                "Objprog: invalid if_check (or), obj %d prog %d",
                                                ovnum, pvnum);
                                else
                                        xprintf(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)
                                        xprintf(buf,
                                                "Mobprog: and without if, mob %d prog %d",
                                                mvnum, pvnum);
                                else if (obj)
                                        xprintf(buf,
                                                "Objprog: and without if, obj %d prog %d",
                                                ovnum, pvnum);
                                else
                                        xprintf(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)
                                        xprintf(buf,
                                                "Mobprog: invalid if_check (and), mob %d prog %d",
                                                mvnum, pvnum);
                                else if (obj)
                                        xprintf(buf,
                                                "Objprog: invalid if_check (and), obj %d prog %d",
                                                ovnum, pvnum);
                                else
                                        xprintf(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)
                                        xprintf(buf,
                                                "Mobprog: endif without if, mob %d prog %d",
                                                mvnum, pvnum);
                                else if (obj)
                                        xprintf(buf,
                                                "Objprog: endif without if, obj %d prog %d",
                                                ovnum, pvnum);
                                else
                                        xprintf(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)
                                        xprintf(buf,
                                                "Mobprog: else without if, mob %d prog %d",
                                                mvnum, pvnum);
                                else if (obj)
                                        xprintf(buf,
                                                "Objprog: else without if, obj %d prog %d",
                                                ovnum, pvnum);
                                else
                                        xprintf(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--;
}

/* 
 * ---------------------------------------------------------------------
 * Trigger handlers. These are called from various parts of the code
 * when an event is triggered.
 * ---------------------------------------------------------------------
 */

/*
 * A general purpose string trigger. Matches argument to a string trigger
 * phrase.
 */
void p_act_trigger(char *argument, CHAR_DATA * mob, OBJ_DATA * obj,
                   ROOM_INDEX_DATA * room, CHAR_DATA * ch,
                   void *arg1, void *arg2, int type)
{
        PROG_LIST *prg;
        SLEEP_DATA *test;

        if ((mob && obj) || (mob && room) || (obj && room))
        {
                bug("Multiple program types in ACT trigger.", 0);
                return;
        }

        if (mob)
        {
                for (prg = mob->pIndexData->mprogs; prg != NULL;
                     prg = prg->next)
                {
                        if (prg->trig_type == type
                            && strstr(argument, prg->trig_phrase) != NULL)
                        {
                                for (test = first_sleep; test != NULL;
                                     test = test->next)
                                        if (test->ch == ch
                                            && test->vnum == prg->vnum)
                                                return;
                                program_flow(prg->vnum, prg->code, mob, NULL,
                                             NULL, ch, arg1, arg2, 1);
                                break;
                        }
                }
        }
        else if (obj)
        {
                for (prg = obj->pIndexData->oprogs; prg != NULL;
                     prg = prg->next)
                {
                        if (prg->trig_type == type
                            && strstr(argument, prg->trig_phrase) != NULL)
                        {
                                for (test = first_sleep; test != NULL;
                                     test = test->next)
                                        if (test->ch == ch
                                            && test->vnum == prg->vnum)
                                                return;
                                program_flow(prg->vnum, prg->code, NULL, obj,
                                             NULL, ch, arg1, arg2, 1);
                                break;
                        }
                }
        }
        else if (room)
        {
                for (prg = room->rprogs; prg != NULL; prg = prg->next)
                {
                        if (prg->trig_type == type
                            && strstr(argument, prg->trig_phrase) != NULL)
                        {
                                for (test = first_sleep; test != NULL;
                                     test = test->next)
                                        if (test->ch == ch
                                            && test->vnum == prg->vnum)
                                                return;
                                program_flow(prg->vnum, prg->code, NULL, NULL,
                                             room, ch, arg1, arg2, 1);
                                break;
                        }
                }
        }
        else
                bug("ACT trigger with no program type.", 0);

        return;
}

/*
 * A general purpose percentage trigger. Checks if a random percentage
 * number is less than trigger phrase
 */
bool p_percent_trigger(CHAR_DATA * mob, OBJ_DATA * obj,
                       ROOM_INDEX_DATA * room, CHAR_DATA * ch, void *arg1,
                       void *arg2, int type)
{
        PROG_LIST *prg;
        SLEEP_DATA *test;

        if ((mob && obj) || (mob && room) || (obj && room))
        {
                bug("Multiple program types in PERCENT trigger.", 0);
                return (FALSE);
        }

        if (mob)
        {
                for (prg = mob->pIndexData->mprogs; prg != NULL;
                     prg = prg->next)
                {
                        if (prg->trig_type == type
                            && number_percent() < atoi(prg->trig_phrase))
                        {
                                for (test = first_sleep; test != NULL;
                                     test = test->next)
                                        if (test->ch == ch
                                            && test->vnum == prg->vnum)
                                                return (TRUE);
                                program_flow(prg->vnum, prg->code, mob, NULL,
                                             NULL, ch, arg1, arg2, 1);
                                return (TRUE);
                        }
                }
        }
        else if (obj)
        {
                for (prg = obj->pIndexData->oprogs; prg != NULL;
                     prg = prg->next)
                {
                        if (prg->trig_type == type
                            && number_percent() < atoi(prg->trig_phrase))
                        {
                                for (test = first_sleep; test != NULL;
                                     test = test->next)
                                        if (test->ch == ch
                                            && test->vnum == prg->vnum)
                                                return (TRUE);
                                program_flow(prg->vnum, prg->code, NULL, obj,
                                             NULL, ch, arg1, arg2, 1);
                                return (TRUE);
                        }
                }
        }
        else if (room)
        {
                for (prg = room->rprogs; prg != NULL; prg = prg->next)
                {
                        if (prg->trig_type == type
                            && number_percent() < atoi(prg->trig_phrase))
                        {
                                for (test = first_sleep; test != NULL;
                                     test = test->next)
                                        if (test->ch == ch
                                            && test->vnum == prg->vnum)
                                                return (TRUE);
                                program_flow(prg->vnum, prg->code, NULL, NULL,
                                             room, ch, arg1, arg2, 1);
                                return (TRUE);
                        }
                }
        }
        else
                bug("PERCENT trigger missing program type.", 0);

        return (FALSE);
}

void p_bribe_trigger(CHAR_DATA * mob, CHAR_DATA * ch, int amount)
{
        PROG_LIST *prg;
        SLEEP_DATA *test;

        for (prg = mob->pIndexData->mprogs; prg; prg = prg->next)
        {
                if (prg->trig_type == TRIG_BRIBE
                    && amount >= atoi(prg->trig_phrase))
                {
                        for (test = first_sleep; test != NULL;
                             test = test->next)
                                if (test->ch == ch && test->vnum == prg->vnum)
                                        return;
                        program_flow(prg->vnum, prg->code, mob, NULL, NULL,
                                     ch, NULL, NULL, 1);
                        break;
                }
        }
        return;
}

bool p_exit_trigger(CHAR_DATA * ch, int dir, int type)
{
        CHAR_DATA *mob;
        OBJ_DATA *obj;
        ROOM_INDEX_DATA *room;
        PROG_LIST *prg;
        SLEEP_DATA *test;

        if (type == PRG_MPROG)
        {
                for (mob = ch->in_room->people; mob != NULL;
                     mob = mob->next_in_room)
                {
                        if (IS_NPC(mob)
                            && (HAS_TRIGGER_MOB(mob, TRIG_EXIT)
                                || HAS_TRIGGER_MOB(mob, TRIG_EXALL)))
                        {
                                for (prg = mob->pIndexData->mprogs; prg;
                                     prg = prg->next)
                                {
                                        /*
                                         * Exit trigger works only if the mobile is not busy
                                         * (fighting etc.). If you want to be sure all players
                                         * are caught, use ExAll trigger
                                         */
                                        if (prg->trig_type == TRIG_EXIT
                                            && dir == atoi(prg->trig_phrase)
                                            && mob->position ==
                                            mob->pIndexData->default_pos
                                            && can_see(mob, ch))
                                        {
                                                for (test = first_sleep;
                                                     test != NULL;
                                                     test = test->next)
                                                        if (test->ch == ch
                                                            && test->vnum ==
                                                            prg->vnum)
                                                                return TRUE;
                                                program_flow(prg->vnum,
                                                             prg->code, mob,
                                                             NULL, NULL, ch,
                                                             NULL, NULL, 1);
                                                return TRUE;
                                        }
                                        else if (prg->trig_type == TRIG_EXALL
                                                 && dir ==
                                                 atoi(prg->trig_phrase))
                                        {
                                                for (test = first_sleep;
                                                     test != NULL;
                                                     test = test->next)
                                                        if (test->ch == ch
                                                            && test->vnum ==
                                                            prg->vnum)
                                                                return TRUE;
                                                program_flow(prg->vnum,
                                                             prg->code, mob,
                                                             NULL, NULL, ch,
                                                             NULL, NULL, 1);
                                                return TRUE;
                                        }
                                }
                        }
                }
        }
        else if (type == PRG_OPROG)
        {
                for (obj = ch->in_room->contents; obj != NULL;
                     obj = obj->next_content)
                {
                        if (HAS_TRIGGER_OBJ(obj, TRIG_EXALL))
                        {
                                for (prg = obj->pIndexData->oprogs; prg;
                                     prg = prg->next)
                                {
                                        if (prg->trig_type == TRIG_EXALL
                                            && dir == atoi(prg->trig_phrase))
                                        {
                                                for (test = first_sleep;
                                                     test != NULL;
                                                     test = test->next)
                                                        if (test->ch == ch
                                                            && test->vnum ==
                                                            prg->vnum)
                                                                return TRUE;
                                                program_flow(prg->vnum,
                                                             prg->code, NULL,
                                                             obj, NULL, ch,
                                                             NULL, NULL, 1);
                                                return TRUE;
                                        }
                                }
                        }
                }

                for (mob = ch->in_room->people; mob; mob = mob->next_in_room)
                {
                        for (obj = mob->carrying; obj;
                             obj = obj->next_content)
                        {
                                if (HAS_TRIGGER_OBJ(obj, TRIG_EXALL))
                                {
                                        for (prg = obj->pIndexData->oprogs;
                                             prg; prg = prg->next)
                                        {
                                                if (prg->trig_type ==
                                                    TRIG_EXALL
                                                    && dir ==
                                                    atoi(prg->trig_phrase))
                                                {
                                                        for (test =
                                                             first_sleep;
                                                             test != NULL;
                                                             test =
                                                             test->next)
                                                                if (test->
                                                                    ch == ch
                                                                    && test->
                                                                    vnum ==
                                                                    prg->vnum)
                                                                        return TRUE;
                                                        program_flow(prg->
                                                                     vnum,
                                                                     prg->
                                                                     code,
                                                                     NULL,
                                                                     obj,
                                                                     NULL, ch,
                                                                     NULL,
                                                                     NULL, 1);
                                                        return TRUE;
                                                }
                                        }
                                }
                        }
                }
        }
        else if (type == PRG_RPROG)
        {
                room = ch->in_room;

                if (HAS_TRIGGER_ROOM(room, TRIG_EXALL))
                {
                        for (prg = room->rprogs; prg; prg = prg->next)
                        {
                                if (prg->trig_type == TRIG_EXALL
                                    && dir == atoi(prg->trig_phrase))
                                {
                                        for (test = first_sleep; test != NULL;
                                             test = test->next)
                                                if (test->ch == ch
                                                    && test->vnum ==
                                                    prg->vnum)
                                                        return TRUE;
                                        program_flow(prg->vnum, prg->code,
                                                     NULL, NULL, room, ch,
                                                     NULL, NULL, 1);
                                        return TRUE;
                                }
                        }
                }
        }

        return FALSE;
}

void p_give_trigger(CHAR_DATA * mob, OBJ_DATA * obj, ROOM_INDEX_DATA * room,
                    CHAR_DATA * ch, OBJ_DATA * dropped, int type)
{

        char      buf[MAX_INPUT_LENGTH], *p;
        PROG_LIST *prg;
        SLEEP_DATA *test;

        if ((mob && obj) || (mob && room) || (obj && room))
        {
                bug("Multiple program types in GIVE trigger.", 0);
                return;
        }

        if (mob)
        {
                for (prg = mob->pIndexData->mprogs; prg; prg = prg->next)
                        if (prg->trig_type == TRIG_GIVE)
                        {
                                p = prg->trig_phrase;
                                /*
                                 * Vnum argument
                                 */
                                if (is_number(p))
                                {
                                        if (dropped->pIndexData->vnum ==
                                            atoi(p))
                                        {
                                                for (test = first_sleep;
                                                     test != NULL;
                                                     test = test->next)
                                                        if (test->ch == ch
                                                            && test->vnum ==
                                                            prg->vnum)
                                                                return;
                                                program_flow(prg->vnum,
                                                             prg->code, mob,
                                                             NULL, NULL, ch,
                                                             (void *) dropped,
                                                             NULL, 1);
                                                return;
                                        }
                                }
                                /*
                                 * Dropped object name argument, e.g. 'sword'
                                 */
                                else
                                {
                                        while (*p)
                                        {
                                                p = one_argument(p, buf);

                                                if (is_name
                                                    (buf, dropped->name)
                                                    || !str_cmp("all", buf))
                                                {
                                                        for (test =
                                                             first_sleep;
                                                             test != NULL;
                                                             test =
                                                             test->next)
                                                                if (test->
                                                                    ch == ch
                                                                    && test->
                                                                    vnum ==
                                                                    prg->vnum)
                                                                        return;
                                                        program_flow(prg->
                                                                     vnum,
                                                                     prg->
                                                                     code,
                                                                     mob,
                                                                     NULL,
                                                                     NULL, ch,
                                                                     (void *)
                                                                     dropped,
                                                                     NULL, 1);
                                                        return;
                                                }
                                        }
                                }
                        }
        }
        else if (obj)
        {
                for (prg = obj->pIndexData->oprogs; prg; prg = prg->next)
                        if (prg->trig_type == type)
                        {
                                for (test = first_sleep; test != NULL;
                                     test = test->next)
                                        if (test->ch == ch
                                            && test->vnum == prg->vnum)
                                                return;
                                program_flow(prg->vnum, prg->code, NULL, obj,
                                             NULL, ch, (void *) obj, NULL, 1);
                                return;
                        }
        }
        else if (room)
        {
                for (prg = room->rprogs; prg; prg = prg->next)
                        if (prg->trig_type == type)
                        {
                                p = prg->trig_phrase;
                                /*
                                 * Vnum argument
                                 */
                                if (is_number(p))
                                {
                                        if (dropped->pIndexData->vnum ==
                                            atoi(p))
                                        {
                                                for (test = first_sleep;
                                                     test != NULL;
                                                     test = test->next)
                                                        if (test->ch == ch
                                                            && test->vnum ==
                                                            prg->vnum)
                                                                return;
                                                program_flow(prg->vnum,
                                                             prg->code, NULL,
                                                             NULL, room, ch,
                                                             (void *) dropped,
                                                             NULL, 1);
                                                return;
                                        }
                                }
                                /*
                                 * Dropped object name argument, e.g. 'sword'
                                 */
                                else
                                {
                                        while (*p)
                                        {
                                                p = one_argument(p, buf);

                                                if (is_name
                                                    (buf, dropped->name)
                                                    || !str_cmp("all", buf))
                                                {
                                                        for (test =
                                                             first_sleep;
                                                             test != NULL;
                                                             test =
                                                             test->next)
                                                                if (test->
                                                                    ch == ch
                                                                    && test->
                                                                    vnum ==
                                                                    prg->vnum)
                                                                        return;
                                                        program_flow(prg->
                                                                     vnum,
                                                                     prg->
                                                                     code,
                                                                     NULL,
                                                                     NULL,
                                                                     room, ch,
                                                                     (void *)
                                                                     dropped,
                                                                     NULL, 1);
                                                        return;
                                                }
                                        }
                                }
                        }
        }
}

void p_greet_trigger(CHAR_DATA * ch, int type)
{
        CHAR_DATA *mob;
        OBJ_DATA *obj;
        ROOM_INDEX_DATA *room;

        if (type == PRG_MPROG)
        {
                for (mob = ch->in_room->people; mob != NULL;
                     mob = mob->next_in_room)
                {
                        if (IS_NPC(mob)
                            && (HAS_TRIGGER_MOB(mob, TRIG_GREET)
                                || HAS_TRIGGER_MOB(mob, TRIG_GRALL)))
                        {
                                /*
                                 * Greet trigger works only if the mobile is not busy
                                 * (fighting etc.). If you want to catch all players, use
                                 * GrAll trigger
                                 */
                                if (HAS_TRIGGER_MOB(mob, TRIG_GREET)
                                    && mob->position ==
                                    mob->pIndexData->default_pos
                                    && can_see(mob, ch))
                                        p_percent_trigger(mob, NULL, NULL, ch,
                                                          NULL, NULL,
                                                          TRIG_GREET);
                                else if (HAS_TRIGGER_MOB(mob, TRIG_GRALL))
                                        p_percent_trigger(mob, NULL, NULL, ch,
                                                          NULL, NULL,
                                                          TRIG_GRALL);
                        }
                }
        }
        else if (type == PRG_OPROG)
        {
                for (obj = ch->in_room->contents; obj != NULL;
                     obj = obj->next_content)
                {
                        if (HAS_TRIGGER_OBJ(obj, TRIG_GRALL))
                        {
                                p_percent_trigger(NULL, obj, NULL, ch, NULL,
                                                  NULL, TRIG_GRALL);
                                return;
                        }
                }

                for (mob = ch->in_room->people; mob; mob = mob->next_in_room)
                {
                        for (obj = mob->carrying; obj;
                             obj = obj->next_content)
                        {
                                if (HAS_TRIGGER_OBJ(obj, TRIG_GRALL))
                                {
                                        p_percent_trigger(NULL, obj, NULL, ch,
                                                          NULL, NULL,
                                                          TRIG_GRALL);
                                        return;
                                }
                        }
                }
        }
        else if (type == PRG_RPROG)
        {
                room = ch->in_room;

                if (HAS_TRIGGER_ROOM(room, TRIG_GRALL))
                        p_percent_trigger(NULL, NULL, room, ch, NULL, NULL,
                                          TRIG_GRALL);
        }

        return;
}

void p_hprct_trigger(CHAR_DATA * mob, CHAR_DATA * ch)
{
        PROG_LIST *prg;
        SLEEP_DATA *test;

        for (prg = mob->pIndexData->mprogs; prg != NULL; prg = prg->next)
        {
                if ((prg->trig_type == TRIG_HPCNT)
                    && ((100 * mob->hit / mob->max_hit) <
                        atoi(prg->trig_phrase)))
                {
                        for (test = first_sleep; test != NULL;
                             test = test->next)
                                if (test->ch == ch && test->vnum == prg->vnum)
                                        return;
                        program_flow(prg->vnum, prg->code, mob, NULL, NULL,
                                     ch, NULL, NULL, 1);
                        break;
                }
        }
}
void mp_regex_trigger(char *argument, CHAR_DATA * mob, CHAR_DATA * ch,
                      const void *arg1, const void *arg2)
{
/*	regex_t exprec;
	int nmemb = 3;
	int i;
	regmatch_t memb[3];
	PROG_LIST *prg;
	SLEEP_DATA *test;
	int reg = REG_EXTENDED|REG_ICASE;
	for (prg = mob->pIndexData->mprogs; prg != NULL; prg = prg->next)
	{
		if ( prg->trig_type == TRIG_REGEX)
		{
			regcomp(&exprec, prg->trig_phrase,reg);
			if (!regexec(&exprec, argument, nmemb, memb, 0))
			{
				for (i = 0; i < nmemb && memb[i+1].rm_so > -1; i++)
				{
					memcpy(match[i], argument+memb[i+1].rm_so,
						memb[i+1].rm_eo-memb[i+1].rm_so);
					match[i][memb[i+1].rm_eo-memb[i+1].rm_so] = '\0';
				}
				for(test = first_sleep; test != NULL; test = test->next)
					if(test->ch == ch && test->vnum == prg->vnum)
						return;
				program_flow(prg->vnum, prg->code, mob, NULL, NULL, ch, arg1, arg2, 1);
			}
			regfree(&exprec);
			return;
		}
	}*/
        return;
}

PROG_CODE *get_mprog_by_vnum(int vnum)
{
        PROG_CODE *temp;

        for (temp = mprog_list; temp != NULL; temp = temp->next)
                if (temp->vnum == vnum)
                        return temp;
        return NULL;
}