1stMud4.5.3/
1stMud4.5.3/backup/
1stMud4.5.3/bin/
1stMud4.5.3/bin/extras/
1stMud4.5.3/data/i3/
1stMud4.5.3/doc/1stMud/
1stMud4.5.3/doc/Diku/
1stMud4.5.3/doc/MPDocs/
1stMud4.5.3/doc/Rom/
1stMud4.5.3/notes/
/**************************************************************************
*  Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,        *
*  Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.   *
*                                                                         *
*  Merc Diku Mud improvements 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          *
*  benefiting.  We hope that you share your changes too.  What goes       *
*  around, comes around.                                                  *
***************************************************************************
*       ROM 2.4 is copyright 1993-1998 Russ Taylor                        *
*       ROM has been brought to you by the ROM consortium                 *
*           Russ Taylor (rtaylor@hypercube.org)                           *
*           Gabrielle Taylor (gtaylor@hypercube.org)                      *
*           Brian Moore (zump@rom.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.        *
***************************************************************************
*          1stMud ROM Derivative (c) 2001-2004 by Markanth                *
*            http://www.firstmud.com/  <markanth@firstmud.com>            *
*         By using this code you have agreed to follow the term of        *
*             the 1stMud license in ../doc/1stMud/LICENSE                 *
***************************************************************************/

#include "merc.h"
#include "tables.h"
#include "prog_cmds.h"
#include "recycle.h"

#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_AFFECTED    (24)
#define    CHK_ACT         (25)
#define    CHK_OFF         (26)
#define    CHK_IMM         (27)
#define    CHK_CARRIES     (28)
#define    CHK_WEARS       (20)
#define    CHK_HAS         (30)
#define    CHK_USES        (31)
#define    CHK_NAME        (32)
#define    CHK_POS         (33)
#define    CHK_CLAN        (34)
#define    CHK_RACE        (35)
#define    CHK_CLASS       (36)
#define    CHK_OBJTYPE     (37)
#define    CHK_VNUM        (38)
#define    CHK_HPCNT       (39)
#define    CHK_ROOM        (40)
#define    CHK_SEX         (41)
#define    CHK_LEVEL       (42)
#define    CHK_ALIGN       (43)
#define    CHK_MONEY       (44)
#define    CHK_OBJVAL0     (45)
#define    CHK_OBJVAL1     (46)
#define    CHK_OBJVAL2     (47)
#define    CHK_OBJVAL3     (48)
#define    CHK_OBJVAL4     (49)
#define    CHK_GRPSIZE     (50)
#define    CHK_ONQUEST     (52)
#define    CHK_HUNTER      (55)
#define    CHK_PLR         (57)
#define    CHK_SKILL       (58)
#define    CHK_WEIGHT      (60)

#define    EVAL_EQ            0
#define    EVAL_GE            1
#define    EVAL_LE            2
#define    EVAL_GT            3
#define    EVAL_LT            4
#define    EVAL_NE            5

const char *fn_keyword[] = {
	"rand",
	"mobhere",
	"objhere",
	"mobexists",
	"objexists",
	"people",
	"players",
	"mobs",
	"clones",
	"order",
	"hour",
	"ispc",
	"isnpc",
	"isgood",
	"isevil",
	"isneutral",
	"isimmort",
	"ischarm",
	"isfollow",
	"isactive",
	"isdelay",
	"isvisible",
	"hastarget",
	"istarget",
	"affected",
	"act",
	"off",
	"imm",
	"carries",
	"wears",
	"has",
	"uses",
	"name",
	"pos",
	"clan",
	"race",
	"class",
	"objtype",
	"vnum",
	"hpcnt",
	"room",
	"sex",
	"level",
	"align",
	"money",
	"objval0",
	"objval1",
	"objval2",
	"objval3",
	"objval4",
	"grpsize",
	"onquest",
	"hunter",
	"plr",
	"skill",
	"weight",
	"\n"
};

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

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

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("invalid oper");
			return 0;
	}
}

CharData *get_random_char(CharData * mob, ObjData * obj, RoomIndex * room)
{
	CharData *vch, *victim = NULL;
	int now = 0, highest = 0;

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

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

	for (; vch; vch = vch->next_in_room)
	{
		if (mob && mob != vch && !IsNPC(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;
}

int count_people_room(CharData * mob, ObjData * obj, RoomIndex * room,
					  int iFlag)
{
	CharData *vch;
	int count;

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

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

	for (count = 0; vch; vch = vch->next_in_room)
	{
		if (mob)
		{
			if (mob != vch
				&& (iFlag == 0 || (iFlag == 1 && !IsNPC(vch))
					|| (iFlag == 2 && IsNPC(vch)) || (iFlag == 3 && IsNPC(mob)
													  && IsNPC(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 && !IsNPC(vch))
				|| (iFlag == 2 && IsNPC(vch)))
				count++;
		}
	}

	return (count);
}

int get_order(CharData * ch, ObjData * obj)
{
	CharData *vch;
	ObjData *vobj;
	int i;

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

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

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

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

			if (IsNPC(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;
}

bool has_item(CharData * ch, vnum_t vnum, item_t pitem_type, bool fWear)
{
	ObjData *obj;

	for (obj = ch->carrying_first; obj; obj = obj->next_content)
		if ((vnum < 0 || obj->pIndexData->vnum == vnum)
			&& (pitem_type < 0 || obj->pIndexData->item_type == pitem_type)
			&& (!fWear || obj->wear_loc != WEAR_NONE))
			return true;
	return false;
}

bool get_mob_vnum_room(CharData * ch, ObjData * obj, RoomIndex * room,
					   vnum_t vnum)
{
	CharData *mob;

	if ((ch && obj) || (ch && room) || (obj && room))
	{
		bug("received multiple prog types");
		return false;
	}

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

	for (; mob; mob = mob->next_in_room)
		if (IsNPC(mob) && mob->pIndexData->vnum == vnum)
			return true;
	return false;
}

bool get_obj_vnum_room(CharData * ch, ObjData * obj, RoomIndex * room,
					   vnum_t vnum)
{
	ObjData *vobj;

	if ((ch && obj) || (ch && room) || (obj && room))
	{
		bug("received multiple prog types");
		return false;
	}

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

	for (; vobj; vobj = vobj->next_content)
		if (vobj->pIndexData->vnum == vnum)
			return true;
	return false;
}

int cmd_eval_mob(vnum_t vnum, const char *line, int check, CharData * mob,
				 CharData * ch, const void *arg1, const void *arg2,
				 CharData * rch)
{
	CharData *lval_char = mob;
	CharData *vch = (CharData *) arg2;
	ObjData *obj1 = (ObjData *) arg1;
	ObjData *obj2 = (ObjData *) arg2;
	ObjData *lval_obj = NULL;
	const char *original;
	char buf[MIL], code;
	int lval = 0, oper = 0, rval = -1;
	flag_t temp;

	original = line;
	line = one_argument(line, buf);
	if (NullStr(buf) || mob == NULL)
		return false;

	if (mob->mprog_target == NULL)
		mob->mprog_target = ch;

	switch (check)
	{

		case CHK_RAND:
			return (number_percent() < atoi(buf));
		case CHK_MOBHERE:
			if (is_number(buf))
				return (get_mob_vnum_room(mob, NULL, NULL, atov(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, atov(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 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:
			;
	}

	if (rval >= 0)
	{
		if ((oper = keyword_lookup(fn_evals, buf)) < 0)
		{
			bugf("prog %ld syntax error(2) '%s'", vnum, original);
			return false;
		}
		one_argument(line, buf);
		lval = rval;
		rval = atoi(buf);
		return (num_eval(lval, oper, rval));
	}

	if (buf[0] != '$' || buf[1] == '\0')
	{
		bugf("prog %ld syntax error(3) '%s'", vnum, original);
		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:
			bugf("prog %ld syntax error(4) '%s'", vnum, original);
			return false;
	}

	if (lval_char == NULL && lval_obj == NULL)
		return false;

	switch (check)
	{
		case CHK_ISPC:
			return (lval_char != NULL && !IsNPC(lval_char));
		case CHK_ISNPC:
			return (lval_char != NULL && IsNPC(lval_char));
		case CHK_ISGOOD:
			return (lval_char != NULL && IsGood(lval_char));
		case CHK_ISEVIL:
			return (lval_char != NULL && IsEvil(lval_char));
		case CHK_ISNEUTRAL:
			return (lval_char != NULL && IsNeutral(lval_char));
		case CHK_ISIMMORT:
			return (lval_char != NULL && IsImmortal(lval_char));
		case CHK_HUNTER:
			return (lval_char != NULL && lval_char->hunting == mob);
		case CHK_ISCHARM:

			return (lval_char != NULL && IsAffected(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_ONQUEST:
			return (lval_char != NULL && IsQuester(lval_char));
		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:
			;
	}

	line = one_argument(line, buf);
	switch (check)
	{
		case CHK_AFFECTED:
			if ((temp = flag_value(affect_flags, buf)) == NO_FLAG)
				return false;
			return (lval_char != NULL && IsAffected(lval_char, temp));
		case CHK_ACT:
			if ((temp = flag_value(act_flags, buf)) == NO_FLAG)
				return false;
			return (lval_char != NULL && IsNPC(lval_char)
					&& IsSet(lval_char->act, temp));
		case CHK_PLR:
			if ((temp = flag_value(plr_flags, buf)) == NO_FLAG)
				return false;
			return (lval_char != NULL && !IsNPC(lval_char)
					&& IsSet(lval_char->act, temp));
		case CHK_IMM:
			if ((temp = flag_value(imm_flags, buf)) == NO_FLAG)
				return false;
			return (lval_char != NULL && IsSet(lval_char->imm_flags, temp));
		case CHK_OFF:
			if ((temp = flag_value(off_flags, buf)) == NO_FLAG)
				return false;
			return (lval_char != NULL && IsSet(lval_char->off_flags, temp));
		case CHK_CARRIES:
			if (is_number(buf))
				return (lval_char != NULL
						&& has_item(lval_char, atov(buf), (item_t) - 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, atov(buf), (item_t) - 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_t) flag_value(type_flags, buf), false));
		case CHK_USES:
			return (lval_char != NULL
					&& has_item(lval_char, -1,
								(item_t) flag_value(type_flags, buf), true));
		case CHK_SKILL:
			return (lval_char != NULL && !IsNPC(lval_char)
					&& skill_lookup(buf) != -1
					&& get_skill(lval_char, skill_lookup(buf)) >= atoi(line));
		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 == flag_value(position_flags,
														 buf));
		case CHK_CLAN:
			return (lval_char != NULL
					&& CharClan(lval_char) == clan_lookup(buf));
		case CHK_CLASS:
			return (lval_char != NULL
					&& prime_class(lval_char) == class_lookup(buf));
		case CHK_RACE:
			return (lval_char != NULL && lval_char->race == race_lookup(buf));
		case CHK_OBJTYPE:
			return (lval_obj != NULL
					&& lval_obj->item_type == flag_value(type_flags, buf));
		default:
			;
	}

	if ((oper = keyword_lookup(fn_evals, buf)) < 0)
	{
		bugf("prog %ld syntax error(5): '%s'", vnum, original);
		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 && IsNPC(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) / (Max(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_WEIGHT:
			if (lval_char != NULL)
				lval = get_carry_weight(lval_char);
			break;
		case CHK_ALIGN:
			if (lval_char != NULL)
				lval = lval_char->alignment;
			break;
		case CHK_MONEY:
			if (lval_char != NULL)
				lval = lval_char->gold;
			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(vnum_t vnum, const char *line, int check, ObjData * obj,
				 CharData * ch, const void *arg1, const void *arg2,
				 CharData * rch)
{
	CharData *lval_char = NULL;
	CharData *vch = (CharData *) arg2;
	ObjData *obj1 = (ObjData *) arg1;
	ObjData *obj2 = (ObjData *) arg2;
	ObjData *lval_obj = obj;
	const char *original;
	char buf[MIL], code;
	int lval = 0, oper = 0, rval = -1;
	flag_t temp;

	original = line;
	line = one_argument(line, buf);
	if (NullStr(buf) || obj == NULL)
		return false;

	if (obj->oprog_target == NULL)
		obj->oprog_target = ch;

	switch (check)
	{

		case CHK_RAND:
			return (number_percent() < atoi(buf));
		case CHK_MOBHERE:
			if (is_number(buf))
				return (get_mob_vnum_room(NULL, obj, NULL, atov(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, atov(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 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.");
			break;
		case CHK_ORDER:
			rval = get_order(NULL, obj);
			break;
		case CHK_HOUR:
			rval = time_info.hour;
			break;
		default:
			;
	}

	if (rval >= 0)
	{
		if ((oper = keyword_lookup(fn_evals, buf)) < 0)
		{
			bugf("Cmd_eval_obj: prog %ld syntax error(2) '%s'", vnum,
				 original);
			return false;
		}
		one_argument(line, buf);
		lval = rval;
		rval = atoi(buf);
		return (num_eval(lval, oper, rval));
	}

	if (buf[0] != '$' || buf[1] == '\0')
	{
		bugf("Cmd_eval_obj: prog %ld syntax error(3) '%s'", vnum, original);
		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:
			bugf("Cmd_eval_obj: prog %ld syntax error(4) '%s'", vnum,
				 original);
			return false;
	}

	if (lval_char == NULL && lval_obj == NULL)
		return false;

	switch (check)
	{
		case CHK_ISPC:
			return (lval_char != NULL && !IsNPC(lval_char));
		case CHK_ISNPC:
			return (lval_char != NULL && IsNPC(lval_char));
		case CHK_ISGOOD:
			return (lval_char != NULL && IsGood(lval_char));
		case CHK_ISEVIL:
			return (lval_char != NULL && IsEvil(lval_char));
		case CHK_ISNEUTRAL:
			return (lval_char != NULL && IsNeutral(lval_char));
		case CHK_ISIMMORT:
			return (lval_char != NULL && IsImmortal(lval_char));
		case CHK_ISCHARM:

			return (lval_char != NULL && IsAffected(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:
			;
	}

	line = one_argument(line, buf);
	switch (check)
	{
		case CHK_AFFECTED:
			if ((temp = flag_value(affect_flags, buf)) == NO_FLAG)
				return false;
			return (lval_char != NULL && IsAffected(lval_char, temp));
		case CHK_ACT:
			if ((temp = flag_value(act_flags, buf)) == NO_FLAG)
				return false;
			return (lval_char != NULL && IsNPC(lval_char)
					&& IsSet(lval_char->act, temp));
		case CHK_PLR:
			if ((temp = flag_value(plr_flags, buf)) == NO_FLAG)
				return false;
			return (lval_char != NULL && !IsNPC(lval_char)
					&& IsSet(lval_char->act, temp));
		case CHK_IMM:
			if ((temp = flag_value(imm_flags, buf)) == NO_FLAG)
				return false;
			return (lval_char != NULL && IsSet(lval_char->imm_flags, temp));
		case CHK_OFF:
			if ((temp = flag_value(off_flags, buf)) == NO_FLAG)
				return false;
			return (lval_char != NULL && IsSet(lval_char->off_flags, temp));
		case CHK_CARRIES:
			if (is_number(buf))
				return (lval_char != NULL
						&& has_item(lval_char, atov(buf), (item_t) - 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, atov(buf), (item_t) - 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_t) flag_value(type_flags, buf), false));
		case CHK_USES:
			return (lval_char != NULL
					&& has_item(lval_char, -1,
								(item_t) flag_value(type_flags, buf), true));
		case CHK_SKILL:
			return (lval_char != NULL && !IsNPC(lval_char)
					&& skill_lookup(buf) != -1
					&& get_skill(lval_char, skill_lookup(buf)) >= atoi(line));
		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 == flag_value(position_flags,
														 buf));
		case CHK_CLAN:
			return (lval_char != NULL
					&& CharClan(lval_char) == clan_lookup(buf));
		case CHK_CLASS:
			return (lval_char != NULL
					&& prime_class(lval_char) == class_lookup(buf));
		case CHK_RACE:
			return (lval_char != NULL && lval_char->race == race_lookup(buf));
		case CHK_OBJTYPE:
			return (lval_obj != NULL
					&& lval_obj->item_type == flag_value(type_flags, buf));
		default:
			;
	}

	if ((oper = keyword_lookup(fn_evals, buf)) < 0)
	{
		bugf("Cmd_eval_obj: prog %ld syntax error(5): '%s'", vnum, original);
		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 && IsNPC(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) / (Max(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_WEIGHT:
			if (lval_char != NULL)
				lval = get_carry_weight(lval_char);
			break;
		case CHK_ALIGN:
			if (lval_char != NULL)
				lval = lval_char->alignment;
			break;
		case CHK_MONEY:
			if (lval_char != NULL)
				lval = lval_char->gold;
			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(vnum_t vnum, const char *line, int check, RoomIndex * room,
				  CharData * ch, const void *arg1, const void *arg2,
				  CharData * rch)
{
	CharData *lval_char = NULL;
	CharData *vch = (CharData *) arg2;
	ObjData *obj1 = (ObjData *) arg1;
	ObjData *obj2 = (ObjData *) arg2;
	ObjData *lval_obj = NULL;
	const char *original;
	char buf[MIL], code;
	int lval = 0, oper = 0, rval = -1;
	flag_t temp;

	original = line;
	line = one_argument(line, buf);
	if (NullStr(buf) || room == NULL)
		return false;

	if (room->rprog_target == NULL)
		room->rprog_target = ch;

	switch (check)
	{

		case CHK_RAND:
			return (number_percent() < atoi(buf));
		case CHK_MOBHERE:
			if (is_number(buf))
				return (get_mob_vnum_room(NULL, NULL, room, atov(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, atov(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 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("received CHK_CLONES.");
			break;
		case CHK_ORDER:
			bug("received CHK_ORDER.");
			break;
		case CHK_HOUR:
			rval = time_info.hour;
			break;
		default:
			;
	}

	if (rval >= 0)
	{
		if ((oper = keyword_lookup(fn_evals, buf)) < 0)
		{
			bugf("prog %ld syntax error(2) '%s'", vnum, original);
			return false;
		}
		one_argument(line, buf);
		lval = rval;
		rval = atoi(buf);
		return (num_eval(lval, oper, rval));
	}

	if (buf[0] != '$' || buf[1] == '\0')
	{
		bugf("prog %ld syntax error(3) '%s'", vnum, original);
		return false;
	}
	else
		code = buf[1];
	switch (code)
	{
		case 'i':
			bug("received code case 'i'.");
			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:
			bugf("prog %ld syntax error(4) '%s'", vnum, original);
			return false;
	}

	if (lval_char == NULL && lval_obj == NULL)
		return false;

	switch (check)
	{
		case CHK_ISPC:
			return (lval_char != NULL && !IsNPC(lval_char));
		case CHK_ISNPC:
			return (lval_char != NULL && IsNPC(lval_char));
		case CHK_ISGOOD:
			return (lval_char != NULL && IsGood(lval_char));
		case CHK_ISEVIL:
			return (lval_char != NULL && IsEvil(lval_char));
		case CHK_ISNEUTRAL:
			return (lval_char != NULL && IsNeutral(lval_char));
		case CHK_ISIMMORT:
			return (lval_char != NULL && IsImmortal(lval_char));
		case CHK_ISCHARM:

			return (lval_char != NULL && IsAffected(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:
			;
	}

	line = one_argument(line, buf);
	switch (check)
	{
		case CHK_AFFECTED:
			if ((temp = flag_value(affect_flags, buf)) == NO_FLAG)
				return false;
			return (lval_char != NULL && IsAffected(lval_char, temp));
		case CHK_ACT:
			if ((temp = flag_value(act_flags, buf)) == NO_FLAG)
				return false;
			return (lval_char != NULL && IsNPC(lval_char)
					&& IsSet(lval_char->act, temp));
		case CHK_PLR:
			if ((temp = flag_value(plr_flags, buf)) == NO_FLAG)
				return false;
			return (lval_char != NULL && !IsNPC(lval_char)
					&& IsSet(lval_char->act, temp));
		case CHK_IMM:
			if ((temp = flag_value(imm_flags, buf)) == NO_FLAG)
				return false;
			return (lval_char != NULL && IsSet(lval_char->imm_flags, temp));
		case CHK_OFF:
			if ((temp = flag_value(off_flags, buf)) == NO_FLAG)
				return false;
			return (lval_char != NULL && IsSet(lval_char->off_flags, temp));
		case CHK_CARRIES:
			if (is_number(buf))
				return (lval_char != NULL
						&& has_item(lval_char, atov(buf), (item_t) - 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, atov(buf), (item_t) - 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_t) flag_value(type_flags, buf), false));
		case CHK_USES:
			return (lval_char != NULL
					&& has_item(lval_char, -1,
								(item_t) flag_value(type_flags, buf), true));
		case CHK_SKILL:
			return (lval_char != NULL && !IsNPC(lval_char)
					&& skill_lookup(buf) != -1
					&& get_skill(lval_char, skill_lookup(buf)) >= atoi(line));
		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 == flag_value(position_flags,
														 buf));
		case CHK_CLAN:
			return (lval_char != NULL
					&& CharClan(lval_char) == clan_lookup(buf));
		case CHK_CLASS:
			return (lval_char != NULL
					&& prime_class(lval_char) == class_lookup(buf));
		case CHK_RACE:
			return (lval_char != NULL && lval_char->race == race_lookup(buf));
		case CHK_OBJTYPE:
			return (lval_obj != NULL
					&& lval_obj->item_type == (item_t) flag_value(type_flags,
																  buf));
		default:
			;
	}

	if ((oper = keyword_lookup(fn_evals, buf)) < 0)
	{
		bugf("prog %ld syntax error(5): '%s'", vnum, original);
		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 && IsNPC(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) / (Max(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_WEIGHT:
			if (lval_char != NULL)
				lval = get_carry_weight(lval_char);
			break;
		case CHK_ALIGN:
			if (lval_char != NULL)
				lval = lval_char->alignment;
			break;
		case CHK_MONEY:
			if (lval_char != NULL)
				lval = lval_char->gold;
			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));
}

void expand_arg_mob(char *buf, const char *format, CharData * mob,
					CharData * ch, const void *arg1, const void *arg2,
					CharData * rch)
{
	const char *someone = "someone";
	const char *something = "something";
	const char *someones = "someone's";

	char name[MIL];
	CharData *vch = (CharData *) arg2;
	ObjData *obj1 = (ObjData *) arg1;
	ObjData *obj2 = (ObjData *) arg2;
	const char *str;
	const char *i;
	char *point;

	if (NullStr(format))
		return;

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

		switch (*str)
		{
			default:
				bugf("bad code %c.", *str);
				i = " <@@@> ";
				break;
			case 'i':
				one_argument(mob->name, name);
				i = name;
				break;
			case 'I':
				i = mob->short_descr;
				break;
			case 'n':
				i = someone;
				if (ch != NULL && can_see(mob, ch))
				{
					one_argument(ch->name, name);
					i = capitalize(name);
				}
				break;
			case 'N':
				i = (ch != NULL
					 && can_see(mob,
								ch)) ? (IsNPC(ch) ? ch->
										short_descr : ch->name) : someone;
				break;
			case 't':
				i = someone;
				if (vch != NULL && can_see(mob, vch))
				{
					one_argument(vch->name, name);
					i = capitalize(name);
				}
				break;
			case 'T':
				i = (vch != NULL
					 && can_see(mob,
								vch)) ? (IsNPC(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, name);
					i = capitalize(name);
				}
				break;
			case 'R':
				if (rch == NULL)
					rch = get_random_char(mob, NULL, NULL);
				i = (rch != NULL
					 && can_see(mob,
								rch)) ? (IsNPC(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, name);
					i = capitalize(name);
				}
				break;
			case 'Q':
				i = (mob->mprog_target != NULL
					 && can_see(mob,
								mob->
								mprog_target)) ? (IsNPC(mob->mprog_target) ?
												  mob->
												  mprog_target->short_descr :
												  mob->mprog_target->
												  name) : someone;
				break;
			case 'j':
				i = he_she[Range(0, mob->sex, 2)];
				break;
			case 'e':
				i = (ch != NULL
					 && can_see(mob, ch)) ? he_she[Range(0, ch->sex,
														 2)] : someone;
				break;
			case 'E':
				i = (vch != NULL
					 && can_see(mob, vch)) ? he_she[Range(0, vch->sex,
														  2)] : someone;
				break;
			case 'J':
				i = (rch != NULL
					 && can_see(mob, rch)) ? he_she[Range(0, rch->sex,
														  2)] : someone;
				break;
			case 'X':
				i = (mob->mprog_target != NULL
					 && can_see(mob, mob->mprog_target)) ? he_she[Range(0,
																		mob->
																		mprog_target->
																		sex,
																		2)] :
					someone;
				break;
			case 'k':
				i = him_her[Range(0, mob->sex, 2)];
				break;
			case 'm':
				i = (ch != NULL
					 && can_see(mob, ch)) ? him_her[Range(0, ch->sex,
														  2)] : someone;
				break;
			case 'M':
				i = (vch != NULL
					 && can_see(mob, vch)) ? him_her[Range(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[Range(0, rch->sex,
														   2)] : someone;
				break;
			case 'Y':
				i = (mob->mprog_target != NULL
					 && can_see(mob, mob->mprog_target)) ? him_her[Range(0,
																		 mob->
																		 mprog_target->
																		 sex,
																		 2)] :
					someone;
				break;
			case 'l':
				i = his_her[Range(0, mob->sex, 2)];
				break;
			case 's':
				i = (ch != NULL
					 && can_see(mob, ch)) ? his_her[Range(0, ch->sex,
														  2)] : someones;
				break;
			case 'S':
				i = (vch != NULL
					 && can_see(mob, vch)) ? his_her[Range(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[Range(0, rch->sex,
														   2)] : someones;
				break;
			case 'Z':
				i = (mob->mprog_target != NULL
					 && can_see(mob, mob->mprog_target)) ? his_her[Range(0,
																		 mob->
																		 mprog_target->
																		 sex,
																		 2)] :
					someones;
				break;
			case 'o':
				i = something;
				if (obj1 != NULL && can_see_obj(mob, obj1))
				{
					one_argument(obj1->name, name);
					i = name;
				}
				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, name);
					i = name;
				}
				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, ObjData * obj,
					  RoomIndex * room, CharData * ch, const void *arg1,
					  const void *arg2, CharData * rch)
{
	const char *someone = "someone";
	const char *something = "something";
	const char *someones = "someone's";

	char name[MIL];
	CharData *vch = (CharData *) arg2;
	ObjData *obj1 = (ObjData *) arg1;
	ObjData *obj2 = (ObjData *) arg2;
	const char *str;
	const char *i;
	char *point;

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

	if (NullStr(format))
		return;

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

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

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

	}
	*point = '\0';

	return;
}

const char *prog_type(prog_t type)
{
	switch (type)
	{
		case PRG_MPROG:
			return "mobile";
		case PRG_OPROG:
			return "object";
		case PRG_RPROG:
			return "room";
		default:
			return "unknown";
	}
}

#define    MAX_NESTED_LEVEL 12

#define    BEGIN_BLOCK       0

#define    IN_BLOCK         -1

#define    END_BLOCK        -2

#define    MAX_CALL_LEVEL    5

#define MAX_PTRACE        	500
int ptrace_current;
int ptrace_calledby[MAX_PTRACE];
vnum_t callstack_pvnum[MAX_CALL_LEVEL];
vnum_t callstack_mvnum[MAX_CALL_LEVEL];
vnum_t callstack_rvnum[MAX_CALL_LEVEL];
int callstack_line[MAX_CALL_LEVEL];
bool callstack_aborted[MAX_CALL_LEVEL];
prog_t callstack_type[MAX_CALL_LEVEL];
vnum_t ptrace_pvnum[MAX_PTRACE];
vnum_t ptrace_mvnum[MAX_PTRACE];
vnum_t ptrace_rvnum[MAX_PTRACE];
prog_t ptrace_type[MAX_PTRACE];
unsigned char ptrace_calllevel[MAX_PTRACE];
time_t ptrace_time[MAX_PTRACE];
bool ptrace_aborted[MAX_PTRACE];

int call_level = 0;

Do_Fun(do_preset)
{
	int i;

	chprintln(ch, "Progs reseted.  (prog callstack depth to 0)");
	for (i = 0; i < MAX_CALL_LEVEL; i++)
	{
		callstack_pvnum[i] = 0;
		callstack_mvnum[i] = 0;
		callstack_rvnum[i] = 0;
		callstack_type[i] = PRG_NONE;
		callstack_aborted[i] = false;
	}
	call_level = 0;

	logf("%s reset the prog callstack", ch->name);
	return;
}

Do_Fun(do_pinfo)
{
	int i;

	Buffer *output;

	output = new_buf();

	bprintlnf(output,
			  "    Displaying program call abort history - calllevel currently is %d\r\n",
			  call_level);

	for (i = 0; i < MAX_CALL_LEVEL; i++)
	{
		if (callstack_aborted[i])
		{
			bprintlnf(output,
					  "      [%2d] Program %ld on %s %ld (in room %ld) {RABORTED!!! (BUGGY?){X\r\n",
					  i, callstack_pvnum[i], prog_type(callstack_type[i]),
					  callstack_mvnum[i], callstack_rvnum[i]);
		}
		else
		{
			bprintlnf(output,
					  "      [%2d] Program %ld on %s %ld (in room %ld)\r\n",
					  i, callstack_pvnum[i], prog_type(callstack_type[i]),
					  callstack_mvnum[i], callstack_rvnum[i]);
		}
	}

	{
		ProgCode *prg;

		for (prg = mprog_first; prg; prg = prg->next)
		{
			if (prg->disabled)
			{
				bprintlnf(output,
						  "Mobprog %ld is disabled with the following text:{x",
						  prg->vnum);
				bprintln(output, prg->disabled_text);
			}
		}
		for (prg = oprog_first; prg; prg = prg->next)
		{
			if (prg->disabled)
			{
				bprintlnf(output,
						  "Objprog %ld is disabled with the following text:{x",
						  prg->vnum);
				bprintln(output, prg->disabled_text);
			}
		}
		for (prg = rprog_first; prg; prg = prg->next)
		{
			if (prg->disabled)
			{
				bprintlnf(output,
						  "Roomprog %ld is disabled with the following text:{x",
						  prg->vnum);
				bprintln(output, prg->disabled_text);
			}
		}
	}

	sendpage(ch, buf_string(output));
	free_buf(output);

	return;
}

Do_Fun(do_ptrace)
{
	char arg1[MIL];
	Buffer *output;
	int lines_to_show;
	int line;
	int count;
	int i;
	int lc;

	argument = one_argument(argument, arg1);
	if (NullStr(arg1))
	{
		lines_to_show = get_scr_cols(ch);
		lines_to_show %= MAX_PTRACE;
	}
	else if (is_number(arg1))
	{
		lines_to_show = atoi(arg1);
		if (lines_to_show < 1)
		{
			chprintln(ch, "Lines to show, increased to 1");
			lines_to_show = 1;
		}
		else if (lines_to_show > MAX_PTRACE)
		{
			chprintlnf(ch,
					   "Lines to show, decreased to the number logged in the trace - %d",
					   MAX_PTRACE);
			lines_to_show = MAX_PTRACE;
		}
	}
	else
	{
		chprintlnf(ch,
				   "{RThe only parameter for ptrace must be a numeric value"
				   NEWLINE
				   "for the number of lines of the trace you wish to see.{x");
		return;
	}
	chprintlnf(ch, "Number of trace lines to show=%d", lines_to_show);

	output = new_buf();

	bprintln(output, "{xPTrace {G#prog{x, {Mtype{x, {Yvnum{x, {Binroom{x");

	line = ptrace_current + MAX_PTRACE - lines_to_show;
	lc = 0;

	for (count = 0; count < lines_to_show; count++)
	{
		line++;
		line %= MAX_PTRACE;

		if (ptrace_time[line] == 0)
			continue;

		bprintf(output, "[%3d]%s  ", ++lc,
				str_time(ptrace_time[line], GetTzone(ch), "%H:%M:%S"));

		for (i = 0; i < ptrace_calllevel[line]; i++)
		{
			bprint(output, "        ");
		}

		bprintlnf(output, "{G%5ld{x,{M%6s,{Y%5ld{x,{B%5ld{x, %s",
				  ptrace_pvnum[line], prog_type(ptrace_type[line]),
				  ptrace_mvnum[line], ptrace_rvnum[line],
				  prog_type_to_name(ptrace_calledby[line]));
	}
	sendpage(ch, buf_string(output));
	free_buf(output);
}

Do_Fun(do_phelp)
{
	cmd_syntax(ch, NULL, n_fun, "trace", "reset", "info", "stat", "dump",
			   NULL);
}

Do_Fun(do_pstat)
{
	char cmd[MIL], arg[MIL];
	ProgList *prg;
	int i;

	argument = one_argument(argument, cmd);
	one_argument(argument, arg);

	if (NullStr(cmd))
	{
		cmd_syntax(ch, NULL, n_fun, "mob <vnum>", "obj <vnum>",
				   "rpstat <vnum>", NULL);
		return;
	}
	else if (!str_prefix(cmd, "room"))
	{
		RoomIndex *room;

		if (NullStr(arg))
			room = ch->in_room;
		else if (!is_number(arg))
		{
			chprintln(ch, "You must provide a number.");
			return;
		}
		else if ((room = get_room_index(atov(arg))) == NULL)
		{
			chprintln(ch, "No such room.");
			return;
		}

		chprintlnf(ch, "Room #%-6ld [%s]", room->vnum, room->name);

		chprintlnf(ch, "Delay   %-6d [%s]", room->rprog_delay,
				   room->rprog_target ==
				   NULL ? "No target" : room->rprog_target->name);

		if (!room->rprog_flags)
		{
			chprintln(ch, "[No programs set]");
			return;
		}

		for (i = 0, prg = room->rprog_first; prg != NULL; prg = prg->next)
		{
			chprintlnf(ch, "[%2d] Trigger [%-8s] Program [%4ld] Phrase [%s]",
					   ++i, prog_type_to_name(prg->trig_type),
					   prg->prog->vnum, prg->trig_phrase);
		}

		return;
	}
	else if (!str_prefix(cmd, "object"))
	{
		ObjData *obj;

		if (NullStr(arg) || (obj = get_obj_world(ch, arg)) == NULL)
		{
			chprintln(ch, "No such object.");
			return;
		}

		chprintlnf(ch, "Object #%-6ld [%s]", obj->pIndexData->vnum,
				   obj->short_descr);

		chprintlnf(ch, "Delay   %-6d [%s]", obj->oprog_delay,
				   obj->oprog_target ==
				   NULL ? "No target" : obj->oprog_target->name);

		if (!obj->pIndexData->oprog_flags)
		{
			chprintln(ch, "[No programs set]");
			return;
		}

		for (i = 0, prg = obj->pIndexData->oprog_first; prg != NULL;
			 prg = prg->next)
		{
			chprintlnf(ch, "[%2d] Trigger [%-8s] Program [%4ld] Phrase [%s]",
					   ++i, prog_type_to_name(prg->trig_type),
					   prg->prog->vnum, prg->trig_phrase);
		}

		return;
	}
	else if (!str_prefix(cmd, "mobile"))
	{
		CharData *victim;

		if (NullStr(arg) || (victim = get_char_world(ch, arg)) == NULL)
		{
			chprintln(ch, "No such creature.");
			return;
		}

		if (!IsNPC(victim))
		{
			chprintln(ch, "That is not a mobile.");
			return;
		}

		chprintlnf(ch, "Mobile #%-6ld [%s]", victim->pIndexData->vnum,
				   victim->short_descr);

		chprintlnf(ch, "Delay   %-6d [%s]", victim->mprog_delay,
				   victim->mprog_target ==
				   NULL ? "No target" : victim->mprog_target->name);

		if (!victim->pIndexData->mprog_flags)
		{
			chprintln(ch, "[No programs set]");
			return;
		}

		for (i = 0, prg = victim->pIndexData->mprog_first; prg != NULL;
			 prg = prg->next)
		{
			chprintlnf(ch, "[%2d] Trigger [%-8s] Program [%4ld] Phrase [%s]",
					   ++i, prog_type_to_name(prg->trig_type),
					   prg->prog->vnum, prg->trig_phrase);
		}

		return;
	}
	else
		do_pstat(n_fun, ch, "");
}

Do_Fun(do_pdump)
{
	char cmd[MIL], buf[MIL];
	ProgCode *prg;

	argument = one_argument(argument, cmd);
	one_argument(argument, buf);

	if (NullStr(cmd))
	{
		cmd_syntax(ch, NULL, n_fun, "mob <vnum>", "obj <vnum>", "room <vnum>",
				   NULL);
		return;
	}
	else if (!str_prefix(cmd, "mobile"))
	{
		if ((prg = get_prog_index(atov(buf), PRG_MPROG)) == NULL)
		{
			chprintln(ch, "No such MOBprogram.");
			return;
		}
		sendpage(ch, prg->code);
	}
	else if (!str_prefix(cmd, "object"))
	{
		if ((prg = get_prog_index(atov(buf), PRG_OPROG)) == NULL)
		{
			chprintln(ch, "No such OBJprogram.");
			return;
		}
		sendpage(ch, prg->code);
	}
	else if (!str_prefix(cmd, "room"))
	{
		if ((prg = get_prog_index(atov(buf), PRG_RPROG)) == NULL)
		{
			chprintln(ch, "No such ROOMprogram.");
			return;
		}
		sendpage(ch, prg->code);
	}
	else
		do_pdump(n_fun, ch, "");
}

Do_Fun(do_programs)
{
	vinterpret(ch, n_fun, argument, "trace", do_ptrace, "reset", do_preset,
			   "info", do_pinfo, "stat", do_pstat, "dump", do_pdump, NULL,
			   do_phelp);
}

void buggy_prog(ProgList * trigger, const char *fmt, ...)
{
	char buf[MSL];
	char *pBuf;
	int i;

	assert(trigger->prog->disabled == false);

	trigger->prog->disabled = true;

	sprintf(buf,
			"#############" NEWLINE
			"             Disabling prog %ld,%ld,%ld,%d,%d:" NEWLINE
			"             ", callstack_pvnum[call_level - 1],
			callstack_mvnum[call_level - 1], callstack_rvnum[call_level - 1],
			callstack_line[call_level - 1], call_level - 1);

	if (!NullStr(fmt))
	{
		va_list args;

		pBuf = &buf[strlen(buf)];
		va_start(args, fmt);
		vsnprintf(pBuf, sizeof(buf), fmt, args);
		va_end(args);
		bug(pBuf);
	}

	pBuf = &buf[strlen(buf)];
	sprintf(pBuf,
			NEWLINE "             Trigger type '%s' phrase '%s'" NEWLINE,
			prog_type_to_name(trigger->trig_type), trigger->trig_phrase);

	strcat(pBuf, "             Call stack info:" NEWLINE);

	for (i = 0; i < call_level; i++)
	{
		if (!callstack_aborted[i] || (i == call_level - 1))
		{
			sprintf(pBuf + strlen(pBuf),
					"             Program %ld on %s %ld (in room %ld), line %d"
					NEWLINE, callstack_pvnum[i], prog_type(callstack_type[i]),
					callstack_mvnum[i], callstack_rvnum[i],
					callstack_line[i]);
		}
	}
	strcat(pBuf, "#############" NEWLINE NEWLINE);

	replace_str(&trigger->prog->disabled_text, buf);

}

void program_flow(ProgList * program, CharData * mob, ObjData * obj,
				  RoomIndex * room, CharData * ch, const void *arg1,
				  const void *arg2)
{
	CharData *rch = NULL;
	const char *source;
	const char *code;
	const char *line;
	char ctrl[MAX_INPUT_LENGTH], data[MAX_STRING_LENGTH];
	static bool init_ptrace = true;
	char buf[MSL];
	prog_t type = PRG_NONE;
	int level, eval, check;
	int state[MAX_NESTED_LEVEL], cond[MAX_NESTED_LEVEL];
	vnum_t mvnum = 0, ovnum = 0, rvnum = 0, pvnum;

	if (init_ptrace)
	{
		memset(&ptrace_time[0], 0, sizeof(time_t));
		init_ptrace = false;
	}

	if (!program)
	{
		bug("program_flow(): program==NULL!!!");
		return;
	}

	if ((mob && obj) || (mob && room) || (obj && room))
	{
		bug("received multiple prog types.");
		return;
	}
	if (mob)
	{
		type = PRG_MPROG;
		mvnum = mob->pIndexData->vnum;
		rvnum = mob->in_room ? mob->in_room->vnum : 0;
	}
	else if (obj)
	{
		type = PRG_OPROG;
		ovnum = obj->pIndexData->vnum;
		mvnum = 0;
		rvnum = obj->in_room ? obj->in_room->vnum : 0;
	}
	else if (room)
	{
		type = PRG_RPROG;
		mvnum = 0;
		rvnum = room->vnum;
	}
	else
	{
		bug("did not receive a prog type.");
		return;
	}

	pvnum = program->prog->vnum;

	callstack_pvnum[call_level] = pvnum;
	callstack_mvnum[call_level] = mvnum;
	callstack_rvnum[call_level] = rvnum;
	callstack_aborted[call_level] = true;

	callstack_line[call_level] = 0;
	callstack_type[call_level] = type;

	ptrace_current++;
	ptrace_current %= MAX_PTRACE;
	ptrace_calledby[ptrace_current] = program->trig_type;
	ptrace_pvnum[ptrace_current] = pvnum;
	ptrace_mvnum[ptrace_current] = mvnum;
	ptrace_rvnum[ptrace_current] = rvnum;
	ptrace_time[ptrace_current] = current_time;
	ptrace_calllevel[ptrace_current] = call_level;
	ptrace_type[ptrace_current] = type;

	if (++call_level > MAX_CALL_LEVEL)
	{
		int i;

		if (mob)
			bugf("Progs: MAX_CALL_LEVEL exceeded, vnum %ld, mprog vnum %ld",
				 mvnum, pvnum);
		else if (obj)
			bugf("Progs: MAX_CALL_LEVEL exceeded, vnum %ld oprog vnum %ld.",
				 ovnum, pvnum);
		else
			bugf("Progs: MAX_CALL_LEVEL exceeded, vnum %ld rprog vnum %ld.",
				 rvnum, pvnum);

		log_string("Program callstack:\n");
		for (i = 0; i < MAX_CALL_LEVEL; i++)
		{
			logf("[%2d] Program %ld on %s %ld (in room %ld)", i,
				 callstack_pvnum[i], prog_type(callstack_type[i]),
				 callstack_mvnum[i], callstack_rvnum[i]);
		}
		return;
	}

	if (program->prog->disabled)
	{
		if (ch)
		{
			bugf
				("Prog %ld triggered (%s) on me by '%s', not run cause prog is disabled.",
				 program->prog->vnum, prog_type_to_name(program->trig_type),
				 ch->name);
		}
		else
		{
			bugf("Prog %ld triggered (%s), not run cause prog is disabled.",
				 program->prog->vnum, prog_type_to_name(program->trig_type));
		}
		call_level--;
		callstack_aborted[call_level] = false;
		return;
	}

	for (level = 0; level < MAX_NESTED_LEVEL; level++)
	{
		state[level] = IN_BLOCK;
		cond[level] = true;
	}
	level = 0;

	source = program->prog->code;
	code = source;

	while (*code)
	{
		bool arg_first = true;
		char *b = buf, *c = ctrl, *d = data;

		while (isspace(*code) && *code)
			code++;
		while (*code)
		{
			if (*code == '\n' || *code == '\r')
				break;
			else if (isspace(*code))
			{
				if (arg_first)
					arg_first = false;
				else
					*d++ = *code;
			}
			else
			{
				if (arg_first)
					*c++ = *code;
				else
					*d++ = *code;
			}
			*b++ = *code++;
		}
		*b = *c = *d = '\0';

		if (NullStr(buf))
			break;
		if (buf[0] == '*')

			continue;

		line = data;

		if (!str_cmp(ctrl, "if"))
		{
			if (state[level] == BEGIN_BLOCK)
			{
				if (mob)
					buggy_prog(program,
							   "misplaced if statement, mob %ld prog %ld",
							   mvnum, pvnum);
				else if (obj)
					buggy_prog(program,
							   "misplaced if statement, obj %ld prog %ld",
							   ovnum, pvnum);
				else
					buggy_prog(program,
							   "misplaced if statement, room %ld prog %ld",
							   rvnum, pvnum);
				call_level--;
				return;
			}
			state[level] = BEGIN_BLOCK;
			if (++level >= MAX_NESTED_LEVEL)
			{
				if (mob)
					buggy_prog(program,
							   "Max nested level exceeded, mob %ld prog %ld",
							   mvnum, pvnum);
				else if (obj)
					buggy_prog(program,
							   "Max nested level exceeded, obj %ld prog %ld",
							   ovnum, pvnum);
				else
					buggy_prog(program,
							   "Max nested level exceeded, room %ld prog %ld",
							   rvnum, pvnum);
				call_level--;
				return;
			}
			if (level && cond[level - 1] == false)
			{
				cond[level] = false;
				continue;
			}
			line = one_argument(line, ctrl);
			if (mob && (check = keyword_lookup(fn_keyword, ctrl)) >= 0)
			{
				cond[level] =
					cmd_eval_mob(pvnum, line, check, mob, ch, arg1, arg2,
								 rch);
			}
			else if (obj && (check = keyword_lookup(fn_keyword, ctrl)) >= 0)
			{
				cond[level] =
					cmd_eval_obj(pvnum, line, check, obj, ch, arg1, arg2,
								 rch);
			}
			else if (room && (check = keyword_lookup(fn_keyword, ctrl)) >= 0)
			{
				cond[level] =
					cmd_eval_room(pvnum, line, check, room, ch, arg1, arg2,
								  rch);
			}
			else
			{
				if (mob)
					buggy_prog(program,
							   "invalid if_check (if), mob %ld prog %ld",
							   mvnum, pvnum);
				else if (obj)
					buggy_prog(program,
							   "invalid if_check (if), obj %ld prog %ld",
							   ovnum, pvnum);
				else
					buggy_prog(program,
							   "invalid if_check (if), room %ld prog %ld",
							   rvnum, pvnum);
				call_level--;
				return;
			}
			state[level] = END_BLOCK;
		}
		else if (!str_cmp(ctrl, "or"))
		{
			if (!level || state[level - 1] != BEGIN_BLOCK)
			{
				if (mob)
					buggy_prog(program, "or without if, mob %ld prog %ld",
							   mvnum, pvnum);
				else if (obj)
					buggy_prog(program, "or without if, obj %ld prog %ld",
							   ovnum, pvnum);
				else
					buggy_prog(program, "or without if, room %ld prog %ld",
							   rvnum, pvnum);
				return;
			}
			if (level && cond[level - 1] == false)
				continue;
			line = one_argument(line, ctrl);
			if (mob && (check = keyword_lookup(fn_keyword, ctrl)) >= 0)
			{
				eval =
					cmd_eval_mob(pvnum, line, check, mob, ch, arg1, arg2,
								 rch);
			}
			else if (obj && (check = keyword_lookup(fn_keyword, ctrl)) >= 0)
			{
				eval =
					cmd_eval_obj(pvnum, line, check, obj, ch, arg1, arg2,
								 rch);
			}
			else if (room && (check = keyword_lookup(fn_keyword, ctrl)) >= 0)
			{
				eval =
					cmd_eval_room(pvnum, line, check, room, ch, arg1, arg2,
								  rch);
			}
			else
			{
				if (mob)
					buggy_prog(program,
							   "invalid if_check (or), mob %ld prog %ld",
							   mvnum, pvnum);
				else if (obj)
					buggy_prog(program,
							   "invalid if_check (or), obj %ld prog %ld",
							   ovnum, pvnum);
				else
					buggy_prog(program,
							   "invalid if_check (or), room %ld prog %ld",
							   rvnum, pvnum);
				call_level--;
				return;
			}
			cond[level] = (eval == (int) true) ? true : cond[level];
		}
		else if (!str_cmp(ctrl, "and"))
		{
			if (!level || state[level - 1] != BEGIN_BLOCK)
			{
				if (mob)
					buggy_prog(program, "and without if, mob %ld prog %ld",
							   mvnum, pvnum);
				else if (obj)
					buggy_prog(program, "and without if, obj %ld prog %ld",
							   ovnum, pvnum);
				else
					buggy_prog(program, "and without if, room %ld prog %ld",
							   rvnum, pvnum);
				call_level--;
				return;
			}
			if (level && cond[level - 1] == false)
				continue;
			line = one_argument(line, ctrl);
			if (mob && (check = keyword_lookup(fn_keyword, ctrl)) >= 0)
			{
				eval =
					cmd_eval_mob(pvnum, line, check, mob, ch, arg1, arg2,
								 rch);
			}
			else if (obj && (check = keyword_lookup(fn_keyword, ctrl)) >= 0)
			{
				eval =
					cmd_eval_obj(pvnum, line, check, obj, ch, arg1, arg2,
								 rch);
			}
			else if (room && (check = keyword_lookup(fn_keyword, ctrl)) >= 0)
			{
				eval =
					cmd_eval_room(pvnum, line, check, room, ch, arg1, arg2,
								  rch);
			}
			else
			{
				if (mob)
					buggy_prog(program,
							   "invalid if_check (and), mob %ld prog %ld",
							   mvnum, pvnum);
				else if (obj)
					buggy_prog(program,
							   "invalid if_check (and), obj %ld prog %ld",
							   ovnum, pvnum);
				else
					buggy_prog(program,
							   "invalid if_check (and), room %ld prog %ld",
							   rvnum, pvnum);
				call_level--;
				return;
			}
			cond[level] = (cond[level] == (int) true)
				&& (eval == (int) true) ? true : false;
		}
		else if (!str_cmp(ctrl, "endif"))
		{
			if (!level || state[level - 1] != BEGIN_BLOCK)
			{
				if (mob)
					buggy_prog(program, "endif without if, mob %ld prog %ld",
							   mvnum, pvnum);
				else if (obj)
					buggy_prog(program, "endif without if, obj %ld prog %ld",
							   ovnum, pvnum);
				else
					buggy_prog(program, "endif without if, room %ld prog %ld",
							   rvnum, pvnum);
				call_level--;
				return;
			}
			cond[level] = true;
			state[level] = IN_BLOCK;
			state[--level] = END_BLOCK;
		}
		else if (!str_cmp(ctrl, "else"))
		{
			if (!level || state[level - 1] != BEGIN_BLOCK)
			{
				if (mob)
					buggy_prog(program, "else without if, mob %ld prog %ld",
							   mvnum, pvnum);
				else if (obj)
					buggy_prog(program, "else without if, obj %ld prog %ld",
							   ovnum, pvnum);
				else
					buggy_prog(program, "else without if, room %ld prog %ld",
							   rvnum, pvnum);
				call_level--;
				return;
			}
			if (level && cond[level - 1] == false)
				continue;
			state[level] = IN_BLOCK;
			cond[level] = (cond[level] == (int) true) ? false : true;
		}
		else if (cond[level] == (int) true
				 && (!str_cmp(ctrl, "break") || !str_cmp(ctrl, "end")))
		{
			call_level--;
			callstack_aborted[call_level] = false;
			return;
		}
		else if ((!level || cond[level] == (int) true) && !NullStr(buf))
		{
			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(ctrl, "mob"))
			{

				line = one_argument(data, ctrl);
				if (!mob)
					bug("mob command in non MOBprog");
				else
					mob_interpret(mob, line);
			}
			else if (!str_cmp(ctrl, "obj"))
			{

				line = one_argument(data, ctrl);
				if (!obj)
					bug("obj command in non OBJprog");
				else
					obj_interpret(obj, line);
			}
			else if (!str_cmp(ctrl, "room"))
			{

				line = one_argument(data, ctrl);
				if (!room)
					bug("room command in non ROOMprog");
				else
					room_interpret(room, line);
			}
			else
			{

				if (!mob)
					bugf("Normal Mud command in non-MOBprog, prog vnum %ld",
						 pvnum);
				else
					interpret(mob, data);
			}
		}
	}
	call_level--;
	callstack_aborted[call_level] = false;
}

void p_act_trigger(const char *argument, CharData * mob, ObjData * obj,
				   RoomIndex * room, CharData * ch, const void *arg1,
				   const void *arg2, flag_t type)
{
	ProgList *prg;

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

	if (mob)
	{
		if (!IsNPC(mob))
		{
			bug("Attempt to access p_act_trigger on a PLAYER.");
			return;
		}

		for (prg = mob->pIndexData->mprog_first; prg != NULL; prg = prg->next)
		{
			if (prg->trig_type == type
				&& strstr(argument, prg->trig_phrase) != NULL)
			{
				program_flow(prg, mob, NULL, NULL, ch, arg1, arg2);
				break;
			}
		}
	}
	else if (obj)
	{
		for (prg = obj->pIndexData->oprog_first; prg != NULL; prg = prg->next)
		{
			if (prg->trig_type == type
				&& strstr(argument, prg->trig_phrase) != NULL)
			{
				program_flow(prg, NULL, obj, NULL, ch, arg1, arg2);
				break;
			}
		}
	}
	else if (room)
	{
		for (prg = room->rprog_first; prg != NULL; prg = prg->next)
		{
			if (prg->trig_type == type
				&& strstr(argument, prg->trig_phrase) != NULL)
			{
				program_flow(prg, NULL, NULL, room, ch, arg1, arg2);
				break;
			}
		}
	}
	else
		bug("ACT trigger with no program type.");

	return;
}

bool p_percent_trigger(CharData * mob, ObjData * obj, RoomIndex * room,
					   CharData * ch, const void *arg1, const void *arg2,
					   flag_t type)
{
	ProgList *prg;

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

	if (mob)
	{
		for (prg = mob->pIndexData->mprog_first; prg != NULL; prg = prg->next)
		{
			if (prg->trig_type == type
				&& number_percent() < atoi(prg->trig_phrase))
			{
				program_flow(prg, mob, NULL, NULL, ch, arg1, arg2);
				return (true);
			}
		}
	}
	else if (obj)
	{
		for (prg = obj->pIndexData->oprog_first; prg != NULL; prg = prg->next)
		{
			if (prg->trig_type == type
				&& number_percent() < atoi(prg->trig_phrase))
			{
				program_flow(prg, NULL, obj, NULL, ch, arg1, arg2);
				return (true);
			}
		}
	}
	else if (room)
	{
		for (prg = room->rprog_first; prg != NULL; prg = prg->next)
		{
			if (prg->trig_type == type
				&& number_percent() < atoi(prg->trig_phrase))
			{
				program_flow(prg, NULL, NULL, room, ch, arg1, arg2);
				return (true);
			}
		}
	}
	else
		bug("PERCENT trigger missing program type.");

	return (false);
}

void p_bribe_trigger(CharData * mob, CharData * ch, money_t amount)
{
	ProgList *prg;

	for (prg = mob->pIndexData->mprog_first; prg; prg = prg->next)
	{
		if (prg->trig_type == TRIG_BRIBE
			&& amount >= strtoul(prg->trig_phrase, (char **) NULL, 10))
		{
			program_flow(prg, mob, NULL, NULL, ch, NULL, NULL);
			break;
		}
	}
	return;
}

bool p_exit_trigger(CharData * ch, int dir, prog_t type)
{
	CharData *mob;
	ObjData *obj;
	RoomIndex *room;
	ProgList *prg;

	if (type == PRG_MPROG)
	{
		for (mob = ch->in_room->person_first; mob != NULL;
			 mob = mob->next_in_room)
		{
			if (IsNPC(mob)
				&& (HasTriggerMob(mob, TRIG_EXIT)
					|| HasTriggerMob(mob, TRIG_EXALL)))
			{
				for (prg = mob->pIndexData->mprog_first; prg; prg = prg->next)
				{

					if (prg->trig_type == TRIG_EXIT
						&& dir == atoi(prg->trig_phrase)
						&& mob->position == mob->pIndexData->default_pos
						&& can_see(mob, ch))
					{
						program_flow(prg, mob, NULL, NULL, ch, NULL, NULL);
						return true;
					}
					else if (prg->trig_type == TRIG_EXALL
							 && dir == atoi(prg->trig_phrase))
					{
						program_flow(prg, mob, NULL, NULL, ch, NULL, NULL);
						return true;
					}
				}
			}
		}
	}
	else if (type == PRG_OPROG)
	{
		for (obj = ch->in_room->content_first; obj != NULL;
			 obj = obj->next_content)
		{
			if (HasTriggerObj(obj, TRIG_EXALL))
			{
				for (prg = obj->pIndexData->oprog_first; prg; prg = prg->next)
				{
					if (prg->trig_type == TRIG_EXALL
						&& dir == atoi(prg->trig_phrase))
					{
						program_flow(prg, NULL, obj, NULL, ch, NULL, NULL);
						return true;
					}
				}
			}
		}

		for (mob = ch->in_room->person_first; mob; mob = mob->next_in_room)
		{
			for (obj = mob->carrying_first; obj; obj = obj->next_content)
			{
				if (HasTriggerObj(obj, TRIG_EXALL))
				{
					for (prg = obj->pIndexData->oprog_first; prg;
						 prg = prg->next)
					{
						if (prg->trig_type == TRIG_EXALL
							&& dir == atoi(prg->trig_phrase))
						{
							program_flow(prg, NULL, obj, NULL, ch, NULL,
										 NULL);
							return true;
						}
					}
				}
			}
		}
	}
	else if (type == PRG_RPROG)
	{
		room = ch->in_room;

		if (HasTriggerRoom(room, TRIG_EXALL))
		{
			for (prg = room->rprog_first; prg; prg = prg->next)
			{
				if (prg->trig_type == TRIG_EXALL
					&& dir == atoi(prg->trig_phrase))
				{
					program_flow(prg, NULL, NULL, room, ch, NULL, NULL);
					return true;
				}
			}
		}
	}

	return false;
}

void p_give_trigger(CharData * mob, ObjData * obj, RoomIndex * room,
					CharData * ch, ObjData * dropped, flag_t type)
{

	char buf[MIL];
	const char *p;
	ProgList *prg;

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

	if (mob)
	{
		for (prg = mob->pIndexData->mprog_first; prg; prg = prg->next)
			if (prg->trig_type == TRIG_GIVE)
			{
				p = prg->trig_phrase;

				if (is_number(p))
				{
					if (dropped->pIndexData->vnum == atov(p))
					{
						program_flow(prg, mob, NULL, NULL, ch,
									 (void *) dropped, NULL);
						return;
					}
				}
				else
				{
					while (*p)
					{
						p = one_argument(p, buf);

						if (is_name(buf, dropped->name)
							|| !str_cmp("all", buf))
						{
							program_flow(prg, mob, NULL, NULL, ch,
										 (void *) dropped, NULL);
							return;
						}
					}
				}
			}
	}
	else if (obj)
	{
		for (prg = obj->pIndexData->oprog_first; prg; prg = prg->next)
			if (prg->trig_type == type)
			{
				program_flow(prg, NULL, obj, NULL, ch, (void *) obj, NULL);
				return;
			}
	}
	else if (room)
	{
		for (prg = room->rprog_first; prg; prg = prg->next)
			if (prg->trig_type == type)
			{
				p = prg->trig_phrase;

				if (is_number(p))
				{
					if (dropped->pIndexData->vnum == atov(p))
					{
						program_flow(prg, NULL, NULL, room, ch,
									 (void *) dropped, NULL);
						return;
					}
				}
				else
				{
					while (*p)
					{
						p = one_argument(p, buf);

						if (is_name(buf, dropped->name)
							|| !str_cmp("all", buf))
						{
							program_flow(prg, NULL, NULL, room, ch,
										 (void *) dropped, NULL);
							return;
						}
					}
				}
			}
	}
}

void p_greet_trigger(CharData * ch, prog_t type)
{
	CharData *mob;
	ObjData *obj;
	RoomIndex *room;

	if (type == PRG_MPROG)
	{
		for (mob = ch->in_room->person_first; mob != NULL;
			 mob = mob->next_in_room)
		{
			if (IsNPC(mob)
				&& (HasTriggerMob(mob, TRIG_GREET)
					|| HasTriggerMob(mob, TRIG_GRALL)))
			{

				if (HasTriggerMob(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 (HasTriggerMob(mob, TRIG_GRALL))
					p_percent_trigger(mob, NULL, NULL, ch, NULL, NULL,
									  TRIG_GRALL);
			}
		}
	}
	else if (type == PRG_OPROG)
	{
		for (obj = ch->in_room->content_first; obj != NULL;
			 obj = obj->next_content)
		{
			if (HasTriggerObj(obj, TRIG_GRALL))
			{
				p_percent_trigger(NULL, obj, NULL, ch, NULL, NULL,
								  TRIG_GRALL);
				return;
			}
		}

		for (mob = ch->in_room->person_first; mob; mob = mob->next_in_room)
		{
			for (obj = mob->carrying_first; obj; obj = obj->next_content)
			{
				if (HasTriggerObj(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 (HasTriggerRoom(room, TRIG_GRALL))
			p_percent_trigger(NULL, NULL, room, ch, NULL, NULL, TRIG_GRALL);
	}

	return;
}

void p_hprct_trigger(CharData * mob, CharData * ch)
{
	ProgList *prg;

	for (prg = mob->pIndexData->mprog_first; prg != NULL; prg = prg->next)
		if ((prg->trig_type == TRIG_HPCNT)
			&& ((100 * mob->hit / mob->max_hit) < atoi(prg->trig_phrase)))
		{
			program_flow(prg, mob, NULL, NULL, ch, NULL, NULL);
			break;
		}
}