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

#include	"stdafx.h"
#include	"smaug.h"
#include	"language.h"
#include	"ActFlags.h"
#include	"SysData.h"
#include	"skill.h"
#include	"mobiles.h"
#include	"affect.h"
#include	"objects.h"
#include	"rooms.h"
#include	"races.h"
#include	"deity.h"
#include	"area.h"
#include	"class.h"
#include	"SmaugWizDoc.h"
#include	"SmaugFiles.h"
#include	"descriptor.h"
#include	"character.h"
#include	"Exits.h"


extern BOOL	fBootDb;


char* const	r_flags	[] = {
"dark", "death", "nomob", "indoors", "lawful", "neutral", "chaotic",
"nomagic", "tunnel", "private", "safe", "solitary", "petshop", "norecall",
"donation", "nodropall", "silence", "logspeech", "nodrop", "clanstoreroom",
"nosummon", "noastral", "teleport", "teleshowdesc", "nofloor",
"nosupplicate", "arena", "nomissile", "r4", "r5", "prototype", "r6"
};

char *	const	mag_flags	[] =
{
"returning", "backstabber", "bane", "loyal", "haste", "drain", 
"lightning_blade" 
};

char *	const	w_flags	[] =
{
"take", "finger", "neck", "body", "head", "legs", "feet", "hands", "arms",
"shield", "about", "waist", "wrist", "wield", "hold", "_dual_", "ears", "eyes",
"missile", "r1","r2","r3","r4","r5","r6",
"r7","r8","r9","r10","r11","r12","r13"
};

char *	const	area_flags	[] =
{
"nopkill", "freekill", "noteleport", "r3", "r4", "r5", "r6", "r7", "r8",
"r9", "r10", "r11", "r12", "r13", "r14", "r15", "r16", "r17",
"r18", "r19","r20","r21","r22","r23","r24",
"r25","r26","r27","r28","r29","r30","r31"
};

char *	const	o_types	[] =
{
"none", "light", "scroll", "wand", "staff", "weapon", "_fireweapon", "_missile",
"treasure", "armor", "potion", "_worn", "furniture", "trash", "_oldtrap",
"container", "_note", "drinkcon", "key", "food", "money", "pen", "boat",
"corpse", "corpse_pc", "fountain", "pill", "blood", "bloodstain",
"scraps", "pipe", "herbcon", "herb", "incense", "fire", "book", "switch",
"lever", "pullchain", "button", "dial", "rune", "runepouch", "match", "trap",
"map", "portal", "paper", "tinder", "lockpick", "spike", "disease", "oil",
"fuel", "shortbow", "longbow", "crossbow", "projectile", "quiver", "shovel",
"salve", "cook", "keyring", "odor"
};

char *	const	a_types	[] =
{
"none", "strength", "dexterity", "intelligence", "wisdom", "constitution",
"sex", "class", "level", "age", "height", "weight", "mana", "hit", "move",
"gold", "experience", "armor", "hitroll", "damroll", "save_poison", "save_rod",
"save_para", "save_breath", "save_spell", "charisma", "affected", "resistant",
"immune", "susceptible", "weaponspell", "luck", "backstab", "pick", "track",
"steal", "sneak", "hide", "palm", "detrap", "dodge", "peek", "scan", "gouge",
"search", "mount", "disarm", "kick", "parry", "bash", "stun", "punch", "climb",
"grip", "scribe", "brew", "wearspell", "removespell", "mentalstate", "emotion",
"stripsn", "remove", "dig", "full", "thirst", "drunk", "blood", "cook",
"recurringspell", "contagious", "xaffected", "odor", "roomflag", "sectortype",
"roomlight", "televnum", "teledelay"
};

char *	const	pc_flags [] =
{
"r1", "deadly", "unauthed", "norecall", "nointro", "gag", "retired", "guest",
"nosummon", "pager", "notitled", "groupwho", "diagnose", "highgag", "r8",
"nstart", "r10", "r11", "r12", "r13", "r14", "r15", "r16", "r17", "r18",
"r19", "r20", "r21", "r22", "r23", "r24", "r25"
};

char *	const	trap_flags [] =
{
"room", "obj", "enter", "leave", "open", "close", "get", "put", "pick",
"unlock", "north", "south", "east", "r1", "west", "up", "down", "examine",
"r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r13",
"r14", "r15" 
};

char *	const	wear_locs [] =
{
"light", "finger1", "finger2", "neck1", "neck2", "body", "head", "legs",
"feet", "hands", "arms", "shield", "about", "waist", "wrist1", "wrist2",
"wield", "hold", "dual_wield", "ears", "eyes", "missile_wield", "back",
"face", "ankle1", "ankle2"
};

char *	const	ris_flags [] =
{
"fire", "cold", "electricity", "energy", "blunt", "pierce", "slash", "acid",
"poison", "drain", "sleep", "charm", "hold", "nonmagic", "plus1", "plus2",
"plus3", "plus4", "plus5", "plus6", "magic", "paralysis", "r1", "r2", "r3",
"r4", "r5", "r6", "r7", "r8", "r9", "r10"
};

char *	const	trig_flags [] =
{
"up", "unlock", "lock", "d_north", "d_south", "d_east", "d_west", "d_up",
"d_down", "door", "container", "open", "close", "passage", "oload", "mload",
"teleport", "teleportall", "teleportplus", "death", "cast", "fakeblade",
"rand4", "rand6", "trapdoor", "anotherroom", "usedial", "absolutevnum",
"showroomdesc", "autoreturn", "r2", "r3"
};

char *	const	part_flags [] =
{
"head", "arms", "legs", "heart", "brains", "guts", "hands", "feet", "fingers",
"ear", "eye", "long_tongue", "eyestalks", "tentacles", "fins", "wings",
"tail", "scales", "claws", "fangs", "horns", "tusks", "tailattack",
"sharpscales", "beak", "haunches", "hooves", "paws", "forelegs", "feathers",
"r1", "r2"
};

/*
 * Note: I put them all in one big set of flags since almost all of these
 * can be shared between mobs, objs and rooms for the exception of
 * bribe and hitprcnt, which will probably only be used on mobs.
 * ie: drop -- for an object, it would be triggered when that object is
 * dropped; -- for a room, it would be triggered when anything is dropped
 *          -- for a mob, it would be triggered when anything is dropped
 *
 * Something to consider: some of these triggers can be grouped together,
 * and differentiated by different arguments... for example:
 *  hour and time, rand and randiw, speech and speechiw
 * 
 */
char *	const	mprog_flags [] =
{
"act", "speech", "rand", "fight", "death", "hitprcnt", "entry", "greet",
"allgreet", "give", "bribe", "hour", "time", "wear", "remove", "sac",
"look", "exa", "zap", "get", "drop", "damage", "repair", "randiw",
"speechiw", "pull", "push", "sleep", "rest", "leave", "script", "use"
};


char *flag_string (int bitvector, char* const flagarray [])
{
	static char	buf [MAX_STRING_LENGTH];
	int			x;

	buf [0] = '\0';
	for (x = 0; x < 32 ; x++)
		if (IS_SET (bitvector, 1 << x)) {
			strcat (buf, flagarray[x]);
			strcat (buf, " ");
		}

	if ((x=strlen (buf)) > 0)
		buf [--x] = '\0';

	return buf;
}


BOOL can_rmodify (CCharacter *ch, CRoomIndexData *room)
{
	int		vnum = room->vnum;

	if (ch->IsNpc ())
		return FALSE;

	if (ch->GetTrustLevel () >= SysData.ModifyProtoLevel)
		return TRUE;

	if (! room->IsPrototype ()) {
		ch->SendText ("You cannot modify this room.\n\r");
		return FALSE;
	}

	CAreaData	&Area = *ch->GetArea ();
	if (! &Area) {
		ch->SendText ("You must have an assigned area to modify this room.\n\r");
		return FALSE;
	}
	if (vnum >= Area.low_r_vnum && vnum <= Area.hi_r_vnum)
		return TRUE;

	ch->SendText ("That room is not in your allocated range.\n\r");
	return FALSE;
}


BOOL can_omodify (CCharacter *ch, CObjData *obj)
{
	int		vnum = obj->pIndexData->vnum;

	if (ch->IsNpc ())
		return FALSE;

	if (ch->GetTrustLevel () >= SysData.ModifyProtoLevel)
		return TRUE;

	if (! obj->IsPrototype ()) {
		ch->SendText ("You cannot modify this object.\n\r");
		return FALSE;
	}

	CAreaData	&Area = *ch->GetArea ();
	if (! &Area) {
		ch->SendText ("You must have an assigned area to modify this object.\n\r");
		return FALSE;
	}
	if (vnum >= Area.low_o_vnum && vnum <= Area.hi_o_vnum)
		return TRUE;

	ch->SendText ("That object is not in your allocated range.\n\r");
	return FALSE;
}


BOOL can_oedit (CCharacter *ch, CObjIndexData *obj)
{
	int		vnum = obj->vnum;

	if (ch->IsNpc ())
		return FALSE;

	if (ch->GetTrustLevel () >= LEVEL_GOD)
		return TRUE;

	if (! obj->IsPrototype ()) {
		ch->SendText ("You cannot modify this object.\n\r");
		return FALSE;
	}
	CAreaData	&Area = *ch->GetArea ();
	if (! &Area) {
		ch->SendText ("You must have an assigned area to modify this object.\n\r");
		return FALSE;
	}
	if (vnum >= Area.low_o_vnum && vnum <= Area.hi_o_vnum)
		return TRUE;

	ch->SendText ("That object is not in your allocated range.\n\r");
	return FALSE;
}


BOOL can_mmodify (CCharacter *ch, CCharacter *mob)
{
	int		vnum;

	if (mob == ch)
		return TRUE;

	if (!mob->IsNpc ()) {
		if (ch->GetTrustLevel () >= SysData.ModifyProtoLevel
		  && ch->GetTrustLevel () > mob->GetTrustLevel ())
			return TRUE;
		else
			ch->SendText ("You can't do that.\n\r");
		return FALSE;
	}

	vnum = mob->GetMobIndex ()->vnum;

	if (ch->IsNpc ())
		return FALSE;

	if (ch->GetTrustLevel () >= SysData.ModifyProtoLevel)
		return TRUE;

	if (! mob->IsAction (ACT_PROTOTYPE)) {
		ch->SendText ("You cannot modify this mobile.\n\r");
		return FALSE;
	}

	CAreaData	&Area = *ch->GetArea ();
	if (! &Area) {
		ch->SendText ("You must have an assigned area to modify this mobile.\n\r");
		return FALSE;
	}
	if (vnum >= Area.low_m_vnum && vnum <= Area.hi_m_vnum)
		return TRUE;

	ch->SendText ("That mobile is not in your allocated range.\n\r");
	return FALSE;
}


BOOL can_medit (CCharacter *ch, CMobIndexData *mob)
{
	int		vnum = mob->vnum;

	if (ch->IsNpc ())
		return FALSE;

	if (ch->GetTrustLevel () >= LEVEL_GOD)
		return TRUE;

	if (! mob->IsPrototype ()) {
		ch->SendText ("You cannot modify this mobile.\n\r");
		return FALSE;
	}

	CAreaData	&Area = *ch->GetArea ();
	if (! &Area) {
		ch->SendText ("You must have an assigned area to modify this mobile.\n\r");
		return FALSE;
	}
	if (vnum >= Area.low_m_vnum && vnum <= Area.hi_m_vnum)
		return TRUE;

	ch->SendText ("That mobile is not in your allocated range.\n\r");
	return FALSE;
}


int get_otype (const char *type)
{
	int x;

	for (x = 0; x < DIM (o_types); ++x)
		if (! str_cmp (type, o_types [x]))
			return x;
	return -1;
}


int get_aflag (const char *flag)
{
	int x;

	for (x = 0; x < MAX_AFFECTED_BY; ++x)
		if (! str_cmp (flag, AffectNames [x]))
			return x;
	return -1;
}


int get_trapflag (char *flag)
{
    int x;

    for (x = 0; x < DIM (trap_flags); ++x)
      if (! str_cmp (flag, trap_flags [x]))
        return x;
    return -1;
}

int get_atype (const char *type)
{
	int x;

	for (x = 0; x < MAX_APPLY_TYPE; ++x)
		if (!str_cmp (type, a_types [x]))
			return x;
	return -1;
}


int get_wearloc (char *type)
{
	int x;

	for (x = 0; x < MAX_WEAR; ++x)
		if (! str_cmp (type, wear_locs [x]))
			return x;
	return -1;
}


int get_exflag (const char *flag)
{
	ASSERT (EX_MAX < 32);

	for (int x = 0; x < EX_MAX; ++x)
		if (! str_cmp (flag, ExitTypeNames [x]))
			return x;
	return -1;
}


int get_rflag (const char *flag)
{
	int x;

	for (x = 0; x < DIM (r_flags); ++x)
		if (! str_cmp (flag, r_flags [x]))
			return x;
	return -1;
}


int get_mpflag (char *flag)
{
	int x;

	for (x = 0; x < DIM (mprog_flags); ++x)
		if (! str_cmp (flag, mprog_flags [x]))
			return x;
	return -1;
}


int get_oflag (const char *flag)
{
	int x;

	for (x = 0; x < DIM (ItemFlagNames); ++x)
		if (! str_cmp (flag, ItemFlagNames [x]))
			return x;
	return -1;
}


int get_areaflag (char *flag)
{
	int x;

	for (x = 0; x < DIM (area_flags); ++x)
		if (! str_cmp (flag, area_flags [x]))
			return x;
	return -1;
}


int get_wflag (const char *flag)
{
	int x;

	for (x = 0; x < DIM (w_flags); ++x)
		if (! str_cmp (flag, w_flags [x]))
			return x;
	return -1;
}


int get_actflag (char *flag)
{
	int x;

	for (x = 0; x < DIM (MobActNames); ++x)
		if (! str_cmp (flag, MobActNames [x]))
			return x;
	return -1;
}


int get_pcflag (char *flag)
{
	int x;

	for (x = 0; x < DIM (pc_flags); ++x)
		if (! str_cmp (flag, pc_flags [x]))
			return x;
	return -1;
}


int get_plrflag (char *flag)
{
	int x;

	for (x = 0; x < PLR_MAX; ++x)
		if (! str_cmp (flag, PlrActNames [x]))
			return x;
	return -1;
}


int get_risflag (const char *flag)
{
    int x;

    for (x = 0; x < DIM (ris_flags); ++x)
      if (! str_cmp (flag, ris_flags [x]))
        return x;
    return -1;
}


int get_trigflag (char *flag)
{
	int x;

	for (x = 0; x < DIM (trig_flags); ++x)
		if (! str_cmp (flag, trig_flags [x]))
			return x;
	return -1;
}


int get_partflag (char *flag)
{
	int x;

	for (x = 0; x < DIM (part_flags); ++x)
		if (! str_cmp (flag, part_flags [x]))
			return x;
	return -1;
}


int get_attackflag (const char *flag)
{
	int x;

	for (x = 0; x < DIM (AttackNames); ++x)
		if (! str_cmp (flag, AttackNames [x]))
			return x;
	return -1;
}


int get_defenseflag (const char *flag)
{
	int x;

	for (x = 0; x < DIM (DefenseNames); ++x)
		if (! str_cmp (flag, DefenseNames [x]))
			return x;
	return -1;
}


// Remove carriage returns from a line
char *strip_cr (const char *str)
{
	static char	newstr [MAX_STRING_LENGTH];
	int			i, j;

	for (i=j=0; str [i] != '\0'; i++)
		if (str [i] != '\r') {
			newstr [j++] = str [i];	
		}

	newstr [j] = '\0';
	return newstr;
}


// Removes the tildes from a line, except if it's the last character.
void smush_tilde (char *str)
{
	int		len;
	char	last;
	char	*strptr;

	strptr = str;

	len = strlen (str);
	if (len)
		last = strptr [len-1];
	else
		last = '\0';

	for (; *str != '\0'; str++) {
		if (*str == '~')
			*str = '-';
	}

	if (len)
		strptr [len-1] = last;
}


void do_goto (CCharacter *ch, char *argument)
{
	char			arg [MAX_INPUT_LENGTH];
	CRoomIndexData	*location;
	CCharacter		*fch;
	CCharacter		*fch_next;
	CRoomIndexData	*in_room;
	CAreaData		*pArea;
	int				vnum;

	one_argument (argument, arg);
	if (arg [0] == '\0') {
		ch->SendText ("Goto where?\n\r");
		return;
	}

	if ((location = find_location (ch, arg)) == NULL) {
		vnum = atoi (arg);
		if (vnum < 0 || RoomTable.GetRoom (vnum)) {
			ch->SendText ("You cannot find that...\n\r");
			return;
		}
		pArea = ch->GetArea ();

		if (ch->GetTrustLevel () < LEVEL_CREATOR
		  || vnum < 1 || ch->IsNpc () || ! pArea) {
			ch->SendText ("No such location.\n\r");
			return;
		}
		// I don't see why anyone should be able to create rooms unless
		// then have an assigned area - Rustry
//		if (ch->GetTrustLevel () < SysData.ModifyProtoLevel) {
			if (! pArea) {
				ch->SendText ("You must have an assigned area to create "
					"rooms.\n\r");
				return;
			}
			if (vnum < pArea->low_r_vnum || vnum > pArea->hi_r_vnum) {
				ch->SendText ("That room is not within your assigned "
					"range.\n\r");
				return;
			}
//		}
		if (FileTable.Exists (FileTable.MakeBuildName (pArea->m_Filename))
		  && ! pArea->IsLoaded ()) {
			ch->SendText ("Your area is not loaded.\n\r");	// Rustry
			return;
		}
		location = make_room (vnum);
		if (! location) {
			bug ("Goto: make_room failed", 0);
			return;
		}
		location->SetArea (pArea);
		set_char_color (AT_WHITE, ch);
		ch->SendText ("Waving your hand, you form order from swirling "
			"chaos,\n\rand step into a new reality...\n\r");
	}

	if (room_is_private (location))
	{
		if (ch->GetTrustLevel () < SysData.OverridePrivateLev) {
			ch->SendText ("That room is private right now.\n\r");
			return;
		}
		else ch->SendText ("Overriding private flag!\n\r");
	}

	in_room = ch->GetInRoom ();
	if (ch->GetFightData ())
		stop_fighting (ch, TRUE);

	if (! ch->IsWizInvis ())
		act (AT_IMMORT, "$n $T", ch, NULL,
			(ch->GetPcData () && ch->GetPcData ()->HasBamfout ())
			? ch->GetPcData ()->GetBamfout () : "leaves in a swirling mist.",
			TO_ROOM);

	ch->regoto = ch->GetInRoom ()->vnum;
	ch->RemoveFromRoom ();

	if (ch->mount) {
		ch->mount->RemoveFromRoom ();
		ch->mount->SendToRoom (location);
	}
	ch->SendToRoom (location);

	if (! ch->IsWizInvis ())
		act (AT_IMMORT, "$n $T", ch, NULL,
			(ch->GetPcData () && ch->GetPcData ()->HasBamfin ())
			? ch->GetPcData ()->GetBamfin () : "appears in a swirling mist.",
			TO_ROOM);

	do_look (ch, "auto");

	if (ch->GetInRoom () == in_room)
		return;

	for (fch = in_room->first_person; fch; fch = fch_next) {
		fch_next = fch->GetNextInRoom ();
		if (fch->GetMaster () == ch && fch->IsImmortal ()) {
			act (AT_ACTION, "You follow $N.", fch, NULL, ch, TO_CHAR);
			do_goto (fch, argument);
		}
	}
}


void do_mset (CCharacter *ch, char *argument)
{
	char		arg1 [MAX_INPUT_LENGTH];
	char		arg2 [MAX_INPUT_LENGTH];
	char		arg3 [MAX_INPUT_LENGTH];
	char		buf [MAX_STRING_LENGTH];
	char		outbuf[MAX_STRING_LENGTH];
	int			num, size, plus;
	char		char1,char2;
	CCharacter	*victim;
	int			value;
	int			minattr, maxattr;
	BOOL		lockvictim;
	char		*origarg = argument;

	set_char_color (AT_PLAIN, ch);

	if (ch->IsNpc ()) {
		ch->SendText ("Mob's can't mset\n\r");
		return;    
	}

	pc_data	&Cpc = *ch->GetPcData ();

	switch (ch->GetSubstate ()) {
	  default:
		break;
	  case SUB_MOB_DESC:
		if (! ch->dest_buf) {
			ch->SendText ("Fatal error: report to an Immortal.\n\r");
			bug ("do_mset: sub_mob_desc: NULL ch->dest_buf");
			ch->SetSubstate (SUB_NONE);
			return;
		}
		victim = (CCharacter*) ch->dest_buf;
		if (char_died (victim)) {
			ch->SendText ("Your victim died!\n\r");
			ch->StopEditing ();
			return;
		}

		victim->SetDescriptionNA (ch->GetEditBuffer ());
		if (victim->IsNpc () && victim->IsAction (ACT_PROTOTYPE)) { 
			STRFREE (victim->GetMobIndex ()->GetDescription ());
			victim->GetMobIndex ()->SetDescription (
				QUICKLINK (victim->GetDescription ()));
		}
		ch->StopEditing ();
		ch->SetSubstate (ch->tempnum);
		return;
	}

	victim = NULL;
	lockvictim = FALSE;
	smash_tilde (argument);

	if (ch->GetSubstate () == SUB_REPEATCMD) {
		victim = (CCharacter*) ch->dest_buf;
		if (char_died (victim)) {
			ch->SendText ("Your victim died!\n\r");
			victim = NULL;
			argument = "done";
		}
		if (argument [0] == '\0' || ! str_cmp (argument, " ")
		  || ! str_cmp (argument, "stat")) {
			if (victim)
				do_mstat (ch, NCCP victim->GetName ());
			else
				ch->SendText ("No victim selected.  Type '?' for help.\n\r");
			return;
		}

		if (! str_cmp (argument, "done") || !str_cmp (argument, "off")) {
			ch->SendText ("Mset mode off.\n\r");
			ch->SetSubstate (SUB_NONE);
			ch->dest_buf = NULL;
			if (&Cpc && Cpc.HasSubPrompt ()) {
				STRFREE (Cpc.GetSubPrompt ());
				Cpc.SetSubPrompt (NULL);
			}
			return;
		}
	}

	if (victim) {
		lockvictim = TRUE;
		strcpy (arg1, victim->GetName ());
		argument = one_argument (argument, arg2);
		strcpy (arg3, argument);
	} else {
		lockvictim = FALSE;
		argument = one_argument (argument, arg1);
		argument = one_argument (argument, arg2);
		strcpy (arg3, argument);
	}

	if (! str_cmp (arg1, "on")) {
		ch->SendText ("Syntax: mset <victim|vnum> on.\n\r");
		return;
	}

	if (arg1 [0] == '\0' || (arg2 [0] == '\0'
	  && ch->GetSubstate () != SUB_REPEATCMD)
	  || ! str_cmp (arg1, "?")) {
		if (ch->GetSubstate () == SUB_REPEATCMD) {
			if (victim)
				ch->SendText ("Syntax: <field>  <value>\n\r");
			else
				ch->SendText ("Syntax: <victim> <field>  <value>\n\r");
		}
		else
			ch->SendText ("Syntax: mset <victim> <field>  <value>\n\r");

		ch->SendText ("\n\r");
		ch->SendText ("Field being one of:\n\r");
		ch->SendText ("  str int wis dex con cha lck sex class\n\r");
		ch->SendText ("  gold maxhp maxmana maxmove practice align race\n\r");
		ch->SendText ("  hitroll damroll armor affected level\n\r");
		ch->SendText ("  thirst drunk full blood flags mobinvislevel\n\r");
		ch->SendText ("  pos defpos part (see BODYPARTS)\n\r");
		ch->SendText ("  sav1 sav2 sav4 sav4 sav5 (see SAVINGTHROWS)\n\r");
		ch->SendText ("  resistant immune susceptible (see RIS)\n\r");
		ch->SendText ("  attack defense numattacks\n\r");
		ch->SendText ("  speaking speaks (see LANGUAGES)\n\r");
		ch->SendText ("  name short long description title spec clan\n\r");
		ch->SendText ("  council quest qp qpa favor deity\n\r");
		ch->SendText ("  rank mentalstate emotion\n\r");
		ch->SendText ("\n\r");
		ch->SendText ("For editing index/prototype mobiles:\n\r");
		ch->SendText ("  hitnumdie hitsizedie hitplus (hit points)\n\r");
		ch->SendText ("  damnumdie damsizedie damplus (damage roll)\n\r");
		ch->SendText ("To toggle area flag: aloaded\n\r");
		ch->SendText ("To toggle pkill flag: pkill\n\r");
		return;
	}

	if (! victim && ch->GetTrustLevel () < LEVEL_GOD) {
		if ((victim = get_char_room (ch, arg1)) == NULL) {
			ch->SendText ("They aren't here.\n\r");
			return;
		}
	}
	else if (! victim) {
		if ((victim = get_char_world (ch, arg1)) == NULL) {
			ch->SendTextf ("No one like that in all the %s.\n\r",
			SysData.GetShortTitle ());
			return;
		}
	}

	pc_data	&Vpc = *victim->GetPcData ();

	if (ch->GetTrustLevel () < victim->GetTrustLevel () && !victim->IsNpc ()){
		ch->SendText ("You can't do that!\n\r");
		ch->dest_buf = NULL;
		return;
	}
	if (lockvictim)
		ch->dest_buf = victim;

	if (victim->IsNpc ()) {
		minattr = 1;
		maxattr = 25;
	} else {
		minattr = 3;
		maxattr = 18;
	}

	if (! str_cmp (arg2, "on")) {
		if (ch->CheckSubrestricted ())
			return;
		ch->SendTextf ("Mset mode on. (Editing %s).\n\r",
		victim->GetName ());
		ch->SetSubstate (SUB_REPEATCMD);
		ch->dest_buf = victim;
		if (&Cpc) {
			if (Cpc.HasSubPrompt ())
				STRFREE (Cpc.GetSubPrompt ());
			if (victim->IsNpc ())
				sprintf (buf, "<&CMset &W#%d&w> %%i", victim->GetMobIndex ()->vnum);
			else
				sprintf (buf, "<&CMset &W%s&w> %%i", victim->GetName ());
			Cpc.SetSubPrompt (STRALLOC (buf));
		}
		return;
	}
	value = is_number (arg3) ? atoi (arg3) : -1;

	if (atoi (arg3) < -1 && value == -1)
		value = atoi (arg3);

	if (! str_cmp (arg2, "str")) {
		if (! can_mmodify (ch, victim))
			return;
		if (value < minattr || value > maxattr) {
			ch->SendTextf ("Strength range is %d to %d.\n\r", minattr, maxattr);
			return;
		}
		victim->perm_str = value;
		if (victim->IsNpc () && victim->IsAction (ACT_PROTOTYPE))
			victim->GetMobIndex ()->perm_str = value;
		return;
	}

	if (! str_cmp (arg2, "int")) {
		if (! can_mmodify (ch, victim))
			return;
		if (value < minattr || value > maxattr) {
			ch->SendTextf ("Intelligence range is %d to %d.\n\r", minattr, maxattr);
			return;
		}
		victim->perm_int = value;
		if (victim->IsNpc () && victim->IsAction (ACT_PROTOTYPE))
			victim->GetMobIndex ()->perm_int = value;
		return;
	}

	if (! str_cmp (arg2, "wis")) {
		if (! can_mmodify (ch, victim))
			return;
		if (value < minattr || value > maxattr) {
			ch->SendTextf ("Wisdom range is %d to %d.\n\r", minattr, maxattr);
			return;
		}
		victim->perm_wis = value;
		if (victim->IsNpc () && victim->IsAction (ACT_PROTOTYPE))
			victim->GetMobIndex ()->perm_wis = value;
		return;
	}

	if (! str_cmp (arg2, "dex")) {
		if (! can_mmodify (ch, victim))
			return;
		if (value < minattr || value > maxattr) {
			ch->SendTextf ("Dexterity range is %d to %d.\n\r", minattr, maxattr);
			return;
		}
		victim->perm_dex = value;
		if (victim->IsNpc () && victim->IsAction (ACT_PROTOTYPE))
			victim->GetMobIndex ()->perm_dex = value;
		return;
	}

	if (! str_cmp (arg2, "con")) {
		if (! can_mmodify (ch, victim))
			return;
		if (value < minattr || value > maxattr) {
			ch->SendTextf ("Constitution range is %d to %d.\n\r", minattr, maxattr);
			return;
		}
		victim->perm_con = value;
		if (victim->IsNpc () && victim->IsAction (ACT_PROTOTYPE))
			victim->GetMobIndex ()->perm_con = value;
		return;
	}

	if (! str_cmp (arg2, "cha")) {
		if (! can_mmodify (ch, victim))
			return;
		if (value < minattr || value > maxattr) {
			ch->SendTextf ("Charisma range is %d to %d.\n\r", minattr, maxattr);
			return;
		}
		victim->perm_cha = value;
		if (victim->IsNpc () && victim->IsAction (ACT_PROTOTYPE))
			victim->GetMobIndex ()->perm_cha = value;
		return;
	}

	if (! str_cmp (arg2, "lck")) {
		if (! can_mmodify (ch, victim))
			return;
		if (value < minattr || value > maxattr) {
			ch->SendTextf ("Luck range is %d to %d.\n\r", minattr, maxattr);
			return;
		}
		victim->perm_lck = value;
		if (victim->IsNpc () && victim->IsAction (ACT_PROTOTYPE))
			victim->GetMobIndex ()->perm_lck = value;
		return;
	}

	if (! str_cmp (arg2, "sav1")) {
		if (! can_mmodify (ch, victim))
			return;
		if (value < -30 || value > 30) {
			ch->SendText ("Saving throw range is -30 to 30.\n\r");
			return;
		}
		victim->saving_poison_death = value;
		if (victim->IsNpc () && victim->IsAction (ACT_PROTOTYPE))
			victim->GetMobIndex ()->saving_poison_death = value;
		return;
	}

	if (! str_cmp (arg2, "sav2")) {
		if (! can_mmodify (ch, victim))
			return;
		if (value < -30 || value > 30) {
			ch->SendText ("Saving throw range is -30 to 30.\n\r");
			return;
		}
		victim->saving_wand = value;
		if (victim->IsNpc () && victim->IsAction (ACT_PROTOTYPE))
			victim->GetMobIndex ()->saving_wand = value;
		return;
	}

	if (! str_cmp (arg2, "sav3")) {
		if (! can_mmodify (ch, victim))
			return;
		if (value < -30 || value > 30) {
			ch->SendText ("Saving throw range is -30 to 30.\n\r");
			return;
		}
		victim->saving_para_petri = value;
		if (victim->IsNpc () && victim->IsAction (ACT_PROTOTYPE))
			victim->GetMobIndex ()->saving_para_petri = value;
		return;
	}

	if (! str_cmp (arg2, "sav4")) {
		if (! can_mmodify (ch, victim))
			return;
		if (value < -30 || value > 30) {
			ch->SendText ("Saving throw range is -30 to 30.\n\r");
			return;
		}
		victim->saving_breath = value;
		if (victim->IsNpc () && victim->IsAction (ACT_PROTOTYPE))
			victim->GetMobIndex ()->saving_breath = value;
		return;
	}

	if (! str_cmp (arg2, "sav5")) {
		if (! can_mmodify (ch, victim))
			return;
		if (value < -30 || value > 30) {
			ch->SendText ("Saving throw range is -30 to 30.\n\r");
			return;
		}
		victim->saving_spell_staff = value;
		if (victim->IsNpc () && victim->IsAction (ACT_PROTOTYPE))
			victim->GetMobIndex ()->saving_spell_staff = value;
		return;
	}

	if (! str_cmp (arg2, "sex")) {
		if (! can_mmodify (ch, victim))
			return;
		if (value < 0 || value > 2) {
			ch->SendText ("Sex range is 0 to 2.\n\r");
			return;
		}
		victim->SetSex (value);
		if (victim->IsNpc () && victim->IsAction (ACT_PROTOTYPE))
			victim->GetMobIndex ()->sex = value;
		return;
	}

	if (! str_cmp (arg2, "class")) {
		if (! can_mmodify (ch, victim))
			return;


		if (victim->IsNpc ()) {
			if (value > MAX_NPC_CLASS || value < 0) {            
				ch->SendTextf ("NPC Class range is 0 to %d.\n",
					MAX_NPC_CLASS-1);
				return;
			}
			victim->SetClass (value);
			if (victim->IsAction (ACT_PROTOTYPE))
				victim->GetMobIndex ()->m_Class = value;
			return;
		}


		if (value < 0 || value >= ClassTable.GetCount ()) {
			ch->SendTextf ("Class range is 0 to %d.\n",
				ClassTable.GetCount ()-1);
			return;
		}
		victim->SetClass (value);
		return;
	}

	if (! str_cmp (arg2, "race")) {
		if (! can_mmodify (ch, victim))
			return;

		if (! victim->IsNpc ()) {
			CRaceData	*pRace = RaceTable.Find (arg3);
			if (! pRace) {
				ch->SendTextf ("Unknown Race: %s.\n", arg3);
				return;
			}
			else value = pRace->GetRace ();
		} else {
			value = is_number (arg3) ? atoi (arg3) :
				RaceTable.GetNpcRace (arg3);

			if (value < 0 || value >= MAX_NPC_RACE) {
				ch->SendTextf ("Race range is 0 to %d.\n", MAX_NPC_RACE-1);
				return;
			}
		}

		victim->SetRace (value);
		if (victim->IsNpc () && victim->IsAction (ACT_PROTOTYPE))
			victim->GetMobIndex ()->race = value;
		return;
	}

	if (! str_cmp (arg2, "armor")) {
		if (! can_mmodify (ch, victim))
			return;
		if (value < -300 || value > 300) {
			ch->SendText ("AC range is -300 to 300.\n\r");
			return;
		}
		victim->SetArmor (value);
		if (victim->IsNpc () && victim->IsAction (ACT_PROTOTYPE))
			victim->GetMobIndex ()->ac = value;
		return;
	}

	if (! str_cmp (arg2, "level")) {
		if (! can_mmodify (ch, victim))
			return;
		if (! victim->IsNpc ()) {
			ch->SendText ("Not on PC's.\n\r");
			return;
		}

		if (value < 0 || value > LEVEL_AVATAR + 5) {
			ch->SendTextf ("Level range is 0 to %d.\n\r", LEVEL_AVATAR + 5);
			return;
		}
		victim->SetLevel (value);
		if (victim->IsNpc () && victim->IsAction (ACT_PROTOTYPE))
			victim->GetMobIndex ()->level = value;
		return;
	}

	if (! str_cmp (arg2, "numattacks")) {
		if (! can_mmodify (ch, victim))
			return;
		if (! victim->IsNpc ()) {
			ch->SendText ("Not on PC's.\n\r");
			return;
		}

		if (value < 0 || value > 20) {
			ch->SendText ("Attacks range is 0 to 20.\n\r");
			return;
		}
		victim->numattacks = value;
		if (victim->IsNpc () && victim->IsAction (ACT_PROTOTYPE))
			victim->GetMobIndex ()->numattacks = value;
		return;
	}

	if (! str_cmp (arg2, "gold")) {
		if (! can_mmodify (ch, victim))
			return;
		victim->SetGold (value);
		if (victim->IsNpc () && victim->IsAction (ACT_PROTOTYPE))
			victim->GetMobIndex ()->gold = value;
		return;
	}

	if (! str_cmp (arg2, "hitroll")) {
		if (! can_mmodify (ch, victim))
			return;
		victim->SetHitroll (URANGE (0, value, 85));
		if (victim->IsNpc () && victim->IsAction (ACT_PROTOTYPE))
			victim->GetMobIndex ()->hitroll = victim->GetHitroll ();
		return;
	}

	if (! str_cmp (arg2, "damroll")) {
		if (! can_mmodify (ch, victim))
			return;
		victim->SetDamroll (URANGE (0, value, 65));
		if (victim->IsNpc () && victim->IsAction (ACT_PROTOTYPE))
			victim->GetMobIndex ()->damroll = victim->GetDamroll ();
		return;
	}

	if (! str_cmp (arg2, "maxhp")) {
		if (! can_mmodify (ch, victim))
			return;
		if (value < 1 || value > 32700) {
			ch->SendText ("Max Hp range is 1 to 32,700 hit points.\n\r");
			return;
		}
		victim->SetMaxHp (value);
		return;
	}

	if (! str_cmp (arg2, "maxmana")) {
		if (! can_mmodify (ch, victim))
			return;
		if (value < 0 || value > 30000) {
			ch->SendText ("Mana range is 0 to 30,000 mana points.\n\r");
			return;
		}
		victim->SetMaxMana (value);
		return;
	}

	if (! str_cmp (arg2, "maxmove")) {
		if (! can_mmodify (ch, victim))
			return;
		if (value < 0 || value > 30000) {
			ch->SendText ("Move range is 0 to 30,000 move points.\n\r");
			return;
		}
		victim->SetMaxMove (value);
		return;
	}

	if (! str_cmp (arg2, "practice")) {
		if (! can_mmodify (ch, victim))
			return;
		if (value < 0 || value > 100) {
			ch->SendText ("Practice range is 0 to 100 sessions.\n\r");
			return;
		}
		victim->SetPractices (value);
		return;
	}

	if (! str_cmp (arg2, "align")) {
		if (! can_mmodify (ch, victim))
			return;
		if (value < -1000 || value > 1000) {
			ch->SendText ("Alignment range is -1000 to 1000.\n\r");
			return;
		}
		victim->SetAlignment (value);
		if (victim->IsNpc () && victim->IsAction (ACT_PROTOTYPE))
			victim->GetMobIndex ()->alignment = value;
		return;
	}

	if (! str_cmp (arg2, "password")) {
		if (ch->GetTrustLevel () < LEVEL_SUB_IMPLEM) {
			ch->SendText ("You can't do that.\n\r");
			return;
		}

		if (victim->IsNpc ()) {
			ch->SendText ("Mobs don't have passwords.\n\r");
			return;
		}

		if (strlen (arg3) < 5) {
			ch->SendText (
				"New password must be at least five characters long.\n\r");
			return;
		}

		// No tilde allowed because of player file format.
		CString	NewPwd = Crypt (arg3, victim->GetName ());
		if (NewPwd.Find ('~') > -1) {
			ch->SendText ("New password not acceptable, try again.\n\r");
			return;
		}

		delete Vpc.GetPassWord ();
		Vpc.SetPassWord (str_dup (NewPwd));
		if (SysData.IsSavePasswordOnChange ())
			save_char_obj (victim);
		ch->SendText ("Ok.\n\r");
		victim->SendTextf ("Your password has been changed by %s.\n\r",
			ch->GetName ());
		return;
	}

	if (! str_cmp (arg2, "rank")) {
		if (ch->GetTrustLevel () < LEVEL_GOD) {
			ch->SendText ("You can't do that.\n\r");
			return;
		}
		if (victim->IsNpc ()) {
			ch->SendText ("Not on NPC's.\n\r");
			return;
		}
		smash_tilde (argument);
		delete Vpc.GetRank ();
		if (! argument || argument [0] == '\0' || ! str_cmp (argument, "none"))
			Vpc.SetRank (str_dup (""));
		else
			Vpc.SetRank (str_dup (argument));
		ch->SendText ("Ok.\n\r");
		return;
	}
    
	if (! str_cmp (arg2, "quest")) {
		if (victim->IsNpc ()) {
			ch->SendText ("Not on NPC's.\n\r");
			return;
		}

		if (value < 0 || value > 500) {
			ch->SendText ("The current quest range is 0 to 500.\n\r");
			return;
		}

		Vpc.quest_number = value;
		return;
	}

	if (! str_cmp (arg2, "qpa")) {
		if (victim->IsNpc ()) {
			ch->SendText ("Not on NPC's.\n\r");
			return;
		}

		Vpc.quest_accum = value;
		return;
	}

	if (! str_cmp (arg2, "qp")) {
		if (victim->IsNpc ()) {
			ch->SendText ("Not on NPC's.\n\r");
			return;
		}

		if (value < 0 || value > 5000) {
			ch->SendText ("The current quest point range is 0 to 5000.\n\r");
			return;
		}

		Vpc.quest_curr = value;
		return;
	}

	if (! str_cmp (arg2, "favor")) {
		if (victim->IsNpc ()) {
			ch->SendText ("Not on NPC's.\n\r");
			return;
		}

		if (value < -1500 || value > 1500) {
			ch->SendText ("Range is from -1500 to 1500.\n\r");
			return;
		}

		Vpc.favor = value;
		return;
	}	

	if (! str_cmp (arg2, "mentalstate")) {
		if (value < -100 || value > 100) {
			ch->SendText ("Value must be in range -100 to +100.\n\r");
			return;
		}
		victim->SetMentalState (value);
		return;
	}

	if (! str_cmp (arg2, "emotion")) {
		if (value < -100 || value > 100) {
			ch->SendText ("Value must be in range -100 to +100.\n\r");
			return;
		}
		victim->emotional_state = value;
		return;
	}

	if (! str_cmp (arg2, "thirst")) {
		if (victim->IsNpc ()) {
			ch->SendText ("Not on NPC's.\n\r");
			return;
		}

		if (value < 0 || value > 100) {
			ch->SendText ("Thirst range is 0 to 100.\n\r");
			return;
		}

		Vpc.condition[COND_THIRST] = value;
		return;
	}

	if (! str_cmp (arg2, "drunk")) {
		if (victim->IsNpc ()) {
			ch->SendText ("Not on NPC's.\n\r");
			return;
		}

		if (value < 0 || value > 100) {
			ch->SendText ("Drunk range is 0 to 100.\n\r");
			return;
		}

		Vpc.condition[COND_DRUNK] = value;
		return;
	}

	if (! strcmp (arg2, "full")) {
		if (victim->IsNpc ()) {
			ch->SendText ("Not on NPC's.\n\r");
			return;
		}

		if (value < 0 || value > 100) {
			ch->SendText ("Full range is 0 to 100.\n\r");
			return;
		}

		Vpc.condition[COND_FULL] = value;
		return;
	}

	if (! str_cmp (arg2, "blood")) {
		if (victim->IsNpc ()) {
			ch->SendText ("Not on NPC's.\n\r");
			return;
		}

		if (value < 0 || value > MAX_LEVEL+10) {
			ch->SendTextf ("Blood range is 0 to %d.\n\r", MAX_LEVEL+10);
			return;
		}

		Vpc.condition [COND_BLOODTHIRST] = value;
		return;
	}

	if (! str_cmp (arg2, "name")) {
		if (! can_mmodify (ch, victim))
			return;
		if (! victim->IsNpc () && ch->GetTrustLevel () < LEVEL_SUB_IMPLEM) {
			ch->SendText ("Not on PC's.\n\r");
			return;
		}

		victim->SetName (arg3);
		if (victim->IsNpc () && victim->IsAction (ACT_PROTOTYPE)) {
			STRFREE (victim->GetMobIndex ()->GetPlayerName ());
			victim->GetMobIndex ()->SetPlayerName (
				QUICKLINK (victim->GetName ()));
		}
		return;
	}

	if (! str_cmp (arg2, "minsnoop")) {
		if (ch->GetTrustLevel () < LEVEL_SUB_IMPLEM) {
			ch->SendText ("You can't do that.\n\r");
			return;
		}

		if (victim->IsNpc ()) {
			ch->SendText ("Not on NPC's.\n\r");
			return;
		}

		if (&Vpc) {
			Vpc.min_snoop = value;
			return;
		}
	}

	if (! str_cmp (arg2, "clan")) {
		if (ch->GetTrustLevel () < LEVEL_GREATER) {
			ch->SendText ("You can't do that.\n\r");
			return;
		}
		if (victim->IsNpc ()) {
			ch->SendText ("Not on NPC's.\n\r");
			return;
		}

		if (! arg3 || arg3 [0] == '\0') {
			STRFREE (Vpc.GetClanName ());
			Vpc.SetClanName (STRALLOC (""));

			CClanData	&Clan = *Vpc.GetClan ();
			if (&Clan) {
				Vpc.SetClan (NULL);
				Clan.AddMembers (-1);
				Clan.Save ();
				ch->SendTextf ("Removed %s from %s.\n\r",
					victim->GetName (), Clan.GetName ());
			}
			return;
		}
		CClanData	&Clan = *ClanList.Find (arg3, CLAN_ALL);
		if (! &Clan) {
			ch->SendText ("No such clan.\n\r");
			return;
		}
		STRFREE (Vpc.GetClanName ());
		Vpc.SetClanName (QUICKLINK (Clan.GetName ()));
		Vpc.SetClan (&Clan);
		Clan.AddMembers (1);
		Clan.Save ();
		ch->SendTextf ("Added %s to %s.\n\r", victim->GetName (),
			Clan.GetName ());
		return;
	}

	if (! str_cmp (arg2, "deity")) {
		CDeityData	*deity;

		if (victim->IsNpc ()) {
			ch->SendText ("Not on NPC's.\n\r");
			return;
		}

		if (! arg3 || arg3 [0] == '\0') {
			STRFREE (Vpc.GetDeityName ());
			Vpc.SetDeityName (STRALLOC (""));
			Vpc.deity = NULL;
			ch->SendText ("Deity removed.\n\r");
			return;
		}

		deity = get_deity (arg3);
		if (! deity) {
			ch->SendText ("No such deity.\n\r");
			return;
		}
		STRFREE (Vpc.GetDeityName ());
		Vpc.SetDeityName (QUICKLINK (deity->GetName ()));
		Vpc.deity = deity;
		ch->SendText ("Done.\n\r");
		return;
	}

	if (! str_cmp (arg2, "council")) {
		if (ch->GetTrustLevel () < LEVEL_SUB_IMPLEM) {
			ch->SendText ("You can't do that.\n\r");
			return;
		}

		if (victim->IsNpc ()) {
			ch->SendText ("Not on NPC's.\n\r");
			return;
		}

		if (! arg3 || arg3 [0] == '\0') {
			STRFREE (Vpc.GetCouncilName ());
			Vpc.SetCouncilName (STRALLOC (""));

			CCouncilData	&Council = *Vpc.council;
			if (&Council) {
				Vpc.council = NULL;
				Council.AddMembers (-1);
				save_council (&Council);
				ch->SendTextf ("Removed %s from %s.\n\r", victim->GetName (),
					Council.GetName ());
			}
			return;
		}

		CCouncilData	&Council = *get_council (arg3);
		if (! &Council) {
			ch->SendText ("No such council.\n\r");
			return;
		}
		STRFREE (Vpc.GetCouncilName ());
		Vpc.SetCouncilName (QUICKLINK (Council.GetName ()));
		Vpc.council = &Council;
		Council.AddMembers (1);
		save_council (&Council);
		ch->SendTextf ("Added %s to %s.\n\r", victim->GetName (),
			Council.GetName ());
		return;
	}

	if (! str_cmp (arg2, "short")) {
		victim->SetShortDescr (arg3);
		if (victim->IsNpc () && victim->IsPrototype ()) {
			STRFREE (victim->GetMobIndex ()->GetShortDescr ());
			victim->GetMobIndex ()->SetShortDescr (
				QUICKLINK (victim->GetShortDescr ()));
		}
		return;
	}

	if (! str_cmp (arg2, "long")) {
		strcpy (buf, arg3);
		strcat (buf, "\n\r");
		victim->SetLongDescr (buf);
		if (victim->IsNpc () && victim->IsPrototype ()) {
			STRFREE (victim->GetMobIndex ()->GetLongDescr ());
			victim->GetMobIndex ()->SetLongDescr (
				QUICKLINK (victim->GetLongDescr ()));
		}
		return;
	}

	if (! str_cmp (arg2, "description")) {
		if (arg3 [0]) {
			victim->SetDescription (arg3);
			if (victim->IsNpc () && victim->IsPrototype ()) {
				STRFREE (victim->GetMobIndex ()->GetDescription ());
				victim->GetMobIndex ()->SetDescription (
					QUICKLINK (victim->GetDescription ()));
			}
			return;
		}

		ch->CheckSubrestricted ();
		if (ch->GetSubstate () == SUB_REPEATCMD)
			ch->tempnum = SUB_REPEATCMD;
		else
			ch->tempnum = SUB_NONE;

		ch->SetSubstate (SUB_MOB_DESC);
		ch->dest_buf = victim;
		start_editing (ch, victim->GetDescription ());
		return;
	}

	if (! str_cmp (arg2, "title")) {
		if (victim->IsNpc ()) {
			ch->SendText ("Not on NPC's.\n\r");
			return;
		}
		set_title (victim, arg3);
		return;
	}

	if (! str_cmp (arg2, "spec")) {
		if (! can_mmodify (ch, victim))
			return;
		if (! victim->IsNpc ()) {
			ch->SendText ("Not on PC's.\n\r");
			return;
		}

		if (! str_cmp (arg3, "none")) {
			victim->SetSpecialMobFunction (NULL);
			ch->SendText ("Special function removed.\n\r");
			if (victim->IsNpc () && victim->IsAction (ACT_PROTOTYPE))
				victim->GetMobIndex ()->spec_fun =
					victim->GetSpecialMobFunction ();
			return;
		}

		victim->SetSpecialMobFunction (spec_lookup (arg3));
		if (victim->GetSpecialMobFunction () == NULL) {
			ch->SendText ("No such spec fun.\n\r");
			return;
		}
		if (victim->IsNpc () && victim->IsAction (ACT_PROTOTYPE))
			victim->GetMobIndex ()->spec_fun =
				victim->GetSpecialMobFunction ();
		return;
	}

	if (! str_cmp (arg2, "flags")) {
		BOOL	pcflag;
		if (! victim->IsNpc () && ch->GetTrustLevel () < LEVEL_GREATER) {
			ch->SendText ("You can only modify a mobile's flags.\n\r");
			return;
		}

		if (! can_mmodify (ch, victim))
			return;

		if (! argument || argument [0] == '\0') {
			ch->SendText ("Usage: mset <victim> flags <flag> [flag]...\n\r");
			return;
		}

		while (argument [0] != '\0') {
			pcflag = FALSE;
			argument = one_argument (argument, arg3);
			value = victim->IsNpc () ?
				get_actflag (arg3) : get_plrflag (arg3);

			if (! victim->IsNpc () && value < 0) {
				pcflag = TRUE;
				value = get_pcflag (arg3);
			}

			if (value < 0) {
				ch->SendTextf ("Unknown flag: %s\n\r", arg3);
				return;
			}

			if (victim->IsNpc ()
			  && value == ACT_PROTOTYPE
			  && ch->GetTrustLevel () < LEVEL_GREATER 
			  && !is_name ("protoflag", Cpc.GetBestowments ()))
				ch->SendText ("You cannot change the prototype flag.\n\r");
			else
				if (victim->IsNpc () && value == ACT_IS_NPC)
					ch->SendText ("If that could be changed, it would "
						"cause many problems.\n\r");
			else
				if (victim->IsNpc () && value == ACT_POLYMORPHED)
					ch->SendText ("Changing that would be a _bad_ thing.\n\r");
			else {
				if (pcflag)
					victim->TogglePcFlag (value);
				else { 
					victim->ToggleActBit (value);
					// NPC check added by Gorog
					if (victim->IsNpc () && (value == ACT_PROTOTYPE))
						victim->GetMobIndex ()->SetActFlags (
							victim->GetActFlags ());
				}
			}
		}

		if (victim->IsNpc () && victim->IsPrototype ())
			victim->GetMobIndex ()->SetActFlags (victim->GetActFlags ());
		return;
	}

	if (! str_cmp (arg2, "mobinvislevel")) {
		if (! victim->IsNpc ()) {
			ch->SendText ("PC's cannot be mobinvis./r/n");
			return;
		}
		victim->SetMobInvisLevel (atoi (argument));
		if (victim->IsPrototype ())
			victim->GetMobIndex ()->SetMobInvisLevel (atoi (argument));
		return;
	}

	if (! str_cmp (arg2, "affected")) {
		if (! victim->IsNpc () && ch->GetTrustLevel () < LEVEL_LESSER) {
			ch->SendText ("You can only modify a mobile's flags.\n\r");
			return;
		}

		if (! can_mmodify (ch, victim))
			return;
		if (! argument || argument[0] == '\0') {
			ch->SendText ("Usage: mset <victim> affected <flag> [flag]...\n\r");
			return;
		}

		while (argument [0] != '\0') {
			argument = one_argument (argument, arg3);
			value = get_aflag (arg3);
			if (value < 0)
				ch->SendTextf ("Unknown flag: %s\n\r", arg3);
			else
				victim->ToggleAffect (value);
		}
		if (victim->IsNpc () && victim->IsAction (ACT_PROTOTYPE))
			victim->GetMobIndex ()->SetAffectFlags (victim->GetAffectFlags ()); 
		return;
	}

	// save some more finger-leather for setting RIS stuff
	if (! str_cmp (arg2, "r")) {
		if (! victim->IsNpc () && ch->GetTrustLevel () < LEVEL_LESSER) {
			ch->SendText ("You can only modify a mobile's ris.\n\r");
			return;
		}
		if (! can_mmodify (ch, victim))
			return;

		sprintf (outbuf,"%s resistant %s",arg1, arg3);
		do_mset (ch, outbuf);
		return;
	}

	if (! str_cmp (arg2, "i")) {
		if (! victim->IsNpc () && ch->GetTrustLevel () < LEVEL_LESSER) {
			ch->SendText ("You can only modify a mobile's ris.\n\r");
			return;
		}
		if (!can_mmodify (ch, victim))
			return;


		sprintf (outbuf,"%s immune %s",arg1, arg3);
		do_mset (ch, outbuf);
		return;
	}

	if (! str_cmp (arg2, "s")) {
		if (! victim->IsNpc () && ch->GetTrustLevel () < LEVEL_LESSER) {
			ch->SendText ("You can only modify a mobile's ris.\n\r");
			return;
		}
		if (! can_mmodify (ch, victim))
			return;

		sprintf (outbuf,"%s susceptible %s",arg1, arg3);
		do_mset (ch, outbuf);
		return;
	}

	if (! str_cmp (arg2, "ri")) {
		if (! victim->IsNpc () && ch->GetTrustLevel () < LEVEL_LESSER) {
			ch->SendText ("You can only modify a mobile's ris.\n\r");
			return;
		}
		if (!can_mmodify (ch, victim))
			return;

		sprintf (outbuf,"%s resistant %s",arg1, arg3);
		do_mset (ch, outbuf);
		sprintf (outbuf,"%s immune %s",arg1, arg3);
		do_mset (ch, outbuf);
		return;
	}

	if (! str_cmp (arg2, "rs")) {
		if (! victim->IsNpc () && ch->GetTrustLevel () < LEVEL_LESSER) {
			ch->SendText ("You can only modify a mobile's ris.\n\r");
			return;
		}
		if (!can_mmodify (ch, victim))
			return;

		sprintf (outbuf,"%s resistant %s",arg1, arg3);
		do_mset (ch, outbuf);
		sprintf (outbuf,"%s susceptible %s",arg1, arg3);
		do_mset (ch, outbuf);
		return;
	}

	if (! str_cmp (arg2, "is")) {
		if (! victim->IsNpc () && ch->GetTrustLevel () < LEVEL_LESSER) {
			ch->SendText ("You can only modify a mobile's ris.\n\r");
			return;
		}
		if (! can_mmodify (ch, victim))
			return;

		sprintf (outbuf,"%s immune %s",arg1, arg3);
		do_mset (ch, outbuf);
		sprintf (outbuf,"%s susceptible %s",arg1, arg3);
		do_mset (ch, outbuf);
		return;
	}

	if (! str_cmp (arg2, "ris")) {
		if (! victim->IsNpc () && ch->GetTrustLevel () < LEVEL_LESSER) {
			ch->SendText ("You can only modify a mobile's ris.\n\r");
			return;
		}
		if (! can_mmodify (ch, victim))
			return;

		sprintf (outbuf,"%s resistant %s",arg1, arg3);
		do_mset (ch, outbuf);
		sprintf (outbuf,"%s immune %s",arg1, arg3);
		do_mset (ch, outbuf);
		sprintf (outbuf,"%s susceptible %s",arg1, arg3);
		do_mset (ch, outbuf);
		return;
	}

	if (! str_cmp (arg2, "resistant")) {
		if (! victim->IsNpc () && ch->GetTrustLevel () < LEVEL_LESSER) {
			ch->SendText ("You can only modify a mobile's resistances.\n\r");
			return;
		}
		if (! can_mmodify (ch, victim))
			return;

		if (! argument || argument[0] == '\0') {
			ch->SendText ("Usage: mset <victim> resistant <flag> [flag]...\n\r");
			return;
		}

		while (argument [0] != '\0') {
			argument = one_argument (argument, arg3);
			value = get_risflag (arg3);
			if (value < 0)
				ch->SendTextf ("Unknown flag: %s\n\r", arg3);
			else
				victim->ToggleResist (1 << value);
		}
		if (victim->IsNpc () && victim->IsAction (ACT_PROTOTYPE))
			victim->GetMobIndex ()->resistant = victim->GetResistFlags (); 
		return;
	}

	if (! str_cmp (arg2, "immune")) {
		if (! victim->IsNpc () && ch->GetTrustLevel () < LEVEL_LESSER) {
			ch->SendText ("You can only modify a mobile's immunities.\n\r");
			return;
		}
		if (! can_mmodify (ch, victim))
			return;

		if (! argument || argument[0] == '\0') {
			ch->SendText ("Usage: mset <victim> immune <flag> [flag]...\n\r");
			return;
		}

		while (argument [0] != '\0') {
			argument = one_argument (argument, arg3);
			value = get_risflag (arg3);
			if (value < 0)
				ch->SendTextf ("Unknown flag: %s\n\r", arg3);
			else
				victim->ToggleImmune (1 << value);
		}
		if (victim->IsNpc () && victim->IsAction (ACT_PROTOTYPE))
			victim->GetMobIndex ()->immune = victim->GetImmuneFlags (); 
		return;
	}

	if (! str_cmp (arg2, "susceptible")) {
		if (! victim->IsNpc () && ch->GetTrustLevel () < LEVEL_LESSER) {
			ch->SendText ("You can only modify a mobile's susceptibilities.\n\r");
			return;
		}
		if (! can_mmodify (ch, victim))
			return;

		if (! argument || argument[0] == '\0') {
			ch->SendText ("Usage: mset <victim> susceptible <flag> [flag]...\n\r");
			return;
		}

		while (argument [0] != '\0') {
			argument = one_argument (argument, arg3);
			value = get_risflag (arg3);
			if (value < 0)
				ch->SendTextf ("Unknown flag: %s\n\r", arg3);
			else
				victim->ToggleSuscept (1 << value);
		}
		if (victim->IsNpc () && victim->IsAction (ACT_PROTOTYPE))
			victim->GetMobIndex ()->susceptible = victim->GetSusceptFlags (); 
		return;
	}

	if (! str_cmp (arg2, "part")) {
		if (! victim->IsNpc () && ch->GetTrustLevel () < LEVEL_LESSER) {
			ch->SendText ("You can only modify a mobile's parts.\n\r");
			return;
		}
		if (! can_mmodify (ch, victim))
			return;

		if (! argument || argument[0] == '\0') {
			ch->SendText ("Usage: mset <victim> part <flag> [flag]...\n\r");
			return;
		}

		while (argument [0] != '\0') {
			argument = one_argument (argument, arg3);
			value = get_partflag (arg3);
			if (value < 0)
				ch->SendTextf ("Unknown flag: %s\n\r", arg3);
			else
				victim->ToggleXFlag (1 << value);
		}
		if (victim->IsNpc () && victim->IsAction (ACT_PROTOTYPE))
			victim->GetMobIndex ()->xflags = victim->GetXFlags (); 
		return;
	}

	if (! str_cmp (arg2, "attack")) {
		if (! victim->IsNpc ()) {
			ch->SendText ("You can only modify a mobile's attacks.\n\r");
			return;
		}
		if (! can_mmodify (ch, victim))
			return;

		if (! argument || argument [0] == '\0') {
			ch->SendText ("Usage: mset <victim> attack <flag> [flag]...\n\r");
			return;
		}

		while (argument [0] != '\0') {
			argument = one_argument (argument, arg3);
			value = get_attackflag (arg3);
			if (value < 0)
				ch->SendTextf ("Unknown flag: %s\n\r", arg3);
			else
				victim->ToggleAttack (value);
		}
		if (victim->IsNpc () && victim->IsAction (ACT_PROTOTYPE))
			victim->GetMobIndex ()->SetAttackFlags (victim->GetAttackFlags ());
		return;
	}

	if (! str_cmp (arg2, "defense")) {
		if (! victim->IsNpc ()) {
			ch->SendText ("You can only modify a mobile's defenses.\n\r");
			return;
		}
		if (! can_mmodify (ch, victim))
			return;

		if (! argument || argument [0] == '\0') {
			ch->SendText ("Usage: mset <victim> defense <flag> [flag]...\n\r");
			return;
		}

		while (argument [0] != '\0') {
			argument = one_argument (argument, arg3);
			value = get_defenseflag (arg3);
			if (value < 0)
				ch->SendTextf ("Unknown flag: %s\n\r", arg3);
			else
				victim->ToggleDefense (value);
		}
		if (victim->IsNpc () && victim->IsAction (ACT_PROTOTYPE))
			victim->GetMobIndex ()->SetDefenseFlags (victim->GetDefenseFlags ());
		return;
	}

	if (! str_cmp (arg2, "pos")) {
		if (! victim->IsNpc ()) {
			ch->SendText ("Mobiles only.\n\r");
			return;
		}
		if (! can_mmodify (ch, victim))
			return;

		if (value < 0 || value > POS_STANDING) {
			ch->SendTextf ("Position range is 0 to %d.\n\r", POS_STANDING);
			return;
		}
		victim->SetPosition (value);
		if (victim->IsNpc () && victim->IsAction (ACT_PROTOTYPE))
			victim->GetMobIndex ()->position = victim->GetPosition (); 
		ch->SendText ("Done.\n\r");
		return;
	}

	if (! str_cmp (arg2, "defpos")) {
		if (! victim->IsNpc ()) {
			ch->SendText ("Mobiles only.\n\r");
			return;
		}
		if (! can_mmodify (ch, victim))
			return;

		if (value < 0 || value > POS_STANDING) {
			ch->SendTextf ("Position range is 0 to %d.\n\r", POS_STANDING);
			return;
		}
		victim->defposition = value;
		if (victim->IsNpc () && victim->IsAction (ACT_PROTOTYPE))
			victim->GetMobIndex ()->defposition = victim->defposition; 
		ch->SendText ("Done.\n\r");
		return;
	}

	// save some finger-leather
	if (! str_cmp (arg2, "hitdie")) {
		if (! victim->IsNpc ()) {
			ch->SendText ("Mobiles only.\n\r");
			return;
		}
		if (! can_mmodify (ch, victim))
			return;

		sscanf (arg3,"%d %c %d %c %d",&num,&char1,&size,&char2,&plus);
		sprintf (outbuf,"%s hitnumdie %d",arg1, num);
		do_mset (ch, outbuf);

		sprintf (outbuf,"%s hitsizedie %d",arg1, size);
		do_mset (ch, outbuf);

		sprintf (outbuf,"%s hitplus %d",arg1, plus);
		do_mset (ch, outbuf);
		return;
	}

	// save some more finger-leather
	if (! str_cmp (arg2, "damdie")) {
		if (! victim->IsNpc ()) {
			ch->SendText ("Mobiles only.\n\r");
			return;
		}
		if (! can_mmodify (ch, victim))
			return;

		sscanf (arg3,"%d %c %d %c %d",&num,&char1,&size,&char2,&plus);
		sprintf (outbuf,"%s damnumdie %d",arg1, num);
		do_mset (ch, outbuf);
		sprintf (outbuf,"%s damsizedie %d",arg1, size);
		do_mset (ch, outbuf);
		sprintf (outbuf,"%s damplus %d",arg1, plus);
		do_mset (ch, outbuf);
		return;
	}

	if (! str_cmp (arg2, "hitnumdie")) {
		if (! victim->IsNpc ()) {
			ch->SendText ("Mobiles only.\n\r");
			return;
		}
		if (! can_mmodify (ch, victim))
			return;

		if (value < 0 || value > 32767) {
			ch->SendText ("Number of hitpoint dice range is 0 to 30000.\n\r");
			return;
		}
		if (victim->IsNpc () && victim->IsAction (ACT_PROTOTYPE))
			victim->GetMobIndex ()->hitnodice = value;
		ch->SendText ("Done.\n\r");
		return;
	}

	if (! str_cmp (arg2, "hitsizedie")) {
		if (! victim->IsNpc ()) {
			ch->SendText ("Mobiles only.\n\r");
			return;
		}
		if (! can_mmodify (ch, victim))
			return;

		if (value < 0 || value > 32767) {
			ch->SendText ("Hitpoint dice size range is 0 to 30000.\n\r");
			return;
		}
		if (victim->IsNpc () && victim->IsAction (ACT_PROTOTYPE))
			victim->GetMobIndex ()->hitsizedice = value;
		ch->SendText ("Done.\n\r");
		return;
	}

	if (! str_cmp (arg2, "hitplus")) {
		if (! victim->IsNpc ()) {
			ch->SendText ("Mobiles only.\n\r");
			return;
		}
		if (! can_mmodify (ch, victim))
			return;

		if (value < 0 || value > 32767) {
			ch->SendText ("Hitpoint bonus range is 0 to 30000.\n\r");
			return;
		}
		if (victim->IsNpc () && victim->IsAction (ACT_PROTOTYPE))
			victim->GetMobIndex ()->hitplus = value;
		ch->SendText ("Done.\n\r");
		return;
	}

	if (! str_cmp (arg2, "damnumdie")) {
		if (! victim->IsNpc ()) {
			ch->SendText ("Mobiles only.\n\r");
			return;
		}
		if (! can_mmodify (ch, victim))
			return;

		if (value < 0 || value > 100) {
			ch->SendText ("Number of damage dice range is 0 to 100.\n\r");
			return;
		}
		if (victim->IsNpc () && victim->IsAction (ACT_PROTOTYPE))
			victim->GetMobIndex ()->damnodice = value;
		ch->SendText ("Done.\n\r");
		return;
	}

	if (! str_cmp (arg2, "damsizedie")) {
		if (! victim->IsNpc ()) {
			ch->SendText ("Mobiles only.\n\r");
			return;
		}
		if (! can_mmodify (ch, victim))
			return;

		if (value < 0 || value > 100) {
			ch->SendText ("Damage dice size range is 0 to 100.\n\r");
			return;
		}
		if (victim->IsNpc () && victim->IsAction (ACT_PROTOTYPE))
			victim->GetMobIndex ()->damsizedice = value;
		ch->SendText ("Done.\n\r");
		return;
	}

	if (! str_cmp (arg2, "damplus")) {
		if (! victim->IsNpc ()) {
			ch->SendText ("Mobiles only.\n\r");
			return;
		}
		if (! can_mmodify (ch, victim))
			return;

		if (value < 0 || value > 1000) {
			ch->SendText ("Damage bonus range is 0 to 1000.\n\r");
			return;
		}
		if (victim->IsNpc () && victim->IsAction (ACT_PROTOTYPE))
			victim->GetMobIndex ()->damplus = value;
		ch->SendText ("Done.\n\r");
		return;
	}

	if (! str_cmp (arg2, "aloaded")) {
		if (victim->IsNpc ()) {
			ch->SendText ("Player Characters only.\n\r");
			return;
		}
		if (! can_mmodify (ch, victim))
			return;

		CAreaData	&Area = *victim->GetArea ();
		if (! Area.IsLoaded ()) {
			Area.SetLoaded ();
			victim->SendText ("Your area set to LOADED!\n\r");
			if (ch != victim)
				ch->SendText ("Area set to LOADED!\n\r");
			return;
		} else {
			Area.ClrLoaded ();
			victim->SendText ("Your area set to NOT-LOADED!\n\r");
			if (ch != victim)
				ch->SendText ("Area set to NON-LOADED!\n\r");
			return;
		}
	}

	if (! str_cmp (arg2, "pkill")) {
		if (victim->IsNpc ()) {
			ch->SendText ("Player Characters only.\n\r");
			return;
		}
		if (! can_mmodify (ch, victim)) {          
			ch->SendText ("You can't do that.\n\r");
			return;
		}

		if (victim->IsPkiller ()) {
			victim->ClrPkiller ();
			victim->SendText ("You are now a NON-PKILL player.\n\r");
			if (ch != victim)
				ch->SendText ("That player is now non-pkill.\n\r");
			return;
		} else {
			victim->SetPkiller ();
			victim->SendText ("You are now a PKILL player.\n\r");
			if (ch != victim)
				ch->SendText ("That player is now pkill.\n\r");
			return;
		}
	}

	if (! str_cmp (arg2, "speaks")) {
		if (! can_mmodify (ch, victim))
			return;
		if (! argument || argument [0] == '\0') {
			ch->SendText (
				"Usage: mset <victim> speaks <language> [language] ...\n\r");
			return;
		}

		bool bLangFound = FALSE;
		while (argument [0] != '\0') {
			argument = one_argument (argument, arg3);
			CLanguage	&La = *LanguageTable.Find (arg3);
			if (! &La) {
				ch->SendTextf ("Unknown language: %s\n\r", arg3);
				continue;
			}
			else if (! victim->IsNpc ()) {
				if (! La.CanLearn ()) {
					ch->SendTextf ("Players may not know %s.\n\r", La.GetName ());
					continue;
				}    		
			}
			victim->SetSpeaks (La.GetLanguage ());
			bLangFound = TRUE;
		}

		if (bLangFound) {
			if (! victim->IsNpc ()) {
				// Again, don't see why racial language not in speaks vector,
				// Removed clearing of the bits - Rustry
//				REMOVE_BIT (victim->speaks,
//					RaceTable.GetLanguages (victim->GetRace ()));
				if (! knows_language (victim, victim->GetSpeaking (), victim))
					victim->SetSpeaksFlags (RaceTable.GetLanguages (
						victim->GetRace ()));
			}
			else
				if (victim->IsPrototype ())
					victim->GetMobIndex ()->SetSpeaksFlags (
						victim->GetSpeaksFlags ());
			ch->SendText ("Done.\n\r");
		}
		return;
	}

	if (! str_cmp (arg2, "speaking")) {
		if (! victim->IsNpc ()) {
			ch->SendText ("Players must choose the language they speak "
				"themselves.\n\r");
			return;
		}
		if (! can_mmodify (ch, victim))
			return;

		if (! argument || argument [0] == '\0') {
			ch->SendText ("Usage: mset <victim> speaking <language>\n\r");
			return;
		}

		argument = one_argument (argument, arg3);
		CLanguage	&La = *LanguageTable.Find (arg3);
		if (&La) {
			victim->SetSpeaking (La.GetLanguage ());

			if (victim->IsNpc () && victim->IsPrototype ())
				victim->GetMobIndex ()->SetSpeaking (victim->GetSpeaking ());
			ch->SendText ("Done.\n\r");
		}
		else
			ch->SendTextf ("Unknown language: %s\n\r", arg3);

		return;
	}

	// Generate usage message.
	if (ch->GetSubstate () == SUB_REPEATCMD) {
		ch->SetSubstate (SUB_RESTRICTED);
		interpret (ch, origarg);
		ch->SetSubstate (SUB_REPEATCMD);
		ch->last_cmd = do_mset;
	}
	else do_mset (ch, "");
}


void do_oset (CCharacter *ch, char *argument)
{
	char			arg1 [MAX_INPUT_LENGTH];
	char			arg2 [MAX_INPUT_LENGTH];
	char			arg3 [MAX_INPUT_LENGTH];
	char			buf  [MAX_STRING_LENGTH];
	char			outbuf  [MAX_STRING_LENGTH];
	CObjData		*obj, *tmpobj;
	CExtraDescrData	*ed;
	BOOL			lockobj;
	char			*origarg = argument;

	int value, tmp;

	if (ch->IsNpc ()) {
		ch->SendText ("Mob's can't oset\n\r");
		return;    
	}

	if (! ch->GetDesc ()) {
		ch->SendText ("You have no descriptor\n\r");
		return;
	}

	switch (ch->GetSubstate ()) {
	  default:
		break;

	  case SUB_OBJ_EXTRA:
		if (! ch->dest_buf) {
			ch->SendTextf ("Fatal error: report to %s.\n\r",
				SysData.GetSupremeEntity ());
			bug ("do_oset: sub_obj_extra: NULL ch->dest_buf");
			ch->SetSubstate (SUB_NONE);
			return;
		}
		// hopefully the object didn't get extracted...
		// if you're REALLY paranoid, you could always go through
		// the object and index-object lists, searching through the
		// extra_descr lists for a matching pointer...
		ed = (CExtraDescrData *) ch->dest_buf;
		STRFREE (ed->description);
		ed->description = ch->GetEditBuffer ();
		tmpobj = (CObjData*) ch->spare_ptr;
		ch->StopEditing ();
		ch->dest_buf = tmpobj;
		ch->SetSubstate (ch->tempnum);
		return;

	  case SUB_OBJ_LONG:
		if (! ch->dest_buf) {
			ch->SendTextf ("Fatal error: report to %s.\n\r",
				SysData.GetSupremeEntity ());
			bug ("do_oset: sub_obj_long: NULL ch->dest_buf");
			ch->SetSubstate (SUB_NONE);
			return;
		}
		obj = (CObjData*) ch->dest_buf;
		if (obj && obj_extracted (obj)) {
			ch->SendText ("Your object was extracted!\n\r");
			ch->StopEditing ();
			return;
		}
		obj->SetDescriptionNA (ch->GetEditBuffer ());

		if (obj->IsPrototype () && can_omodify (ch, obj))
			obj->pIndexData->SetDescription (obj->GetDescription ());

		tmpobj = (CObjData*) ch->spare_ptr;
		ch->StopEditing ();
		ch->SetSubstate (ch->tempnum);
		ch->dest_buf = tmpobj;
		return;
	}

	obj = NULL;
	smash_tilde (argument);

	if (ch->GetSubstate () == SUB_REPEATCMD) {
		obj = (CObjData*) ch->dest_buf;
		if (obj && obj_extracted (obj)) {
			ch->SendText ("Your object was extracted!\n\r");
			obj = NULL;
			argument = "done";
		}
		if (argument [0] == '\0' || !str_cmp (argument, " ")
		  || ! str_cmp (argument, "stat")) {
			if (obj)
				do_ostat (ch, NCCP obj->GetName ());
			else
				ch->SendText ("No object selected.  Type '?' for help.\n\r");
			return;
		}
		if (! str_cmp (argument, "done") || !str_cmp (argument, "off")) {
			ch->SendText ("Oset mode off.\n\r");
			ch->SetSubstate (SUB_NONE);
			ch->dest_buf = NULL;
			if (ch->GetPcData () && ch->GetPcData ()->HasSubPrompt ()) {
				STRFREE (ch->GetPcData ()->GetSubPrompt ());
				ch->GetPcData ()->SetSubPrompt (NULL);
			}
			return;
		}
	}

	if (obj) {
		lockobj = TRUE;
		strcpy (arg1, obj->GetName ());
		argument = one_argument (argument, arg2);
		strcpy (arg3, argument);
	} else {
		lockobj = FALSE;
		argument = one_argument (argument, arg1);
		argument = one_argument (argument, arg2);
		strcpy (arg3, argument);
	}

	if (! str_cmp (arg1, "on")) {
		ch->SendText ("Syntax: oset <object|vnum> on.\n\r");
		return;
	}

	if (arg1 [0] == '\0' || arg2 [0] == '\0' || ! str_cmp (arg1, "?")) {
		if (ch->GetSubstate () == SUB_REPEATCMD) {
			if (obj)
				ch->SendText ("Syntax: <field>  <value>\n\r");
			else
				ch->SendText ("Syntax: <object> <field>  <value>\n\r");
		}
		else
			ch->SendText ("Syntax: oset <object> <field>  <value>\n\r");

		ch->SendText ("\n\r");
		ch->SendText ("Field being one of: anticlass,\n\r");
		ch->SendText ("  flags wear level weight cost timer\n\r");
		ch->SendText ("  name short long ed rmed actiondesc\n\r");
		ch->SendText ("  type value0 value1 value2 value3 value4 value5\n\r");
		ch->SendText ("  affect rmaffect layers\n\r");
		ch->SendText ("For weapons:             For armor:\n\r");
		ch->SendText ("  weapontype condition     ac condition\n\r");
		ch->SendText ("For scrolls, potions and pills:\n\r");
		ch->SendText ("  slevel spell1 spell2 spell3\n\r");
		ch->SendText ("For wands and staves:\n\r");
		ch->SendText ("  slevel spell maxcharges charges\n\r");
		ch->SendText ("For containers:          For levers and switches:\n\r");
		ch->SendText ("  cflags key capacity      tflags\n\r");
		return;
	}

	if (! obj && ch->GetTrustLevel () < LEVEL_GOD) {
		if ((obj = get_obj_here (ch, arg1)) == NULL) {
			ch->SendText ("You can't find that here.\n\r");
			return;
		}
	}

	else if (! obj) {
		if ((obj = get_obj_world (ch, arg1)) == NULL) {
			ch->SendTextf ("There is nothing like that in all the %s.\n\r",
			SysData.GetShortTitle ());
			return;
		}
	}

	if (lockobj)
		ch->dest_buf = obj;
	else
		ch->dest_buf = NULL;

	separate_obj (obj);
	value = atoi (arg3);

	if (! str_cmp (arg2, "on")) {
		ch->SendTextf ("Oset mode on. (Editing '%s' vnum %d).\n\r",
			obj->GetName (), obj->pIndexData->vnum);
		ch->SetSubstate (SUB_REPEATCMD);
		ch->dest_buf = obj;
		if (ch->GetPcData ()) {
			if (ch->GetPcData ()->HasSubPrompt ())
			STRFREE (ch->GetPcData ()->GetSubPrompt ());
			sprintf (buf, "<&COset &W#%d&w> %%i", obj->pIndexData->vnum);
			ch->GetPcData ()->SetSubPrompt (STRALLOC (buf));
		}
		return;
	}

	if (! str_cmp (arg2, "value0") || !str_cmp (arg2, "v0")) {
		if (! can_omodify (ch, obj))
			return;
		obj->value [0] = value;
		if (obj->IsPrototype ())
			obj->pIndexData->value [0] = value;
		return;
	}

	if (! str_cmp (arg2, "value1") || ! str_cmp (arg2, "v1")) {
		if (! can_omodify (ch, obj))
			return;
		obj->value [1] = value;
		if (obj->IsPrototype ())
			obj->pIndexData->value [1] = value;
		return;
	}

	if (! str_cmp (arg2, "value2") || ! str_cmp (arg2, "v2")) {
		if (! can_omodify (ch, obj))
			return;
		obj->value [2] = value;
		if (obj->IsPrototype ()) {
			obj->pIndexData->value [2] = value;
			if (obj->item_type == ITEM_WEAPON && value != 0)
				obj->value [2] =
					obj->pIndexData->value [1] * obj->pIndexData->value [2];
		}
		return;
	}

	if (! str_cmp (arg2, "value3") || ! str_cmp (arg2, "v3")) {
		if (! can_omodify (ch, obj))
			return;
		obj->value [3] = value;
		if (obj->IsPrototype ())
			obj->pIndexData->value [3] = value;
		return;
	}

	if (! str_cmp (arg2, "value4") || ! str_cmp (arg2, "v4")) {
		if (! can_omodify (ch, obj))
			return;
		obj->value [4] = value;
		if (obj->IsPrototype ())
			obj->pIndexData->value [4] = value;
		return;
	}

	if (! str_cmp (arg2, "value5") || ! str_cmp (arg2, "v5")) {
		if (! can_omodify (ch, obj))
			return;
		obj->value [5] = value;
		if (obj->IsPrototype ())
			obj->pIndexData->value [5] = value;
		return;
	}

	if (! str_cmp (arg2, "type")) {
		if (! can_omodify (ch, obj))
			return;
		if (! argument || argument [0] == '\0') {
			ch->SendText ("Usage: oset <object> type <type>\n\r");
			return;
		}
		value = get_otype (argument);
		if (value < 1) {
			ch->SendTextf ("Unknown type: %s\n\r", arg3);
			return;	
		}
		obj->item_type = (short) value;
		if (obj->IsPrototype ())
			obj->pIndexData->item_type = obj->item_type; 
		return;	
	}

	if (! str_cmp (arg2, "anticlass")) {
		if (! can_omodify (ch, obj))
			return;
		if (! argument || argument [0] == '\0') {
			ch->SendText ("Usage: oset <object> anticlass <classname> [classname]...\n\r");
			return;
		}

		while (argument [0] != '\0') {
			argument = one_argument (argument, arg3);
			CClassData	*pClass = ClassTable.Find (arg3);
			if (! pClass)
				ch->SendTextf ("Unknown class: %s\n\r", arg3);
			else obj->ToggleAntiClass (pClass->GetClass ());
		}
		if (obj->IsPrototype ())
			obj->pIndexData->SetAntiClassFlags (obj->GetAntiClassFlags ());
		return; 
	}

	if (! str_cmp (arg2, "flags")) {
		if (! can_omodify (ch, obj))
			return;
		if (! argument || argument [0] == '\0') {
			ch->SendText ("Usage: oset <object> flags <flag> [flag]...\n\r");
			return;
		}

		while (argument [0] != '\0') {
			argument = one_argument (argument, arg3);
			value = get_oflag (arg3);
			if (value < 0)
				ch->SendTextf ("Unknown flag: %s\n\r", arg3);
			else {
				if (value == ITEM_PROTOTYPE
				  && ch->GetTrustLevel () < LEVEL_GREATER 
				  && !is_name ("protoflag", ch->GetPcData ()->GetBestowments ()))
					ch->SendText ("You cannot change the prototype flag.\n\r");
				else {
					obj->m_ExtraFlags.ToggleBit (value);
					if (value == ITEM_PROTOTYPE)
						obj->pIndexData->m_ExtraFlags = obj->m_ExtraFlags;
				}
			}
		}

		if (obj->IsPrototype ())
			obj->pIndexData->m_ExtraFlags = obj->m_ExtraFlags; 
		return;
	}

	if (! str_cmp (arg2, "wear")) {
		if (! can_omodify (ch, obj))
			return;
		if (! argument || argument [0] == '\0') {
			ch->SendText ("Usage: oset <object> wear <flag> [flag]...\n\r");
			return;
		}
		while (argument [0] != '\0') {
			argument = one_argument (argument, arg3);
			value = get_wflag (arg3);
			if (value < 0)
				ch->SendTextf ("Unknown flag: %s\n\r", arg3);
			else
				TOGGLE_BIT (obj->wear_flags, 1 << value);
		}

		if (obj->IsPrototype ())
			obj->pIndexData->wear_flags = obj->wear_flags;
		return;
	}

	if (! str_cmp (arg2, "level")) {
		if (can_omodify (ch, obj)) {
			obj->level = value;
			if (obj->IsPrototype ())				// Added 8/30/98 RCP
				obj->pIndexData->level = value;
		}
		return;
	}

	if (! str_cmp (arg2, "weight")) {
		if (can_omodify (ch, obj)) {
			obj->weight = value;
			if (obj->IsPrototype ())
				obj->pIndexData->weight = value;
		}
		return;
	}

	if (! str_cmp (arg2, "cost")) {
		if (can_omodify (ch, obj)) {
			obj->cost = value;
			if (obj->IsPrototype ())
				obj->pIndexData->cost = value;
		}
		return;
	}

	if (! str_cmp (arg2, "layers")) {
		if (can_omodify (ch, obj)) {
			if (obj->IsPrototype ())
				obj->pIndexData->layers = value;
			else
				ch->SendText (
					"Item must have prototype flag to set this value.\n\r");
		}
		return;
	}

	if (! str_cmp (arg2, "timer")) {
		if (can_omodify (ch, obj))
			obj->timer = value;
		return;
	}

	if (! str_cmp (arg2, "name")) {
		if (can_omodify (ch, obj)) {
			obj->SetName (arg3);
			if (obj->IsPrototype ())
				obj->pIndexData->SetName (arg3);
		}
		return;
	}

	if (! str_cmp (arg2, "short")) {
		obj->SetShortDescr (arg3);
		if (obj->IsPrototype ())
			obj->pIndexData->SetShortDescr (arg3);
		else {
			// Feature added by Narn, Apr/96 
			// If the item is not proto, add the word 'rename' to the keywords
			// if it is not already there.
			if (str_infix ("rename", obj->GetName ())) {
				sprintf (buf, "%s %s", obj->GetName (), "rename");
				obj->SetName (buf);
			}
		}
		return;
	}

	if (! str_cmp (arg2, "actiondesc")) {
		if (strstr (arg3, "%n") || strstr (arg3, "%d")
		  || strstr (arg3, "%l")) {
			ch->SendText ("Illegal characters!\n\r");
			return;
		}
		obj->SetActionDescr (arg3);

		if (obj->IsPrototype ())
			obj->pIndexData->SetActionDescr (arg3);
		return;
	}

	if (! str_cmp (arg2, "long")) {
		if (arg3 [0]) {
			obj->SetDescription (arg3);

			if (obj->IsPrototype ())
				obj->pIndexData->SetDescription (arg3);
			return;
		}
		ch->CheckSubrestricted ();
		if (ch->GetSubstate () == SUB_REPEATCMD)
			ch->tempnum = SUB_REPEATCMD;
		else
			ch->tempnum = SUB_NONE;

		if (lockobj)
			ch->spare_ptr = obj;
		else
			ch->spare_ptr = NULL;

		ch->SetSubstate (SUB_OBJ_LONG);
		ch->dest_buf = obj;
		start_editing (ch, obj->GetDescription ());
		return;
	}

	if (! str_cmp (arg2, "affect")) {
		CAffectData	*paf;
		short		loc;
		int			bitv;

		argument = one_argument (argument, arg2);
		if (! arg2 || arg2 [0] == '\0' || ! argument || argument [0] == 0) {
			ch->SendText ("Usage: oset <object> affect <field> <value>\n\r");
			return;
		}

		loc = get_atype (arg2);
		if (loc < 1) {
			ch->SendTextf ("Unknown field: %s\n\r", arg2);
			return;	
		}

		if (loc >= APPLY_AFFECT && loc < APPLY_WEAPONSPELL) {
			bitv = 0;
			while (argument [0] != '\0') {
				argument = one_argument (argument, arg3);
				if (loc == APPLY_AFFECT)
					value = get_aflag (arg3);
				else
					value = get_risflag (arg3);

				if (value < 0)
					ch->SendTextf ("Unknown flag: %s\n\r", arg3);
				else
					SET_BIT (bitv, 1 << value);
			}
			if (! bitv)
				return;
			value = bitv;
		} else {
			argument = one_argument (argument, arg3);
			value = atoi (arg3);
		}

		paf = new CAffectData;
		paf->location = loc;
		paf->modifier = value;

		if (obj->IsPrototype ())
			obj->pIndexData->AddAffect (paf);
		else
			obj->AddAffect (paf);

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

	if (! str_cmp (arg2, "rmaffect")) {
		if (! argument || argument [0] == '\0') {
			ch->SendText ("Usage: oset <object> rmaffect <affect#>\n\r");
			return;
		}

		int		loc = atoi (argument);
		if (loc < 1) {
			ch->SendText ("Invalid number.\n\r");
			return;	
		}

		// get the affect list from the index or the object
		
		CAffectList	*pList = &obj->pIndexData->AffList;
		if (loc > pList->GetCount ()) {
			loc -= pList->GetCount ();
			pList = &obj->AffList;
		}

		POSITION	apos = pList->FindIndex (loc-1);

		if (apos) {
			delete pList->GetAt (apos);
			pList->RemoveAt (apos);
			--top_affect;
			ch->SendText ("Removed.\n\r");
		}
		else ch->SendText ("Not found.\n\r");
		return;
	}

	if (! str_cmp (arg2, "ed")) {
		if (! arg3 || arg3 [0] == '\0') {
			ch->SendText ("Syntax: oset <object> ed <keywords>\n\r");
			return;
		}

		if (ch->CheckSubrestricted ())
			return;
		if (obj->timer) {
			ch->SendText ("It's not safe to edit an extra description on "
				"an object with a timer.\n\rTurn it off first.\n\r");
			return;
		}

		if (obj->item_type == ITEM_PAPER) {
			ch->SendText ("You can not add an extra description to a "
				"note paper at the moment.\n\r");
			return;
		}

		if (obj->IsPrototype ())
			ed = SetOExtraProto (obj->pIndexData, arg3);
		else
			ed = SetOExtra (obj, arg3);

		if (ch->GetSubstate () == SUB_REPEATCMD)
			ch->tempnum = SUB_REPEATCMD;
		else
			ch->tempnum = SUB_NONE;

		if (lockobj)
			ch->spare_ptr = obj;
		else
			ch->spare_ptr = NULL;

		ch->SetSubstate (SUB_OBJ_EXTRA);
		ch->dest_buf = ed;
		start_editing (ch, ed->description);
		return;
	}

	if (! str_cmp (arg2, "rmed")) {
		if (! arg3 || arg3 [0] == '\0') {
			ch->SendText ("Syntax: oset <object> rmed <keywords>\n\r");
			return;
		}

		if (obj->IsPrototype ()) {
			if (DelOExtraProto (obj->pIndexData, arg3))
				ch->SendText ("Deleted.\n\r");
			else
				ch->SendText ("Not found.\n\r");
			return;
		}
		if (DelOExtra (obj, arg3))
			ch->SendText ("Deleted.\n\r");
		else
			ch->SendText ("Not found.\n\r");
		return;
	}

	// save some finger-leather
	if (! str_cmp (arg2, "ris")) {
		sprintf (outbuf, "%s affect resistant %s", arg1, arg3);
		do_oset (ch, outbuf);
		sprintf (outbuf, "%s affect immune %s", arg1, arg3);
		do_oset (ch, outbuf);
		sprintf (outbuf, "%s affect susceptible %s", arg1, arg3);
		do_oset (ch, outbuf);
		return;
	}

	if (! str_cmp (arg2, "r")) {
		sprintf (outbuf, "%s affect resistant %s", arg1, arg3);
		do_oset (ch, outbuf);
		return;
	}

	if (! str_cmp (arg2, "i")) {
		sprintf (outbuf, "%s affect immune %s", arg1, arg3);
		do_oset (ch, outbuf);
		return;
	}

	if (! str_cmp (arg2, "s")) {
		sprintf (outbuf, "%s affect susceptible %s", arg1, arg3);
		do_oset (ch, outbuf);
		return;
	}

	if (! str_cmp (arg2, "ri")) {
		sprintf (outbuf, "%s affect resistant %s", arg1, arg3);
		do_oset (ch, outbuf);
		sprintf (outbuf, "%s affect immune %s", arg1, arg3);
		do_oset (ch, outbuf);
		return;
	}

	if (! str_cmp (arg2, "rs")) {
		sprintf (outbuf, "%s affect resistant %s", arg1, arg3);
		do_oset (ch, outbuf);
		sprintf (outbuf, "%s affect susceptible %s", arg1, arg3);
		do_oset (ch, outbuf);
		return;
	}

	if (! str_cmp (arg2, "is")) {
		sprintf (outbuf, "%s affect immune %s", arg1, arg3);
		do_oset (ch, outbuf);
		sprintf (outbuf, "%s affect susceptible %s", arg1, arg3);
		do_oset (ch, outbuf);
		return;
	}

	// Make it easier to set special object values by name than number
	// -Thoric
	tmp = -1;
	switch (obj->item_type) {
	  case ITEM_WEAPON:
		if (!str_cmp (arg2, "weapontype")) {
			int		x;

			value = -1;
			for (x = 0; x < DIM (attack_table); ++x)
				if (! str_cmp (arg3, attack_table [x]))
					value = x;

			if (value < 0) {
				ch->SendText ("Unknown weapon type.\n\r");
				return;
			}
			tmp = 3;
			break;
		}

		if (! str_cmp (arg2, "condition")) then tmp = 0;
		break;

	  case ITEM_ARMOR:
		if (! str_cmp (arg2, "condition")) then tmp = 3;
		if (!str_cmp (arg2, "ac")) then tmp = 1;
		break;

	  case ITEM_SALVE:
		if (!str_cmp (arg2, "slevel"  )) then tmp = 0;
		if (!str_cmp (arg2, "maxdoses")) then tmp = 1;
		if (!str_cmp (arg2, "doses"   )) then tmp = 2;
		if (!str_cmp (arg2, "delay"   )) then tmp = 3;
		if (!str_cmp (arg2, "spell1"  )) then tmp = 4;
		if (!str_cmp (arg2, "spell2"  )) then tmp = 5;
		if (tmp >=4 && tmp <= 5) then value = SkillTable.Lookup (arg3);
		break;

	  case ITEM_SCROLL:
	  case ITEM_POTION:
	  case ITEM_PILL:
		if (!str_cmp (arg2, "slevel")) then tmp = 0;
		if (!str_cmp (arg2, "spell1")) then tmp = 1;
		if (!str_cmp (arg2, "spell2")) then tmp = 2;
		if (!str_cmp (arg2, "spell3")) then tmp = 3;
		if (tmp >=1 && tmp <= 3) then value = SkillTable.Lookup (arg3);
		break;

	  case ITEM_STAFF:
	  case ITEM_WAND:
		if (! str_cmp (arg2, "slevel")) then tmp = 0;
		if (! str_cmp (arg2, "spell")) {
			tmp = 3;
			value = SkillTable.Lookup (arg3);
		}
		if (! str_cmp (arg2, "maxcharges")) then tmp = 1;
		if (! str_cmp (arg2, "charges")) then tmp = 2;
		break;

	  case ITEM_CONTAINER:
		if (! str_cmp (arg2, "capacity")) then tmp = 0;
		if (! str_cmp (arg2, "cflags")) then tmp = 1;
		if (! str_cmp (arg2, "key")) then tmp = 2;
		break;

		case ITEM_SWITCH:
		case ITEM_LEVER:
		case ITEM_PULLCHAIN:
		case ITEM_BUTTON:
		if (! str_cmp (arg2, "tflags")) {
			tmp = 0;
			value = get_trigflag (arg3);
		}
		break;
	}
	if (tmp >= 0 && tmp <= 3) {
		if (! can_omodify (ch, obj))
			return;
		obj->value [tmp] = value;
		if (obj->IsPrototype ())
			obj->pIndexData->value [tmp] = value;
		return;
	}

	// Generate usage message.
	if (ch->GetSubstate () == SUB_REPEATCMD) {
		ch->SetSubstate (SUB_RESTRICTED);
		interpret (ch, origarg);
		ch->SetSubstate (SUB_REPEATCMD);
		ch->last_cmd = do_oset;
	}
	else
		do_oset (ch, "");
}


// Obsolete Merc room editing routine
void do_rset (CCharacter *ch, char *argument)
{
	char			arg1 [MAX_INPUT_LENGTH];
	char			arg2 [MAX_INPUT_LENGTH];
	char			arg3 [MAX_INPUT_LENGTH];
	CRoomIndexData	*location;
	int				value;
	BOOL			proto;

	smash_tilde (argument);
	argument = one_argument (argument, arg1);
	argument = one_argument (argument, arg2);
	strcpy (arg3, argument);

	if (arg1[0] == '\0' || arg2[0] == '\0' || arg3[0] == '\0') {
		ch->SendText ("Syntax: rset <location> <field> value\n\r");
		ch->SendText ("\n\r");
		ch->SendText ("Field being one of:\n\r");
		ch->SendText ("  flags sector\n\r");
		return;
	}

	if ((location = find_location (ch, arg1)) == NULL) {
		ch->SendText ("No such location.\n\r");
		return;
	}

	if (!can_rmodify (ch, location))
		return;

	if (!is_number (arg3)) {
		ch->SendText ("Value must be numeric.\n\r");
		return;
	}
	value = atoi (arg3);

	// Set something.
	if (!str_cmp (arg2, "flags")) {
		// Protect from messing up prototype flag
		proto = location->IsPrototype ();

		location->SetRoomFlags (value);
		if (proto)
			location->SetPrototype ();
		return;
	}

	if (!str_cmp (arg2, "sector")) {
		location->sector_type	= value;
		return;
	}

	// Generate usage message.
	do_rset (ch, "");
}


// Returns value 0 - 9 based on directional text.
int get_dir (char *txt)
{
	int		edir;
	char	c1, c2;

	if (! str_cmp (txt, "northeast"))
		return DIR_NORTHEAST;
	if (! str_cmp (txt, "northwest"))
		return DIR_NORTHWEST;
	if (! str_cmp (txt, "southeast"))
		return DIR_SOUTHEAST;
	if (! str_cmp (txt, "southwest"))
		return DIR_SOUTHWEST;
	if (! str_cmp (txt, "somewhere"))
		return 10;

	c1 = txt [0];
	if (c1 == '\0')
		return 0;

	c2 = txt [1];
	edir = 0;
	switch (c1) {
	  case 'n':
		switch (c2) {
		  default:
			edir = 0; break;	// north
		  case 'e':
			edir = 6; break;	// ne
		  case 'w':
			edir = 7; break;	// nw
		}
		break;
	  case '0':
		edir = 0; break;		// north
	  case 'e':
	  case '1':
		edir = 1; break;		// east
	  case 's':
		switch (c2) {
		  default:
			edir = 2; break;	// south
		  case 'e':
			edir = 8; break;	// se
		  case 'w':
			edir = 9; break;	// sw
		}
		break;
	  case '2':
		edir = 2; break;		// south
	  case 'w':
	  case '3':
		edir = 3; break;		// west
	  case 'u':
	  case '4':
		edir = 4; break;		// up
	  case 'd':
	  case '5':
		edir = 5; break;		// down
	  case '6':
		edir = 6; break;		// ne
	  case '7':
		edir = 7; break;		// nw
	  case '8':
		edir = 8; break;		// se
	  case '9':
		edir = 9; break;		// sw
	  case '?':
		edir = 10; break;		// somewhere
	}
	return edir;
}


void do_redit (CCharacter *ch, char *argument)
{
	char			arg [MAX_INPUT_LENGTH];
	char			arg2[MAX_INPUT_LENGTH];
	char			arg3[MAX_INPUT_LENGTH];
	char			buf [MAX_STRING_LENGTH];
	CRoomIndexData	*location, *tmp;
	CExtraDescrData	*ed;
	char			dir;
	CExitData		*xit, *texit;
	int				value;
	int				edir, ekey, evnum;
	char			*origarg = argument;

	set_char_color (AT_PLAIN, ch);

	switch (ch->GetSubstate ()) {
	  default:
		break;
	  case SUB_ROOM_DESC:
		location = (CRoomIndexData*) ch->dest_buf;
		if (! location) {
			bug ("redit: sub_room_desc: NULL ch->dest_buf");
			location = ch->GetInRoom ();
		}

		location->SetDescriptionNA (ch->GetEditBuffer ());
		ch->StopEditing ();
		ch->SetSubstate (ch->tempnum);
		return;

	  case SUB_ROOM_EXTRA:
		ed = (CExtraDescrData *) ch->dest_buf;
		if (! ed) {
			bug ("redit: sub_room_extra: NULL ch->dest_buf");
			ch->StopEditing ();
			return;
		}

		STRFREE (ed->description);
		ed->description = ch->GetEditBuffer ();
		ch->StopEditing ();
		ch->SetSubstate (ch->tempnum);
		return;
	}

	location = ch->GetInRoom ();

	smash_tilde (argument);
	argument = one_argument (argument, arg);
	if (ch->GetSubstate () == SUB_REPEATCMD) {
		if (arg [0] == '\0') {
			do_rstat (ch, "");
			return;
		}
		if (! str_cmp (arg, "done") || ! str_cmp (arg, "off")) {
			ch->SendText ("Redit mode off.\n\r");
			if (ch->GetPcData () && ch->GetPcData ()->HasSubPrompt ()) {
				STRFREE (ch->GetPcData ()->GetSubPrompt ());
				ch->GetPcData ()->SetSubPrompt (NULL);
			}
			ch->SetSubstate (SUB_NONE);
			return;
		}
	}

	if (arg [0] == '\0' || ! str_cmp (arg, "?")) {
		if (ch->GetSubstate () == SUB_REPEATCMD)
			ch->SendText ("Syntax: <field> value\n\r");
		else
			ch->SendText ("Syntax: redit <field> value\n\r");

		ch->SendText ("\n\r");
		ch->SendText ("Field being one of:\n\r");
		ch->SendText ("  name desc ed rmed\n\r");
		ch->SendText ("  exit bexit exdesc exflags exname exkey\n\r");
		ch->SendText ("  flags sector teledelay televnum tunnel\n\r");
		ch->SendText ("  rlist exdistance\n\r");
		return;
	}

	if (! can_rmodify (ch, location))
		return;

	if (! str_cmp (arg, "on")) {
		ch->SendText ("Redit mode on.\n\r");
		ch->SetSubstate (SUB_REPEATCMD);
		if (ch->GetPcData ()) {
			if (ch->GetPcData ()->HasSubPrompt ())
			STRFREE (ch->GetPcData ()->GetSubPrompt ());
			ch->GetPcData ()->SetSubPrompt (STRALLOC ("<&CRedit &W#%r&w> %i"));
		}
		return;
	}

	if (! str_cmp (arg, "substate")) {
		argument = one_argument (argument, arg2);
		if (! str_cmp (arg2, "north")) {
			ch->SetSubstate (SUB_NORTH); 
			return;
		}
		if (! str_cmp (arg2, "east")) {
			ch->SetSubstate (SUB_EAST); 
			return;
		}
		if (! str_cmp (arg2, "south")) {
			ch->SetSubstate (SUB_SOUTH); 
			return;
		}
		if (! str_cmp (arg2, "west")) {
			ch->SetSubstate (SUB_WEST); 
			return;
		}
		if (! str_cmp (arg2, "up")) {
			ch->SetSubstate (SUB_UP); 
			return;
		}
		if (! str_cmp (arg2, "down")) {
			ch->SetSubstate (SUB_DOWN); 
			return;
		}
		ch->SendText (" unrecognized substate in redit\n\r");
		return;
	}


	if (! str_cmp (arg, "name")) {
		if (argument [0] == '\0') {
			ch->SendText ("Set the room name.  A very brief single line "
				"room description.\n\r");
			ch->SendText ("Usage: redit name <Room summary>\n\r");
			return;
		}
		location->SetName (STRALLOC (argument));
		return;
	}

	if (! str_cmp (arg, "desc")) {
		if (ch->GetSubstate () == SUB_REPEATCMD)
			ch->tempnum = SUB_REPEATCMD;
		else
			ch->tempnum = SUB_NONE;

		ch->SetSubstate (SUB_ROOM_DESC);
		ch->dest_buf = location;
		start_editing (ch, location->GetDescription ());
		return;
	}

	if (! str_cmp (arg, "tunnel")) {
		if (! argument || argument [0] == '\0') {
			ch->SendText ("Set the maximum characters allowed in the "
				"room at one time. (0 = unlimited).\n\r");
			ch->SendText ("Usage: redit tunnel <value>\n\r");
			return;
		}
		location->tunnel = URANGE (0, atoi (argument), 1000);
		ch->SendText ("Done.\n\r");
		return;
	}

	if (! str_cmp (arg, "ed")) {
		if (! argument || argument [0] == '\0') {
			ch->SendText ("Create an extra description.\n\r");
			ch->SendText ("You must supply keyword (s).\n\r");
			return;
		}
		if (ch->CheckSubrestricted ())
			return;
		ed = SetRExtra (location, argument);

		if (ch->GetSubstate () == SUB_REPEATCMD)
			ch->tempnum = SUB_REPEATCMD;
		else
			ch->tempnum = SUB_NONE;

		ch->SetSubstate (SUB_ROOM_EXTRA);
		ch->dest_buf = ed;
		start_editing (ch, ed->description);
		return;
	}

	if (! str_cmp (arg, "rmed")) {
		if (! argument || argument [0] == '\0') {
			ch->SendText ("Remove an extra description.\n\r");
			ch->SendText ("You must supply keyword (s).\n\r");
			return;
		}
		if (DelRExtra (location, argument))
			ch->SendText ("Deleted.\n\r");
		else
			ch->SendText ("Not found.\n\r");
		return;
	}

	if (! str_cmp (arg, "flags")) {
		if (! argument || argument [0] == '\0') {
			ch->SendText ("Toggle the room flags.\n\r");
			ch->SendText ("Usage: redit flags <flag> [flag]...\n\r");
			return;
		}

		while (argument [0] != '\0') {
			argument = one_argument (argument, arg2);
			value = get_rflag (arg2);
			if (value < 0)
				ch->SendTextf ("Unknown flag: %s\n\r", arg2);
			else {
				if (1 << value == ROOM_PROTOTYPE
					&& ch->GetTrustLevel () < LEVEL_GREATER)
						ch->SendText (
							"You cannot change the prototype flag.\n\r");
				else
					location->ToggleRoomFlags (1 << value);
			}
		}
		return;
	}

    if (!str_cmp (arg, "teledelay"))
    {
	if (!argument || argument[0] == '\0')
	{
	   ch->SendText ("Set the delay of the teleport. (0 = off).\n\r");
	   ch->SendText ("Usage: redit teledelay <value>\n\r");
	   return;
	}
	location->tele_delay = atoi (argument);
	ch->SendText ("Done.\n\r");
	return;
    }

    if (!str_cmp (arg, "televnum"))
    {
	if (!argument || argument[0] == '\0')
	{
	   ch->SendText ("Set the vnum of the room to teleport to.\n\r");
	   ch->SendText ("Usage: redit televnum <vnum>\n\r");
	   return;
	}
	location->tele_vnum = atoi (argument);
	ch->SendText ("Done.\n\r");
	return;
    }

    if (!str_cmp (arg, "sector"))
    {
	if (!argument || argument[0] == '\0')
	{
	   ch->SendText ("Set the sector type.\n\r");
	   ch->SendText ("Usage: redit sector <value>\n\r");
	   return;
	}
	location->sector_type = atoi (argument);
	if (location->sector_type < 0 || location->sector_type >= SECT_MAX)
	{
	  location->sector_type = 1;
	  ch->SendText ("Out of range\n\r."); 
	}
	else
	  ch->SendText ("Done.\n\r");
	return;
    }

    if (!str_cmp (arg, "exkey"))
    {
	argument = one_argument (argument, arg2);
	argument = one_argument (argument, arg3);
	if (arg2[0] == '\0' || arg3[0] == '\0')
	{
	   ch->SendText ("Usage: redit exkey <dir> <key vnum>\n\r");
	   return;
	}
	if (arg2[0] == '#')
	{
	   edir = atoi (arg2+1);
	   xit = get_exit_num (location, edir);
	}
	else
	{
	   edir = get_dir (arg2);
	   xit = get_exit (location, edir);
	}
	value = atoi (arg3);
	if (!xit)
	{
	   ch->SendText ("No exit in that direction.  Use 'redit exit ...' first.\n\r");
	   return;
	}
	xit->key = value;
	ch->SendText ("Done.\n\r");
	return;
    }

    if (!str_cmp (arg, "exname"))
    {
	argument = one_argument (argument, arg2);
	if (arg2[0] == '\0')
	{
	   ch->SendText ("Change or clear exit keywords.\n\r");
	   ch->SendText ("Usage: redit exname <dir> [keywords]\n\r");
	   return;
	}
	if (arg2[0] == '#')
	{
	   edir = atoi (arg2+1);
	   xit = get_exit_num (location, edir);
	}
	else
	{
	   edir = get_dir (arg2);
	   xit = get_exit (location, edir);
	}
	if (!xit)
	{
	   ch->SendText ("No exit in that direction.  Use 'redit exit ...' first.\n\r");
	   return;
	}
	STRFREE (xit->keyword);
	xit->keyword = STRALLOC (argument);
	ch->SendText ("Done.\n\r");
	return;
    }

	if (! str_cmp (arg, "exflags")) {
		if (! argument || argument [0] == '\0') {
			ch->SendText ("Toggle or display exit flags.\n\r");
			ch->SendText ("Usage: redit exflags <dir> <flag> [flag]...\n\r");
			return;
		}
		argument = one_argument (argument, arg2);
		if (arg2 [0] == '#') {
			edir = atoi (arg2+1);
			xit = get_exit_num (location, edir);
		} else {
			edir = get_dir (arg2);
			xit = get_exit (location, edir);
		}

		if (! xit) {
			ch->SendText ("No exit in that direction.  Use 'redit "
				"exit ...' first.\n\r");
			return;
		}

		if (argument [0] == '\0') {
			sprintf (buf, "Flags for exit direction: %d  Keywords: %s  "
				"Key: %d\n\r[ ", xit->vdir, xit->keyword, xit->key);

			for (value = 0; value < EX_MAX; ++value) {
				if (xit->IsSet (value)) {
					strcat (buf, ExitTypeNames [value]);
					strcat (buf, " ");
				}
			}
			strcat (buf, "]\n\r");
			ch->SendText (buf);
			return;
		}

		while (argument [0] != '\0') {
			argument = one_argument (argument, arg2);
			value = get_exflag (arg2);
			if (value < 0)
				ch->SendTextf ("Unknown flag: %s\n\r", arg2);
			else
				xit->Toggle (value);
		}
		return;
    }



	if (! str_cmp (arg, "ex_flags")) {
		argument = one_argument (argument, arg2);
		switch (ch->GetSubstate ()) {
		  case SUB_EAST : dir = 'e'; edir = 1; break;
		  case SUB_WEST : dir = 'w'; edir = 3; break;
		  case SUB_SOUTH: dir = 's'; edir = 2; break;
		  case SUB_UP   : dir = 'u'; edir = 4; break;
		  case SUB_DOWN : dir = 'd'; edir = 5; break;
		  default:
		  case SUB_NORTH: dir = 'n'; edir = 0; break;
		}

		value = get_exflag (arg2);
		if (value < 0) {
			ch->SendText ("Bad exit flag. \n\r");
			return;
		}

		if ((xit = get_exit (location, edir)) == NULL) {
			sprintf (buf,"exit %c 1",dir);
			do_redit (ch,buf);
			xit = get_exit (location,edir);
		}
		xit->Toggle (value);
		return;
	}


    if (!str_cmp (arg, "ex_to_room"))
    {
	argument = one_argument (argument, arg2);
        switch (ch->GetSubstate ())
	{
           case SUB_EAST : dir = 'e'; edir = 1; break;
           case SUB_WEST : dir = 'w'; edir = 3; break;
           case SUB_SOUTH: dir = 's'; edir = 2; break;
           case SUB_UP   : dir = 'u'; edir = 4; break;
           case SUB_DOWN : dir = 'd'; edir = 5; break;
	   default:
           case SUB_NORTH: dir = 'n'; edir = 0; break;
	}
	evnum = atoi (arg2);
	if (evnum < 1 || evnum > 2097152000)	// was 32766
	{
	    ch->SendText ("Invalid room number.\n\r");
	    return;
	}
	if ((tmp = RoomTable.GetRoom (evnum)) == NULL)
	{
	    ch->SendText ("Non-existant room.\n\r");
	    return;
	}
	if ((xit = get_exit (location,edir)) == NULL)
	{ 
	   sprintf (buf,"exit %c 1",dir);
	   do_redit (ch,buf);
	   xit = get_exit (location,edir);
	}     
	xit->vnum = evnum;
	return;
    }

    if (!str_cmp (arg, "ex_key"))
    {
	argument = one_argument (argument, arg2);
        switch (ch->GetSubstate ())
	{
           case SUB_EAST : dir = 'e'; edir = 1; break;
           case SUB_WEST : dir = 'w'; edir = 3; break;
           case SUB_SOUTH: dir = 's'; edir = 2; break;
           case SUB_UP   : dir = 'u'; edir = 4; break;
           case SUB_DOWN : dir = 'd'; edir = 5; break;
	   default:
           case SUB_NORTH: dir = 'n'; edir = 0; break;
	}
	if ((xit = get_exit (location,edir)) == NULL)
	{ 
	   sprintf (buf,"exit %c 1",dir);
	   do_redit (ch,buf);
	   xit = get_exit (location,edir);
	}     
	xit->key = atoi (arg2);
	return;
    }

    if (!str_cmp (arg, "ex_exdesc"))  
    {
        switch (ch->GetSubstate ())
	{
           case SUB_EAST : dir = 'e'; edir = 1; break;
           case SUB_WEST : dir = 'w'; edir = 3; break;
           case SUB_SOUTH: dir = 's'; edir = 2; break;
           case SUB_UP   : dir = 'u'; edir = 4; break;
           case SUB_DOWN : dir = 'd'; edir = 5; break;
	   default:
           case SUB_NORTH: dir = 'n'; edir = 0; break;
	}
	if ((xit = get_exit (location, edir)) == NULL)
	{ 
	   sprintf (buf,"exit %c 1",dir);
	   do_redit (ch,buf);
	}     
	sprintf (buf,"exdesc %c %s",dir,argument);
	do_redit (ch,buf);
	return;
    }

    if (!str_cmp (arg, "ex_keywords"))  /* not called yet */
    {
        switch (ch->GetSubstate ())
	{
           case SUB_EAST : dir = 'e'; edir = 1; break;
           case SUB_WEST : dir = 'w'; edir = 3; break;
           case SUB_SOUTH: dir = 's'; edir = 2; break;
           case SUB_UP   : dir = 'u'; edir = 4; break;
           case SUB_DOWN : dir = 'd'; edir = 5; break;
	   default:
           case SUB_NORTH: dir = 'n'; edir = 0; break;
	}
	if ((xit = get_exit (location, edir)) == NULL)
	{ 
	   sprintf (buf, "exit %c 1", dir);
	   do_redit (ch,buf);
	   if ((xit = get_exit (location, edir)) == NULL)
	     return;
	}     
	sprintf (buf, "%s %s", xit->keyword, argument);
	STRFREE (xit->keyword);
	xit->keyword = STRALLOC (buf);
	return;
    }

    if (!str_cmp (arg, "exit"))
    {
	BOOL addexit, numnotdir;

	argument = one_argument (argument, arg2);
	argument = one_argument (argument, arg3);
	if (!arg2 || arg2[0] == '\0')
	{
	    ch->SendText ("Create, change or remove an exit.\n\r");
	    ch->SendText ("Usage: redit exit <dir> [room] [flags] [key] [keywords]\n\r");
	    return;
	}
	addexit = numnotdir = FALSE;
	switch (arg2[0])
	{
	    default:	edir = get_dir (arg2);			  break;
	    case '+':	edir = get_dir (arg2+1);	addexit = TRUE;	  break;
	    case '#':	edir = atoi (arg2+1);	numnotdir = TRUE; break;  
	}
	if (!arg3 || arg3[0] == '\0')
	    evnum = 0;
	else
	    evnum = atoi (arg3);
	if (numnotdir)
	{
	    if ((xit = get_exit_num (location, edir)) != NULL)
	      edir = xit->vdir;
	}
	else
	    xit = get_exit (location, edir);
	if (!evnum)
	{
	    if (xit)
	    {
		extract_exit (location, xit);
		ch->SendText ("Exit removed.\n\r");
		return;
	    }
	    ch->SendText ("No exit in that direction.\n\r");
	    return;
	}
	if (evnum < 1 || evnum > 2097152000)	// was 32766
	{
	    ch->SendText ("Invalid room number.\n\r");
	    return;
	}
	if ((tmp = RoomTable.GetRoom (evnum)) == NULL)
	{
	    ch->SendText ("Non-existant room.\n\r");
	    return;
	}
	if (addexit || ! xit) {
	    if (numnotdir) {
			ch->SendText ("Cannot add an exit by number, sorry.\n\r");
			return;
	    }

	    if (addexit && xit && get_exit_to (location, edir, tmp->vnum)) {
			ch->SendText ("There is already an exit in that direction "
				"leading to that location.\n\r");
			return;
	    }
	    xit = make_exit (location, tmp, edir);
	    xit->keyword = STRALLOC ("");
	    xit->description = STRALLOC ("");
	    xit->key = -1;
	    act (AT_IMMORT, "$n reveals a hidden passage!", ch, NULL, NULL, TO_ROOM);
	}
	else
	    act (AT_IMMORT, "Something is different...", ch, NULL, NULL, TO_ROOM);

	if (xit->GetToRoom () != tmp)
	{
	    xit->SetToRoom (tmp);
	    xit->vnum = evnum;
	    texit = get_exit_to (xit->GetToRoom (), rev_dir[edir], location->vnum);
	    if (texit)
	    {
		texit->rexit = xit;
		xit->rexit = texit;
	    }
	}
	argument = one_argument (argument, arg3);

	// Here we are setting flags by value. This allows the builder to set
	// all the exit flags at once, if he knows the flag values and how to
	// combine them properly.  It would be better to change the code to set
	// the flags individually by name - a project for another day - RCP.
	if (arg3 && arg3 [0] != '\0')
	    xit->SetFlags (atoi (arg3));

	if (argument && argument[0] != '\0') {
	    one_argument (argument, arg3);
	    ekey = atoi (arg3);
	    if (ekey != 0 || arg3[0] == '0') {
			argument = one_argument (argument, arg3);
			xit->key = ekey;
	    }
	    if (argument && argument[0] != '\0') {
			STRFREE (xit->keyword);
			xit->keyword = STRALLOC (argument);
	    }
	}
	ch->SendText ("Done.\n\r");
	return;
    }

    /*
     * Twisted and evil, but works				-Thoric
     * Makes an exit, and the reverse in one shot.
     */
    if (!str_cmp (arg, "bexit"))
    {
	CExitData *xit, *rxit;
	char tmpcmd[MAX_INPUT_LENGTH];
	CRoomIndexData *tmploc;
	int vnum, exnum;
	char rvnum[MAX_INPUT_LENGTH];
	BOOL numnotdir;

	argument = one_argument (argument, arg2);
	argument = one_argument (argument, arg3);
	if (!arg2 || arg2[0] == '\0')
	{
	    ch->SendText ("Create, change or remove a two-way exit.\n\r");
	    ch->SendText ("Usage: redit bexit <dir> [room] [flags] [key] [keywords]\n\r");
	    return;
	}
	numnotdir = FALSE;
	switch (arg2[0])
	{
	    default:
		edir = get_dir (arg2);
		break;
	    case '#':
		numnotdir = TRUE;
		edir = atoi (arg2+1);
		break;
	    case '+':
		edir = get_dir (arg2+1);
		break;
	}
	tmploc = location;
	exnum = edir;
	if (numnotdir)
	{
	    if ((xit = get_exit_num (tmploc, edir)) != NULL)
	      edir = xit->vdir;
	}
	else
	    xit = get_exit (tmploc, edir);
	rxit = NULL;
	vnum = 0;
	rvnum[0] = '\0';
	if (xit)
	{
	    vnum = xit->vnum;
	    if (arg3[0] != '\0')
	      sprintf (rvnum, "%d", tmploc->vnum);
	    if (xit->GetToRoom ())
	      rxit = get_exit (xit->GetToRoom (), rev_dir [edir]);
	    else
	      rxit = NULL;
	}
	sprintf (tmpcmd, "exit %s %s %s", arg2, arg3, argument);
	do_redit (ch, tmpcmd);
	if (numnotdir)
	  xit = get_exit_num (tmploc, exnum);
	else
	  xit = get_exit (tmploc, edir);
	if (!rxit && xit)
	{
	    vnum = xit->vnum;
	    if (arg3[0] != '\0')
	      sprintf (rvnum, "%d", tmploc->vnum);
	    if (xit->GetToRoom ())
	      rxit = get_exit (xit->GetToRoom (), rev_dir[edir]);
	    else
	      rxit = NULL;
	}
	if (vnum)
	{
	    sprintf (tmpcmd, "%d redit exit %d %s %s",
				vnum,
				rev_dir[edir],
				rvnum,
				argument);
	    do_at (ch, tmpcmd);
	}
	return;
    }

    if (!str_cmp (arg, "exdistance"))
    {
	argument = one_argument (argument, arg2);
	if (!arg2 || arg2[0] == '\0')
	{
	   ch->SendText ("Set the distance (in rooms) between this room, and the destination room.\n\r");
	   ch->SendText ("Usage: redit exdistance <dir> [distance]\n\r");
	   return;
	}
	if (arg2[0] == '#')
	{
	   edir = atoi (arg2+1);
	   xit = get_exit_num (location, edir);
	}
	else
	{
	   edir = get_dir (arg2);
	   xit = get_exit (location, edir);
	}
	if (xit)
	{
	   xit->distance = URANGE (1, atoi (argument), 50);
	   ch->SendText ("Done.\n\r");
	   return;
	}
	ch->SendText ("No exit in that direction.  Use 'redit exit ...' first.\n\r");
	return;
    }

    if (!str_cmp (arg, "exdesc"))
    {
	argument = one_argument (argument, arg2);
	if (!arg2 || arg2[0] == '\0')
	{
	   ch->SendText ("Create or clear a description for an exit.\n\r");
	   ch->SendText ("Usage: redit exdesc <dir> [description]\n\r");
	   return;
	}
	if (arg2[0] == '#')
	{
	   edir = atoi (arg2+1);
	   xit = get_exit_num (location, edir);
	}
	else
	{
	   edir = get_dir (arg2);
	   xit = get_exit (location, edir);
	}
	if (xit)
	{
	   STRFREE (xit->description);
	   if (!argument || argument[0] == '\0')
	     xit->description = STRALLOC ("");
	   else
	   {
	     sprintf (buf, "%s\n\r", argument);
	     xit->description = STRALLOC (buf);
	   }
	   ch->SendText ("Done.\n\r");
	   return;
	}
	ch->SendText ("No exit in that direction.  Use 'redit exit ...' first.\n\r");
	return;
    }

    /*
     * Generate usage message.
     */
    if (ch->GetSubstate () == SUB_REPEATCMD)
    {
	ch->SetSubstate (SUB_RESTRICTED);
	interpret (ch, origarg);
	ch->SetSubstate (SUB_REPEATCMD);
	ch->last_cmd = do_redit;
    }
    else
	do_redit (ch, "");
    return;
}


void do_ocreate (CCharacter *ch, char *argument)
{
	char			arg [MAX_INPUT_LENGTH];
	char			arg2 [MAX_INPUT_LENGTH];
	CObjIndexData	*pObjIndex;
	CObjData		*obj;
	int				vnum, cvnum;

	if (ch->IsNpc ()) {
		ch->SendText ("Mobiles cannot create.\n\r");
		return;
	}

	argument = one_argument (argument, arg);

	vnum = is_number (arg) ? atoi (arg) : -1;

	if (vnum == -1 || !argument || argument[0] == '\0') {
		ch->SendText ("Usage: ocreate <vnum> [copy vnum] <item name>\n\r");
		return;
	}

	if (vnum < 1 || vnum > 2097152000) {	// was 32767
		ch->SendText ("Vnum out of range.\n\r");
		return;
	}

	one_argument (argument, arg2);
	cvnum = atoi (arg2);
	if (cvnum != 0)
		argument = one_argument (argument, arg2);
	if (cvnum < 1)
		cvnum = 0;

	if (OIdxTable.GetObj (vnum)) {
		ch->SendText ("An object with that number already exists.\n\r");
		return;
	}    

	if (ch->IsNpc ())
		return;

	if (ch->GetTrustLevel () < LEVEL_LESSER) {
		CAreaData	&Area = *ch->GetArea ();

		if (! &Area) {
			ch->SendText ("You must have an assigned area to create objects.\n\r");
			return;
		}
		if (vnum < Area.low_o_vnum && vnum > Area.hi_o_vnum) {
			ch->SendText ("That number is not in your allocated range.\n\r");
			return;
		}
	}

	pObjIndex = make_object (vnum, cvnum, argument);
	if (! pObjIndex) {
		ch->SendText ("Error.\n\r");
		gpDoc->LogString ("do_ocreate: make_object failed.", LOG_BUG);
		return;
	}

	obj = create_object (pObjIndex, ch->GetTrustLevel ());
	obj_to_char (obj, ch);
	act (AT_IMMORT, "$n makes some ancient arcane gestures, and opens $s hands", ch, NULL, NULL, TO_ROOM);
	act (AT_IMMORT, "to reveal $p!", ch, obj, NULL, TO_ROOM);
    ch->SendColorf ("&YYou make arcane gestures, and open your hands "
		"to reveal %s!\n\rObjVnum:  &W%d   &YKeywords:  &W%s\n\r",
		pObjIndex->GetShortDescr (), pObjIndex->vnum, pObjIndex->GetName ());
}


void do_mcreate (CCharacter *ch, char *argument)
{
	char			arg [MAX_INPUT_LENGTH];
	char			arg2 [MAX_INPUT_LENGTH];
	CMobIndexData	*pMobIndex;
	CCharacter		*mob;
	int				vnum, cvnum;

	if (ch->IsNpc ()) {
		ch->SendText ("Mobiles cannot create.\n\r");
		return;
	}

	argument = one_argument (argument, arg);

	vnum = is_number (arg) ? atoi (arg) : -1;

	if (vnum == -1 || !argument || argument[0] == '\0') {
		ch->SendText ("Usage: mcreate <vnum> [cvnum] <mobile name>\n\r");
		return;
	}

	if (vnum < 1 || vnum > 2097152000) {	// was 32767
		ch->SendText ("Vnum out of range.\n\r");
		return;
	}

	one_argument (argument, arg2);
	cvnum = atoi (arg2);
	if (cvnum != 0)
		argument = one_argument (argument, arg2);
	if (cvnum < 1)
		cvnum = 0;

	if (MobTable.GetMob (vnum)) {
		ch->SendText ("A mobile with that number already exists.\n\r");
		return;
	}    

	if (ch->IsNpc ())
		return;

	if (ch->GetTrustLevel () < LEVEL_LESSER) {
		CAreaData	&Area = *ch->GetArea ();

		if (! &Area) {
			ch->SendText ("You must have an assigned area to create mobiles.\n\r");
			return;
		}
		if (vnum < Area.low_m_vnum && vnum > Area.hi_m_vnum) {
			ch->SendText ("That number is not in your allocated range.\n\r");
			return;
		}
	}

	pMobIndex = make_mobile (vnum, cvnum, argument);
	if (! pMobIndex) {
		ch->SendText ("Error.\n\r");
		gpDoc->LogString ("do_mcreate: make_mobile failed.", LOG_BUG);
		return;
	}
	mob = create_mobile (pMobIndex);
	mob->SendToRoom (ch->GetInRoom ());
	act (AT_IMMORT, "$n waves $s arms about, and $N appears at $s command!", ch, NULL, mob, TO_ROOM);
	ch->SendColorf ("&YYou wave your arms about, and %s appears at your "
		"command!\n\rMobVnum:  &W%d   &YKeywords:  &W%s\n\r",
		pMobIndex->GetShortDescr (), pMobIndex->vnum,
		pMobIndex->GetPlayerName ());
}


void assign_area (CCharacter *ch)
{
	char		buf [MAX_STRING_LENGTH];
	char		taf [1024];
	BOOL		created = FALSE;

	if (ch->IsNpc ())
		return;

	CAreaData	*pArea;

	if (ch->GetTrustLevel () > LEVEL_IMMORTAL
	  && ch->GetPcData ()->r_range_lo
	  && ch->GetPcData ()->r_range_hi) {
		pArea = ch->GetArea ();
		if (! pArea) {
			strcpy (taf, capitalize (ch->GetName ()));
			pArea = BuildList.FindByAuthor (taf);
		}

		if (! pArea) {
			sprintf (buf, "Creating area entry for %s", ch->GetName ());
			gpDoc->LogString (buf, LOG_BUILD, ch->GetLevel ());
			pArea = new CAreaData (ch->GetName (), "", SW_CURRENT_AV);
			BuildList.AddTail (pArea);

			sprintf (buf, "{PROTO} %s's area in progress", ch->GetName ());
			pArea->SetName (buf);
			pArea->m_Author = ch->GetName ();
			pArea->hi_soft_range = MAX_LEVEL;			// Rustry
			pArea->hi_hard_range = MAX_LEVEL;
			created = TRUE;
		} else {
			sprintf (buf, "Updating area entry for %s", ch->GetName ());
			gpDoc->LogString (buf, LOG_BUILD, ch->GetLevel ());
		}
		pArea->low_r_vnum	= ch->GetPcData ()->r_range_lo;
		pArea->low_o_vnum	= ch->GetPcData ()->o_range_lo;
		pArea->low_m_vnum	= ch->GetPcData ()->m_range_lo;
		pArea->hi_r_vnum	= ch->GetPcData ()->r_range_hi;
		pArea->hi_o_vnum	= ch->GetPcData ()->o_range_hi;
		pArea->hi_m_vnum	= ch->GetPcData ()->m_range_hi;
		// If the area has never been saved (no file) then set the loaded
		// flag, so it can be saved.
		if (! FileTable.Exists (FileTable.MakeBuildName (pArea->m_Author)))
			pArea->SetLoaded ();
		ch->GetPcData ()->area	= pArea;
		if (created)
			sort_area (pArea, TRUE);
	}
}


void do_aassign (CCharacter *ch, char *argument)
{
	char	buf [MAX_STRING_LENGTH];

	if (ch->IsNpc ())
		return;

	set_char_color (AT_IMMORT, ch);

	if (argument [0] == '\0') {
		ch->SendText ("Syntax: aassign <filename.are>\n\r");
		return;
	}

	if (! str_cmp ("none", argument) || ! str_cmp ("null", argument)
	  || !str_cmp ("clear", argument)) {
		ch->GetPcData ()->area = NULL;
		assign_area (ch);
		if (! ch->GetArea ())
			ch->SendText ("Area pointer cleared.\n\r");
		else
			ch->SendText ("Originally assigned area restored.\n\r");
		return;
	} 

	CAreaData	*pArea;

	if (ch->GetTrustLevel () >= LEVEL_SUB_IMPLEM
		|| (is_name (buf, ch->GetPcData ()->GetBestowments ())
		&& ch->GetTrustLevel () >= SysData.ModifyProtoLevel))
			pArea = AreaList.FindByName (argument);

	if (! pArea)
		pArea = BuildList.FindByAuthor (argument);

	if (pArea) {
		if (ch->GetTrustLevel () < LEVEL_GREATER
		  && !is_name (pArea->m_Filename,
			ch->GetPcData ()->GetBestowments ())) {  
				ch->SendText ("You do not have permission to use that area.\n\r");
				return;
			}
	}

	if (! pArea) {
		if (ch->GetTrustLevel () >= SysData.ModifyProtoLevel)
			ch->SendText ("No such area.  Use 'zones'.\n\r");
		else
			ch->SendText ("No such area.  Use 'newzones'.\n\r");
		return;
	}
	ch->GetPcData ()->area = pArea;
	ch->SendTextf ("Assigning you: %s\n\r", NCCP pArea->GetName ());
}


CExtraDescrData *SetRExtra (CRoomIndexData *room, char *keywords)
{
	CExtraDescrData *ed = NULL;

	POSITION	pos = room->ExDesList.GetHeadPosition ();
	while (pos) {
		ed = (CExtraDescrData*) room->ExDesList.GetNext (pos);
		if (is_name (keywords, ed->keyword))
			return ed;
	}

	ed = new CExtraDescrData;
	room->ExDesList.AddTail (ed);
	ed->keyword	= STRALLOC (keywords);
	ed->description	= STRALLOC ("");
	++top_ed;

	return ed;
}


BOOL DelRExtra (CRoomIndexData *room, char *keywords)
{
    CExtraDescrData *ed = NULL;
    
	POSITION	CurPos, pos = room->ExDesList.GetHeadPosition ();
	while (pos) {
		CurPos = pos;
		ed = (CExtraDescrData*) room->ExDesList.GetNext (pos);
		if (is_name (keywords, ed->keyword)) {
			room->ExDesList.RemoveAt (CurPos);
			delete ed;
			return TRUE;
		}
	}
    return FALSE;
}


CExtraDescrData *SetOExtra (CObjData *obj, char *keywords)
{
    CExtraDescrData *ed = NULL;
    
	POSITION	pos = obj->ExDesList.GetHeadPosition ();
	while (pos) {
		ed = (CExtraDescrData*) obj->ExDesList.GetNext (pos);
		if (is_name (keywords, ed->keyword))
			return ed;
	}

	ed = new CExtraDescrData;
	obj->ExDesList.AddTail (ed);
	ed->keyword	= STRALLOC (keywords);
	ed->description	= STRALLOC ("");
	++top_ed;
	return ed;
}


BOOL DelOExtra (CObjData *obj, char *keywords)
{
    CExtraDescrData *ed = NULL;
    
	POSITION	CurPos, pos = obj->ExDesList.GetHeadPosition ();
	while (pos) {
		CurPos = pos;
		ed = (CExtraDescrData*) obj->ExDesList.GetNext (pos);
		if (is_name (keywords, ed->keyword)) {
			obj->ExDesList.RemoveAt (CurPos);
			delete ed;
			return TRUE;
		}
	}
	return FALSE;
}


CExtraDescrData *SetOExtraProto (CObjIndexData *obj, char *keywords)
{
    CExtraDescrData *ed = NULL;
    
	POSITION	pos = obj->ExDesList.GetHeadPosition ();
	while (pos) {
		ed = (CExtraDescrData*) obj->ExDesList.GetNext (pos);
		if (is_name (keywords, ed->keyword))
			return ed;
	}

	ed = new CExtraDescrData;
	obj->ExDesList.AddTail (ed);
	ed->keyword	= STRALLOC (keywords);
	ed->description	= STRALLOC ("");
	++top_ed;
	return ed;
}


BOOL DelOExtraProto (CObjIndexData *obj, char *keywords)
{
    CExtraDescrData *ed = NULL;
    
	POSITION	CurPos, pos = obj->ExDesList.GetHeadPosition ();
	while (pos) {
		CurPos = pos;
		ed = (CExtraDescrData*) obj->ExDesList.GetNext (pos);
		if (is_name (keywords, ed->keyword)) {
			obj->ExDesList.RemoveAt (CurPos);
			delete ed;
			return TRUE;
		}
	}
	return FALSE;
}


void fold_area (CAreaData *tarea, int bType /* = FA_LIVE */,
				BOOL bInstall /* = FALSE */)
{
	CMobIndexData	*pMobIndex;
	CObjIndexData	*pObjIndex;
	CShopData		*pShop;
	CRepairShopData	*pRepair;
	FILE			*fpout;

	// If we are saving, and not installing, a build area, then tell
	// MakeAreaName to give us a build name, otherwise a regular area name.
	CString	Aname;
	if (bType == FA_BUILD && ! bInstall)
		Aname = FileTable.MakeAreaName (tarea->m_Author, FA_INSTALL);
	else
		Aname = FileTable.MakeAreaName (tarea->m_Name);

	gpDoc->LogStringf (LOG_BUILD, LEVEL_GREATER, "Writing %s...", NCCP Aname);

	// Make a backup
	CString	Bname = FileTable.MakeBackupName (Aname);
	remove (Bname);
	rename (Aname, Bname);

	fclose (fpReserve);
	if ((fpout = fopen (Aname, "w")) == NULL) {
		bug ("fold_area: fopen");
		perror (Aname);
		fpReserve = fopen (FileTable.GetName (SM_NULL_FILE), "r");
		return;
	}

	fprintf (fpout, "#VERSION   %d\n", SW_CURRENT_AV);
	fprintf (fpout, "#AREA      %s~\n", NCCP tarea->GetName ());
	fprintf (fpout, "#AUTHOR    %s~\n\n", NCCP tarea->m_Author);

	fprintf (fpout, "#RANGES\n");
	fprintf (fpout, "%d %d %d %d\n", tarea->low_soft_range,
	tarea->hi_soft_range,
	tarea->low_hard_range,
	tarea->hi_hard_range);
	fprintf (fpout, "$\n\n");

	if (! tarea->m_Resetmsg.IsEmpty ())					// Rennard
		fprintf (fpout, "#RESETMSG %s~\n\n", NCCP tarea->m_Resetmsg);
	if (tarea->reset_frequency)
		fprintf (fpout, "#FLAGS\n%d %d\n\n",
		tarea->GetFlags (), tarea->reset_frequency);
	else
		fprintf (fpout, "#FLAGS\n%d\n\n", tarea->GetFlags ());

	fprintf (fpout, "#ECONOMY %d %d\n\n", tarea->high_economy,
		tarea->low_economy);


	// save mobiles
	int		vnum;
	fprintf (fpout, "#MOBILES\n");
	for (vnum = tarea->low_m_vnum; vnum <= tarea->hi_m_vnum; vnum++) {
		if ((pMobIndex = MobTable.GetMob (vnum)) == NULL)
			continue;
		if (bInstall)
			pMobIndex->ClrPrototype ();

		pMobIndex->Write (fpout);
	}
	fprintf (fpout, "#0\n\n\n");
	if (bInstall && vnum < tarea->hi_m_vnum)
		tarea->hi_m_vnum = vnum - 1;


	// save objects
	fprintf (fpout, "#OBJECTS\n");
	for (vnum = tarea->low_o_vnum; vnum <= tarea->hi_o_vnum; vnum++) {
		if ((pObjIndex = OIdxTable.GetObj (vnum)) == NULL)
			continue;
		if (bInstall)
			pObjIndex->ClrPrototype ();

		pObjIndex->Write (fpout);
	}
	fprintf (fpout, "#0\n\n\n");
	if (bInstall && vnum < tarea->hi_o_vnum)
		tarea->hi_o_vnum = vnum - 1;


	// save rooms
	CRoomIndexData	*pRoom;
	fprintf (fpout, "#ROOMS\n");
	for (vnum = tarea->low_r_vnum; vnum <= tarea->hi_r_vnum; vnum++) {
		if ((pRoom = RoomTable.GetRoom (vnum)) == NULL)
			continue;
		if (bInstall) {
			CCharacter	*victim, *vnext;
			CObjData	*obj;

			// remove prototype flag from room
			pRoom->ClrPrototype ();
			// purge room of (prototyped) mobiles
			for (victim = pRoom->first_person; victim; victim = vnext) {
				vnext = victim->GetNextInRoom ();
				if (victim->IsNpc ())
					extract_char (victim, TRUE);
			}
			// purge room of (prototyped) objects
			POSITION	Rpos = pRoom->GetHeadContentPos ();
			while (obj = pRoom->GetNextContent (Rpos))
				extract_obj (obj);
		}

		pRoom->Write (fpout);
	}
	fprintf (fpout, "#0\n\n\n");
	if (bInstall && vnum < tarea->hi_r_vnum)
		tarea->hi_r_vnum = vnum - 1;


	// save shops
	fprintf (fpout, "#SHOPS\n");
	for (vnum = tarea->low_m_vnum; vnum <= tarea->hi_m_vnum; vnum++) {
		if ((pMobIndex = MobTable.GetMob (vnum)) == NULL)
			continue;
		if ((pShop = pMobIndex->pShop) == NULL)
			continue;

		fprintf (fpout, " %d   %2d %2d %2d %2d %2d   %3d %3d",
			pShop->keeper, pShop->buy_type [0], pShop->buy_type [1],
			pShop->buy_type [2], pShop->buy_type [3],
			pShop->buy_type [4], pShop->profit_buy, pShop->profit_sell);

		fprintf (fpout, "        %2d %2d    ; %s\n", pShop->open_hour,
			pShop->close_hour, pMobIndex->GetShortDescr ());
	}
	fprintf (fpout, "0\n\n\n");

	// save repair shops
	fprintf (fpout, "#REPAIRS\n");
	for (vnum = tarea->low_m_vnum; vnum <= tarea->hi_m_vnum; vnum++) {
		if ((pMobIndex = MobTable.GetMob (vnum)) == NULL)
			continue;
		if ((pRepair = pMobIndex->rShop) == NULL)
			continue;

		fprintf (fpout, " %d   %2d %2d %2d         %3d %3d", pRepair->keeper,
			pRepair->fix_type [0], pRepair->fix_type[1],
			pRepair->fix_type [2], pRepair->profit_fix, pRepair->shop_type);

		fprintf (fpout, "        %2d %2d    ; %s\n", pRepair->open_hour,
			pRepair->close_hour, pMobIndex->GetShortDescr ());
	}
	fprintf (fpout, "0\n\n\n");

	// save specials
	fprintf (fpout, "#SPECIALS\n");
	for (vnum = tarea->low_m_vnum; vnum <= tarea->hi_m_vnum; vnum++) {
		if ((pMobIndex = MobTable.GetMob (vnum)) == NULL)
			continue;
		if (!pMobIndex->spec_fun)
			continue;
		fprintf (fpout, "M  %d %s\n",	pMobIndex->vnum,
			lookup_spec (pMobIndex->spec_fun));
	}
	fprintf (fpout, "S\n\n\n");

	// END
	fprintf (fpout, "#$\n");
	fclose (fpout);
	fpReserve = fopen (FileTable.GetName (SM_NULL_FILE), "r");
}


void do_savearea (CCharacter *ch, char *argument)
{
	if (ch->IsNpc () || ch->GetTrustLevel () < LEVEL_CREATOR
	  || !ch->GetPcData ()
	  || (argument[0] == '\0' && !ch->GetArea ())) {
		ch->SendText ("You don't have an assigned area to save.\n\r");
		return;
	}

	CAreaData	*pArea;

	if (argument [0]) {
		if (ch->GetTrustLevel () < LEVEL_GOD) {
			ch->SendText ("You can only save your own area.\n\r");
			return;
		}
		pArea = BuildList.FindByAuthor (argument);
		if (! pArea) {
			ch->SendText ("Area not found.\n\r");
			return;
		}
	}
	else pArea = ch->GetArea ();

	if (! pArea) {
		ch->SendText ("No area to save.\n\r");
		return;
	}

	// Ensure not wiping out their area with save before load - Scryn 8/11
	if (! pArea->IsLoaded ()) {
		ch->SendText ("Your area is not loaded!\n\r");
		return;
	}

	fold_area (pArea, FA_BUILD);
	ch->SendText ("Done.\n\r");
}


void do_loadarea (CCharacter *ch, char *argument)
{
	int		tmp;

	if (ch->IsNpc () || ch->GetTrustLevel () < LEVEL_CREATOR
	  || !ch->GetPcData ()
	  || (argument [0] == '\0' && !ch->GetArea ())) {
		ch->SendText ("You don't have an assigned area to load.\n\r");
		return;
	}

	CAreaData	*pArea = NULL;

	if (argument [0]) {
		if (ch->GetTrustLevel () < LEVEL_GOD) {
			ch->SendText ("You can only load your own area.\n\r");
			return;
		}
		pArea = BuildList.FindByAuthor (capitalize (argument));

		if (! pArea) {
			ch->SendText ("Area not found.\n\r");
			return;
		}
	}
	else pArea = ch->GetArea ();

	if (! pArea) {
		ch->SendText ("No area to load.\n\r");
		return;
	}

	// Stops char from loading when already loaded - Scryn 8/11
	if (pArea->IsLoaded ()) { 
		ch->SendText ("Your area is already loaded.\n\r");
		return;
	}
	ch->SendText ("Loading...\n\r");
	LoadAreaFile (pArea->m_Author, NOBOOT, pArea, BUILD);
	
	if (pArea->IsLoaded ()) {
		ch->SendText ("Linking exits...\n\r");
		fix_area_exits (pArea);

		tmp = pArea->nplayer;
		pArea->nplayer = 0;
		ch->SendText ("Resetting area...\n\r");
		pArea->Reset ();
		pArea->nplayer = tmp;

		ch->SendText ("Done.\n\r");
	}
	else
		ch->SendText ("Bad Area file\n\r");
}


// Dangerous command.  Can be used to install an area that was either:
//   (a) already installed but removed from area.lst
//   (b) designed offline
// The mud will likely crash if:
//   (a) this area is already loaded
//   (b) it contains vnums that exist
//   (c) the area has errors
//
// NOTE: Use of this command is not recommended.		-Thoric
void do_unfoldarea (CCharacter *ch, char *argument)
{
}


void do_foldarea (CCharacter *ch, char *argument)
{
	if (!argument || argument [0] == '\0') {
		ch->SendText ("Fold what?\n\r");
		return;
	}

	CAreaData	*pArea = AreaList.FindByName (argument);

	if (pArea) {
		ch->SendText ("Folding...\n\r");
		fold_area (pArea);
		ch->SendText ("Done.\n\r");
		return;
	}
	ch->SendText ("No such area exists.\n\r");
}


void write_area_list ()
{
	FILE	*fp;

	fp = fopen (FileTable.GetName (SM_AREA_LIST), "w");
	if (! fp) {
		bug ("FATAL: cannot open area.lst for writing!\n\r");
		return;
	}	  
	fprintf (fp, "help.are\n");

	POSITION	pos = AreaList.GetHeadPosition ();
	while (pos)
		fprintf (fp, "%s.are\n", NCCP AreaList.GetNext (pos)->GetName ());

	fprintf (fp, "$\n");
	fclose (fp);
}


// A complicated to use command as it currently exists.		-Thoric
// Once area->author and area->name are cleaned up... it will be easier
void do_installarea (CCharacter *ch, char *argument)
{
	char		arg [MAX_INPUT_LENGTH];
	int			num;

	argument = one_argument (argument, arg);
	if (arg [0] == 0) {
		ch->SendText ("Syntax: installarea <Author> <Area Title>\n\r");
		ch->SendText ("        installarea <filename>\n\r");
		return;
	}

	if (argument [0]) {		// install by author
		if (! str_cmp (arg, argument)) {
			ch->SendText ("You cannot name an area after yourself.\n\r");
			return;
		}

		CAreaData	*pArea = BuildList.FindByAuthor (capitalize (arg));
		if (pArea) {
			if (! pArea->IsLoaded ()) { 
				ch->SendText ("The area must be loaded before it can be "
					"installed.\n\r");
				return;
			}
			pArea->SetName (capitalize (argument));
			pArea->m_Filename = pArea->GetName ();

			// Fold area with install flag -- auto-removes prototype flags
			ch->SendText ("Saving and installing file...\n\r");
			fold_area (pArea, FA_BUILD, FA_INSTALL);

			// Remove from prototype area list
			BuildList.Remove (pArea);

			// Add to real area list
			AreaList.AddTail (pArea);

			// Fix up author if online
			POSITION	pos = DList.GetHeadPosition ();
			while (pos) {
				CDescriptor	&Ds = *DList.GetNext (pos);
				if (Ds.IsDisconnecting ())
					continue;

				if (Ds.m_pCharacter && Ds.m_pCharacter->GetPcData ()
				  && Ds.m_pCharacter->GetArea () == pArea) {
					pc_data	&Pc = *Ds.m_pCharacter->GetPcData ();
					// remove area from author
					Pc.area = NULL;
					// clear out author vnums
					Pc.r_range_lo = 0;
					Pc.r_range_hi = 0;
					Pc.o_range_lo = 0;
					Pc.o_range_hi = 0;
					Pc.m_range_lo = 0;
					Pc.m_range_hi = 0;
					break;
				}
			}

			ch->SendText ("Writing area.lst...\n\r");
			write_area_list ();

			ch->SendText ("Resetting new area.\n\r");
			num = pArea->nplayer;
			pArea->nplayer = 0;
			pArea->Reset ();
			pArea->nplayer = num;

			ch->SendText ("Renaming author's building file.\n\r");
			CString	BuildName = FileTable.MakeBuildName (pArea->m_Author);
			CString	Iname = BuildName + ".Installed";
			remove (Iname);
			rename (BuildName, Iname);
			ch->SendText ("Done.\n\r");
			return;
		}

	} else {				// install by filename

		CString	AName = FileTable.MakeAreaName (arg);
		if (FileTable.Exists (AName)) {
			CAreaData	*pArea = NULL;
			TRY pArea = LoadAreaFile (arg, BOOT);
			CATCH (CException, ex) {
				ch->SendText (
					"Fatal Errors in Area File.\n\rUnable to Install.\n\r");
				return;
			}
			END_CATCH

			if (pArea) {
				if (AreaList.FindByName (pArea->m_Name)) {
					ch->SendTextf ("Area %s is already installed.\n\r",
						NCCP pArea->m_Name);
					delete pArea;
					return;
				}
				ch->SendTextf ("Installing %s...\n\r", NCCP pArea->m_Name);
				AreaList.AddTail (pArea);
				sort_area (pArea, FALSE);
				ch->SendText ("Writing area.lst...\n\r");
				write_area_list ();
				ch->SendText ("Fixing Exits...\n\r");
				pArea->FixExits (TRUE);			// True makes it log errors
				ch->SendText ("Resetting new area.\n\r");
				pArea->Reset ();
				ch->SendText ("Done.\n\r");
				return;
			}
		}
	}

	ch->SendText ("No such area exists.\n\r");
}


void do_astat (CCharacter *ch, char *argument)
{
	CAreaData	*pArea = NULL;
	BOOL		bProto = FALSE;
 
	set_char_color (AT_PLAIN, ch);

	if (argument && argument [0] != '\0') {
		pArea = AreaList.FindByName (argument);

		if (! pArea) {
			pArea = BuildList.FindByAuthor (argument);
			if (pArea)
				bProto = TRUE;
		}

		if (! pArea) {
			ch->SendText ("Area not found.  Check 'zones'.\n\r");
			return;
		}
	}

	if (! pArea) {
		pArea = ch->GetInRoom ()->GetArea ();
		bProto = ch->GetInRoom ()->IsPrototype ();
	}

    ch->SendTextf ("Name: %s     Prototype: %s\n\r",
			NCCP pArea->GetName (), bProto ? "yes" : "no");
    if (! bProto) {
		ch->SendTextf ("Max players: %d  IllegalPks: %d  Gold Looted: %d\n\r",
			pArea->max_players, pArea->illegal_pk, pArea->gold_looted);
		if (pArea->high_economy)
			ch->SendTextf ("Area economy: %d billion and %d gold coins.\n\r",
				pArea->high_economy, pArea->low_economy);
		else
			ch->SendTextf ("Area economy: %d gold coins.\n\r",
				pArea->low_economy);
		ch->SendTextf ("Mdeaths: %d  Mkills: %d  Pdeaths: %d  Pkills: %d\n\r",
			pArea->mdeaths, pArea->mkills, pArea->pdeaths, pArea->pkills);
    }

	ch->SendTextf ("Author: %s\n\rAge: %d   Number of players: %d\n\r",
		NCCP pArea->m_Author, pArea->age, pArea->nplayer);

	ch->SendTextf ("Area flags: %s\n\r",
		flag_string (pArea->GetFlags (), area_flags));
	ch->SendTextf ("low_room: %5d  hi_room: %d\n\r", pArea->low_r_vnum,
		pArea->hi_r_vnum);
	ch->SendTextf ("low_obj : %5d  hi_obj : %d\n\r", pArea->low_o_vnum,
		pArea->hi_o_vnum);
	ch->SendTextf ("low_mob : %5d  hi_mob : %d\n\r", pArea->low_m_vnum,
		pArea->hi_m_vnum);
	ch->SendTextf ("soft range: %d - %d.  hard range: %d - %d.\n\r",
		pArea->low_soft_range, pArea->hi_soft_range,
		pArea->low_hard_range, pArea->hi_hard_range);

	ch->SendTextf ("Resetmsg: %s\n\r",
		pArea->m_Resetmsg.IsEmpty () ? "(default)" : NCCP pArea->m_Resetmsg);
	ch->SendTextf ("Reset frequency: %d minutes.\n\r",
		pArea->reset_frequency ? pArea->reset_frequency : 15);
}


void do_aset (CCharacter *ch, char *argument)
{
	char	arg1 [MAX_INPUT_LENGTH];
	char	arg2 [MAX_INPUT_LENGTH];
	char	arg3 [MAX_INPUT_LENGTH];
	int		vnum, value;

	set_char_color (AT_IMMORT, ch);

	argument = one_argument (argument, arg1);
	argument = one_argument (argument, arg2);
	vnum = atoi (argument);

	if (arg1 [0] == '\0' || arg2 [0] == '\0') {
		ch->SendText ("Usage: aset <area name> <field> <value>\n\r");
		ch->SendText ("\n\rField being one of:\n\r");
		ch->SendText ("  low_room hi_room low_obj hi_obj low_mob hi_mob\n\r");
		ch->SendText ("  name low_soft hi_soft low_hard hi_hard\n\r");
		ch->SendText ("  author resetmsg resetfreq flags\n\r");
		ch->SendText ("See HELP ASET for information on changing name.\n\r");
		return;
	}

	BOOL	bProto = FALSE;

	CAreaData	*pArea = AreaList.FindByName (arg1);

	if (! pArea) {
		pArea = BuildList.FindByAuthor (arg1);
		if (pArea)
			bProto = TRUE;
	}

	if (! pArea) {
		ch->SendText ("Area not found.\n\r");
		return;
	}

	if (! str_cmp (arg2, "name")) {
		CString	OldName = FileTable.MakeAreaName (pArea->GetName ());

		if (pArea->GetName () != argument) {
			pArea->SetName (argument);

			if (! bProto) {
				CString	NewName = FileTable.MakeAreaName (argument);
				rename (OldName, NewName);
				fold_area (pArea);
				write_area_list ();
			}
		}
		ch->SendText ("Done.\n\r");
		ch->SendText ("See HELP ASET for information on changing name.\n\r");
		return;
	}

	if (! str_cmp (arg2, "low_economy")) {
		pArea->low_economy = vnum;
		ch->SendText ("Done.\n\r");
		return;
	}

	if (! str_cmp (arg2, "high_economy")) {
		pArea->high_economy = vnum;
		ch->SendText ("Done.\n\r");
		return;
	}

	if (! str_cmp (arg2, "low_room")) {
		pArea->low_r_vnum = vnum;
		ch->SendText ("Done.\n\r");
		return;
	}

	if (! str_cmp (arg2, "hi_room")) {
		pArea->hi_r_vnum = vnum;
		ch->SendText ("Done.\n\r");
		return;
	}

	if (! str_cmp (arg2, "low_obj")) {
		pArea->low_o_vnum = vnum;
		ch->SendText ("Done.\n\r");
		return;
	}

	if (! str_cmp (arg2, "hi_obj")) {
		pArea->hi_o_vnum = vnum;
		ch->SendText ("Done.\n\r");
		return;
	}

	if (! str_cmp (arg2, "low_mob")) {
		pArea->low_m_vnum = vnum;
		ch->SendText ("Done.\n\r");
		return;
	}

	if (! str_cmp (arg2, "hi_mob")) {
		pArea->hi_m_vnum = vnum;
		ch->SendText ("Done.\n\r");
		return;
	}

	if (! str_cmp (arg2, "low_soft")) {
		if (vnum < 0 || vnum > MAX_LEVEL) {
			ch->SendText ("That is not an acceptable value.\n\r");
			return;
		}

		pArea->low_soft_range = vnum;
		ch->SendText ("Done.\n\r");
		return;
	}

	if (! str_cmp (arg2, "hi_soft")) {
		if (vnum < 0 || vnum > MAX_LEVEL) {
			ch->SendText ("That is not an acceptable value.\n\r");
			return;
		}

		pArea->hi_soft_range = vnum;
		ch->SendText ("Done.\n\r");
		return;
	}

	if (! str_cmp (arg2, "low_hard")) {
		if (vnum < 0 || vnum > MAX_LEVEL) {
			ch->SendText ("That is not an acceptable value.\n\r");
			return;
		}

		pArea->low_hard_range = vnum;
		ch->SendText ("Done.\n\r");
		return;
	}

	if (! str_cmp (arg2, "hi_hard")) {
		if (vnum < 0 || vnum > MAX_LEVEL) {
			ch->SendText ("That is not an acceptable value.\n\r");
			return;
		}

		pArea->hi_hard_range = vnum;
		ch->SendText ("Done.\n\r");
		return;
	}

	if (! str_cmp (arg2, "author")) {
		pArea->m_Author = argument;
		ch->SendText ("Done.\n\r");
		return;
	}

	if (! str_cmp (arg2, "resetmsg")) {
		pArea->m_Resetmsg.Empty ();
		if (str_cmp (argument, "clear"))
			pArea->m_Resetmsg = argument;
		ch->SendText ("Done.\n\r");
		return;
	}

	if (! str_cmp (arg2, "resetfreq")) {
		pArea->reset_frequency = vnum;
		ch->SendText ("Done.\n\r");
		return;
	}

	if (! str_cmp (arg2, "flags")) {
		if (! argument || argument [0] == '\0') {
			ch->SendText ("Usage: aset <filename> flags <flag> [flag]...\n\r");
			return;
		}

		while (argument [0] != '\0') {
			argument = one_argument (argument, arg3);
			value = get_areaflag (arg3);
			if (value < 0)
				ch->SendTextf ("Unknown flag: %s\n\r", arg3);
			else {
				if (pArea->TestFlag (1 << value))
					pArea->ClrFlag (1 << value);
				else
					pArea->SetFlag (1 << value);
			}
		}
		return;
	}

	do_aset (ch, "");
}


void do_rlist (CCharacter *ch, char *argument)
{
    CRoomIndexData	*room;
    int			 vnum;
    char arg1[MAX_INPUT_LENGTH];
    char arg2[MAX_INPUT_LENGTH];
    CAreaData		*tarea;
    int lrange;
    int trange;

	set_pager_color (AT_PLAIN, ch);

    if (ch->IsNpc () || ch->GetTrustLevel () < LEVEL_CREATOR || !ch->GetPcData ()
    || (! ch->GetArea () && ch->GetTrustLevel () < LEVEL_GREATER))
    {
	ch->SendText ("You don't have an assigned area.\n\r");
	return;
    }

    tarea = ch->GetArea ();
    argument = one_argument (argument, arg1);
    argument = one_argument (argument, arg2);

    if (tarea)
    {
      if (arg1[0] == '\0')		/* cleaned a big scary mess */
        lrange = tarea->low_r_vnum;	/* here.	    -Thoric */
      else
        lrange = atoi (arg1);
      if (arg2[0] == '\0')
        trange = tarea->hi_r_vnum;
      else
        trange = atoi (arg2);

      if ((lrange < tarea->low_r_vnum || trange > tarea->hi_r_vnum)
  	&& ch->GetTrustLevel () < LEVEL_GREATER)
      {
	ch->SendText ("That is out of your vnum range.\n\r");
	return;
      }
    }
   else
    {
      lrange = (is_number (arg1) ? atoi (arg1) : 1);
      trange = (is_number (arg2) ? atoi (arg2) : 1);
    }

    for (vnum = lrange; vnum <= trange; vnum++)
    {
	if ((room = RoomTable.GetRoom (vnum)) == NULL)
	  continue;
	ch->SendTextf ("%5d) %s\n\r", vnum, room->GetName ());
    }
    return;
}

void do_olist (CCharacter *ch, char *argument)
{
    CObjIndexData	*obj;
    int			 vnum;
    CAreaData		*tarea;
    char arg1[MAX_INPUT_LENGTH];
    char arg2[MAX_INPUT_LENGTH];
    int lrange;
    int trange;

    /*
     * Greater+ can list out of assigned range - Tri (mlist/rlist as well)
     */
    set_pager_color (AT_PLAIN, ch);

    if (ch->IsNpc () || ch->GetTrustLevel () < LEVEL_CREATOR || !ch->GetPcData ()
    || (! ch->GetArea () && ch->GetTrustLevel () < LEVEL_GREATER))
    {
	ch->SendText ("You don't have an assigned area.\n\r");
	return;
    }
    tarea = ch->GetArea ();
    argument = one_argument (argument, arg1);
    argument = one_argument (argument, arg2);

    if (tarea)
    {
      if (arg1[0] == '\0')		/* cleaned a big scary mess */
        lrange = tarea->low_o_vnum;	/* here.	    -Thoric */
      else
        lrange = atoi (arg1);
      if (arg2[0] == '\0')
        trange = tarea->hi_o_vnum;
      else
        trange = atoi (arg2);

      if ((lrange < tarea->low_o_vnum || trange > tarea->hi_o_vnum)
      &&   ch->GetTrustLevel () < LEVEL_GREATER)
      {
	ch->SendText ("That is out of your vnum range.\n\r");
	return;
      }
    }
   else
    {
      lrange = (is_number (arg1) ? atoi (arg1) : 1);
      trange = (is_number (arg2) ? atoi (arg2) : 3);
    }

    for (vnum = lrange; vnum <= trange; vnum++)
    {
	if ((obj = OIdxTable.GetObj (vnum)) == NULL)
	  continue;
	ch->SendTextf ("%5d) %-20s (%s)\n\r", vnum, obj->GetName (),
					     obj->GetShortDescr ());
    }
    return;
}

void do_mlist (CCharacter *ch, char *argument)
{
    CMobIndexData	*mob;
    int			 vnum;
    CAreaData		*tarea;
    char arg1[MAX_INPUT_LENGTH];
    char arg2[MAX_INPUT_LENGTH];
    int lrange;
    int trange;

    set_pager_color (AT_PLAIN, ch);

    if (ch->IsNpc () || ch->GetTrustLevel () < LEVEL_CREATOR || !ch->GetPcData ()
    ||  (! ch->GetArea () && ch->GetTrustLevel () < LEVEL_GREATER))
    {
	ch->SendText ("You don't have an assigned area.\n\r");
	return;
    }

    tarea = ch->GetArea ();
    argument = one_argument (argument, arg1);
    argument = one_argument (argument, arg2);

    if (tarea)
    {
      if (arg1[0] == '\0')		/* cleaned a big scary mess */
        lrange = tarea->low_m_vnum;	/* here.	    -Thoric */
      else
        lrange = atoi (arg1);
      if (arg2[0] == '\0')
        trange = tarea->hi_m_vnum;
      else
        trange = atoi (arg2);

      if ((lrange < tarea->low_m_vnum || trange > tarea->hi_m_vnum)
	&& ch->GetTrustLevel () < LEVEL_GREATER)
      {
  	ch->SendText ("That is out of your vnum range.\n\r");
	return;
      }
    }
    else
    {
      lrange = (is_number (arg1) ? atoi (arg1) : 1);
      trange = (is_number (arg2) ? atoi (arg2) : 1);
    }
    
    for (vnum = lrange; vnum <= trange; vnum++)
    {
	  if ((mob = MobTable.GetMob (vnum)) == NULL)
	    continue;
	  ch->SendTextf ("%5d) %-20s '%s'\n\r", vnum,
					 mob->GetPlayerName (), mob->GetShortDescr ());
    }
}


void mpedit (CCharacter *ch, CMobProgData *mprg, int mptype, char *argument)
{
	if (mptype != -1) {
		mprg->type = mptype;
		if (mprg->arglist)
			STRFREE (mprg->arglist);
		mprg->arglist = STRALLOC (argument);
	}
	ch->SetSubstate (SUB_MPROG_EDIT);
	ch->dest_buf = mprg;
	if (! mprg->comlist)
		mprg->comlist = STRALLOC ("");
	start_editing (ch, mprg->comlist);
}


// Mobprogram editing - cumbersome				-Thoric
void do_mpedit (CCharacter *ch, char *argument)
{
	char		arg1 [MAX_INPUT_LENGTH];
	char		arg2 [MAX_INPUT_LENGTH];
	char		arg3 [MAX_INPUT_LENGTH];
	char		arg4 [MAX_INPUT_LENGTH];
	int			value, mptype;
	CCharacter	*victim;
	CMobProgData *mprog;

	set_char_color (AT_PLAIN, ch);

	if (ch->IsNpc ()) {
		ch->SendText ("Mob's can't mpedit\n\r");
		return;    
	}

	if (! ch->GetDesc ()) {
		ch->SendText ("You have no descriptor\n\r");
		return;
	}

	switch (ch->GetSubstate ()) {
	  default:
		break;
	  case SUB_MPROG_EDIT:
		if (! ch->dest_buf) {
			ch->SendTextf ("Fatal error: report to %s.\n\r",
				SysData.GetSupremeEntity ());
			bug ("do_mpedit: sub_mprog_edit: NULL ch->dest_buf");
			ch->SetSubstate (SUB_NONE);
			return;
		}
		mprog = (CMobProgData*) ch->dest_buf;
		if (mprog->comlist)
			STRFREE (mprog->comlist);
		mprog->comlist = ch->GetEditBuffer ();
		ch->StopEditing ();
		return;
	}

	smash_tilde (argument);
	argument = one_argument (argument, arg1);
	argument = one_argument (argument, arg2);
	argument = one_argument (argument, arg3);
	value = atoi (arg3);

	if (arg1 [0] == '\0' || arg2 [0] == '\0') {
		ch->SendText ("Syntax: mpedit <victim> <command> [number] <program>"
			" <value>\n\r\n\r");
		ch->SendText ("Command being one of:\n\r");
		ch->SendText ("  add delete insert edit list\n\r");
		ch->SendText ("Program being one of:\n\r");
		ch->SendText ("  act speech rand fight hitprcnt greet allgreet\n\r");
		ch->SendText ("  entry give bribe death time hour script\n\r");
		return;
	}

	if (ch->GetTrustLevel () < LEVEL_GOD) {
		if ((victim = get_char_room (ch, arg1)) == NULL) {
			ch->SendText ("They aren't here.\n\r");
			return;
		}
	} else {
		if ((victim = get_char_world (ch, arg1)) == NULL) {
			ch->SendTextf ("No one like that in all the %s.\n\r",
				SysData.GetShortTitle ());
			return;
		}
	}

	if (ch->GetTrustLevel () < victim->GetLevel () || ! victim->IsNpc ()) {
		ch->SendText ("You can't do that!\n\r");
		return;
	}

	if (! can_mmodify (ch, victim))
		return;

	if (! victim->IsAction (ACT_PROTOTYPE)) {
		ch->SendText ("A mobile must have a prototype flag to be mpset.\n\r");
		return;
	}

	set_char_color (AT_GREEN, ch);

	CPtrList	&PList = victim->GetMobIndex ()->MobPrgList;

	if (! str_cmp (arg2, "list")) {
		if (PList.IsEmpty ()) {
			ch->SendText ("That mobile has no mob programs.\n\r");
			return;
		}

		int			cnt = 0;
		POSITION	pos = PList.GetHeadPosition ();
		while (pos) {
			CMobProgData	&Mprg = *(CMobProgData*) PList.GetNext (pos);
			ch->SendTextf ("%d>%s %s\n\r%s\n\r", ++cnt,
				mprog_type_to_name (Mprg.type), Mprg.arglist, Mprg.comlist);
		}
		return;
	}

	if (! str_cmp (arg2, "edit")) {
		if (PList.IsEmpty ()) {
			ch->SendText ("That mobile has no mob programs.\n\r");
			return;
		}

		argument = one_argument (argument, arg4);
		if (arg4 [0] != '\0') {
			mptype = get_mpflag (arg4);
			if (mptype == -1) {
				ch->SendText ("Unknown program type.\n\r");
				return;
			}
		}
		else mptype = -1;

		if (value < 1) {
			ch->SendText ("Program not found.\n\r");
			return;
		}

		// get the position of the prog to edit
		POSITION	pos = PList.FindIndex (--value);	// index is base 0
		if (! pos) {
			ch->SendText ("Program not found.\n\r");
			return;
		}

		CMobProgData	&Mprg = *(CMobProgData*) PList.GetAt (pos);
		mpedit (ch, &Mprg, mptype, argument);
		victim->GetMobIndex ()->m_Progtypes.Empty ();
				
		pos = PList.GetHeadPosition ();
		while (pos) {
			CMobProgData	&Mprg =
				*(CMobProgData*) PList.GetNext (pos);
			victim->GetMobIndex ()->m_Progtypes.SetBit (Mprg.type);
		}
		return;
	}

	if (! str_cmp (arg2, "delete")) {
		if (PList.IsEmpty ()) {
			ch->SendText ("That mobile has no mob programs.\n\r");
			return;
		}

		argument = one_argument (argument, arg4);
		if (value < 1) {
			ch->SendText ("Program not found.\n\r");
			return;
		}

		// Find the position of the prog to delete
		POSITION	pos = PList.FindIndex (--value);	// index is base 0

		if (! pos) {
			ch->SendText ("Program not found.\n\r");
			return;
		}

		// delete and remove it
		CMobProgData	*pMprg = (CMobProgData*) PList.GetAt (pos);
		mptype = pMprg->type;		// save the type
		delete pMprg;
		PList.RemoveAt (pos);

		// now find out if any others have the same type as the removed prog
		BOOL	bRemoveType = TRUE;
		pos = PList.GetHeadPosition ();
		while (pos) {
			CMobProgData	&Mprg = *(CMobProgData*) PList.GetNext (pos);
			if (Mprg.type == mptype) {
				bRemoveType = FALSE;
				break;
			}
		}

		if (bRemoveType)
			victim->GetMobIndex ()->m_Progtypes.ClrBit (mptype);
		ch->SendText ("Program removed.\n\r");
		return;
	}

	if (! str_cmp (arg2, "insert")) {
		if (PList.IsEmpty ()) {
			ch->SendText ("That mobile has no mob programs.\n\r");
			return;
		}

		argument = one_argument (argument, arg4);
		mptype = get_mpflag (arg4);
		if (mptype == -1) {
			ch->SendText ("Unknown program type.\n\r");
			return;
		}

		if (value < 1) {
			ch->SendText ("Program not found.\n\r");
			return;
		}

		// Get position of item to insert before
		POSITION	pos = PList.FindIndex (--value);	// index is base 0

		if (! pos) {
			ch->SendText ("Program not found.\n\r");
			return;
		}

		CMobProgData	*pMprg = new CMobProgData;
		victim->GetMobIndex ()->m_Progtypes.SetBit (mptype);
		mpedit (ch, pMprg, mptype, argument);
		PList.InsertBefore (pos, pMprg);
		return;
	}

	if (! str_cmp (arg2, "add")) {
		mptype = get_mpflag (arg3);
		if (mptype == -1) {
			ch->SendText ("Unknown program type.\n\r");
			return;
		}

		CMobProgData	*pMprg = new CMobProgData;
		PList.AddTail (pMprg);
		victim->GetMobIndex ()->m_Progtypes.SetBit (mptype);
		mpedit (ch, pMprg, mptype, argument);
		return;
	}

	do_mpedit (ch, "");
}


void do_opedit (CCharacter *ch, char *argument)
{
    char arg1 [MAX_INPUT_LENGTH];
    char arg2 [MAX_INPUT_LENGTH];
    char arg3 [MAX_INPUT_LENGTH];
    char arg4 [MAX_INPUT_LENGTH];
    CObjData   *obj;
    CMobProgData *mprog;
    int value, mptype;

    set_char_color (AT_PLAIN, ch);

    if (ch->IsNpc ())
    {
	ch->SendText ("Mob's can't opedit\n\r");
	return;    
    }

    if (!ch->GetDesc ())
    {
	ch->SendText ("You have no descriptor\n\r");
	return;
    }

    switch (ch->GetSubstate ())
    {
	default:
	  break;
	case SUB_MPROG_EDIT:
	  if (!ch->dest_buf)
	  {
		ch->SendTextf ("Fatal error: report to %s.\n\r",
			SysData.GetSupremeEntity ());
		bug ("do_opedit: sub_oprog_edit: NULL ch->dest_buf");
		ch->SetSubstate (SUB_NONE);
		return;
	  }
	  mprog	 = (CMobProgData *) ch->dest_buf;
	  if (mprog->comlist)
	    STRFREE (mprog->comlist);
	  mprog->comlist = ch->GetEditBuffer ();
	  ch->StopEditing ();
	  return;
    }

    smash_tilde (argument);
    argument = one_argument (argument, arg1);
    argument = one_argument (argument, arg2);
    argument = one_argument (argument, arg3);
    value = atoi (arg3);

    if (arg1[0] == '\0' || arg2[0] == '\0')
    {
	ch->SendText ("Syntax: opedit <object> <command> [number] <program> <value>\n\r");
	ch->SendText ("\n\r");
	ch->SendText ("Command being one of:\n\r");
	ch->SendText ("  add delete insert edit list\n\r");
	ch->SendText ("Program being one of:\n\r");
	ch->SendText ("  act speech rand wear remove sac zap get\n\r");
	ch->SendText ("  drop damage repair greet exa use\n\r");
	ch->SendText ("  pull push (for levers,pullchains,buttons)\n\r");
	ch->SendText ("\n\r");
	ch->SendText ("Object should be in your inventory to edit.\n\r");
	return;
    }

    if (ch->GetTrustLevel () < LEVEL_GOD)
    {
      if ((obj = get_obj_carry (ch, arg1)) == NULL)
      {
	ch->SendText ("You aren't carrying that.\n\r");
	return;
      }
    }
    else
    {
      if ((obj = get_obj_world (ch, arg1)) == NULL)
      {
	ch->SendTextf ("Nothing like that in all the %s.\n\r",
		SysData.GetShortTitle ());
	return;
      }
    }

    if (!can_omodify (ch, obj))
	return;

	if (! obj->IsPrototype ()) {
		ch->SendText ("An object must have a prototype flag to be opset.\n\r");
		return;
	}

	set_char_color (AT_GREEN, ch);

	CPtrList	&PList = obj->pIndexData->ObjPrgList;

	if (! str_cmp (arg2, "list")) {
		if (PList.IsEmpty ()) {
			ch->SendText ("That object has no obj programs.\n\r");
			return;
		}

		int		cnt = 0;
		POSITION	pos = PList.GetHeadPosition ();
		while (pos) {
			CMobProgData	&Oprg = *(CMobProgData*) PList.GetNext (pos);
			ch->SendTextf ("%d>%s %s\n\r%s\n\r", ++cnt,
				mprog_type_to_name (Oprg.type), Oprg.arglist, Oprg.comlist);
		}
		return;
	}

	if (! str_cmp (arg2, "edit")) {
		if (PList.IsEmpty ()) {
			ch->SendText ("That object has no obj programs.\n\r");
			return;
		}

		argument = one_argument (argument, arg4);
		if (arg4 [0] != '\0') {
			mptype = get_mpflag (arg4);    
			if (mptype == -1) {
				ch->SendText ("Unknown program type.\n\r");
				return;
			}
		}
		else mptype = -1;

		if (value < 1) {
			ch->SendText ("Program not found.\n\r");
			return;
		}

		// get the position of the prog to edit
		POSITION	pos = PList.FindIndex (--value);	// index is base 0
		if (! pos) {
			ch->SendText ("Program not found.\n\r");
			return;
		}

		CMobProgData	&Oprg = *(CMobProgData*) PList.GetAt (pos);
		mpedit (ch, &Oprg, mptype, argument);
		obj->pIndexData->m_Progtypes.Empty ();

		pos = PList.GetHeadPosition ();
		while (pos) {
			CMobProgData	&Oprg =
				*(CMobProgData*) PList.GetNext (pos);
			obj->pIndexData->m_Progtypes.SetBit (Oprg.type);
		}
		return;
	}

	if (! str_cmp (arg2, "delete")) {
		if (PList.IsEmpty ()) {
			ch->SendText ("That object has no obj programs.\n\r");
			return;
		}

		argument = one_argument (argument, arg4);
		if (value < 1) {
			ch->SendText ("Program not found.\n\r");
			return;
		}

		// Find the position of the prog to delete
		POSITION	pos = PList.FindIndex (--value);	// index is base 0

		if (! pos) {
			ch->SendText ("Program not found.\n\r");
			return;
		}

		// delete and remove it
		CMobProgData	*pOprg = (CMobProgData*) PList.GetAt (pos);
		mptype = pOprg->type;		// save the type
		delete pOprg;
		PList.RemoveAt (pos);

		// now find out if any others have the same type as the removed prog
		BOOL	bRemoveType = TRUE;
		pos = PList.GetHeadPosition ();
		while (pos) {
			CMobProgData	&Oprg = *(CMobProgData*) PList.GetNext (pos);
			if (Oprg.type == mptype) {
				bRemoveType = FALSE;
				break;
			}
		}

		if (bRemoveType)
			obj->pIndexData->m_Progtypes.ClrBit (mptype);
		ch->SendText ("Program removed.\n\r");
		return;
	}

	if (! str_cmp (arg2, "insert")) {
		if (PList.IsEmpty ()) {
			ch->SendText ("That object has no obj programs.\n\r");
			return;
		}

		argument = one_argument (argument, arg4);
		mptype = get_mpflag (arg4);
		if (mptype == -1) {
			ch->SendText ("Unknown program type.\n\r");
			return;
		}

		if (value < 1) {
			ch->SendText ("Program not found.\n\r");
			return;
		}

		// Get position of item to insert before
		POSITION	pos = PList.FindIndex (--value);	// index is base 0

		if (! pos) {
			ch->SendText ("Program not found.\n\r");
			return;
		}

		CMobProgData	*pOprg = new CMobProgData;
		obj->pIndexData->m_Progtypes.SetBit (mptype);
		mpedit (ch, pOprg, mptype, argument);
		PList.InsertBefore (pos, pOprg);
		return;
	}

	if (! str_cmp (arg2, "add")) {
		mptype = get_mpflag (arg3);
		if (mptype == -1) {
			ch->SendText ("Unknown program type.\n\r");
			return;
		}

		CMobProgData	*pOprg = new CMobProgData;
		PList.AddTail (pOprg);
		obj->pIndexData->m_Progtypes.SetBit (mptype);
		mpedit (ch, pOprg, mptype, argument);
		return;
	}

	do_opedit (ch, "");
}



// RoomProg Support
void rpedit (CCharacter *ch, CMobProgData *mprg, int mptype, char *argument)
{
	if (mptype != -1) {
		mprg->type = mptype;
		if (mprg->arglist)
			STRFREE (mprg->arglist);
		mprg->arglist = STRALLOC (argument);
	}
	ch->SetSubstate (SUB_MPROG_EDIT);
	ch->dest_buf = mprg;
	if (! mprg->comlist)
		mprg->comlist = STRALLOC ("");
	start_editing (ch, mprg->comlist);
}


void do_rpedit (CCharacter *ch, char *argument)
{
	char	arg1 [MAX_INPUT_LENGTH];
	char	arg2 [MAX_INPUT_LENGTH];
	char	arg3 [MAX_INPUT_LENGTH];
	int		value, mptype;
	CMobProgData	*mprog;

	set_char_color (AT_PLAIN, ch);

	if (ch->IsNpc ()) {
		ch->SendText ("Mob's can't rpedit\n\r");
		return;    
	}

	if (! ch->GetDesc ()) {
		ch->SendText ("You have no descriptor\n\r");
		return;
	}

	switch (ch->GetSubstate ()) {
	  default:
		break;
	  case SUB_MPROG_EDIT:
		if (! ch->dest_buf) {
			ch->SendTextf ("Fatal error: report to %s.\n\r",
				SysData.GetSupremeEntity ());
			bug ("do_opedit: sub_oprog_edit: NULL ch->dest_buf");
			ch->SetSubstate (SUB_NONE);
			return;
		}
		mprog = (CMobProgData*) ch->dest_buf;
		if (mprog->comlist)
			STRFREE (mprog->comlist);
		mprog->comlist = ch->GetEditBuffer ();
		ch->StopEditing ();
		return;
	}

	smash_tilde (argument);
	argument = one_argument (argument, arg1);
	argument = one_argument (argument, arg2);
	value = atoi (arg2);
	// argument = one_argument (argument, arg3);

	if (arg1 [0] == '\0') {
		ch->SendText ("Syntax: rpedit <command> [number] <program> <value>"
			"\n\r\n\r");
		ch->SendText ("Command being one of:\n\r");
		ch->SendText ("  add delete insert edit list\n\r");
		ch->SendText ("Program being one of:\n\r");
		ch->SendText ("  act speech rand sleep rest rfight enter\n\r");
		ch->SendText ("  leave death\n\r");
		ch->SendText ("\n\r");
		ch->SendText ("You should be standing in room you wish to edit.\n\r");
		return;
	}

	if (! can_rmodify (ch, ch->GetInRoom ()))
		return;

	set_char_color (AT_GREEN, ch);

	CPtrList	&PList = ch->GetInRoom ()->RoomPrgList;

	if (! str_cmp (arg1, "list")) {
		if (PList.IsEmpty ()) {
			ch->SendText ("This room has no room programs.\n\r");
			return;
		}

		int		cnt = 0;
		POSITION	pos = PList.GetHeadPosition ();
		while (pos) {
			CMobProgData	&Rprg = *(CMobProgData*) PList.GetNext (pos);
			ch->SendTextf ("%d>%s %s\n\r%s\n\r", ++cnt,
				mprog_type_to_name (Rprg.type), Rprg.arglist, Rprg.comlist);
		}
		return;
	}

	if (! str_cmp (arg1, "edit")) {
		if (PList.IsEmpty ()) {
			ch->SendText ("This room has no room programs.\n\r");
			return;
		}

		argument = one_argument (argument, arg3);
		if (arg3 [0] != '\0') {
			mptype = get_mpflag (arg3);    
			if (mptype == -1) {
				ch->SendText ("Unknown program type.\n\r");
				return;
			}
		}
		else mptype = -1;

		if (value < 1) {
			ch->SendText ("Program not found.\n\r");
			return;
		}

		// get the position of the prog to edit
		POSITION	pos = PList.FindIndex (--value);	// index is base 0
		if (! pos) {
			ch->SendText ("Program not found.\n\r");
			return;
		}

		CMobProgData	&Rprg = *(CMobProgData*) PList.GetAt (pos);
		mpedit (ch, &Rprg, mptype, argument);
		ch->GetInRoom ()->m_Progtypes.Empty ();

		pos = PList.GetHeadPosition ();
		while (pos) {
			CMobProgData	&Rprg =
				*(CMobProgData*) PList.GetNext (pos);
			ch->GetInRoom ()->m_Progtypes.SetBit (Rprg.type);
		}
		return;
	}

	if (! str_cmp (arg1, "delete")) {
		if (PList.IsEmpty ()) {
			ch->SendText ("That room has no room programs.\n\r");
			return;
		}

		argument = one_argument (argument, arg3);
		if (value < 1) {
			ch->SendText ("Program not found.\n\r");
			return;
		}

		// Find the position of the prog to delete
		POSITION	pos = PList.FindIndex (--value);	// index is base 0

		if (! pos) {
			ch->SendText ("Program not found.\n\r");
			return;
		}

		// delete and remove it
		CMobProgData	*pRprg = (CMobProgData*) PList.GetAt (pos);
		mptype = pRprg->type;		// save the type
		delete pRprg;
		PList.RemoveAt (pos);

		// now find out if any others have the same type as the removed prog
		BOOL	bRemoveType = TRUE;
		pos = PList.GetHeadPosition ();
		while (pos) {
			CMobProgData	&Rprg = *(CMobProgData*) PList.GetNext (pos);
			if (Rprg.type == mptype) {
				bRemoveType = FALSE;
				break;
			}
		}

		if (bRemoveType)
			ch->GetInRoom ()->m_Progtypes.ClrBit (mptype);
		ch->SendText ("Program removed.\n\r");
		return;
	}

	if (! str_cmp (arg2, "insert")) {
		if (PList.IsEmpty ()) {
			ch->SendText ("That room has no room programs.\n\r");
			return;
		}

		argument = one_argument (argument, arg3);
		mptype = get_mpflag (arg2);
		if (mptype == -1) {
			ch->SendText ("Unknown program type.\n\r");
			return;
		}

		if (value < 1) {
			ch->SendText ("Program not found.\n\r");
			return;
		}

		// Get position of item to insert before
		POSITION	pos = PList.FindIndex (--value);	// index is base 0

		if (! pos) {
			ch->SendText ("Program not found.\n\r");
			return;
		}

		CMobProgData	*pRprg = new CMobProgData;
		ch->GetInRoom ()->m_Progtypes.SetBit (mptype);
		mpedit (ch, pRprg, mptype, argument);
		PList.InsertBefore (pos, pRprg);
		return;
	}

	if (! str_cmp (arg1, "add")) {
		mptype = get_mpflag (arg2);
		if (mptype == -1) {
			ch->SendText ("Unknown program type.\n\r");
			return;
		}

		CMobProgData	*pRprg = new CMobProgData;
		PList.AddTail (pRprg);
		ch->GetInRoom ()->m_Progtypes.SetBit (mptype);
		mpedit (ch, pRprg, mptype, argument);
		return;
	}

	do_rpedit (ch, "");
}


void do_rdelete (CCharacter *ch, char *argument)
{
    char arg[MAX_INPUT_LENGTH];
    CRoomIndexData *location;

    argument = one_argument (argument, arg);

    /* Temporarily disable this command. */
    return;

    if (arg[0] == '\0')
    {
	ch->SendText ("Delete which room?\n\r");
	return;
    }

    /* Find the room. */
    if ((location = find_location (ch, arg)) == NULL)
    {
	ch->SendText ("No such location.\n\r");
	return;
    }

    /* Does the player have the right to delete this room? */
    if (ch->GetTrustLevel () < SysData.ModifyProtoLevel
    && (location->vnum < ch->GetPcData ()->r_range_lo || 
         location->vnum > ch->GetPcData ()->r_range_hi))
    {
      ch->SendText ("That room is not in your assigned range.\n\r");
      return;
    }

	// We could go to the trouble of clearing out the room, but why?
	if (location->first_person || location->IsEmpty ()) {
		ch->SendText ("The room must be empty first.\n\r");
		return;
	}

	// Ok, we've determined that the room exists, it is empty and the 
	// player has the authority to delete it, so let's dump the thing. 
	RoomTable.Remove (location);
	delete location;

	ch->SendText ("Room deleted.\n\r");
}


void do_odelete (CCharacter *ch, char *argument)
{
}
void do_mdelete (CCharacter *ch, char *argument)
{
}


void ClearAddrRange (void* start, void* end, UINT SizEnd)
{
	memset (start, 0, (int) ((char*) end + SizEnd - (char*) start));
}