SmaugWizard/Backup/
SmaugWizard/Backup/L/
SmaugWizard/Boards/
SmaugWizard/Building/
SmaugWizard/Corpses/
SmaugWizard/Councils/
SmaugWizard/Deity/
SmaugWizard/Gods/
SmaugWizard/MudProgs/
SmaugWizard/Player/L/
SmaugWizard/Src/
SmaugWizard/Src/res/
/****************************************************************************
 * [S]imulated [M]edieval [A]dventure multi[U]ser [G]ame      |				*
 * -----------------------------------------------------------|   \\._.//	*
 * SmaugWiz (C) 1998 by Russ Pillsbury (Windows NT version)   |   (0...0)	*
 * -----------------------------------------------------------|    ).:.(	*
 * SMAUG (C) 1994, 1995, 1996 by Derek Snider                 |    {o o}	*
 * -----------------------------------------------------------|   / ' ' \	*
 * SMAUG code team: Thoric, Altrag, Blodkai, Narn, Haus,      |~'~.VxvxV.~'~*
 * Scryn, Swordbearer, Rennard, Tricops, and Gorog.           |				*
 * ------------------------------------------------------------------------ *
 * Merc 2.1 Diku Mud improvments copyright (C) 1992, 1993 by Michael        *
 * Chastain, Michael Quan, and Mitchell Tse.                                *
 * Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,          *
 * Michael Seifert, Hans Henrik Staerfeldt, Tom Madsen, and Katja Nyboe.    *
 * ------------------------------------------------------------------------ *
 *			 Online Reset Editing Module								    *
 ****************************************************************************/

/*
 * This file relies heavily on the fact that your linked lists are correct,
 * and that pArea->reset_first is the first reset in pArea.  Likewise,
 * pArea->reset_last *MUST* be the last reset in pArea.  Weird and
 * wonderful things will happen if any of your lists are messed up, none
 * of them good.  The most important are your pRoom->contents,
 * pRoom->people, rch->carrying, obj->contains, and pArea->reset_first ..
 * pArea->reset_last.  -- Altrag
 */
 
#include	"stdafx.h"
#include	"smaug.h"
#include	"mobiles.h"
#include	"objects.h"
#include	"rooms.h"
#include	"area.h"
#include	"Exits.h"
#include	"descriptor.h"
#include	"character.h"

// Externals
int		get_wearloc (char* type);
int		get_trapflag (char* flag);

// internal function prototypes
int		generate_itemlevel (CAreaData* pArea, CObjIndexData* pObjIndex);

void	add_obj_reset (CRoomIndexData* pRoom, char cm, CObjData* obj,
					int v2, int v3);
void	DeleteReset (CRoomIndexData* pRoom, CResetData* pReset);
void	InstaRoom (CRoomIndexData* pRoom, BOOL dodoors);
void	RenumberPutResetsForRoom (CRoomIndexData* pRoom);
void	edit_reset (CCharacter* ch, char* argument, CAreaData* pArea,
			CRoomIndexData* aRoom);
void	list_resets (CCharacter* ch, CRoomIndexData* pRoom,
			int start, int end);
void	WipeResets (CRoomIndexData* pRoom);
void	PlaceReset (CRoomIndexData* pRoom, CResetData* pReset);

CRoomIndexData	*find_room (CCharacter* ch, char* argument,
					CRoomIndexData* pRoom);
CResetData	*ParseReset (CRoomIndexData* pRoom, char* argument, CCharacter* ch);
CResetData	*FindReset (CRoomIndexData* pRoom, int num);
CResetData	*find_mreset (CCharacter* ch, CRoomIndexData* pRoom, char* name);
CResetData	*find_oreset (CCharacter* ch, CRoomIndexData* pRoom, char* name);


CResetData *FindReset (CRoomIndexData* pRoom, int numb)
{
	POSITION	pos;
	CResetList	&RList = pRoom->ResetList;

	pos = RList.FindIndex (numb-1);
	return pos ? RList.GetAt (pos) : NULL;
}


#ifdef XXXX
// This is one loopy function.  Ugh. -- Altrag
BOOL is_room_reset (CResetData *pReset, CRoomIndexData *aRoom,
                    CAreaData *pArea)
{
	CRoomIndexData	*pRoom;
	POSITION		pos;
	int				pr;

	if (! aRoom)
		return TRUE;
	switch (pReset->command) {
	  case 'M':
	  case 'O':
		pRoom = RoomTable.GetRoom (pReset->arg3);
		if (! pRoom || pRoom != aRoom)
			return FALSE;
		return TRUE;
	  case 'P':
	  case 'T':
	  case 'H':
		pr = (pReset->command == 'H') ? pReset->arg1 : pReset->arg3;

		pos = pArea->ResetList.GetCurPos ();
		while (pos) {
			CResetData	&Res = *pArea->ResetList.GetPrev (pos);
			if ((Res.command == 'O' || Res.command == 'P' ||
				Res.command == 'G' || Res.command == 'E') &&
				(!pr || pr == Res.arg1) && OIdxTable.GetObj (Res.arg1))
					return is_room_reset (&Res, aRoom, pArea);
		}
		return FALSE;

	  case 'B':
		switch (pReset->arg2 & BIT_RESET_TYPE_MASK) {
		  case BIT_RESET_DOOR:
		  case BIT_RESET_ROOM:
			return (aRoom->vnum == pReset->arg1);

		  case BIT_RESET_MOBILE:
			pos = pArea->ResetList.GetCurPos ();
			while (pos) {
				CResetData	&Res = *pArea->ResetList.GetPrev (pos);
				if (Res.command == 'M' && MobTable.GetMob (Res.arg1))
					return is_room_reset (&Res, aRoom, pArea);
			}
			return FALSE;

		  case BIT_RESET_OBJECT:
			pos = pArea->ResetList.GetCurPos ();
			while (pos) {
				CResetData	&Res = *pArea->ResetList.GetPrev (pos);
				if ((Res.command == 'O' || Res.command == 'P' ||
					Res.command == 'G' || Res.command == 'E') &&
					(!pReset->arg1 || pReset->arg1 == Res.arg1) &&
					OIdxTable.GetObj (Res.arg1))
						return is_room_reset (&Res, aRoom, pArea);
			}
			return FALSE;
		}
		return FALSE;

	  case 'G':
	  case 'E':
		pos = pArea->ResetList.GetCurPos ();
		while (pos) {
			CResetData	&Res = *pArea->ResetList.GetPrev (pos);
			if (Res.command == 'M' && MobTable.GetMob (Res.arg1)) then
				return is_room_reset (&Res, aRoom, pArea);
		}
		return FALSE;
		

	  case 'D':
	  case 'R':
		pRoom = RoomTable.GetRoom (pReset->arg1);
		if (!pRoom || pRoom->GetArea () != pArea || (aRoom && pRoom != aRoom))
			return FALSE;
		return TRUE;
	}
	return FALSE;
}
#endif


CRoomIndexData *find_room (CCharacter *ch, char *argument,
                            CRoomIndexData *pRoom)
{
	char arg [MAX_INPUT_LENGTH];

	if (pRoom)
		return pRoom;

	one_argument (argument, arg);
	if (! is_number (arg) && arg [0] != '\0') {
		ch->SendText ("Reset to which room?\n\r");
		return NULL;
	}

	if (arg [0] == '\0')
		pRoom = ch->GetInRoom ();
	else
		pRoom = RoomTable.GetRoom (atoi (arg));

	if (! pRoom) {
		ch->SendText ("Room does not exist.\n\r");
		return NULL;
	}
	return pRoom;
}


void DeleteReset (CRoomIndexData *pRoom, CResetData *pReset)
{
	CResetList	&RList = pRoom->ResetList;
	POSITION	InPos = RList.Find (pReset);
	POSITION	pos = InPos;

	// Set pos to the item after the pReset that we entered with
	RList.GetNext (pos);

	if (pReset->command == 'M') {
		while (pos) {
			CResetData	&Res = *RList.GetNext (pos);
			// Break when a new mob found
			if (Res.command == 'M')
				break;
			// Delete anything mob is holding
			if (Res.command == 'G' || Res.command == 'E') {
				// here we need to ensure that the value pos ends up pointing
				// at the list item after the one we are about to delete. So
				// we first set it to before that item, delete the item, and
				// then advance pos to the next item - the one we want...
				pos = RList.GetCurPos ();
				RList.GetPrev (pos);				// pos = prev
				DeleteReset (pRoom, &Res);
				RList.GetNext (pos);				// pos = next
				continue;
			}

			if (Res.command == 'B' &&
				(Res.arg2 & BIT_RESET_TYPE_MASK) == BIT_RESET_MOBILE &&
				(! Res.arg1 || Res.arg1 == pReset->arg1)) {
					pos = RList.GetCurPos ();
					RList.GetPrev (pos);				// pos = prev
					DeleteReset (pRoom, &Res);
					continue;
				}
		}
	}

	else if (pReset->command == 'O' || pReset->command == 'P' ||
	  pReset->command == 'G' || pReset->command == 'E') {
		while (pos) {
			CResetData	&Res = *RList.GetNext (pos);
			if (Res.command == 'T' &&
				(!Res.arg3 || Res.arg3 == pReset->arg1)) {
					pos = RList.GetCurPos ();
					RList.GetPrev (pos);				// pos = prev
					DeleteReset (pRoom, &Res);
					continue;
				}

			if (Res.command == 'H' &&
				(!Res.arg1 || Res.arg1 == pReset->arg1)) {
					pos = RList.GetCurPos ();
					RList.GetPrev (pos);				// pos = prev
					DeleteReset (pRoom, &Res);
					continue;
				}

			// Delete nested objects, even if they are the same object.
			if (Res.command == 'P' && (Res.arg3 > 0 ||
				pReset->command != 'P' || Res.extra-1 == pReset->extra) &&
				(!Res.arg3 || Res.arg3 == pReset->arg1)) {
					pos = RList.GetCurPos ();
					RList.GetPrev (pos);				// pos = prev
					DeleteReset (pRoom, &Res);
					continue;
				}

			if (Res.command == 'B' &&
				(Res.arg2 & BIT_RESET_TYPE_MASK) == BIT_RESET_OBJECT &&
				(!Res.arg1 || Res.arg1 == pReset->arg1)) {
					pos = RList.GetCurPos ();
					RList.GetPrev (pos);				// pos = prev
					DeleteReset (pRoom, &Res);
					continue;
				}

			// Break when a new object of same type is found
			if ((Res.command == 'O' || Res.command == 'P' ||
				Res.command == 'G' || Res.command == 'E') &&
				Res.arg1 == pReset->arg1)
					break;
		}
	}

	RList.RemoveAt (InPos);
	delete pReset;
}


CResetData *find_oreset (CCharacter *ch, CRoomIndexData *pRoom, char *name)
{
	CResetData	*pReset = NULL;
	POSITION	pos;
	CResetList	&RList = pRoom->ResetList;

	if (! *name) {
		pos = RList.GetTailPosition ();
		while (pos) {
			pReset = RList.GetPrev (pos);
			char	c = pReset->command;
			if (c == 'O' || c == 'E' || c == 'G' || c == 'P') then
				return pReset;
		}

		ch->SendText ("No object resets in list.\n\r");
		return NULL;
	}

	char	arg [MAX_INPUT_LENGTH];
	int		cnt = 0;
	int		num = number_argument (name, arg);
	CObjIndexData *pObjTo = NULL;

	pos = RList.GetHeadPosition ();
	while (pos) {
		pReset = RList.GetNext (pos);
		char	c = pReset->command;
		if (c == 'O' || c == 'E' || c == 'G' || c == 'P')
			if ((pObjTo = OIdxTable.GetObj (pReset->arg1)) &&
				is_name (arg, pObjTo->GetName ()) && ++cnt == num)
					return pReset;
	}

	ch->SendText ("To object not in reset list.\n\r");
	return NULL;
}


CResetData *find_mreset (CCharacter *ch, CRoomIndexData *pRoom, char *name)
{
	CResetData	*pReset = NULL;
	POSITION	pos;
	CResetList	&RList = pRoom->ResetList;

	if (! *name) {
		pos = RList.GetTailPosition ();
		while (pos) {
			pReset = RList.GetPrev (pos);
			if (pReset->command == 'M') then return pReset;
		}
		ch->SendText ("No mobile resets in list.\n\r");
		return NULL;
	}

	char	arg [MAX_INPUT_LENGTH];
	int		cnt = 0, num = number_argument (name, arg);
	CMobIndexData *pMob = NULL;

	pos = RList.GetHeadPosition ();
	while (pos) {
		pReset = RList.GetNext (pos);
		if (pReset->command == 'M') {
			if ((pMob = MobTable.GetMob (pReset->mob)) &&
				is_name (arg, pMob->GetPlayerName ()) && ++cnt == num)
					return pReset;
		}
	}
	ch->SendText ("Mobile not in reset list.\n\r");
	return NULL;
}


void edit_reset (CCharacter *ch, char *argument, CAreaData *pArea,
				 CRoomIndexData *pRoom)
{
	CMobIndexData	*pMob = NULL;
	CObjIndexData	*pObj;

	CResetData	*pReset = NULL;
	CResetData	*reset = NULL;

	int		num = 0;
	int		vnum;
	char	*origarg = argument;
	char	arg [MAX_INPUT_LENGTH];

	argument = one_argument (argument, arg);
	if (! *arg || ! str_cmp (arg, "?")) {
		char *nm = (ch->GetSubstate () == SUB_REPEATCMD ? "" : "reset ");
		ch->SendTextf (
			"Syntax: %s<list|edit|delete|add|insert|place|room|area>\n\r", nm);
		ch->SendTextf ("Syntax: %sremove <#>\n\r", nm);
		ch->SendTextf ("Syntax: %smobile <mob#> [limit]\n\r", nm);
		ch->SendTextf ("Syntax: %sobject <obj#> [limit [room]]\n\r",
			nm);
		ch->SendTextf ("Syntax: %sequip <mob name> <obj#> <location> [limit]\n\r", nm);
		ch->SendTextf ("Syntax: %sgive <mob name> <obj#> [limit]\n\r", nm);
		ch->SendTextf ("Syntax: %sobject <obj#> put <to_obj name> [limit]\n\r",
			nm);
		ch->SendTextf ("Syntax: %shide <obj name>\n\r", nm);
		ch->SendTextf ("Syntax: %strap <obj name> <type> <charges> "
			"<flags>\n\r", nm);
		ch->SendTextf ("Syntax: %strap room <type> <charges> <flags>\n\r", nm);
		ch->SendTextf ("Syntax: %sbit <set|toggle|remove> door <dir> "
			"<exit flags>\n\r", nm);
		ch->SendTextf ("Syntax: %sbit <set|toggle|remove> object <obj name> "
			"<extra flags>\n\r", nm);
		ch->SendTextf ("Syntax: %sbit <set|toggle|remove> mobile <mob name> "
			"<affect flags>\n\r", nm);
		ch->SendTextf ("Syntax: %sbit <set|toggle|remove> room "
			"<room flags>\n\r", nm);
		ch->SendTextf ("Syntax: %srandom <last dir>\n\r", nm);
		return;
	}

	if (! str_cmp (arg, "on")) {
		ch->SetSubstate (SUB_REPEATCMD);
		ch->dest_buf = (pRoom ? (void *)pRoom : (void *)pArea);
		ch->SendText ("Reset mode on.\n\r");
		return;
	}

	if (! str_cmp (arg, "area")) {
		num = pArea->nplayer;
		pArea->nplayer = 0;
		pArea->Reset ();
		pArea->nplayer = num;
		ch->SendText ("Done.\n\r");
		return;
	}

	if (! str_cmp (arg, "room")) {
		num = pArea->nplayer;
		pArea->nplayer = 0;
		ResetRoom (pRoom, pArea);
		pArea->nplayer = num;
		ch->SendText ("Done.\n\r");
		return;
	}

	if (! str_cmp (arg, "list")) {
		argument = one_argument (argument, arg);
		int	start = is_number (arg) ? atoi (arg) : -1;
		argument = one_argument (argument, arg);
		int	end = is_number (arg) ? atoi (arg) : -1;
		list_resets (ch, pRoom, start, end);
		return;
	}

	CResetList	&RList = pRoom->ResetList;

	if (! str_cmp (arg, "edit")) {
		argument = one_argument (argument, arg);
		if (! *arg || ! is_number (arg)) {
			ch->SendText ("Usage: reset edit <number> <command>\n\r");
			return;
		}
		num = atoi (arg);

		if (! (pReset = FindReset (pRoom, num))) {
			ch->SendText ("Reset not found.\n\r");
			return;
		}

		if (! (reset = ParseReset (pRoom, argument, ch))) {
			ch->SendText ("Error in reset.  Reset not changed.\n\r");
			return;
		}

		POSITION	pos = RList.Find (pReset);
		RList.SetAt (pos, reset);

		delete pReset;
		ch->SendText ("Done.\n\r");
		return;
	}

	if (! str_cmp (arg, "add")) {
		if ((pReset = ParseReset (pRoom, argument, ch)) == NULL) {
			ch->SendText ("Error in reset.  Reset not added.\n\r");
			return;
		}
		RList.AddTail (pReset);

		ch->SendText ("Done.\n\r");
		return;
	}

	if (! str_cmp (arg, "place")) {
		if ((pReset = ParseReset (pRoom, argument, ch)) == NULL) {
			ch->SendText ("Error in reset.  Reset not added.\n\r");
			return;
		}
		PlaceReset (pRoom, pReset);
		ch->SendText ("Done.\n\r");
		return;
	}

	if (! str_cmp (arg, "insert")) {
		char	*uMsg = "USAGE: insert <number> <command>\n\r";
		argument = one_argument (argument, arg);
		if (! *arg || ! is_number (arg)) {
			ch->SendText (uMsg);
			return;
		}

		num = atoi (arg);
		if ((reset = FindReset (pRoom, num)) == NULL) {
			ch->SendText ("Reset not found.\n\r");
			ch->SendText (uMsg);
			return;
		}

		if ((pReset = ParseReset (pRoom, argument, ch)) == NULL) {
			ch->SendText ("Error in reset.  Reset not inserted.\n\r");
			return;
		}

		POSITION	pos = RList.Find (reset);
		RList.InsertBefore (pos, pReset);
		ch->SendText ("Done.\n\r");
		return;
	}

	if (! str_prefix (arg, "delete")) {
		if (! *argument) {
			ch->SendText ("USAGE: delete <start> [end]\n\r");
			return;
		}

		argument = one_argument (argument, arg);
		int	start = is_number (arg) ? atoi (arg) : -1;
		one_argument (argument, arg);
		int	end = is_number (arg) ? atoi (arg) : start;
		BOOL	bFound = FALSE;

		POSITION	pos = RList.GetHeadPosition ();
		for (int num=1; pos; ++num) {
			pReset = RList.GetNext (pos);
			if (num > end) then break;
			if (num >= start) {
				RList.Remove (pReset);
				bFound = TRUE;
			}
		}
		if (! bFound)
			ch->SendText ("Reset not found.\n\r");
		else
			ch->SendText ("Done.\n\r");
		return;
	}

	if (! str_prefix (arg, "remove")) {
		argument = one_argument (argument, arg);
		if (arg [0] == '\0' || ! is_number (arg)) {
			ch->SendText ("Remove which reset?\n\r");
			return;
		}

		int iarg = atoi (arg);
		POSITION	pos = RList.FindIndex (iarg-1);
		if (! pos) {
			ch->SendText ("Reset does not exist.\n\r");
			return;
		}
		DeleteReset (pRoom, RList.GetAt (pos));
		ch->SendText ("Reset deleted.\n\r");
		return;
	}

	if (! str_prefix (arg, "mobile")) {
		char	*uMsg = "USAGE: mobile <mob#> [limit]\n\r";
		argument = one_argument (argument, arg);
		if (arg [0] == '\0' || ! is_number (arg)) {
			ch->SendText ("Reset which mobile vnum?\n\r");
			ch->SendText (uMsg);
			return;
		}

		if (! (pMob = MobTable.GetMob (atoi (arg)))) {
			ch->SendText ("Mobile does not exist.\n\r");
			ch->SendText (uMsg);
			return;
		}

		argument = one_argument (argument, arg);
		if (arg [0] == '\0')
			num = 1;
		else if (! is_number (arg)) {
			ch->SendText ("Reset how many mobiles?\n\r");
			ch->SendText (uMsg);
			return;
		}
		else num = atoi (arg);

		pReset = new CResetData ('M', 0, pMob->vnum,
			pMob->vnum, num, pRoom->vnum);
		RList.AddTail (pReset);
		ch->SendText ("Mobile reset added.\n\r");
		return;
	}

	if (! str_prefix (arg, "equip")) {
		char *uMsg = "USAGE: equip <mob name> <obj#> <wear loc> [limit]\n\r";
		argument = one_argument (argument, arg);
		if (! (reset = find_mreset (ch, pRoom, arg))) {
			ch->SendText ("Equip which object vnum?\n\r");
			ch->SendText (uMsg);
			return;
		}
		int		MobVnum = reset->mob;

		argument = one_argument (argument, arg);
		if (arg [0] == '\0' || ! is_number (arg)) {
			ch->SendText ("Equip which object vnum?\n\r");
			ch->SendText (uMsg);
			return;
		}

		if (! (pObj = OIdxTable.GetObj (atoi (arg)))) {
			ch->SendText ("Object does not exist.\n\r");
			ch->SendText (uMsg);
			return;
		}

		argument = one_argument (argument, arg);
		num = get_wearloc (arg);
		if (num < 0) {
			ch->SendText ("Equip mob at which location?\n\r");
			ch->SendText (uMsg);
			return;
		}

		// Find the insert location, goes after any Bit resets, and
		// after any other Eq resets for same mob
		BOOL		bFound = FALSE;
		POSITION	CurPos = NULL, pos = RList.Find (reset);
		RList.GetNext (pos);
		while (pos) {
			reset = RList.GetNext (pos);
			char	cmd = reset->command;
			if (reset->mob != MobVnum || (cmd != 'B' && cmd != 'E')) {
				CurPos = RList.GetCurPos ();
				break;
			}
		}

		while (pos) {
			reset = RList.GetNext (pos);
			if (reset->command == 'M') then break;
			if (reset->command == 'E' && reset->arg3 == num) {
				ch->SendText ("Mobile already has an item equipped "
					"there.\n\r");
				return;
			}
		}

		// find the limit
		argument = one_argument (argument, arg);
		int		limit = max (atoi (arg), 1);

		pReset = new CResetData ('E', 1, MobVnum, pObj->vnum, limit, num);

		if (CurPos) then RList.InsertBefore (CurPos, pReset);
		else RList.AddTail (pReset);
		ch->SendText ("Object equip to Mobile reset created.\n\r");
		return;
	}

	if (! str_prefix (arg, "give")) {
		char	*uMsg = "USAGE: give <mob name> <obj#> [limit]\n\r";
		argument = one_argument (argument, arg);
		if (! (reset = find_mreset (ch, pRoom, arg))) {
			ch->SendText (uMsg);
			return;
		}
		int		MobVnum = reset->mob;

		argument = one_argument (argument, arg);
		if (arg [0] == '\0' || ! is_number (arg)) {
			ch->SendText ("Give which object vnum?\n\r");
			ch->SendText (uMsg);
			return;
		}

		if (! (pObj = OIdxTable.GetObj (atoi (arg)))) {
			ch->SendText ("Object does not exist.\n\r");
			ch->SendText (uMsg);
			return;
		}

		argument = one_argument (argument, arg);
		int		limit = max (atoi (arg), 1);

		// Find the insert location, goes after all other resets for this mob
		BOOL		bFound = FALSE;
		POSITION	CurPos = NULL, pos = RList.Find (reset);
		RList.GetNext (pos);
		while (pos) {
			reset = RList.GetNext (pos);
			if (reset->mob != MobVnum) {
				CurPos = RList.GetCurPos ();
				break;
			}
		}

		pReset = new CResetData ('G', 1, MobVnum, pObj->vnum, limit, 0);

		if (CurPos) then RList.InsertBefore (CurPos, pReset);
		else RList.AddTail (pReset);

		ch->SendText ("Object give to mobile reset created.\n\r");
		return;
	}

	if (! str_prefix (arg, "object")) {
		argument = one_argument (argument, arg);
		if (arg [0] == '\0' || ! is_number (arg)) {
			ch->SendText ("Reset which object vnum?\n\r");
			return;
		}

		if (! (pObj = OIdxTable.GetObj (atoi (arg)))) {
			ch->SendText ("Object does not exist.\n\r");
			return;
		}

		argument = one_argument (argument, arg);
		if (arg [0] == '\0')
			strcpy (arg, "room");
		if (! str_prefix (arg, "put")) {
			argument = one_argument (argument, arg);
			if (! (reset = find_oreset (ch, pRoom, arg)))
				return;

			argument = one_argument (argument, arg);
			if ((vnum = atoi (arg)) < 1)
				vnum = 1;

			pReset = new CResetData ('P', reset->extra+1, 0,
				pObj->vnum, vnum, 0);

			// Put in_objects after hide and trap resets
			BOOL		bFound = FALSE;
			POSITION	pos = RList.Find (reset);
			while (pos) {
				reset = RList.GetNext (pos);
				char	c = reset->command;
				if (c != 'H' && c != 'T' && c != 'B') {
					RList.InsertBefore (RList.GetCurPos (), pReset);
					bFound = TRUE;
					break;
				}
			}
			if (! bFound) then RList.AddTail (pReset);

			ch->SendText ("Object reset in object created.\n\r");
			return;
		}

		if (arg [0] == '\0' || ! (num = (int) str_cmp (arg, "room"))
		  || is_number (arg)) {
			if (! (BOOL) num)
				argument = one_argument (argument, arg);

			if ((vnum = atoi (arg)) < 1)
				vnum = 1;

			pReset = new CResetData ('O', 0, 0, pObj->vnum, vnum, pRoom->vnum);
			RList.AddTail (pReset);
			ch->SendText ("Object reset added.\n\r");
			return;
		}
		ch->SendText ("Reset object to where?\n\r");
		return;
	}

	if (! str_cmp (arg, "random")) {
		argument = one_argument (argument, arg);
		vnum = get_dir (arg);
		if (vnum < 0 || vnum > 9) {
			ch->SendText ("Reset which random doors?\n\r");
			return;
		}

		if (vnum == 0) {
			ch->SendText ("There is no point in randomizing one door.\n\r");
			return;
		}

		pReset = new CResetData ('R', 0, 0, pRoom->vnum, vnum, 0);
		RList.AddTail (pReset);
		ch->SendText ("Reset random doors created.\n\r");
		return;
	}

	if (! str_cmp (arg, "trap")) {
		char	oname [MAX_INPUT_LENGTH];
		int		value, extra = 0;
		char	*uMsg =
			"USAGE: trap <\"room\"|object name> <type> <charges> [flags]\n\r";

		argument = one_argument (argument, oname);
		BOOL	bRoom = is_name (oname, "room");

		argument = one_argument (argument, arg);
		int		TrapType = is_number (arg) ? atoi (arg) : -1;

		argument = one_argument (argument, arg);
		int		chrg = is_number (arg) ? atoi (arg) : -1;

		if (bRoom) {
			vnum = pRoom->vnum;
			extra = TRAP_ROOM;
			reset = NULL;
		} else {
			if (! (reset = find_oreset (ch, pRoom, oname))) {
				ch->SendText ("TRAP: invalid object name\n\r");
				ch->SendText (uMsg);
				return;
			}
			vnum = 0;
			extra = TRAP_OBJ;
		}

		if (TrapType < 1 || TrapType > MAX_TRAPTYPE) {
			ch->SendText ("TRAP: invalid trap type\n\r");
			ch->SendText (uMsg);
			return;
		}

		if (chrg < 0 || chrg > 10000) {
			ch->SendText ("TRAP: invalid trap charges\n\r");
			ch->SendText (uMsg);
			return;
		}

		while (*argument) {
			argument = one_argument (argument, arg);
			value = get_trapflag (arg);
			if (value < 0 || value > 31) {
				ch->SendText ("TRAP: bad flag\n\r");
				ch->SendText (uMsg);
				return;
			}
			SET_BIT (extra, 1 << value);
		}

		pReset = new CResetData ('T', extra, 0, TrapType, chrg, vnum);
		if (reset) {
			POSITION	pos = RList.Find (reset);
			if (pos) then RList.InsertAfter (pos, pReset);
			else RList.AddTail (pReset);
		}
		else RList.AddTail (pReset);
		ch->SendText ("Trap created.\n\r");
		return;
	}

	if (! str_cmp (arg, "bit")) {
		int		(*flfunc) (const char *type);
		int		flags = 0;
		char	option [MAX_INPUT_LENGTH];

		argument = one_argument (argument, option);
		if (! *option) {
			ch->SendText ("You must specify SET, REMOVE, or TOGGLE.\n\r");
			return;
		}
		num = 0;
		if (! str_prefix (option, "set"))
			SET_BIT (num, BIT_RESET_SET);
		else if (!str_prefix (option, "toggle"))
			SET_BIT (num, BIT_RESET_TOGGLE);
		else if (str_prefix (option, "remove")) {
			ch->SendText ("You must specify SET, REMOVE, or TOGGLE.\n\r");
			return;
		}

		argument = one_argument (argument, option);
		if (! *option) {
			ch->SendText ("Must specify OBJECT, MOBILE, ROOM, or DOOR.\n\r");
			return;
		}

		if (! str_prefix (option, "door")) {
			SET_BIT (num, BIT_RESET_DOOR);
			argument = one_argument (argument, arg);
			if (! *arg) {
				ch->SendText ("Must specify direction.\n\r");
				return;
			}

			vnum = get_dir (arg);
			SET_BIT (num, vnum << BIT_RESET_DOOR_THRESHOLD);
			vnum = pRoom->vnum;
			flfunc = &get_exflag;
			reset = NULL;
		}
		else if (! str_prefix (option, "object")) {
			SET_BIT (num, BIT_RESET_OBJECT);
			vnum = 0;
			flfunc = &get_oflag;
			if (! (reset = find_oreset (ch, pRoom, arg)))
				return;
		}
		else if (! str_prefix (option, "mobile")) {
			SET_BIT (num, BIT_RESET_MOBILE);
			vnum = 0;
			flfunc = &get_aflag;
			if (! (reset = find_mreset (ch, pRoom, arg)))
				return;
		}
		else if (! str_prefix (option, "room")) {
			SET_BIT (num, BIT_RESET_ROOM);
			vnum = pRoom->vnum;
			flfunc = &get_rflag;
			reset = NULL;
		} else {
			ch->SendText ("Must specify OBJECT, MOBILE, ROOM, or DOOR.\n\r");
			return;
		}

		while (*argument) {
			int value;
			argument = one_argument (argument, arg);
			value = (*flfunc) (arg);
			if (value < 0 || value > 31) {
				ch->SendText ("Reset: BIT: bad flag\n\r");
				return;
			}
			SET_BIT (flags, 1 << value);
		}

		if (! flags) {
			ch->SendText ("Set which flags?\n\r");
			return;
		}

		pReset = new CResetData ('B', 1, 0, vnum, num, flags);
		if (reset) {
			POSITION	pos = RList.Find (reset);
			if (pos) then RList.InsertAfter (pos, pReset);
			else RList.AddTail (pReset);
		}
		else RList.AddTail (pReset);
		ch->SendText ("Bitvector reset created.\n\r");
		return;
	}

	if (! str_cmp (arg, "hide")) {
		argument = one_argument (argument, arg);
		if (! (reset = find_oreset (ch, pRoom, arg)))
			return;
		pReset = new CResetData ('H', 1, 0, 0, 0, 0);
		POSITION	pos = RList.Find (reset);
		if (pos) then RList.InsertBefore (pos, pReset);
		else RList.AddTail (pReset);

		ch->SendText ("Object hide reset created.\n\r");
		return;
	}

	if (ch->GetSubstate () == SUB_REPEATCMD) {
		ch->SetSubstate (SUB_NONE);
		interpret (ch, origarg);
		ch->SetSubstate (SUB_REPEATCMD);
		ch->last_cmd = (pRoom ? do_rreset : do_reset);
	}
	else
		edit_reset (ch, "", pArea, pRoom);
}


// Parse a reset command string into a CResetData structure
CResetData *ParseReset (CRoomIndexData* pRoom, char* argument, CCharacter* ch)
{
	char arg1 [MAX_INPUT_LENGTH];
	char arg2 [MAX_INPUT_LENGTH];
	char arg3 [MAX_INPUT_LENGTH];
	char arg4 [MAX_INPUT_LENGTH];
	char letter;
	int extra, val1, val2, val3;
	int value;
	CRoomIndexData *room;
	CExitData	*pexit;
	int	mob = 0;

	argument = one_argument (argument, arg1);
	argument = one_argument (argument, arg2);
	argument = one_argument (argument, arg3);
	argument = one_argument (argument, arg4);
	extra = 0; letter = '*';
	val1 = atoi (arg2);
	val2 = atoi (arg3);
	val3 = atoi (arg4);

	if (arg1 [0] == '\0') {
	   ch->SendText ("Reset commands: mob obj door rand trap hide.\n\r");
	   return NULL;
	}

	if (! str_cmp (arg1, "hide")) {
		if (arg2 [0] != '\0' && ! OIdxTable.GetObj (val1)) {
			ch->SendText ("Reset: HIDE: no such object\n\r");
			return NULL;
		}
		else
			val1 = 0;
		extra = 1;
		val2 = 0;
		val3 = 0;
		letter = 'H';
	}
	else if (arg2 [0] == '\0') {
	    ch->SendText ("Reset: not enough arguments.\n\r");
	    return NULL;
	}
	else if (val1 < 1 || val1 > 2097152000) {	// was 32767
	    ch->SendText ("Reset: value out of range.\n\r");
	    return NULL;
	}
	else if (! str_cmp (arg1, "mob")) {
	    if (! MobTable.GetMob (val1)) {
			ch->SendText ("Reset: MOB: no such mobile\n\r");
			return NULL;
	    }
		val3 = val2;
	    if (val3 < 1)
			val3 = 1;
		val2 = pRoom->vnum;
	    letter = 'M';
		mob = val1;
	}
	else if (!str_cmp (arg1, "obj")) {
	    if (! OIdxTable.GetObj (val1)) {
			ch->SendText ("Reset: OBJ: no such object\n\r");
			return NULL;
	    }
	    val3 = val2;
		if (val3 < 1)
			val3 = 1;
		val2 = pRoom->vnum;
	    letter = 'O';
	}
	else if (!str_cmp (arg1, "put")) {
	    if (! OIdxTable.GetObj (val1)) {
			ch->SendText ("Reset: PUT: no such object\n\r");
			return NULL;
	    }
	    if (val2 > 0 && !OIdxTable.GetObj (val2)) {
			ch->SendText ("Reset: PUT: no such container\n\r");
			return NULL;
		}
		extra = UMAX (val3, 0);
	    argument = one_argument (argument, arg4);
	    val3 = (is_number (argument) ? atoi (arg4) : 0);
	    if (val3 < 0)
			val3 = 0;
	    letter = 'P';
	}
	else if (!str_cmp (arg1, "door")) {
	    if ((room = RoomTable.GetRoom (val1)) == NULL) {
			ch->SendText ("Reset: DOOR: no such room\n\r");
			return NULL;
	    }
	    if (val2 < 0 || val2 > 9) {
			ch->SendText ("Reset: DOOR: invalid exit\n\r");
			return NULL;
	    }
	    if ((pexit = get_exit (room, val2)) == NULL
	      || ! pexit->IsDoor ()) {
			ch->SendText ("Reset: DOOR: no such door\n\r");
			return NULL;
	    }
	    if (val3 < 0 || val3 > 2) {
			ch->SendText ("Reset: DOOR: invalid door state (0 = open, 1 = close, 2 = lock)\n\r");
			return NULL;
	    }
	    letter = 'D';
	    value = val3;
	    val3  = val2;
	    val2  = value;
	}
	else if (! str_cmp (arg1, "rand")) {
		val1 = pRoom->vnum;
	    if (val2 < 0 || val2 > 9) {
			ch->SendText ("Reset: RAND: invalid max exit\n\r");
			return NULL;
	    }
	    val3 = val2;
	    val2 = 0;
	    letter = 'R';
	}
	else if (! str_cmp (arg1, "trap")) {
	    if (val2 < 1 || val2 > MAX_TRAPTYPE) {
			ch->SendText ("Reset: TRAP: invalid trap type\n\r");
			return NULL;
	    }
	    if (val3 < 0 || val3 > 10000) {
			ch->SendText ("Reset: TRAP: invalid trap charges\n\r");
			return NULL;
	    }
	    while (argument [0] != '\0') {
			argument = one_argument (argument, arg4);
			value = get_trapflag (arg4);
			if (value >= 0 || value < 32)
			    SET_BIT (extra, 1 << value);
			else {
				ch->SendText ("Reset: TRAP: bad flag\n\r");
				return NULL;
			}
	    }
	    if (IS_SET (extra, TRAP_ROOM) && IS_SET (extra, TRAP_OBJ)) {
			ch->SendText ("Reset: TRAP: Must specify room OR object, "
				"not both!\n\r");
			return NULL;
	    }
	    if (IS_SET (extra, TRAP_ROOM))
			val1 = pRoom->vnum;

	    if (IS_SET (extra, TRAP_OBJ)  && val1>0 && !OIdxTable.GetObj (val1)){
			ch->SendText ("Reset: TRAP: no such object\n\r");
			return NULL;
	    }
	    if (! IS_SET (extra, TRAP_ROOM) && !	IS_SET (extra, TRAP_OBJ)) {
			ch->SendText ("Reset: TRAP: Must specify ROOM or OBJECT\n\r");
			return NULL;
	    }
	    // fix order
	    value = val1;
	    val1  = val2;
	    val2  = value;
	    letter = 'T';
	}
	if (letter == '*')
	    return NULL;
	else
	    return new CResetData (letter, extra, mob, val1, val3, val2);
}


void do_rreset (CCharacter *ch, char *argument)
{
	do_reset (ch, argument);
}


void do_reset (CCharacter *ch, char *argument)
{
	CRoomIndexData	*pRoom;

	if (ch->GetSubstate () == SUB_REPEATCMD) {
		pRoom = (CRoomIndexData*) ch->dest_buf;
		if (! pRoom) {
			ch->SendText ("Your room pointer got lost.  Reset mode off.\n\r");
			bug ("do_reset: %s's dest_buf points to invalid room",
				(int) ch->GetName ());
			ch->SetSubstate (SUB_NONE);
			ch->dest_buf = NULL;
			return;
		}
	}
	else pRoom = ch->GetInRoom ();

	if (can_rmodify (ch, pRoom))
		edit_reset (ch, argument, pRoom->GetArea (), pRoom);
}


void add_obj_reset (CRoomIndexData *pRoom, char cm, CObjData *obj,
					int v2, int v3)
{
	CObjData	*inobj;
	static int	iNest;

	if ((cm == 'O' || cm == 'P') && obj->pIndexData->vnum == OBJ_VNUM_TRAP) {
		if (cm == 'O')
			pRoom->AddReset ('T', obj->value [3], 0, obj->value [1],
				obj->value [0], v3);
		return;
	}

	pRoom->AddReset (cm, (cm == 'P' ? iNest : 1), 0, obj->pIndexData->vnum,
		v2, v3);
	
	// Only add hide for in-room objects that are hidden and cant be
	// moved, as hide is an update reset, not a load-only reset.
	if (cm == 'O' && obj->IsHidden () && ! IS_SET (obj->wear_flags, ITEM_TAKE))
		pRoom->AddReset ('H', 1, 0, 0, 0, 0);

	POSITION	Cpos = obj->GetHeadContentPos ();
	while (inobj = obj->GetNextContent (Cpos))
		if (inobj->pIndexData->vnum == OBJ_VNUM_TRAP)
			add_obj_reset (pRoom, 'O', inobj, 0, 0);

	if (cm == 'P')
		iNest++;

	Cpos = obj->GetHeadContentPos ();
	while (inobj = obj->GetNextContent (Cpos))
		add_obj_reset (pRoom, 'P', inobj, 1, 0);

	if (cm == 'P')
		iNest--;
}


void InstaRoom (CRoomIndexData *pRoom, BOOL dodoors)
{
	CCharacter	*rch;
	CObjData	*obj;

	for (rch = pRoom->first_person; rch; rch = rch->GetNextInRoom ()) {
		if (! rch->IsNpc ())
			continue;
		int		MobVnum = rch->GetMobIndex ()->vnum;
		pRoom->AddReset ('M', 1, MobVnum, MobVnum,
			rch->GetMobIndex ()->count, pRoom->vnum);

		POSITION	Cpos = rch->GetHeadCarryPos ();
		while (obj = rch->GetNextCarrying (Cpos)) {
			if (obj->wear_loc == WEAR_NONE)
				add_obj_reset (pRoom, 'G', obj, 1, 0);
			else
				add_obj_reset (pRoom, 'E', obj, 1, obj->wear_loc);
		}
	}

	POSITION	Cpos = pRoom->GetHeadContentPos ();
	while (obj = pRoom->GetNextContent (Cpos))
		add_obj_reset (pRoom, 'O', obj, 1, pRoom->vnum);

	if (dodoors) {
		CExitData	*pexit;

		for (pexit = pRoom->first_exit; pexit; pexit = pexit->GetNext ()) {
			int state = 0;

			if (! pexit->IsDoor ())
				continue;
			if (pexit->IsClosed ()) {
				if (pexit->IsLocked ())
					state = 2;
				else
					state = 1;
			}
			pRoom->AddReset ('D', 0, 0, pRoom->vnum, pexit->vdir, state);
		}
	}
}


void WipeResets (CRoomIndexData *pRoom)
{
	CResetData	*pReset;
	CResetList	&RList = pRoom->ResetList;

	POSITION	pos = RList.GetHeadPosition ();
	while (pos) {
		pReset = RList.GetNext (pos);
		if (pReset->command != 'R') {
			// Resets always go forward, so we can safely use the
			// previous reset, providing it exists, or first_reset
			// if it doesnt.  -- Altrag
			pos = RList.GetCurPos ();
			RList.GetPrev (pos);			// set pos to previous (or NULL)

			DeleteReset (pRoom, pReset);
			if (! pos) then pos = RList.GetHeadPosition ();
		}
	}
}


void do_instaroom (CCharacter *ch, char *argument)
{
	CRoomIndexData	*pRoom;
	BOOL			dodoors;
	char			arg [MAX_INPUT_LENGTH];

	CAreaData	*pArea = ch->GetArea ();
	if (ch->IsNpc () || ch->GetTrustLevel () < LEVEL_SAVIOR || ! pArea) {
		ch->SendText ("You don't have an assigned area to create resets for.\n\r");
		return;
	}
	argument = one_argument (argument, arg);
	if (!str_cmp (argument, "nodoors"))
		dodoors = FALSE;
	else
		dodoors = TRUE;

	if (! (pRoom = find_room (ch, arg, NULL))) {
		ch->SendText ("Room doesn't exist.\n\r");
		return;
	}
	if (!can_rmodify (ch, pRoom))
		return;

	if (pRoom->GetArea () != pArea && ch->GetTrustLevel () < LEVEL_GREATER) {
		ch->SendText ("You cannot reset that room.\n\r");
		return;
	}
	if (! pRoom->ResetList.IsEmpty ())
		WipeResets (pRoom);
	InstaRoom (pRoom, dodoors);
	ch->SendText ("Room resets installed.\n\r");
}


void do_instazone (CCharacter *ch, char *argument)
{
	int				vnum;
	CRoomIndexData	*pRoom;
	BOOL			dodoors;

	CAreaData	*pArea = ch->GetArea ();
	if (ch->IsNpc () || ch->GetTrustLevel () < LEVEL_SAVIOR || ! pArea) {
		ch->SendText ("You don't have an assigned area to create resets for.\n\r");
		return;
	}

	if (!str_cmp (argument, "nodoors"))
		dodoors = FALSE;
	else
		dodoors = TRUE;

	for (vnum = pArea->low_r_vnum; vnum <= pArea->hi_r_vnum; vnum++) {
		if (! (pRoom = RoomTable.GetRoom (vnum)) || pRoom->GetArea () != pArea)
			continue;
		WipeResets (pRoom);
		InstaRoom (pRoom, dodoors);
	}
	ch->SendText ("Area resets installed.\n\r");
}


int generate_itemlevel (CAreaData *pArea, CObjIndexData *pObjIndex)
{
    int olevel;
    int min = UMAX (pArea->low_soft_range, 1);
    int max = UMIN (pArea->hi_soft_range, min + 15);

    if (pObjIndex->level > 0)
	olevel = UMIN (pObjIndex->level, MAX_LEVEL);
    else
	switch (pObjIndex->item_type)
	{
	    default:			olevel = 0;				break;
	    case ITEM_PILL:		olevel = number_range ( min, max);	break;
	    case ITEM_POTION:	olevel = number_range ( min, max);	break;
	    case ITEM_SCROLL:	olevel = pObjIndex->value[0];		break;
	    case ITEM_WAND:		olevel = number_range (min+4, max+1);	break;
	    case ITEM_STAFF:	olevel = number_range (min+9, max+5);	break;
	    case ITEM_ARMOR:	olevel = number_range (min+4, max+1);	break;
	    case ITEM_WEAPON:	olevel = number_range (min+4, max+1);	break;
	}
	return olevel;
}


// Reset one room.
void ResetRoom (CRoomIndexData* pRoom, CAreaData* pArea)
{
	CResetData		*pReset;
	CCharacter		*mob;
	CObjData		*obj;
	CObjData		*lastobj;
	CMobIndexData	*pMobIndex;
	CObjIndexData	*pObjIndex;
	CObjIndexData	*pObjToIndex;
	CExitData		*pexit;
	CObjData		*to_obj;
	int				level = 0;
	int				*bits = NULL;
	CIntVector		*pIv = NULL;
	CBitVector		*pBv = NULL;

	if (! pRoom) {
		bug ("ResetRoom: NULL pRoom");
		return;
	}

	mob = NULL;
	obj = NULL;
	lastobj = NULL;
	CResetList	&RList = pRoom->ResetList;

	level = 0;
	POSITION	pos = RList.GetHeadPosition ();
	while (pos) {
		pReset = RList.GetNext (pos);
		switch (pReset->command) {
		  default:
			bug ("ResetRoom %s: bad command %c.",
				NCCP pRoom->GetName (), pReset->command);
			break;

		  case 'M': {
			if (! (pMobIndex = MobTable.GetMob (pReset->mob))) {
				bug ("ResetRoom %s: 'M': bad mob vnum %d.",
					NCCP pRoom->GetName (), pReset->mob);
				continue;
			}
			if (pMobIndex->count >= pReset->arg2) {
				mob = NULL;
				break;
			}
			mob = create_mobile (pMobIndex);

			CRoomIndexData *pRoomPrev = RoomTable.GetRoom (pRoom->vnum - 1);

			if (pRoomPrev && pRoomPrev->IsPetShop ())
				mob->SetPetFlag ();

			if (room_is_dark (pRoom))
				mob->SetSeeInfra ();
			mob->SendToRoom (pRoom);
			economize_mobgold (mob);
			level = URANGE (0, mob->GetLevel () - 2, LEVEL_AVATAR);
			}
			break;

		  case 'G':
		  case 'E':
			if (! (pObjIndex = OIdxTable.GetObj (pReset->arg1))) {
				bug ("ResetRoom %s: 'E' or 'G': bad obj vnum %d.",
					NCCP pRoom->GetName (), pReset->arg1);
				continue;
			}
			if (! mob)
				lastobj = NULL;
			else {
				int olevel = pObjIndex->level;
				if (mob->GetMobIndex ()->pShop) {
					if (olevel == 0)
						olevel = URANGE (0, generate_itemlevel (pArea, pObjIndex), LEVEL_AVATAR);
					obj = create_object (pObjIndex, olevel);
					obj->SetInventory ();
					obj = obj_to_char (obj, mob, ADDBYLEVEL);
				} else {
					if (olevel == 0)
						olevel = number_fuzzy (level);
					obj = create_object (pObjIndex, olevel);
					obj->level = URANGE (0, obj->level, LEVEL_AVATAR);
					obj = obj_to_char (obj, mob);
				}
				if (pReset->command == 'E')
					equip_char (mob, obj, pReset->arg3);
				lastobj = obj;
			}
			break;

		  case 'O':
			if (! (pObjIndex = OIdxTable.GetObj (pReset->arg1))) {
				bug ("ResetRoom %s: 'O': bad obj vnum %d.",
					NCCP pRoom->GetName (), pReset->arg1);
				continue;
			}
			if (pArea->nplayer > 0 ||
				count_obj_list (pObjIndex, pRoom->GetContentList ()) > 0)
					obj = NULL;
			else {
				int		lev = pObjIndex->level;
				if (lev == 0)
					lev = number_fuzzy (generate_itemlevel (pArea, pObjIndex));
				obj = create_object (pObjIndex, lev);
				obj->level = UMIN (obj->level, LEVEL_AVATAR);
				obj->cost = 0;
				obj_to_room (obj, pRoom);
			}
			lastobj = obj;
			break;

		  case 'P':
			if (! (pObjIndex = OIdxTable.GetObj (pReset->arg1))) {
				bug ("ResetRoom %s: 'P': bad obj vnum %d.",
					NCCP pRoom->GetName (), pReset->arg1);
				continue;
			}
			if (pReset->arg3 > 0) {
				if (! (pObjToIndex = OIdxTable.GetObj (pReset->arg3))) {
					bug ("ResetRoom %s: 'P': bad objto vnum %d.",
						NCCP pRoom->GetName (), pReset->arg3);
					continue;
				}
				if (pArea->nplayer > 0
				  || ! (to_obj = pObjToIndex->GetLast ())
				  || ! to_obj->in_room
				  || count_obj_list (pObjIndex, to_obj->GetContentList ()) > 0) {
					obj = NULL;
					break;
				}
				lastobj = to_obj;
			} else {
				int		iNest;

				if (! lastobj)
					break;
				to_obj = lastobj;
				for (iNest = 0; iNest < pReset->extra; iNest++)
					if (! (to_obj = to_obj->GetLastContent ())) {
						bug ("ResetRoom %s: 'P': Invalid nesting obj %d.",
							NCCP pRoom->GetName (), pReset->arg1);
						iNest = -1;
						break;
					}
					if (iNest < 0)
						continue;
			}
			{
				int		lev = pObjIndex->level;
				if (lev == 0)
					lev = number_fuzzy (UMAX (generate_itemlevel (pArea, pObjIndex),
						to_obj->level));
				obj = create_object (pObjIndex, lev);
				obj->level = UMIN (obj->level, LEVEL_AVATAR);
				obj_to_obj (obj, to_obj);
			}
			break;

		  case 'T':
			if (IS_SET (pReset->extra, TRAP_OBJ)) {
				// We need to preserve obj for future 'T' and 'H' checks
				CObjData	*pobj;

				if (pReset->arg3 > 0) {
					if (! (pObjToIndex = OIdxTable.GetObj (pReset->arg3))) {
						bug ("ResetRoom %s: 'T': bad objto vnum %d.",
							NCCP pRoom->GetName (), pReset->arg3);
						continue;
					}
					if (pArea->nplayer > 0
						|| ! (to_obj = pObjToIndex->GetLast ())
						|| (to_obj->carried_by
							&& ! to_obj->carried_by->IsNpc ())
						|| to_obj->IsTrapped ())
							break;
				} else {
					if (!lastobj || !obj)
						break;
					to_obj = obj;
				}
				pobj = make_trap (pReset->arg2, pReset->arg1,
					number_fuzzy (to_obj->level), pReset->extra);
				obj_to_obj (pobj, to_obj);
			} else {
				if (pArea->nplayer > 0
					|| count_obj_list (OIdxTable.GetObj (OBJ_VNUM_TRAP),
						pRoom->GetContentList ()) > 0)
							break;
				to_obj = make_trap (pReset->arg1, pReset->arg1, 10,
					pReset->extra);
				obj_to_room (to_obj, pRoom);
			}
			break;

		  case 'H':
			if (pReset->arg1 > 0) {
				if (! (pObjToIndex = OIdxTable.GetObj (pReset->arg1))) {
					bug ("ResetRoom %s: 'H': bad objto vnum %d.",
						NCCP pRoom->GetName (), pReset->arg1);
					continue;
				}
				if (pArea->nplayer > 0
					|| ! (to_obj = pObjToIndex->GetLast ())
					|| ! to_obj->in_room
					|| to_obj->in_room->GetArea () != pArea
					|| to_obj->IsHidden ())
						break;
			} else {
				if (! lastobj || !obj)
					break;
				to_obj = obj;
			}

			to_obj->SetHidden ();
			break;

		  case 'B':
			switch (pReset->arg2 & BIT_RESET_TYPE_MASK) {
			  case BIT_RESET_DOOR:
				{
				int		doornum = (pReset->arg2 & BIT_RESET_DOOR_MASK)
					>> BIT_RESET_DOOR_THRESHOLD;
				if (! (pexit = get_exit (pRoom, doornum)))
					break;
				pIv = pexit->GetFlags ();
				}
				break;

			  case BIT_RESET_ROOM:
				bits = pRoom->GetRoomFlagsAddr ();
				break;

			  case BIT_RESET_OBJECT:
				if (pReset->arg1 > 0) {
					if (! (pObjToIndex = OIdxTable.GetObj (pReset->arg1))) {
						bug ("ResetRoom %s: 'B': object: bad objto vnum %d.",
							NCCP pRoom->GetName (), pReset->arg1);
						continue;
					}
					to_obj = pObjToIndex->GetLast ();
					if (! to_obj || ! to_obj->in_room
						|| to_obj->in_room->GetArea () != pArea)
							continue;
				} else {
					if (!lastobj || !obj)
						continue;
					to_obj = obj;
				}
				pBv = &to_obj->m_ExtraFlags;
				break;

			  case BIT_RESET_MOBILE:
				if (!mob)
					continue;
				pBv = mob->GetAffectBitsAddr ();
				break;

			  default:
				bug ("ResetRoom %s: 'B': bad options %d.",
					NCCP pRoom->GetName (), pReset->arg2);
				continue;
			}

			if (bits) {
				if (IS_SET (pReset->arg2, BIT_RESET_SET))
					SET_BIT (*bits, pReset->arg3);
				else if (IS_SET (pReset->arg2, BIT_RESET_TOGGLE))
					TOGGLE_BIT (*bits, pReset->arg3);
				else
					REMOVE_BIT (*bits, pReset->arg3);
				break;
			}
    
			if (pBv) {
				if (IS_SET (pReset->arg2, BIT_RESET_SET))
					pBv->SetBit (pReset->arg3);
				else if (IS_SET (pReset->arg2, BIT_RESET_TOGGLE))
					pBv->ToggleBit (pReset->arg3);
				else
					pBv->ClrBit (pReset->arg3);
				break;
			}
    
			if (pIv) {
				if (IS_SET (pReset->arg2, BIT_RESET_SET))
					pIv->SetBit (pReset->arg3);
				else if (IS_SET (pReset->arg2, BIT_RESET_TOGGLE))
					pIv->ToggleBit (pReset->arg3);
				else
					pIv->ClrBit (pReset->arg3);
				break;
			}
			break;
    
		  case 'D':
			if (! (pexit = get_exit (pRoom, pReset->arg2)))
				break;
			switch (pReset->arg3) {
			  case 0:
				pexit->ClrClosed ();
				pexit->ClrLocked ();
				break;

			  case 1:
				pexit->SetClosed ();
				pexit->ClrLocked ();
				if (pexit->CanSearch ())
					pexit->SetSecret ();
				break;

			  case 2:
				pexit->SetClosed ();
				pexit->SetLocked ();
				if (pexit->CanSearch ())
					pexit->SetSecret ();
				break;
			}
			break;
    
		  case 'R':
			randomize_exits (pRoom, pReset->arg2-1);
			break;
		}
	}
}


void list_resets (CCharacter *ch, CRoomIndexData *pRoom, int start, int end)
{
	CResetData		*pReset;
	CRoomIndexData	*room;
	CMobIndexData	*mob;
	CObjIndexData	*obj, *obj2;
	CObjIndexData	*lastobj;
	BOOL			found;
	int				num = 0;
	const char		*rname, *mname, *oname;
	char			buf [256];
	char			*pbuf;

	if (!ch || !pRoom)
		return;

	room = NULL;
	mob = NULL;
	obj = NULL;
	lastobj = NULL;
	found = FALSE;

	ch->SendColorf ("&YResets for:  %s (%d):\n", pRoom->GetName (), pRoom->vnum);

	CResetList	&RList = pRoom->ResetList;
	if (RList.IsEmpty ()) {
		ch->SendText ("No Resets\n");
		return;
	}

	POSITION	LoResetPos = NULL;
	POSITION	pos = RList.GetHeadPosition ();

	while (pos) {
		pReset = RList.GetNext (pos);
		++num;
		sprintf (buf, "&G%2d) ", num);
		pbuf = buf+strlen (buf);
		switch (pReset->command) {
		  default:
			sprintf (pbuf, "*** BAD RESET: %c %d %d %d %d %d ***\n\r",
				pReset->command, pReset->extra, pReset->mob, pReset->arg1,
				pReset->arg2, pReset->arg3);
			break;
		  case 'M':
			if (! (mob = MobTable.GetMob (pReset->mob)))
				mname = "Mobile: *BAD VNUM*";
			else
				mname = mob->GetPlayerName ();

			sprintf (pbuf, "%s (%d) [%d]%s\n\r", mname, pReset->mob,
				pReset->arg2, pRoom->IsPetShop () ? " (pet)" : "");
			break;

		  case 'G':
		  case 'E':
			if (! mob)
				mname = "* ERROR: NO MOBILE! *";
			if (! (obj = OIdxTable.GetObj (pReset->arg1)))
				oname = "Object: *BAD VNUM*";
			else
				oname = obj->GetName ();
			sprintf (pbuf, "%s (%d) -> %s (%s) [%d]", oname, pReset->arg1,
				mname, (pReset->command == 'G' ? "carry" :
					wear_locs[pReset->arg3]), pReset->arg2);

			if (mob && mob->pShop)
				strcat (buf, " (shop)\n\r");
			else
				strcat (buf, "\n\r");
			lastobj = obj;
			LoResetPos = pos;
			break;

		  case 'O':
			if (! (obj = OIdxTable.GetObj (pReset->arg1)))
				oname = "Object: *BAD VNUM*";
			else
				oname = obj->GetName ();

			sprintf (pbuf, "(object) %s (%d) [%d]\n\r", oname,
				pReset->arg1, pReset->arg2);

			lastobj = obj;
			LoResetPos = pos;
			break;

		  case 'P':
			if (! (obj = OIdxTable.GetObj (pReset->arg1)))
				oname = "Object1: *BAD VNUM*";
			else
				oname = obj->GetName ();
			obj2 = NULL;
			if (pReset->arg3 > 0) {
				obj2 = OIdxTable.GetObj (pReset->arg3);
				rname = (obj2 ? obj2->GetName () : "Object2: *BAD VNUM*");
				lastobj = obj2;
			}
			else if (!lastobj)
				rname = "Object2: *NULL obj*";
			else if (pReset->extra == 0) {
				rname = lastobj->GetName ();
				obj2 = lastobj;
			} else {
				int iNest;
				CResetData *reset;

				reset = RList.GetAt (LoResetPos);
				POSITION pos = LoResetPos;
				for (iNest = 0; iNest < pReset->extra; iNest++) {
					while (pos) {
						reset = RList.GetNext (pos);
						char	c = reset->command;
						if (c == 'O' || c == 'G' || c == 'E'
						  || (c == 'P' && !reset->arg3
							&& reset->extra == iNest))
								break;
						reset = NULL;
					}
					if (! reset || reset->command != 'P')
						break;
				}
				if (! reset)
					rname = "Object2: *BAD NESTING*";
				else if (! (obj2 = OIdxTable.GetObj (reset->arg1)))
					rname = "Object2: *NESTED BAD VNUM*";
				else
					rname = obj2->GetName ();
			}
			sprintf (pbuf, "(Put) %s (%d) -> %s (%d) [%d] {nest %d}\n\r",
				oname, pReset->arg1, rname, (obj2 ? obj2->vnum :
				pReset->arg3),
			pReset->arg2, pReset->extra);
			break;

		  case 'T':
			sprintf (pbuf, "TRAP: %d %d %d %d (%s)\n\r", pReset->extra,
				pReset->arg1, pReset->arg2, pReset->arg3,
				flag_string (pReset->extra, trap_flags));
			break;

		  case 'H':
			if (pReset->arg1 > 0)
				if (! (obj2 = OIdxTable.GetObj (pReset->arg1)))
					rname = "Object: *BAD VNUM*";
				else
					rname = obj2->GetName ();
				else if (!obj)
					rname = "Object: *NULL obj*";
				else
					rname = oname;
			sprintf (pbuf, "Hide %s (%d)\n\r", rname,
				(pReset->arg1 > 0 ? pReset->arg1 : obj ? obj->vnum : 0));
			break;

		  case 'B':
			{
			char* const *flagarray;

			strcpy (pbuf, "BIT: ");
			pbuf += 5;
			if (IS_SET (pReset->arg2, BIT_RESET_SET)) {
				strcpy (pbuf, "Set: ");
				pbuf += 5;
			}
			else if (IS_SET (pReset->arg2, BIT_RESET_TOGGLE)) {
				strcpy (pbuf, "Toggle: ");
				pbuf += 8;
			} else {
				strcpy (pbuf, "Remove: ");
				pbuf += 8;
			}

			switch (pReset->arg2 & BIT_RESET_TYPE_MASK) {
			  case BIT_RESET_DOOR:
				{
				int door;

				if (! (room = RoomTable.GetRoom (pReset->arg1)))
					rname = "Room: *BAD VNUM*";
				else
					rname = room->GetName ();
				door = (pReset->arg2 & BIT_RESET_DOOR_MASK)
					>> BIT_RESET_DOOR_THRESHOLD;
				door = URANGE (0, door, MAX_DIR+1);
				sprintf (pbuf, "Exit %s%s (%d), Room %s (%d)",
					dir_name [door], (room && get_exit (room, door) ?
					"" : " (NO EXIT!)"), door, rname, pReset->arg1);
				}
				flagarray = (char* const *)  ExitTypeNames;
				break;

			  case BIT_RESET_ROOM:
				sprintf (pbuf, "Room %s (%d)", pRoom->GetName (), pReset->arg1);
				flagarray = r_flags;
				break;

			  case BIT_RESET_OBJECT:
				if (pReset->arg1 > 0)
					if (! (obj2 = OIdxTable.GetObj (pReset->arg1)))
						rname = "Object: *BAD VNUM*";
					else
						rname = obj2->GetName ();
					else if (!obj)
						rname = "Object: *NULL obj*";
					else
						rname = oname;
				sprintf (pbuf, "Object %s (%d)", rname,
					(pReset->arg1 > 0 ? pReset->arg1 : obj ? obj->vnum : 0));
				flagarray = (char* const *)  ItemFlagNames;
				break;

			  case BIT_RESET_MOBILE:
				if (pReset->arg1 > 0) {
					CMobIndexData *mob2;

					if (! (mob2 = MobTable.GetMob (pReset->arg1)))
						rname = "Mobile: *BAD VNUM*";
					else
						rname = mob2->GetPlayerName ();
				}
				else if (!mob)
					rname = "Mobile: *NULL mob*";
				else
					rname = mname;
				sprintf (pbuf, "Mobile %s (%d)", rname,
					(pReset->arg1 > 0 ? pReset->arg1 : mob ? mob->vnum : 0));
				flagarray = (char* const *) AffectNames;
				break;

			  default:
				sprintf (pbuf, "bad type %d",
					pReset->arg2 & BIT_RESET_TYPE_MASK);
				flagarray = NULL;
				break;
			}

			pbuf += strlen (pbuf);
			if (flagarray)
				sprintf (pbuf, "; flags: %s [%d]\n\r",
					flag_string (pReset->arg3, flagarray), pReset->arg3);
			else
				sprintf (pbuf, "; flags %d\n\r", pReset->arg3);
			}
			break;

		  case 'D':
			{
			char *ef_name;

			pReset->arg2 = URANGE (0, pReset->arg2, MAX_DIR+1);

			switch (pReset->arg3) {
			  default:	ef_name = "(* ERROR *)";	break;
			  case 0:	ef_name = "Open";			break;
			  case 1:	ef_name = "Close";			break;
			  case 2:	ef_name = "Close and lock";	break;
			}

			CExitData	*pExit = get_exit (pRoom, pReset->arg2);
			const char		*eroom = "UNASSIGNED";
			int			evnum = 0;
			if (pExit && pExit->GetToRoom ()) {
				eroom = pExit->GetToRoom ()->GetName ();
				evnum = pExit->GetToRoom ()->vnum;
			}

			sprintf (pbuf, "%s [%d] the %s [%d] door to %s (%d)\n\r",
				ef_name, pReset->arg3, dir_name [pReset->arg2], pReset->arg2,
				pExit ? eroom : "NO EXIT!", evnum);
			}
			break;

		  case 'R':
			sprintf (pbuf, "Randomize exits 0 to %d -> %s (%d)\n\r",
				pReset->arg2, pRoom->GetName (), pReset->arg1);
			break;
		}
		if (start == -1 || num >= start)
			ch->SendColor (buf);
		if (end != -1 && num >= end)
			break;
	}

	if (num == 0)
		ch->SendText ("You don't have any resets defined.\n\r");
}


void RenumberPutResets (CAreaData* pArea)
{
	POSITION	pos = pArea->RoomList.GetHeadPosition ();
	while (pos)
		RenumberPutResetsForRoom (
			(CRoomIndexData*) pArea->RoomList.GetNext (pos));
}


// Setup put nesting levels, regardless of whether or not the resets will
// actually reset, or if they're bugged.
void RenumberPutResetsForRoom (CRoomIndexData* pRoom)
{
	CResetData	*pLast = NULL;

	POSITION	pos = pRoom->ResetList.GetHeadPosition ();
	while (pos) {
		CResetData	&Res = *pRoom->ResetList.GetNext (pos);
		switch (Res.command) {
		  case 'G':
		  case 'E':
		  case 'O':
			pLast = &Res;
			break;
		  case 'P':
			if (Res.arg3 == 0) {
				if (! pLast)
					Res.extra = 1000000;
				else if (pLast->command != 'P' || pLast->arg3 > 0)
					Res.extra = 0;
				else
					Res.extra = pLast->extra + 1;
				pLast = &Res;
			}
		}
	}
}


// Search backwards to find a reset with an equal command letter.
// Enter with pos set to NULL to start at end, or else with pos set to the
// reset which you want to search backwards from.
// If a previous equal command is found then set pos to it, and return TRUE.
// Otherwise return FALSE and with pos set to the last reset with a command
// which is less than the one we are searching for, or to the head of the
// list (or to NULL if the list is empty).
BOOL CResetList::FindPrevCommand (POSITION& pos, char cmd)
{
	if (! pos) then pos = GetTailPosition ();
	else GetPrev (pos);

	 while (pos) {
		CResetData	*rd = GetPrev (pos);
		if (rd->command <= cmd) {
			pos = m_CurPos;
			return rd->command == cmd;
		}
	}
	pos = GetHeadPosition ();
	return FALSE;
}

enum { A1A1, A1A2, A1A3, A3A1 };

POSITION FindInsertLocation (CResetList& RList, char c,
							 int i1, int i2, int Type)
{
	CResetData	tmp;

	int		*p1, *p2;
	switch (Type) {
	  case A1A1:
		p1 = &tmp.arg1;
		p2 = &tmp.arg1;
		break;
	  case A1A2:
		p1 = &tmp.arg1;
		p2 = &tmp.arg2;
		break;
	  case A1A3:
		p1 = &tmp.arg1;
		p2 = &tmp.arg3;
		break;
	  case A3A1:
		p1 = &tmp.arg3;
		p2 = &tmp.arg1;
		break;
	}
	int	&t1 = *p1;
	int &t2 = *p2;

	POSITION	pos = NULL;		// makes FindPrevCommand start at end
	if (! RList.FindPrevCommand (pos, c))
		return pos;				// pos points to the reset to insert after,
								// (or NULL)

	int	test = 1;
	for (;;) {
		tmp = *RList.GetAt (pos);
		switch (test) {
		  case 1:
			if (i1 < t1) then break;
			if (i1 == t1) then ++test;
		  case 2:
			if (i1 != t1 || i2 > t2) {
				RList.GetNext (pos);
				return pos;
			}
			break;
		}
		POSITION LastPos = pos;
		if (! RList.FindPrevCommand (pos, c))
			return pos;
	}
}


// Place a reset into a room, insert sorting it		-Thoric
void PlaceReset (CRoomIndexData *pRoom, CResetData* pReset)
{
	CResetData	*tmp = NULL;

	int		Cmd = pReset->command;
	int		Extra = pReset->extra;
	int		Mob = pReset->mob;
	int		arg1 = pReset->arg1;
	int		arg2 = pReset->arg2;
	int		arg3 = pReset->arg3;

	CResetList	&RList = pRoom->ResetList;

	if (! RList.IsEmpty ()) {
		POSITION	pos;
		switch (Cmd) {
		  default:
			bug ("place_reset: Bad reset type %c", Cmd);
			delete pReset;
			return;

		  case 'D':
		  case 'R':
			pos = FindInsertLocation (RList, Cmd, arg1, arg2, A1A2);

			if (pos) then RList.InsertBefore (pos, pReset);
			else RList.AddTail (pReset);
			return;

//			for (tmp = tarea->last_reset; tmp; tmp = tmp->GetPrev ())
//				if (tmp->command == letter)
//					break;
//			if (tmp)	// organize by location
//				for (; tmp && tmp->command == letter && tmp->arg1 > arg1;
//					tmp = tmp->GetPrev ());
//			if (tmp)	// organize by direction
//				for (; tmp && tmp->command == letter &&
//					tmp->arg1 == tmp->arg1 && tmp->arg2 > arg2;
//						tmp = tmp->GetPrev ());
//			if (tmp)
//				INSERT (pReset, tmp, tarea->first_reset);
//			else
//				LINK (pReset, tarea->first_reset, tarea->last_reset);
//			return pReset;

		  case 'M':
		  case 'O':
			// for a new mob or object we want to insert just after the
			// last mob or object with a lower vnum
			pos = FindInsertLocation (RList, Cmd, arg3, arg1, A3A1);
			if (pos) {
				// skip over E or G for that mob
				if (Cmd == 'M') {
					while (pos) {
						tmp = RList.GetNext (pos);
						if (tmp->command != 'E' && tmp->command != 'G')
							break;
					}
				}
				// skip over P, T or H for that obj
				else if (Cmd == 'O') {
					while (pos) {
						tmp = RList.GetNext (pos);
						char  c = tmp->command;
						if (c != 'P' && c != 'T' && c != 'H') then break;
					}
				}
			}
			if (pos)
				RList.InsertBefore (pos, pReset);
			else
				RList.AddTail (pReset);
			return;

		  case 'G':
		  case 'E':
			pos = FindInsertLocation (RList, Cmd, arg1, arg1, A1A1);
			if (pos) then RList.InsertBefore (pos, pReset);
			else RList.AddTail (pReset);
			return;

		  case 'P':
		  case 'T':
		  case 'H':
			// find the object in question
			if ((Cmd == 'P' && arg3 == 0)
			  || (Cmd == 'T' && IS_SET (Extra, TRAP_OBJ) && arg1 == 0)
			  || (Cmd == 'H' && arg1 == 0)) {
//			  && (tmp = tarea->last_obj_reset)) {
				pos = RList.Find (tmp);
				RList.GetNext (pos);
				if (pos)
					RList.InsertBefore (pos, pReset);
				else
					RList.AddTail (pReset);
				return;
			}

			pos = FindInsertLocation (RList, Cmd, arg1, arg3, A1A3);
			if (pos) then RList.InsertBefore (pos, pReset);
			else RList.AddTail (pReset);
			return;
		}
	}
	RList.AddTail (pReset);
}


void CRoomIndexData::AddReset (char cm, int ex, int mob, int a1,
								int a2, int a3)
{
	CResetData	*pReset = new CResetData (cm, ex, mob, a1, a2, a3);
	ResetList.AddTail (pReset);
}