roh/conf.old/area/
roh/config/code/python/
roh/config/game/area/
roh/config/game/signs/
roh/help/dmhelp/
roh/help/help/
roh/log/
roh/log/staff/
roh/monsters/ocean/
roh/objects/misc/
roh/objects/ocean/
roh/player/
roh/rooms/area/1/
roh/rooms/misc/
roh/rooms/ocean/
roh/src-2.47e/
/*
 * dmroom.cpp
 *	 Staff functions related to rooms.
 *   ____            _
 *  |  _ \ ___  __ _| |_ __ ___  ___
 *  | |_) / _ \/ _` | | '_ ` _ \/ __|
 *  |  _ <  __/ (_| | | | | | | \__ \
 *  |_| \_\___|\__,_|_|_| |_| |_|___/
 *
 * Permission to use, modify and distribute is granted via the
 *  Creative Commons - Attribution - Non Commercial - Share Alike 3.0 License
 *    http://creativecommons.org/licenses/by-nc-sa/3.0/
 *
 * 	Copyright (C) 2007-2012 Jason Mitchell, Randi Mitchell
 * 	   Contributions by Tim Callahan, Jonathan Hseu
 *  Based on Mordor (C) Brooke Paul, Brett J. Vickers, John P. Freeman
 *
 */
#include "mud.h"
#include "commands.h"
#include "dm.h"
#include "factions.h"
#include "property.h"
#include "ships.h"
#include "tokenizer.h"
#include "effects.h"
#include "traps.h"

#include <signal.h>
#include <dirent.h>
#include <iomanip>


//*********************************************************************
//							checkTeleportRange
//*********************************************************************

void checkTeleportRange(const Player* player, CatRef cr) {
	// No warning for the test range
	if(cr.isArea("test"))
		return;

	const CatRefInfo* cri = gConfig->getCatRefInfo(cr.area);
	if(!cri) {
		player->printColor("^yNo CatRefInfo zone found for this room's area. Contact a dungeonmaster to fix this.\n");
		return;
	}

	if(cr.id > cri->getTeleportWeight()) {
		player->printColor("^yThis room is outside the CatRefInfo zone's teleport range.\n");
		return;
	}
}


//*********************************************************************
//							isCardinal
//*********************************************************************

bool isCardinal(bstring xname) {
	return(	xname == "north" ||
			xname == "east" ||
			xname == "south" ||
			xname == "west" ||
			xname == "northeast" ||
			xname == "northwest" ||
			xname == "southeast" ||
			xname == "southwest"
	);
}


//*********************************************************************
//							wrapText
//*********************************************************************

bstring wrapText(const bstring& text, int wrap) {
	if(text == "")
		return("");
	
	bstring wrapped = "";
	int		len = text.length(), i=0, sp=0, spLast=0, spLen=0;
	char	ch, chLast;

	// find our starting position
	while(text.at(i) == ' ' || text.at(i) == '\n' || text.at(i) == '\r')
		i++;

	for(; i < len; i++) {

		ch = text.at(i);

		// convert linebreaks to spaces
		if(ch == '\r')
			ch = ' ';
		if(ch == '\n')
			ch = ' ';

		// skiping 2x spacing (or greater)
		if(ch == ' ' && chLast == ' ') {
			do {
				i++;
			} while(i+1 < len && (text.at(i+1) == ' ' || text.at(i+1) == '\n' || text.at(i+1) == '\r'));
			if(i < len)
				ch = text.at(i);
		}


		// don't add trailing spaces
		if(ch != ' ' || i+1 < len) {

			// If there is color in the room description, the color characters
			// shouldn't count toward string length.
			if(ch == '^')
				spLen += 2;

			// wrap
			if(ch == ' ') {
				// We went over! spLast points to the last non-overboard space.
				if(wrap <= (sp - spLen)) {
					wrapped.replace(spLast, 1, "\n");
					spLen = spLast;
				}

				spLast = sp;
			}

			wrapped += ch;
			sp++;
			chLast = ch;
		}
	}
	return(wrapped);
}

//*********************************************************************
//							expand_exit_name
//*********************************************************************

bstring expand_exit_name(const bstring& name) {
	if(name == "n")
		return("north");
	if(name == "s")
		return("south");
	if(name == "e")
		return("east");
	if(name == "w")
		return("west");
	if(name == "sw")
		return("southwest");
	if(name == "nw")
		return("northwest");
	if(name == "se")
		return("southeast");
	if(name == "ne")
		return("northeast");
	if(name == "d")
		return("door");
	if(name == "o")
		return("out");
	if(name == "p")
		return("passage");
	if(name == "t")
		return("trap door");
	if(name == "a")
		return("arch");
	if(name == "g")
		return("gate");
	if(name == "st")
		return("stairs");
	return(name);
}


//*********************************************************************
//							opposite_exit_name
//*********************************************************************

bstring opposite_exit_name(const bstring& name) {
	if(name == "south")
		return("north");
	if(name == "north")
		return("south");
	if(name == "west")
		return("east");
	if(name == "east")
		return("west");
	if(name == "northeast")
		return("southwest");
	if(name == "southeast")
		return("northwest");
	if(name == "northwest")
		return("southeast");
	if(name == "southwest")
		return("northeast");
	if(name == "up")
		return("down");
	if(name == "down")
		return("up");

	return(name);
}




//*********************************************************************
//						dmPurge
//*********************************************************************
// This function allows staff to purge a room of all its objects and
// monsters.

int dmPurge(Player* player, cmd* cmnd) {
	BaseRoom* room = player->getRoomParent();

	if(!player->canBuildMonsters() && !player->canBuildObjects())
		return(cmdNoAuth(player));
	if(!player->checkBuilder(player->getUniqueRoomParent())) {
		player->print("Error: this room is out of your range; you cannot *purge here.\n");
		return(0);
	}

	room->purge(false);

	player->print("Purged.\n");

	if(!player->isDm())
		log_immort(false,player, "%s purged room %s.\n", player->getCName(), player->getRoomParent()->fullName().c_str());

	return(0);
}


//*********************************************************************
//						dmEcho
//*********************************************************************
// This function allows a staff specified by the socket descriptor in
// the first parameter to echo the rest of their command line to all
// the other people in the room.

int dmEcho(Player* player, cmd* cmnd) {
	if(!player->checkBuilder(player->getUniqueRoomParent())) {
		player->print("Error: room number not in any of your allotted ranges.\n");
		return(0);
	}

	bstring text = getFullstrText(cmnd->fullstr, 1);
	if(text == "" || Pueblo::is(text)) {
		player->print("Echo what?\n");
		return(0);
	}

	if(!player->isCt())
		broadcast(isStaff, "^G*** %s (%s) echoed: %s",
			player->getCName(), player->getRoomParent()->fullName().c_str(), text.c_str());

	broadcast(NULL, player->getRoomParent(), "%s", text.c_str());
	return(0);
}

//*********************************************************************
//						dmReloadRoom
//*********************************************************************
// This function allows a staff to reload a room from disk.

int dmReloadRoom(Player* player, cmd* cmnd) {

	if(!player->checkBuilder(player->getUniqueRoomParent())) {
		player->print("Error: this room is out of your range; you cannot reload this room.\n");
		return(0);
	}

	if(gConfig->reloadRoom(player->getRoomParent()))
		player->print("Ok.\n");
	else
		player->print("Reload failed.\n");

	return(0);
}

//*********************************************************************
//						resetPerms
//*********************************************************************
// This function allows a staff to reset perm timeouts in the room

int dmResetPerms(Player* player, cmd* cmnd) {
	std::map<int, crlasttime>::iterator it;
	crlasttime* crtm=0;
	std::map<int, long> tempMonsters;
	std::map<int, long> tempObjects;
	UniqueRoom	*room = player->getUniqueRoomParent();
	//long	temp_obj[10], temp_mon[10];

	if(!needUniqueRoom(player))
		return(0);

	if(!player->checkBuilder(player->getUniqueRoomParent())) {
		player->print("Error: this room is out of your range; you cannot reload this room.\n");
		return(0);
	}


	for(it = room->permMonsters.begin(); it != room->permMonsters.end() ; it++) {
		crtm = &(*it).second;
		tempMonsters[(*it).first] = crtm->interval;
		crtm->ltime = time(0);
		crtm->interval = 0;
	}
	for(it = room->permObjects.begin(); it != room->permObjects.end() ; it++) {
		crtm = &(*it).second;
		tempObjects[(*it).first] = crtm->interval;
		crtm->ltime = time(0);
		crtm->interval = 0;
	}

	player->print("Permanent object and creature timeouts reset.\n");
	room->addPermCrt();

	for(it = room->permMonsters.begin(); it != room->permMonsters.end() ; it++) {
		crtm = &(*it).second;
		crtm->interval = tempMonsters[(*it).first];
		crtm->ltime = time(0);
	}
	for(it = room->permObjects.begin(); it != room->permObjects.end() ; it++) {
		crtm = &(*it).second;
		crtm->interval = tempObjects[(*it).first];
		crtm->ltime = time(0);
	}

	log_immort(true, player, "%s reset perm timeouts in room %s\n", player->getCName(), player->getRoomParent()->fullName().c_str());

	if(gConfig->resaveRoom(room->info) < 0)
		player->print("Room fail saved.\n");
	else
		player->print("Room saved.\n");

	return(0);
}

//*********************************************************************
//						stat_rom_exits
//*********************************************************************
// Display information on room given to staff.

void stat_rom_exits(Creature* player, BaseRoom* room) {
	char	str[1024], temp[25], tempstr[32];
	int		i=0, flagcount=0;
	UniqueRoom*	uRoom = room->getAsUniqueRoom();

	if(room->exits.empty())
		return;

	player->print("Exits:\n");

	for(Exit* exit : room->exits) {

		if(!exit->getLevel())
			player->print("  %s: ", exit->getCName());
		else
			player->print("  %s(L%d): ", exit->getCName(), exit->getLevel());

		if(!exit->target.mapmarker.getArea())
			player->printColor("%s ", exit->target.room.str(uRoom ? uRoom->info.area : "", 'y').c_str());
		else
			player->print(" A:%d X:%d Y:%d Z:%d  ",
				exit->target.mapmarker.getArea(), exit->target.mapmarker.getX(),
				exit->target.mapmarker.getY(), exit->target.mapmarker.getZ());

		*str = 0;
		strcpy(str, "Flags: ");


		for(i=0; i<MAX_EXIT_FLAGS; i++) {
			if(exit->flagIsSet(i)) {
				sprintf(tempstr, "%s(%d), ", get_xflag(i), i+1);
				strcat(str, tempstr);
				flagcount++;
			}
		}

		if(flagcount) {
			str[strlen(str) - 2] = '.';
			str[strlen(str) - 1] = 0;
		}


		if(flagcount)
			player->print("%s", str);


		if(exit->flagIsSet(X_LOCKABLE)) {
			player->print(" Key#: %d ", exit->getKey());
			if(exit->getKeyArea() != "")
				player->printColor(" Area: ^y%s^x ", exit->getKeyArea().c_str());
		}

		if(exit->flagIsSet(X_TOLL_TO_PASS))
			player->print(" Toll: %d ", exit->getToll());

		player->print("\n");

		if(exit->getDescription() != "")
			player->print("    Description: \"%s\"\n", exit->getDescription().c_str());

		if(	(exit->flagIsSet(X_CAN_LOOK) || exit->flagIsSet(X_LOOK_ONLY)) &&
			exit->flagIsSet(X_NO_SCOUT)
		)
			player->printColor("^rExit is flagged as no-scout, but it flagged as lookable.\n");


		if(exit->flagIsSet(X_PLEDGE_ONLY)) {
			for(i=1; i<15; i++)
				if(exit->flagIsSet(i+40)) {
					sprintf(temp, "Clan: %d, ",i);
					strcat(str, temp);
				}
			player->print("    Clan: %s\n", temp);
		}
		if(exit->flagIsSet(X_PORTAL)) {
			player->printColor("    Owner: ^c%s^x  Uses: ^c%d^x\n", exit->getPassPhrase().c_str(), exit->getKey());
		} else if(exit->getPassPhrase() != "") {
			player->print("    Passphrase: \"%s\"\n", exit->getPassPhrase().c_str());
			if(exit->getPassLanguage())
				player->print("    Passlang: %s\n", get_language_adj(exit->getPassLanguage()));
		}
		if(exit->getEnter() != "")
			player->print("    OnEnter: \"%s\"\n", exit->getEnter().c_str());
		if(exit->getOpen() != "")
			player->print("    OnOpen: \"%s\"\n", exit->getOpen().c_str());

		
		if(exit->getSize() || exit->getDirection()) {
			if(exit->getSize())
				player->print("    Size: %s", getSizeName(exit->getSize()).c_str());
			if(exit->getDirection()) {
				player->print("    Direction: %s", getDirName(exit->getDirection()).c_str());
				if(getDir(exit->getName()) != NoDirection)
					player->printColor("\n^rThis exit has a direction set, but the exit is a cardinal exit.");
			}
			player->print("\n");
		}

		if(exit->effects.effectList.size())
			player->printColor("    Effects:\n%s", exit->effects.getEffectsString(player).c_str());
		player->printColor("%s", exit->hooks.display().c_str());
	}
}


//*********************************************************************
//						trainingFlagSet
//*********************************************************************

bool trainingFlagSet(const BaseRoom* room, const TileInfo *tile, const AreaZone *zone, int flag) {
	return(	(room && room->flagIsSet(flag)) ||
			(tile && tile->flagIsSet(flag)) ||
			(zone && zone->flagIsSet(flag))
	);
}

//*********************************************************************
//						whatTraining
//*********************************************************************
// determines what class can train here

int whatTraining(const BaseRoom* room, const TileInfo *tile, const AreaZone *zone, int extra) {
	int i = 0;

	if(R_TRAINING_ROOM - 1 == extra || trainingFlagSet(room, tile, zone, R_TRAINING_ROOM - 1))
		i += 16;
	if(R_TRAINING_ROOM == extra || trainingFlagSet(room, tile, zone, R_TRAINING_ROOM))
		i += 8;
	if(R_TRAINING_ROOM + 1 == extra || trainingFlagSet(room, tile, zone, R_TRAINING_ROOM + 1))
		i += 4;
	if(R_TRAINING_ROOM + 2 == extra || trainingFlagSet(room, tile, zone, R_TRAINING_ROOM + 2))
		i += 2;
	if(R_TRAINING_ROOM + 3 == extra || trainingFlagSet(room, tile, zone, R_TRAINING_ROOM + 3))
		i += 1;

	return(i > CLASS_COUNT - 1 ? 0 : i);
}

int BaseRoom::whatTraining(int extra) const {
	return(::whatTraining(this, (const TileInfo*)0, (const AreaZone*)0, extra));
}


//*********************************************************************
//						showRoomFlags
//*********************************************************************

void showRoomFlags(const Player* player, const BaseRoom* room, const TileInfo *tile, const AreaZone *zone) {
	bool	flags=false;
	int		i=0;
	std::ostringstream oStr;
	oStr << "^@Flags set: ";

	for(; i<MAX_ROOM_FLAGS; i++) {
		if(i >=2 && i <= 6)	// skips training flags
			continue;
		if(	(room && room->flagIsSet(i)) ||
			(tile && tile->flagIsSet(i)) ||
			(zone && zone->flagIsSet(i))
		) {
			if(flags)
				oStr << ", ";
			flags = true;
			oStr << get_rflag(i) << "(" << (int)(i+1) << ")";
		}
	}

	if(!flags)
		oStr << "None";
	oStr << ".^x\n";

	i = whatTraining(room, tile, zone, 0);

	if(i)
		oStr << "^@Training: " << get_class_string(i) << "^x\n";
	player->printColor("%s", oStr.str().c_str());

	// inform user of redundant flags
	if(room && room->getAsConstUniqueRoom()) {
		int whatTraining = room->whatTraining();
		bool limboOrCoven = room->flagIsSet(R_LIMBO) || room->flagIsSet(R_VAMPIRE_COVEN);

		if(room->flagIsSet(R_NO_TELEPORT)) {
			if(	limboOrCoven ||
				room->flagIsSet(R_JAIL) ||
				room->flagIsSet(R_ETHEREAL_PLANE) ||
				whatTraining
			)
				player->printColor("^rThis room does not need flag 13-No Teleport set.\n");
		}

		if(room->flagIsSet(R_NO_SUMMON_OUT)) {
			if(	room->flagIsSet(R_IS_STORAGE_ROOM) ||
				room->flagIsSet(R_LIMBO)
			)
				player->printColor("^rThis room does not need flag 29-No Summon Out set.\n");
		}

		if(room->flagIsSet(R_NO_LOGIN)) {
			if(	room->flagIsSet(R_LOG_INTO_TRAP_ROOM) ||
				whatTraining
			)
				player->printColor("^rThis room does not need flag 34-No Log set.\n");
		}

		if(room->flagIsSet(R_NO_CLAIR_ROOM)) {
			if(limboOrCoven)
				player->printColor("^rThis room does not need flag 43-No Clair set.\n");
		}

		if(room->flagIsSet(R_NO_TRACK_TO)) {
			if(	limboOrCoven ||
				room->flagIsSet(R_IS_STORAGE_ROOM) ||
				room->flagIsSet(R_NO_TELEPORT) ||
				room->whatTraining()
			)
				player->printColor("^rThis room does not need flag 52-No Track To set.\n");
		}

		if(room->flagIsSet(R_NO_SUMMON_TO)) {
			if(	limboOrCoven ||
				room->flagIsSet(R_NO_TELEPORT) ||
				room->flagIsSet(R_ONE_PERSON_ONLY) ||
				whatTraining
			)
				player->printColor("^rThis room does not need flag 54-No Summon To set.\n");
		}

		if(room->flagIsSet(R_NO_TRACK_OUT)) {
			if(	room->flagIsSet(R_LIMBO) ||
				room->flagIsSet(R_ETHEREAL_PLANE)
			)
				player->printColor("^rThis room does not need flag 54-No Summon To set.\n");
		}

		if(room->flagIsSet(R_OUTLAW_SAFE)) {
			if(	limboOrCoven ||
				whatTraining
			)
				player->printColor("^rThis room does not need flag 57-Outlaw Safe set.\n");
		}

		if(room->flagIsSet(R_LOG_INTO_TRAP_ROOM)) {
			if(whatTraining)
				player->printColor("^rThis room does not need flag 63-Log To Trap Exit set.\n");
		}

		if(room->flagIsSet(R_LIMBO)) {
			if(room->flagIsSet(R_POST_OFFICE))
				player->printColor("^rThis room does not need flag 11-Post Office set.\n");
			if(room->flagIsSet(R_FAST_HEAL))
				player->printColor("^rThis room does not need flag 14-Fast Heal set.\n");
			if(room->flagIsSet(R_NO_POTION))
				player->printColor("^rThis room does not need flag 32-No Potion set.\n");
			if(room->flagIsSet(R_NO_CAST_TELEPORT))
				player->printColor("^rThis room does not need flag 38-No Cast Teleport set.\n");
			if(room->flagIsSet(R_NO_FLEE))
				player->printColor("^rThis room does not need flag 44-No Flee set.\n");
			if(room->flagIsSet(R_BANK))
				player->printColor("^rThis room does not need flag 58-Bank set.\n");
			if(room->flagIsSet(R_MAGIC_MONEY_MACHINE))
				player->printColor("^rThis room does not need flag 59-Magic Money Machine set.\n");
		}

	}
}


//*********************************************************************
//						stat_rom
//*********************************************************************

int stat_rom(Player* player, AreaRoom* room) {
	std::list<AreaZone*>::iterator it;
	AreaZone* zone=0;
	TileInfo* tile=0;

	if(!player->checkBuilder(0))
		return(0);

	if(player->getClass() == CARETAKER)
		log_immort(false,player, "%s statted room %s.\n", player->getCName(), player->getRoomParent()->fullName().c_str());

	player->print("Room: %s %s\n\n",
		room->area->name.c_str(), room->fullName().c_str());
	tile = room->area->getTile(room->area->getTerrain(0, &room->mapmarker, 0, 0, 0, true), false);

	for(it = room->area->zones.begin() ; it != room->area->zones.end() ; it++) {
		zone = (*it);
		if(zone->inside(room->area, &room->mapmarker)) {
			player->printColor("^yZone:^x %s\n", zone->name.c_str());
			if(zone->wander.getTraffic()) {
				zone->wander.show(player);
			} else {
				player->print("    No monsters come in this zone.\n");
			}
			player->print("\n");
		}
	}

	if(room->getSize())
		player->printColor("Size: ^y%s\n", getSizeName(room->getSize()).c_str());
	
	if(tile->wander.getTraffic())
		tile->wander.show(player);

	player->print("Terrain: %c\n", tile->getDisplay());
	if(room->isWater())
		player->printColor("Water: ^gyes\n");
	if(room->isRoad())
		player->printColor("Road: ^gyes\n");

	player->printColor("Generic Room: %s\n", room->canSave() ? "^rNo" : "^gYes");
	if(room->unique.id) {
		player->printColor("Links to unique room ^y%s^x.\n", room->unique.str().c_str());
		player->printColor("needsCompass: %s^x    decCompass: %s",
			room->getNeedsCompass() ? "^gYes" : "^rNo", room->getDecCompass() ? "^gYes" : "^rNo");
	}
	player->print("\n");

	showRoomFlags(player, room, 0, 0);
	if(room->effects.effectList.size())
		player->printColor("Effects:\n%s", room->effects.getEffectsString(player).c_str());
	player->printColor("%s", room->hooks.display().c_str());
	stat_rom_exits(player, room);
	return(0);
}

//*********************************************************************
//						validateShop
//*********************************************************************

void validateShop(const Player* player, const UniqueRoom* shop, const UniqueRoom* storage) {
	// basic checks
	if(!shop) {
		player->printColor("^rThe shop associated with this storage room does not exist.\n");
		return;
	}
	if(!storage) {
		player->printColor("^rThe storage room associated with this shop does not exist.\n");
		return;
	}

	if(shop->info == storage->info) {
		player->printColor("^rThe shop and the storage room cannot be the same room. Set the shop's trap exit appropriately.\n");
		return;
	}

	CatRef cr = shopStorageRoom(shop);

	if(shop->info == cr) {
		player->printColor("^rThe shop and the storage room cannot be the same room. Set the shop's trap exit appropriately.\n");
		return;
	}

	bstring name = "Storage: ";
	name += shop->getName();

	if(cr != storage->info)
		player->printColor("^rThe shop's storage room of %s does not match the storage room %s.\n", cr.str().c_str(), storage->info.str().c_str());
	if(storage->getTrapExit() != shop->info)
		player->printColor("^yThe storage room's trap exit of %s does not match the shop room %s.\n", storage->info.str().c_str(), shop->info.str().c_str());

	if(!shop->flagIsSet(R_SHOP))
		player->printColor("^rThe shop's flag 1-Shoppe is not set.\n");
	if(!storage->flagIsSet(R_SHOP_STORAGE))
		player->printColor("^rThe storage room's flag 97-Shop Storage is not set.\n");

	// what DOESN'T the storage room need?
	if(storage->flagIsSet(R_NO_LOGIN))
		player->printColor("^rThe storage room does not need flag 34-No Log set.\n");
	if(storage->flagIsSet(R_LOG_INTO_TRAP_ROOM))
		player->printColor("^rThe storage room does not need flag 63-Log To Trap Exit set.\n");
	if(storage->flagIsSet(R_NO_TELEPORT))
		player->printColor("^rThe storage room does not need flag 13-No Teleport set.\n");
	if(storage->flagIsSet(R_NO_SUMMON_TO))
		player->printColor("^rThe storage room does not need flag 54-No Summon To set.\n");
	if(storage->flagIsSet(R_NO_TRACK_TO))
		player->printColor("^rThe storage room does not need flag 52-No Track To set.\n");
	if(storage->flagIsSet(R_NO_CLAIR_ROOM))
		player->printColor("^rThe storage room does not need flag 43-No Clair set.\n");

	if(storage->exits.empty()) {
		player->printColor("^yThe storage room does not have an out exit pointing to the shop.\n");
	} else {
		const Exit* exit = storage->exits.front();
		
		if(	exit->target.room != shop->info || exit->getName() != "out")
			player->printColor("^yThe storage room does not have an out exit pointing to the shop.\n");
		else if(storage->exits.size() > 1)
			player->printColor("^yThe storage room has more than one exit - it only needs one out exit pointing to the shop.\n");
	}
}

//*********************************************************************
//						stat_rom
//*********************************************************************

int stat_rom(Player* player, UniqueRoom* room) {
	std::map<int, crlasttime>::iterator it;
	crlasttime* crtm=0;
	CatRef	cr;
	Monster* monster=0;
	Object* object=0;
	UniqueRoom* shop=0;
	time_t t = time(0);

	if(!player->checkBuilder(room))
		return(0);

	if(player->getClass() == CARETAKER)
		log_immort(false,player, "%s statted room %s.\n", player->getCName(), player->getRoomParent()->fullName().c_str());

	player->printColor("Room: %s", room->info.str("", 'y').c_str());
	if(gConfig->inSwapQueue(room->info, SwapRoom, true))
		player->printColor("        ^eThis room is being swapped.");

	player->print("\nTimes People have entered this room: %d\n", room->getBeenHere());
	player->print("Name: %s\n", room->getCName());

	Property *p = gConfig->getProperty(room->info);
	if(p) {
		player->printColor("Property Belongs To: ^y%s^x\nProperty Type: ^y%s\n",
			p->getOwner().c_str(), p->getTypeStr().c_str());
	}

	if(player->isCt()) {
		if(room->last_mod[0])
			player->printColor("^cLast modified by: %s on %s\n", room->last_mod, stripLineFeeds(room->lastModTime));
		if(room->lastPly[0])
			player->printColor("^cLast player here: %s on %s\n", room->lastPly, stripLineFeeds(room->lastPlyTime));
	} else
		player->print("\n");

	if(room->getSize())
		player->printColor("Size: ^y%s\n", getSizeName(room->getSize()).c_str());
	if(room->getRoomExperience())
		player->print("Experience for entering this room: %d\n", room->getRoomExperience());

	if(room->getFaction() != "")
		player->printColor("Faction: ^g%s^x\n", room->getFaction().c_str());
	if(room->getFishingStr() != "")
		player->printColor("Fishing: ^g%s^x\n", room->getFishingStr().c_str());

	if(room->getMaxMobs() > 0)
		player->print("Max mob allowance: %d\n", room->getMaxMobs());

	room->wander.show(player, room->info.area);
	player->print("\n");

	player->print("Perm Objects:\n");
	for(it = room->permObjects.begin(); it != room->permObjects.end() ; it++) {
		crtm = &(*it).second;
		loadObject((*it).second.cr, &object);

		player->printColor("^y%2d) ^x%14s ^y::^x %-30s ^yInterval:^x %-5d  ^yTime Until Spawn:^x %-5d", (*it).first+1,
			crtm->cr.str("", 'y').c_str(), object ? object->getCName() : "", crtm->interval, tMAX<long>(0, crtm->ltime + crtm->interval-t));

		if(room->flagIsSet(R_SHOP_STORAGE) && object)
			player->printColor(" ^yCost:^x %s", object->value.str().c_str());

		player->print("\n");

		// warning about deeds in improper areas
		if(object && object->deed.low.id && !object->deed.isArea(room->info.area))
			player->printColor("      ^YCaution:^x this object's deed area does not match the room's area.\n");

		if(object) {
			delete object;
			object = 0;
		}
	}
	player->print("\n");

	player->print("Perm Monsters:\n");
	for(it = room->permMonsters.begin(); it != room->permMonsters.end() ; it++) {
		crtm = &(*it).second;
		loadMonster((*it).second.cr, &monster);

		player->printColor("^m%2d) ^x%14s ^m::^x %-30s ^mInterval:^x %d  ^yTime until Spawn:^x %-5d\n", (*it).first+1,
			crtm->cr.str("", 'm').c_str(), monster ? monster->getCName() : "", crtm->interval, tMAX<long>(0, crtm->ltime + crtm->interval-t));

		if(monster) {
			free_crt(monster);
			monster = 0;
		}
	}
	player->print("\n");

	if(room->track.getDirection() != "" && room->flagIsSet(R_PERMENANT_TRACKS))
		player->print("Perm Tracks: %s.\n", room->track.getDirection().c_str());

	if(room->getLowLevel() || room->getHighLevel()) {
		player->print("Level Boundary: ");
		if(room->getLowLevel())
			player->print("%d+ level  ", room->getLowLevel());
		if(room->getHighLevel())
			player->print("%d- level  ", room->getHighLevel());
		player->print("\n");
	}

	if(	room->flagIsSet(R_LOG_INTO_TRAP_ROOM) ||
		room->flagIsSet(R_SHOP_STORAGE) ||
		room->whatTraining()
	) {
		if(room->getTrapExit().id)
			player->print("Players will relog into room %s from here.\n", room->getTrapExit().str(room->info.area).c_str());
		else
			player->printColor("^rTrap exit needs to be set to %s room number.\n", room->flagIsSet(R_SHOP_STORAGE) ? "shop" : "relog");
	}

	if(	room->getSize() == NO_SIZE && (
			room->flagIsSet(R_INDOORS) ||
			room->flagIsSet(R_UNDERGROUND)
		)
	)
		player->printColor("^yThis room does not have a size set.\n");

	checkTeleportRange(player, room->info);

	// isShopValid
	if(room->flagIsSet(R_SHOP)) {
		cr = shopStorageRoom(room);
		player->print("Shop storage room: %s (%s)\n", cr.str().c_str(),
			cr.id == room->info.id+1 && cr.isArea(room->info.area) ? "default" : "trapexit");

		if(room->getFaction() == "" && room->info.area != "shop")
			player->printColor("^yThis shop does not have a faction set.\n");

		loadRoom(cr, &shop);
		validateShop(player, room, shop);
	} else if(room->flagIsSet(R_PAWN_SHOP)) {
		if(room->getFaction() == "")
			player->printColor("^yThis pawn shop does not have a faction set.\n");
	}

	if(room->flagIsSet(R_SHOP_STORAGE)) {
		loadRoom(room->getTrapExit(), &shop);
		validateShop(player, shop, room);
	}

	if(room->getTrap()) {
		if(room->getTrapWeight())
			player->print("Trap weight: %d/%d lbs\n", room->getWeight(), room->getTrapWeight());
		player->print("Trap type: ");
		switch(room->getTrap()) {
		case TRAP_PIT:
			player->print("Pit Trap (exit rm %s)\n", room->getTrapExit().str(room->info.area).c_str());
			break;
		case TRAP_DART:
			player->print("Poison Dart Trap\n");
			break;
		case TRAP_BLOCK:
			player->print("Falling Block Trap\n");
			break;
		case TRAP_MPDAM:
			player->print("MP Damage Trap\n");
			break;
		case TRAP_RMSPL:
			player->print("Negate Spell Trap\n");
			break;
		case TRAP_NAKED:
			player->print("Naked Trap\n");
			break;
		case TRAP_TPORT:
			player->print("Teleport Trap\n");
			break;
		case TRAP_ARROW:
			player->print("Arrow Trap\n");
			break;
		case TRAP_SPIKED_PIT:
			player->print("Spiked Pit Trap (exit rm %s)\n", room->getTrapExit().str(room->info.area).c_str());
			break;
		case TRAP_WORD:
			player->print("Word of Recall Trap\n");
			break;
		case TRAP_FIRE:
			player->print("Fire Trap\n");
			break;
		case TRAP_FROST:
			player->print("Frost Trap\n");
			break;
		case TRAP_ELEC:
			player->print("Electricity Trap\n");
			break;
		case TRAP_ACID:
			player->print("Acid Trap\n");
			break;
		case TRAP_ROCKS:
			player->print("Rockslide Trap\n");
			break;
		case TRAP_ICE:
			player->print("Icicle Trap\n");
			break;
		case TRAP_SPEAR:
			player->print("Spear Trap\n");
			break;
		case TRAP_CROSSBOW:
			player->print("Crossbow Trap\n");
			break;
		case TRAP_GASP:
			player->print("Poison Gas Trap\n");
			break;
		case TRAP_GASB:
			player->print("Blinding Gas Trap\n");
			break;
		case TRAP_GASS:
			player->print("Stun Gas Trap\n");
			break;
		case TRAP_MUD:
			player->print("Mud Trap\n");
			break;
		case TRAP_DISP:
			player->print("Room Displacement Trap (exit rm %s)\n", room->getTrapExit().str(room->info.area).c_str());
			break;
		case TRAP_FALL:
			player->print("Deadly Fall Trap (exit rm %s)\n", room->getTrapExit().str(room->info.area).c_str());
			break;
		case TRAP_CHUTE:
			player->print("Chute Trap (exit rm %s)\n", room->getTrapExit().str(room->info.area).c_str());
			break;
		case TRAP_ALARM:
			player->print("Alarm Trap (guard rm %s)\n", room->getTrapExit().str(room->info.area).c_str());
			break;
		case TRAP_BONEAV:
			player->print("Bone Avalanche Trap (exit rm %s)\n", room->getTrapExit().str(room->info.area).c_str());
			break;
		case TRAP_PIERCER:
			player->print("Piercer trap (%d piercers)\n", room->getTrapStrength());
			break;
		case TRAP_ETHEREAL_TRAVEL:
			player->print("Ethereal travel trap.\n");
			break;
		case TRAP_WEB:
			player->print("Sticky spider web trap.\n");
			break;
		default:
			player->print("Invalid trap #\n");
			break;
		}
	}

	if(room->flagIsSet(R_CAN_SHOPLIFT))
		player->print("Store guardroom: rm %s\n", cr.str(room->info.area).c_str());

	showRoomFlags(player, room, 0, 0);


	if(room->effects.effectList.size())
		player->printColor("Effects:\n%s", room->effects.getEffectsString(player).c_str());
	player->printColor("%s", room->hooks.display().c_str());
	stat_rom_exits(player, room);
	return(0);
}

//*********************************************************************
//						dmAddRoom
//*********************************************************************
// This function allows staff to add a new, empty room to the current
// database of rooms.

int dmAddRoom(Player* player, cmd* cmnd) {
	UniqueRoom	*newRoom=0;
	char	file[80];
	int		i=1;

	if(!strcmp(cmnd->str[1], "c") && (cmnd->num > 1)) {
		dmAddMob(player, cmnd);
		return(0);
	}
	if(!strcmp(cmnd->str[1], "o") && (cmnd->num > 1)) {
		dmAddObj(player, cmnd);
		return(0);
	}

	CatRef	cr;
	bool extra = !strcmp(cmnd->str[1], "r");
	getCatRef(getFullstrText(cmnd->fullstr, extra ? 2 : 1), &cr, player);

	if(cr.id < 1) {
		player->print("Index error: please specify room number.\n");
		return(0);
	}
	if(!player->checkBuilder(cr, false)) {
		player->print("Error: Room number not inside any of your alotted ranges.\n");
		return(0);
	}
	if(gConfig->moveRoomRestrictedArea(cr.area)) {
		player->print("Error: ""%s"" is a restricted range. You cannot create unique rooms in that area.\n");
		return(0);
	}
	Path::checkDirExists(cr.area, roomPath);

	if(!strcmp(cmnd->str[extra ? 3 : 2], "loop"))
		i = MAX(1, MIN(100, cmnd->val[extra ? 3 : 2]));

	for(; i; i--) {
		if(!player->checkBuilder(cr, false)) {
			player->print("Error: Room number not inside any of your alotted ranges.\n");
			return(0);
		}

		sprintf(file, "%s", roomPath(cr));
		if(file_exists(file)) {
			player->print("Room already exists.\n");
			return(0);
		}

		newRoom = new UniqueRoom;
		if(!newRoom)
			merror("dmAddRoom", FATAL);
		newRoom->info = cr;

		newRoom->setFlag(R_CONSTRUCTION);
		newRoom->setName("New Room");

		if(newRoom->saveToFile(0) < 0) {
			player->print("Write failed.\n");
			return(0);
		}

		delete newRoom;

		log_immort(true, player, "%s created room %s.\n", player->getCName(), cr.str().c_str());
		player->print("Room %s created.\n", cr.str().c_str());
		checkTeleportRange(player, cr);
		cr.id++;
	}
	return(0);
}

//*********************************************************************
//						dmSetRoom
//*********************************************************************
// This function allows staff to set a characteristic of a room.

int dmSetRoom(Player* player, cmd* cmnd) {
	BaseRoom *room = player->getRoomParent();
	int		a=0, num=0;
	CatRef	cr;

	if(cmnd->num < 3) {
		player->print("Syntax: *set r [option] [<value>]\n");
		return(0);
	}

	if(!player->checkBuilder(player->getUniqueRoomParent())) {
		player->print("Error: Room number not inside any of your alotted ranges.\n");
		return(0);
	}

	switch(low(cmnd->str[2][0])) {
	case 'b':
		if(!player->inUniqueRoom()) {
			player->print("Error: You need to be in a unique room to do that.\n");
			return(0);
		}
		if(low(cmnd->str[2][1]) == 'l') {
			player->getUniqueRoomParent()->setLowLevel(cmnd->val[2]);
			player->print("Low level boundary %d\n", player->getUniqueRoomParent()->getLowLevel());
		} else if(low(cmnd->str[2][1]) == 'h') {
			player->getUniqueRoomParent()->setHighLevel(cmnd->val[2]);
			player->print("Upper level boundary %d\n", player->getUniqueRoomParent()->getHighLevel());
		}

		break;
	case 'd':
		if(!player->inAreaRoom()) {
			player->print("Error: You need to be in an area room to do that.\n");
			return(0);
		}
		if(!player->getAreaRoomParent()->unique.id) {
			player->print("Error: The area room must have the unique field set [*set r unique #].\n");
			return(0);
		}
		player->getAreaRoomParent()->setDecCompass(!player->getAreaRoomParent()->getDecCompass());
		player->printColor("DecCompass toggled, set to %s^x.\n",
			player->getAreaRoomParent()->getDecCompass() ? "^gYes" : "^rNo");

		log_immort(true, player, "%s set decCompass to %s in room %s.\n", player->getCName(),
			player->getAreaRoomParent()->getDecCompass() ? "true" : "false", room->fullName().c_str());

		break;
	case 'e':
		if(cmnd->str[2][1] == 'f') {
			if(cmnd->num < 4) {
				player->print("Set what effect to what?\n");
				return(0);
			}

			long duration = -1;
			int strength = 1;

			bstring txt = getFullstrText(cmnd->fullstr, 4);
			if(txt != "")
				duration = atoi(txt.c_str());
			txt = getFullstrText(cmnd->fullstr, 5);
			if(txt != "")
				strength = atoi(txt.c_str());

			if(duration > EFFECT_MAX_DURATION || duration < -1) {
				player->print("Duration must be between -1 and %d.\n", EFFECT_MAX_DURATION);
				return(0);
			}

			if(strength < 0 || strength > EFFECT_MAX_STRENGTH) {
				player->print("Strength must be between 0 and %d.\n", EFFECT_MAX_STRENGTH);
				return(0);
			}

			bstring effectStr = cmnd->str[3];
			EffectInfo* toSet = 0;
			if((toSet = room->getExactEffect(effectStr))) {
				// We have an existing effect we're modifying
				if(duration == 0) {
					// Duration is 0, so remove it
					room->removeEffect(toSet, true);
					player->print("Effect '%s' (room) removed.\n", effectStr.c_str());
				} else {
					// Otherwise modify as appropriate
					toSet->setDuration(duration);
					if(strength != -1)
						toSet->setStrength(strength);
					player->print("Effect '%s' (room) set to duration %d and strength %d.\n", effectStr.c_str(), toSet->getDuration(), toSet->getStrength());
				}
				break;
			} else {
				// No existing effect, add a new one
				if(strength == -1)
					strength = 1;
				if(room->addEffect(effectStr, duration, strength, 0, true) != NULL){
					player->print("Effect '%s' (room) added with duration %d and strength %d.\n", effectStr.c_str(), duration, strength);
				} else {
					player->print("Unable to add effect '%s' (room)\n", effectStr.c_str());
				}
				break;
			}
		} else if(cmnd->str[2][1] == 'x') {
			if(!player->inUniqueRoom()) {
				player->print("Error: You need to be in a unique room to do that.\n");
				return(0);
			}
			player->getUniqueRoomParent()->setRoomExperience(cmnd->val[2]);

			player->print("Room experience set to %d.\n", player->getUniqueRoomParent()->getRoomExperience());
			log_immort(true, player, "%s set roomExp to %d in room %s.\n", player->getCName(),
				player->getUniqueRoomParent()->getRoomExperience(), room->fullName().c_str());
		} else {
			player->print("Invalid option.\n");
			return(0);
		}
		break;
	case 'f':
		if(low(cmnd->str[2][1]) == 'i') {
			if(!strcmp(cmnd->str[3], "")) {
				player->getUniqueRoomParent()->setFishing("");

				player->print("Fishing list cleared.\n");
				log_immort(true, player, "%s cleared fishing list in room %s.\n", player->getCName(),
					room->fullName().c_str());
			} else {
				const Fishing* list = gConfig->getFishing(cmnd->str[3]);

				if(!list) {
					player->print("Fishing list \"%s\" does not exist!\n", cmnd->str[3]);
					return(0);
				}

				player->getUniqueRoomParent()->setFishing(cmnd->str[3]);

				player->print("Fishing list set to %s.\n", player->getUniqueRoomParent()->getFishingStr().c_str());
				log_immort(true, player, "%s set fishing list to %s in room %s.\n", player->getCName(),
					player->getUniqueRoomParent()->getFishingStr().c_str(), room->fullName().c_str());
			}
		} else if(low(cmnd->str[2][1]) == 'a') {
			if(!player->inUniqueRoom()) {
				player->print("Error: You need to be in a unique room to do that.\n");
				return(0);
			}

			if(cmnd->num < 3) {
				player->print("Set faction to what?\n");
				return(0);
			} else if(cmnd->num == 3) {
				player->getUniqueRoomParent()->setFaction("");
				player->print("Faction cleared.\n");
				log_immort(true, player, "%s cleared faction in room %s.\n", player->getCName(),
					room->fullName().c_str());
				return(0);
			}

			Property* p = gConfig->getProperty(player->getUniqueRoomParent()->info);

			if(p && p->getType() == PROP_SHOP) {
				player->print("You can't set room faction on player shops!\n");
				return(0);
			}

			const Faction* faction = gConfig->getFaction(cmnd->str[3]);
			if(!faction) {
				player->print("'%s' is an invalid faction.\n", cmnd->str[3]);
				return(0);
			}
			player->getUniqueRoomParent()->setFaction(faction->getName());
			player->print("Faction set to %s.\n", player->getUniqueRoomParent()->getFaction().c_str());
			log_immort(true, player, "%s set faction to %s in room %s.\n", player->getCName(),
				player->getUniqueRoomParent()->getFaction().c_str(), room->fullName().c_str());
			break;
		} else {
			if(!player->inUniqueRoom()) {
				player->print("Error: You need to be in a unique room to do that.\n");
				return(0);
			}
			num = cmnd->val[2];
			if(num < 1 || num > MAX_ROOM_FLAGS) {
				player->print("Error: outside of range.\n");
				return(0);
			}

			if(!player->isCt() && num == R_CONSTRUCTION+1) {
				player->print("Error: you cannot set/clear that flag.\n");
				return(0);
			}

			if(!strcmp(cmnd->str[3], "del")) {
				for(a=0;a<MAX_ROOM_FLAGS;a++)
					player->getUniqueRoomParent()->clearFlag(a);

				player->print("All room flags cleared.\n");
				log_immort(true, player, "%s cleared all flags in room %s.\n",
					player->getCName(), room->fullName().c_str());
				break;
			}

			if(player->getUniqueRoomParent()->flagIsSet(num - 1)) {
				player->getUniqueRoomParent()->clearFlag(num - 1);
				player->print("Room flag #%d(%s) off.\n", num, get_rflag(num-1));

				log_immort(true, player, "%s cleared flag #%d(%s) in room %s.\n", player->getCName(), num, get_rflag(num-1),
					room->fullName().c_str());
			} else {
	 			if(num >= R_TRAINING_ROOM && num - 4 <= R_TRAINING_ROOM) {
	 				// setting a training flag - do we let them?
	 				if(!player->getUniqueRoomParent()->whatTraining(num-1)) {
	 					player->print("You are setting training for a class that does not exist.\n");
	 					return(0);
	 				}
	 			}

	 			player->getUniqueRoomParent()->setFlag(num - 1);
				player->print("Room flag #%d(%s) on.\n", num, get_rflag(num-1));
				log_immort(true, player, "%s set flag #%d(%s) in room %s.\n", player->getCName(), num, get_rflag(num-1),
					room->fullName().c_str());

				if(num-1 == R_SHOP)
					player->printColor("^YNote:^x you must set the Shop Storage (97) to use this room as a shop.\n");
				if((num-1 == R_INDOORS || num-1 == R_VAMPIRE_COVEN || num-1 == R_UNDERGROUND) && player->getUniqueRoomParent()->getSize() == NO_SIZE)
					player->printColor("^YNote:^x don't forget to set the size for this room.\n");
			}

			// try and be smart
 			if(	num-1 == R_SHOP_STORAGE &&
 				player->getUniqueRoomParent()->getName() == "New Room" &&
 				player->getUniqueRoomParent()->exits.empty())
 			{
 				cr = player->getUniqueRoomParent()->info;
 				UniqueRoom* shop=0;
 				bstring storageName = "Storage: ";

 				cr.id--;
 				if(loadRoom(cr, &shop)) {
 					if(	shop->flagIsSet(R_SHOP) &&
 						(!shop->getTrapExit().id || shop->getTrapExit() == cr)
 					) {
 						player->printColor("^ySetting up this storage room for you...\n");
 						player->printColor("^y * ^xSetting the trap exit to %s...\n", cr.str().c_str());
 						player->getUniqueRoomParent()->setTrapExit(cr);
 						player->printColor("^y * ^xCreating exit ""out"" to %s...\n", cr.str().c_str());
 						link_rom(player->getUniqueRoomParent(), cr, "out");
 						player->getUniqueRoomParent()->setTrapExit(cr);
 						player->printColor("^y * ^xNaming this room...\n");
 						storageName += shop->getName();
 						player->getUniqueRoomParent()->setName(storageName);
 						player->print("Done!\n");
 					}
 				}
 			}
		}
		break;
	case 'l':
		if(!player->inUniqueRoom()) {
			player->print("Error: You need to be in a unique room to do that.\n");
			return(0);
		}
		if(strcmp(cmnd->str[3], "clear")) {
			player->print("Are you sure?\nType \"*set r last clear\" to clear last-arrived info.\n");
			return(0);
		}
		strcpy(player->getUniqueRoomParent()->lastPly, "");
		strcpy(player->getUniqueRoomParent()->lastPlyTime, "");
		player->print("Last-arrived info cleared.\n");
		break;
	case 'm':
		if(!player->inUniqueRoom()) {
			player->print("Error: You need to be in a unique room to do that.\n");
			return(0);
		}
		player->getUniqueRoomParent()->setMaxMobs(cmnd->val[2]);

		if(!player->getUniqueRoomParent()->getMaxMobs())
			player->print("The limit on the number of creatures that can be here has been removed.\n");
		else
			player->print("Only %d creature%s can now be in here at a time.\n", player->getUniqueRoomParent()->getMaxMobs(), player->getUniqueRoomParent()->getMaxMobs() != 1 ? "s" : "");

		log_immort(true, player, "%s set max %d mobs in room %s.\n", player->getCName(), player->getUniqueRoomParent()->getMaxMobs(),
			room->fullName().c_str());

		break;
	case 'n':
		if(!player->inAreaRoom()) {
			player->print("Error: You need to be in an area room to do that.\n");
			return(0);
		}
		if(!player->getAreaRoomParent()->unique.id) {
			player->print("Error: The area room must have the unique field set [*set r unique #].\n");
			return(0);
		}
		player->getAreaRoomParent()->setNeedsCompass(!player->getAreaRoomParent()->getNeedsCompass());
		player->printColor("NeedsCompass toggled, set to %s^x.\n",
			player->getAreaRoomParent()->getNeedsCompass() ? "^gYes" : "^rNo");

		log_immort(true, player, "%s set needsCompass to %s in room %s.\n", player->getCName(),
			player->getAreaRoomParent()->getNeedsCompass() ? "true" : "false", room->fullName().c_str());

		break;
	case 'r':
		if(!player->inUniqueRoom()) {
			player->print("Error: You need to be in a unique room to do that.\n");
			return(0);
		}
		num = atoi(&cmnd->str[2][1]);
		if(num < 1 || num > NUM_RANDOM_SLOTS) {
			player->print("Error: outside of range.\n");
			return(PROMPT);
		}

		getCatRef(getFullstrText(cmnd->fullstr, 3), &cr, player);

		if(!cr.id) {
			player->getUniqueRoomParent()->wander.random.erase(num-1);
			player->print("Random #%d has been cleared.\n", num);
		} else {
			player->getUniqueRoomParent()->wander.random[num-1] = cr;
			player->print("Random #%d is now %s.\n", num, cr.str().c_str());
		}

		log_immort(false,player, "%s set mob slot %d to mob %s in room %s.\n",
			player->getCName(), num, cr.str().c_str(),
			room->fullName().c_str());

		break;
	case 's':
		if(!player->inUniqueRoom()) {
			player->print("Error: You need to be in a unique room to do that.\n");
			return(0);
		}
		player->getUniqueRoomParent()->setSize(getSize(cmnd->str[3]));

		player->print("Size set to %s.\n", getSizeName(player->getUniqueRoomParent()->getSize()).c_str());
		log_immort(true, player, "%s set room %s's %s to %s.\n",
			player->getCName(), room->fullName().c_str(), "Size", getSizeName(player->getUniqueRoomParent()->getSize()).c_str());
		break;
	case 't':
		if(!player->inUniqueRoom()) {
			player->print("Error: You need to be in a unique room to do that.\n");
			return(0);
		}
		player->getUniqueRoomParent()->wander.setTraffic(cmnd->val[2]);
		log_immort(true, player, "%s set room %s's traffic to %ld.\n", player->getCName(),
			player->getUniqueRoomParent()->info.str().c_str(), player->getUniqueRoomParent()->wander.getTraffic());
		player->print("Traffic is now %d%%.\n", player->getUniqueRoomParent()->wander.getTraffic());

		break;
	case 'x':
		if(!player->inUniqueRoom()) {
			player->print("Error: You need to be in a unique room to do that.\n");
			return(0);
		}
		if(low(cmnd->str[2][1]) == 'x') {
			getCatRef(getFullstrText(cmnd->fullstr, 3), &cr, player);

			if(!player->checkBuilder(cr)) {
				player->print("Trap's exit must be within an assigned range.\n");
				return(0);
			}
			player->getUniqueRoomParent()->setTrapExit(cr);
			player->print("Room's trap exit is now %s.\n", player->getUniqueRoomParent()->getTrapExit().str().c_str());
			log_immort(true, player, "%s set trapexit to %s in room %s.\n", player->getCName(),
				player->getUniqueRoomParent()->getTrapExit().str().c_str(), room->fullName().c_str());
		} else if(low(cmnd->str[2][1]) == 'w') {
			num = (int)cmnd->val[2];
			if(num < 0 || num > 5000) {
				player->print("Trap weight cannot be less than 0 or greater than 5000.\n");
				return(0);
			}
			player->getUniqueRoomParent()->setTrapWeight(num);
			player->print("Room's trap weight is now %d.\n", player->getUniqueRoomParent()->getTrapWeight());
			log_immort(true, player, "%s set trapweight to %d in room %s.\n", player->getCName(), player->getUniqueRoomParent()->getTrapWeight(),
				room->fullName().c_str());
		} else if(low(cmnd->str[2][1]) == 's') {
			num = (int)cmnd->val[2];
			if(num < 0 || num > 5000) {
				player->print("Trap strength cannot be less than 0 or greater than 5000.\n");
				return(0);
			}
			player->getUniqueRoomParent()->setTrapStrength(num);
			player->print("Room's trap strength is now %d.\n", player->getUniqueRoomParent()->getTrapStrength());
			log_immort(true, player, "%s set trapstrength to %d in room %s.\n", player->getCName(), player->getUniqueRoomParent()->getTrapStrength(),
				room->fullName().c_str());
		} else {
			player->getUniqueRoomParent()->setTrap(cmnd->val[2]);
			player->print("Room has trap #%d set.\n", player->getUniqueRoomParent()->getTrap());
			log_immort(true, player, "%s set trap #%d in room %s.\n", player->getCName(), player->getUniqueRoomParent()->getTrap(),
				room->fullName().c_str());
		}

		break;
	case 'u':
		if(!player->inAreaRoom()) {
			player->print("Error: You need to be in an area room to do that.\n");
			return(0);
		}

		getCatRef(getFullstrText(cmnd->fullstr, 3), &cr, player);

		player->getAreaRoomParent()->unique = cr;
		player->print("Unique room set to %s.\n", player->getAreaRoomParent()->unique.str().c_str());
		if(player->getAreaRoomParent()->unique.id)
			player->print("You'll need to use *teleport to get to this room in the future.\n");

		log_immort(true, player, "%s set unique room to %s in room %s.\n",
			player->getCName(), player->getAreaRoomParent()->unique.str().c_str(),
			room->fullName().c_str());

		break;
	default:
		player->print("Invalid option.\n");
		return(0);
	}

	if(player->inUniqueRoom())
		player->getUniqueRoomParent()->escapeText();
	room_track(player);
	return(0);
}


//*********************************************************************
//						dmSetExit
//*********************************************************************
// This function allows staff to set a characteristic of an exit.

int dmSetExit(Player* player, cmd* cmnd) {
	BaseRoom* room = player->getRoomParent();
	int		num=0;
	//char	orig_exit[30];
	short	n=0;

	if(!player->checkBuilder(player->getUniqueRoomParent())) {
		player->print("Error: Room number not inside any of your alotted ranges.\n");
		return(0);
	}

	// setting something on the exit
	if(cmnd->str[1][1]) {

		if(cmnd->num < 3) {
			player->print("Invalid syntax.\n");
			return(0);
		}

		Exit* exit = findExit(player, cmnd->str[2], 1);

		if(!exit) {
			player->print("Exit not found.\n");
			return(0);
		}

		switch(cmnd->str[1][1]) {
		case 'd':
		{
			if(cmnd->str[1][2] == 'i') {
				Direction dir = getDir(cmnd->str[3]);
				if(getDir(exit->getName()) != NoDirection && dir != NoDirection) {
					player->print("This exit does not need a direction set on it.\n");
					return(0);
				}

				exit->setDirection(dir);

				player->printColor("%s^x's %s set to %s.\n", exit->getCName(), "Direction", getDirName(exit->getDirection()).c_str());
				log_immort(true, player, "%s set %s %s^g's %s to %s.\n",
					player->getCName(), "exit", exit->getCName(), "Direction", getDirName(exit->getDirection()).c_str());
			} else if(cmnd->str[1][2] == 'e') {

				bstring desc = getFullstrText(cmnd->fullstr, 3);
				desc.Replace("*CR*", "\n");
				exit->setDescription(desc);

				if(exit->getDescription() == "") {
					player->print("Description cleared.\n");
					log_immort(true, player, "%s cleared %s^g's %s.\n", player->getCName(), exit->getCName(), "Description");
				} else {
					player->print("Description set to \"%s\".\n", exit->getDescription().c_str());
					log_immort(true, player, "%s set %s^g's %s to \"%s\".\n", player->getCName(), exit->getCName(), "Description", exit->getDescription().c_str());
				}

			} else {
				player->print("Description or direction?\n");
				return(0);
			}
			break;
		}
		case 'e':
			if(cmnd->str[1][2] == 'f') {
				if(cmnd->num < 4) {
					player->print("Set what effect to what?\n");
					return(0);
				}

				long duration = -1;
				int strength = 1;

				bstring txt = getFullstrText(cmnd->fullstr, 4);
				if(txt != "")
					duration = atoi(txt.c_str());
				txt = getFullstrText(cmnd->fullstr, 5);
				if(txt != "")
					strength = atoi(txt.c_str());

				if(duration > EFFECT_MAX_DURATION || duration < -1) {
					player->print("Duration must be between -1 and %d.\n", EFFECT_MAX_DURATION);
					return(0);
				}

				if(strength < 0 || strength > EFFECT_MAX_STRENGTH) {
					player->print("Strength must be between 0 and %d.\n", EFFECT_MAX_STRENGTH);
					return(0);
				}

				bstring effectStr = cmnd->str[3];
				EffectInfo* toSet = 0;
				if((toSet = exit->getExactEffect(effectStr))) {
					// We have an existing effect we're modifying
					if(duration == 0) {
						// Duration is 0, so remove it
						exit->removeEffect(toSet, true);
						player->print("Effect '%s' (exit) removed.\n", effectStr.c_str());
					} else {
						// Otherwise modify as appropriate
						toSet->setDuration(duration);
						if(strength != -1)
							toSet->setStrength(strength);
						player->print("Effect '%s' (exit) set to duration %d and strength %d.\n", effectStr.c_str(), toSet->getDuration(), toSet->getStrength());
					}
				} else {
					// No existing effect, add a new one
					if(strength == -1)
						strength = 1;
					if(exit->addEffect(effectStr, duration, strength, 0, true) != NULL) {
						player->print("Effect '%s' (exit) added with duration %d and strength %d.\n", effectStr.c_str(), duration, strength);
					} else {
						player->print("Unable to add effect '%s' (exit)\n", effectStr.c_str());
					}
				}
				break;
			} else {
				exit->setEnter(getFullstrText(cmnd->fullstr, 3));

				if(exit->getEnter() == "" || Pueblo::is(exit->getEnter())) {
					exit->setEnter("");
					player->print("OnEnter cleared.\n");
					log_immort(true, player, "%s cleared %s^g's %s.\n", player->getCName(), exit->getCName(), "OnEnter");
				} else {
					player->print("OnEnter set to \"%s\".\n", exit->getEnter().c_str());
					log_immort(true, player, "%s set %s^g's %s to \"%s\".\n", player->getCName(), exit->getCName(), "OnEnter", exit->getEnter().c_str());
				}
			}

			break;
		case 'f':
			num = cmnd->val[2];
			if(num < 1 || num > MAX_EXIT_FLAGS) {
				player->print("Error: flag out of range.\n");
				return(PROMPT);
			}

			if(exit->flagIsSet(num - 1)) {
			exit->clearFlag(num - 1);
			player->printColor("%s^x exit flag #%d off.\n", exit->getCName(), num);

			log_immort(true, player, "%s cleared %s^g exit flag #%d(%s) in room %s.\n", player->getCName(), exit->getCName(), num, get_xflag(num-1),
				room->fullName().c_str());
			} else {
				exit->setFlag(num - 1);
				player->printColor("%s^x exit flag #%d on.\n", exit->getCName(), num);
				log_immort(true, player, "%s turned on %s^g exit flag #%d(%s) in room %s.\n", player->getCName(), exit->getCName(), num, get_xflag(num-1),
					room->fullName().c_str());
			}
			break;
		case 'k':

			if(cmnd->str[1][2] == 'a') {
				exit->setKeyArea(cmnd->str[3]);
				if(exit->getKeyArea() == "") {
					player->print("Key Area cleared.\n");
					log_immort(true, player, "%s cleared %s^g's %s.\n", player->getCName(), exit->getCName(), "Key Area");
				} else {
					player->print("Key Area set to \"%s\".\n", exit->getKeyArea().c_str());
					log_immort(true, player, "%s set %s^g's %s to \"%s\".\n", player->getCName(), exit->getCName(), "Key Area", exit->getKeyArea().c_str());
				}
			} else {

				if(cmnd->val[2] > 255 || cmnd->val[2] < 0) {
					player->print("Error: key out of range.\n");
					return(0);
				}

				exit->setKey(cmnd->val[2]);
				player->printColor("Exit %s^x key set to %d.\n", exit->getCName(), exit->getKey());
				log_immort(true, player, "%s set %s^g's %s to %ld.\n", player->getCName(), exit->getCName(), "Key", exit->getKey());
			}
			break;
		case 'l':
			if(cmnd->val[2] > MAXALVL || cmnd->val[2] < 0) {
				player->print("Level must be from 0 to %d.\n", MAXALVL);
				return(0);
			}

			exit->setLevel(cmnd->val[2]);
			player->printColor("Exit %s^x's level is now set to %d.\n", exit->getCName(), exit->getLevel());
			log_immort(true, player, "%s set %s^g's %s to %ld.\n", player->getCName(), exit->getCName(), "Pick Level", exit->getLevel());
			break;
		case 'o':

			exit->setOpen(getFullstrText(cmnd->fullstr, 3));

			if(exit->getOpen() == "" || Pueblo::is(exit->getOpen())) {
				exit->setOpen("");
				player->print("OnOpen cleared.\n");
				log_immort(true, player, "%s cleared %s^g's %s.\n", player->getCName(), exit->getCName(), "OnOpen");
			} else {
				player->print("OnOpen set to \"%s\".\n", exit->getOpen().c_str());
				log_immort(true, player, "%s set %s^g's %s to \"%s\".\n", player->getCName(), exit->getCName(), "OnOpen", exit->getOpen().c_str());
			}

			break;
		case 'p':
			if(low(cmnd->str[1][2]) == 'p') {

				exit->setPassPhrase(getFullstrText(cmnd->fullstr, 3));

				if(exit->getPassPhrase() == "") {
					player->print("Passphrase cleared.\n");
					log_immort(true, player, "%s cleared %s^g's %s.\n", player->getCName(), exit->getCName(), "Passphrase");
				} else {
					player->print("Passphrase set to \"%s\".\n", exit->getPassPhrase().c_str());
					log_immort(true, player, "%s set %s^g's %s to \"%s\".\n", player->getCName(), exit->getCName(), "Passphrase", exit->getPassPhrase().c_str());
				}

			} else if(low(cmnd->str[1][2]) == 'l') {

				n = cmnd->val[2];

				if(n < 0 || n > LANGUAGE_COUNT) {
					player->print("Error: pass language out of range.\n");
					return(0);
				}
				n--;
				if(n < 0)
					n = 0;
				exit->setPassLanguage(n);

				player->print("Pass language %s.\n", n ? "set" : "cleared");
				log_immort(true, player, "%s set %s^g's %s to %s(%ld).\n", player->getCName(), exit->getCName(), "Passlang", n ? get_language_adj(n) : "Nothing", n+1);

			} else {
				player->print("Passphrase (xpp) or passlang (xpl)?\n");
				return(0);
			}
			break;
		case 's':
			exit->setSize(getSize(cmnd->str[3]));

			player->printColor("%s^x's %s set to %s.\n", exit->getCName(), "Size", getSizeName(exit->getSize()).c_str());
			log_immort(true, player, "%s set %s %s^g's %s to %s.\n",
				player->getCName(), "exit", exit->getCName(), "Size", getSizeName(exit->getSize()).c_str());
			break;
		case 't':
			n = (short)cmnd->val[2];
			if(n > 30000 || n < 0) {
				player->print("Must be between 0-30000.\n");
				return(0);
			}
			exit->setToll(n);
			player->printColor("Exit %s^x's toll is now set to %d.\n", exit->getCName(), exit->getToll());
			log_immort(true, player, "%s set %s^g's %s to %ld.\n", player->getCName(), exit->getCName(), "Toll", exit->getToll());
			break;
		default:
			player->print("Invalid syntax.\n");
			return(0);
		}

		if(player->inUniqueRoom())
			player->getUniqueRoomParent()->escapeText();
		room_track(player);
		return(0);
	}



	// otherwise, we have other plans for this function
	if(cmnd->num < 3) {
		player->print("Syntax: *set x <name> <#> [. or name]\n");
		return(0);
	}


	// we need more variables to continue our work
	MapMarker mapmarker;
	BaseRoom* room2=0;
	AreaRoom* aRoom=0;
	UniqueRoom	*uRoom=0;
	Area	*area=0;
	CatRef	cr;
	bstring	returnExit = getFullstrText(cmnd->fullstr, 4);


	getDestination(getFullstrText(cmnd->fullstr, 3).c_str(), &mapmarker, &cr, player);


	if(!mapmarker.getArea() && !cr.id) {
		// if the expanded exit wasnt found
		// and the exit was expanded, check to delete the original
		if(room->delExit(cmnd->str[2]))
			player->print("Exit %s deleted.\n", cmnd->str[2]);
		else
			player->print("Exit %s not found.\n", cmnd->str[2]);
		return(0);
	}


	if(cr.id) {
		if(!player->checkBuilder(cr))
			return(0);

		if(!loadRoom(cr, &uRoom)) {
			player->print("Room %s does not exist.\n", cr.str().c_str());
			return(0);
		}

		room2 = uRoom;
	} else {
		if(player->getClass() == BUILDER) {
			player->print("Sorry, builders cannot link exits to the overland.\n");
			return(0);
		}
		area = gConfig->getArea(mapmarker.getArea());
		if(!area) {
			player->print("Area does not exist.\n");
			return(0);
		}
		aRoom = area->loadRoom(0, &mapmarker, false);
		room2 = aRoom;
	}


	bstring newName = getFullstrText(cmnd->fullstr, 2, ' ', false, true);


	if(newName.getLength() > 20) {
		player->print("Exit names must be 20 characters or less in length.\n");
		return(0);
	}

	newName = expand_exit_name(newName);


	if(returnExit != "") {

		if(returnExit == ".")
			returnExit = opposite_exit_name(newName);

		if(cr.id) {
			link_rom(room, cr, newName);

			if(player->inUniqueRoom())
				link_rom(uRoom, player->getUniqueRoomParent()->info, returnExit);
			else
				link_rom(uRoom, &player->getAreaRoomParent()->mapmarker, returnExit);

			gConfig->resaveRoom(cr);

		} else {
			link_rom(room, &mapmarker, newName);

			if(player->inUniqueRoom())
				link_rom(aRoom, player->getUniqueRoomParent()->info, returnExit);
			else
				link_rom(aRoom, &player->getAreaRoomParent()->mapmarker, returnExit);

			aRoom->save();
		}


		log_immort(true, player, "%s linked room %s to room %s in %s^g direction, both ways.\n",
			player->getCName(), room->fullName().c_str(), room2->fullName().c_str(), newName.c_str());
		player->printColor("Room %s linked to room %s in %s^x direction, both ways.\n",
			room->fullName().c_str(), room2->fullName().c_str(), newName.c_str());

	} else {

		if(cr.id)
			link_rom(room, cr, newName);
		else
			link_rom(room, &mapmarker, newName);

		player->printColor("Room %s linked to room %s in %s^x direction.\n",
		      room->fullName().c_str(), room2->fullName().c_str(), newName.c_str());

		log_immort(true, player, "%s linked room %s to room %s in %s^g direction.\n",
			player->getCName(), room->fullName().c_str(), room2->fullName().c_str(), newName.c_str());

	}

	if(player->inUniqueRoom())
		gConfig->resaveRoom(player->getUniqueRoomParent()->info);

	if(aRoom && aRoom->canDelete())
		area->remove(aRoom);

	room_track(player);
	return(0);
}

//*********************************************************************
//						room_track
//*********************************************************************

int room_track(Creature* player) {
	long	t = time(0);

	if(player->isMonster() || !player->inUniqueRoom())
		return(0);

	strcpy(player->getUniqueRoomParent()->last_mod, player->getCName());
	strcpy(player->getUniqueRoomParent()->lastModTime, ctime(&t));
	return(0);
}


//*********************************************************************
//						dmReplace
//*********************************************************************
// this command lets staff replace words or phrases in a room description

int dmReplace(Player* player, cmd* cmnd) {
	UniqueRoom	*room = player->getUniqueRoomParent();
	int		n=0, skip=0, skPos=0;
	bstring::size_type i=0, pos=0;
	char	delim = ' ';
	bool	sdesc=false, ldesc=false;
	bstring search = "", temp = "";

	if(!needUniqueRoom(player))
		return(0);

	if(!player->checkBuilder(player->getUniqueRoomParent())) {
		player->print("Room is not in any of your alotted room ranges.\n");
		return(0);
	}

	if(cmnd->num < 3) {
		player->print("syntax: *replace [-SL#<num>] <search> <replace>\n");
		return(0);
	}

	// we have flags!
	// let's find out what they are
	if(cmnd->str[1][0] == '-') {

		i=1;
		while(i < strlen(cmnd->str[1])) {
			switch(cmnd->str[1][i]) {
			case 'l':
				ldesc = true;
				break;
			case 's':
				sdesc = true;
				break;
			case '#':
				skip = atoi(&cmnd->str[1][++i]);
				break;
			default:
				break;
			}
			i++;
		}

	}

	// we need to get fullstr into a nicer format
	i = strlen(cmnd->str[0]);
	if(ldesc || sdesc || skip)
		i += strlen(cmnd->str[1]) + 1;

	// which deliminator should we use?
	if(cmnd->fullstr[i+1] == '\'' || cmnd->fullstr[i+1] == '"' || cmnd->fullstr[i+1] == '*') {
		delim = cmnd->fullstr[i+1];
		i++;
	}

	// fullstr is now our search text and replace text seperated by a space
	cmnd->fullstr = cmnd->fullstr.substr(i+1);
	//strcpy(cmnd->fullstr, &cmnd->fullstr[i+1]);


	// we search until we find the deliminator we're looking for
	pos=0;
	i = cmnd->fullstr.length();
	while(pos < i) {
		if(cmnd->fullstr[pos] == delim)
			break;
		pos++;
	}

	if(pos == i) {
		if(delim != ' ')
			player->print("Deliminator not found.\n");
		else
			player->print("No replace text found.\n");
		return(0);
	}

	// cut the string apart
	cmnd->fullstr[pos] = 0;
	search = cmnd->fullstr;

	// if it's not a space, we need to add 2 to get rid of the space and
	// next deliminator
	if(delim != ' ') {
		pos += 2;
		// although we don't have to, we're enforcing that the deliminators
		// equal each other so people are consistent with the usage of this function
		if(cmnd->fullstr[pos] != delim) {
			player->print("Deliminators do not match up.\n");
			return(0);
		}
	}

	// fullstr now has our replace text
	//strcpy(cmnd->fullstr, &cmnd->fullstr[pos+1]);
	cmnd->fullstr = cmnd->fullstr.substr(pos+1);
	if(delim != ' ') {
		if(cmnd->fullstr[cmnd->fullstr.length()-1] != delim) {
			player->print("Deliminators do not match up.\n");
			return(0);
		}
		cmnd->fullstr[cmnd->fullstr.length()-1] = 0;
	}


	// the text we are searching for is in "search"
	// the text we are replacing it with is in "fullstr"


	// we will use i to help us reuse code
	// 0 = sdesc, 1 = ldesc
	i = 0;
	// if only long desc, skip short desc
	if(ldesc && !sdesc)
		i++;

	// loop for the short and long desc
	do {
		// loop for skip
		do {
			if(!i)
				n = room->getShortDescription().find(search.c_str(), skPos);
			else
				n = room->getLongDescription().find(search.c_str(), skPos);

			if(n >= 0) {
				if(--skip > 0)
					skPos = n + 1;
				else {
					if(!i) {
						temp = room->getShortDescription().substr(0, n);
						temp += cmnd->fullstr;
						temp += room->getShortDescription().substr(n + search.length(), room->getShortDescription().length());
						room->setShortDescription(temp);
					} else {
						temp = room->getLongDescription().substr(0, n);
						temp += cmnd->fullstr;
						temp += room->getLongDescription().substr(n + search.length(), room->getLongDescription().length());
						room->setLongDescription(temp);
					}
					player->print("Replaced.\n");
					room->escapeText();
					return(0);
				}
			}
		} while(n >= 0 && skip);

		// if we're on long desc (i=1), we'll stop after this, so no worries
		// if we're on short desc and not doing long desc, we need to stop
		if(sdesc && !ldesc)
			i++;
		i++;
	} while(i<2);

	player->print("Pattern not found.\n");
	return(0);
}


//*********************************************************************
//						dmDelete
//*********************************************************************
// Allows staff to delete some/all of the room description

int dmDelete(Player* player, cmd* cmnd) {
	UniqueRoom	*room = player->getUniqueRoomParent();
	int		pos=0;
	int unsigned i=0;
	bool	sdesc=false, ldesc=false, phrase=false, after=false;

	if(!needUniqueRoom(player))
		return(0);

	if(!player->checkBuilder(player->getUniqueRoomParent())) {
		player->print("Room is not in any of your alotted room ranges.\n");
		return(0);
	}

	if(cmnd->num < 2) {
		player->print("syntax: *delete [-ASLPE] <delete_word>\n");
		return(0);
	}

	// take care of the easy one
	if(!strcmp(cmnd->str[1], "-a")) {
		room->setShortDescription("");
		room->setLongDescription("");
	} else {

		// determine our flags
		i=1;
		while(i < strlen(cmnd->str[1])) {
			switch(cmnd->str[1][i]) {
			case 'l':
				ldesc = true;
				break;
			case 's':
				sdesc = true;
				break;
			case 'p':
				phrase = true;
				break;
			case 'e':
				after = true;
				break;
			default:
				break;
			}
			i++;
		}

		// a simple delete operation
		if(!phrase && !after) {
			if(sdesc)
				room->setShortDescription("");
			if(ldesc)
				room->setLongDescription("");
			if(!sdesc && !ldesc) {
				player->print("Invalid syntax.\n");
				return(0);
			}
		} else {

			// we need to figure out what our phrase is
			// turn fullstr into what we want to delete
			i = strlen(cmnd->str[0]) + strlen(cmnd->str[1]) + 1;

			// fullstr is now our phrase
			cmnd->fullstr = cmnd->fullstr.substr(i+1);
			//strcpy(cmnd->fullstr, &cmnd->fullstr[i+1]);


			// we will use i to help us reuse code
			// 0 = sdesc, 1 = ldesc
			i = 0;
			// if only long desc, skip short desc
			if(ldesc && !sdesc)
				i++;

			// loop!
			do {
				if(!i)
					pos = room->getShortDescription().find(cmnd->fullstr);
				else
					pos = room->getLongDescription().find(cmnd->fullstr);

				if(pos >= 0)
					break;

				// if we're on long desc (i=1), we'll stop after this, so no worries
				// if we're on short desc and not doing long desc, we need to stop
				if(sdesc && !ldesc)
					i++;
				i++;
			} while(i<2);


			// did we find it?
			if(pos < 0) {
				player->print("Pattern not found.\n");
				return(0);
			}

			// we delete everything after the phrase
			if(after) {

				if(!phrase)
					pos += cmnd->fullstr.length();

				// if it's in the short desc, and they wanted to delete
				// from both short and long, then delete all of long
				if(!i && !(sdesc ^ ldesc))
					room->setLongDescription("");

				if(!i)
					room->setShortDescription(room->getShortDescription().substr(0, pos));
				else
					room->setLongDescription(room->getLongDescription().substr(0, pos));

			// only delete the phrase
			} else {

				if(!i)
					room->setShortDescription(room->getShortDescription().substr(0, pos) + room->getShortDescription().substr(pos + cmnd->fullstr.length(), room->getShortDescription().length()));
				else
					room->setLongDescription(room->getLongDescription().substr(0, pos) + room->getLongDescription().substr(pos + cmnd->fullstr.length(), room->getLongDescription().length()));

			}

		} // phrase && after

	} // *del -A

	log_immort(true, player, "%s deleted description in room %s.\n", player->getCName(),
		player->getUniqueRoomParent()->info.str().c_str());

	player->print("Deleted.\n");
	return(0);
}

//*********************************************************************
//						dmNameRoom
//*********************************************************************
int dmNameRoom(Player* player, cmd* cmnd) {

	if(!needUniqueRoom(player))
		return(0);
	if(!player->checkBuilder(player->getUniqueRoomParent())) {
		player->print("Room is not in any of your alotted room ranges.\n");
		return(0);
	}

	bstring	name = getFullstrText(cmnd->fullstr, 1);

	if(name == "" || Pueblo::is(name)) {
		player->print("Rename room to what?\n");
		return(0);
	}

	if(name.getLength() > 79)
		name = name.left(79);

	player->getUniqueRoomParent()->setName(name);
	log_immort(true, player, "%s renamed room %s.\n", player->getCName(), player->getRoomParent()->fullName().c_str());
	player->print("Done.\n");

	return(0);
}

//*********************************************************************
//						dmDescription
//*********************************************************************
// Allows a staff to add the given text to the room description.

int dmDescription(Player* player, cmd* cmnd, bool append) {
	UniqueRoom	*room = player->getUniqueRoomParent();
	int unsigned i=0;
	bool	sdesc=false, newline=false;

	if(!needUniqueRoom(player))
		return(0);
	if(!player->checkBuilder(player->getUniqueRoomParent())) {
		player->print("Room is not in any of your alotted room ranges.\n");
		return(0);
	}

	if(cmnd->num < 2) {
		player->print("syntax: *%s [-sn] <text>\n", append ? "append" : "prepend");
		return(0);
	}

	// turn fullstr into what we want to append
	i = strlen(cmnd->str[0]);

	sdesc = cmnd->str[1][0] == '-' && (cmnd->str[1][1] == 's' || cmnd->str[1][2] == 's');
	newline = !(cmnd->str[1][0] == '-' && (cmnd->str[1][1] == 'n' || cmnd->str[1][2] == 'n'));

	// keep chopping
	if(sdesc || !newline)
		i += strlen(cmnd->str[1]) + 1;

	cmnd->fullstr = cmnd->fullstr.substr(i+1);
//	strcpy(cmnd->fullstr, &cmnd->fullstr[i+1]);

	if(cmnd->fullstr.find("  ") != cmnd->fullstr.npos)
		player->printColor("Do not use double spaces in room descriptions! Use ^W*wrap^x to fix this.\n");

	if(sdesc) {
		// short descriptions
		newline = newline && room->getShortDescription() != "";

		if(append) {
			room->appendShortDescription(newline ? "\n" : "");
			room->appendShortDescription(cmnd->fullstr);
		} else {
			if(newline)
			    cmnd->fullstr += "\n";
			room->setShortDescription(cmnd->fullstr + room->getShortDescription());
		}

		player->print("Short description %s.\n", append ? "appended" : "prepended");
	} else {
		// long descriptions
		newline = newline && room->getLongDescription() != "";

		if(append) {
			room->appendLongDescription(newline ? "\n" : "");
			room->appendLongDescription(cmnd->fullstr);
		} else {
			if(newline)
			    cmnd->fullstr += "\n";
			room->setLongDescription(cmnd->fullstr + room->getLongDescription());
		}

		player->print("Long description %s.\n", append ? "appended" : "prepended");
	}

	player->getUniqueRoomParent()->escapeText();
	log_immort(true, player, "%s descripted in room %s.\n", player->getCName(),
		player->getUniqueRoomParent()->info.str().c_str());
	return(0);
}

//*********************************************************************
//						dmAppend
//*********************************************************************
int dmAppend(Player* player, cmd* cmnd) {
	return(dmDescription(player, cmnd, true));
}
//*********************************************************************
//						dmPrepend
//*********************************************************************
int dmPrepend(Player* player, cmd* cmnd) {
	return(dmDescription(player, cmnd, false));
}



//*********************************************************************
//						dmMobList
//*********************************************************************
// Display information about what mobs will randomly spawn.

void showMobList(Player* player, WanderInfo *wander, bstring type) {
	std::map<int, CatRef>::iterator it;
	Monster *monster=0;
	bool	found=false, maybeAggro=false;
	std::ostringstream oStr;

	for(it = wander->random.begin(); it != wander->random.end() ; it++) {
		if(!found)
			oStr << "^cTraffic = " << wander->getTraffic() << "\n";
		found=true;

		if(!(*it).second.id)
			continue;
		if(!loadMonster((*it).second, &monster))
			continue;

		if(monster->flagIsSet(M_AGGRESSIVE))
			oStr << "^r";
		else {
			maybeAggro = monster->flagIsSet(M_AGGRESSIVE_EVIL) ||
				monster->flagIsSet(M_AGGRESSIVE_GOOD) ||
				monster->flagIsSet(M_AGGRESSIVE_AFTER_TALK) ||
				monster->flagIsSet(M_CLASS_AGGRO_INVERT) ||
				monster->flagIsSet(M_RACE_AGGRO_INVERT) ||
				monster->flagIsSet(M_DEITY_AGGRO_INVERT);

			if(!maybeAggro) {
				std::map<int, RaceData*>::iterator rIt;
				for(rIt = gConfig->races.begin() ; rIt != gConfig->races.end() ; rIt++) {
					if(monster->isRaceAggro((*rIt).second->getId(), false)) {
						maybeAggro = true;
						break;
					}
				}
			}

			if(!maybeAggro) {
				for(int n=1; n<STAFF; n++) {
					if(monster->isClassAggro(n, false)) {
						maybeAggro = true;
						break;
					}
				}
			}

			if(!maybeAggro) {
				std::map<int, DeityData*>::iterator dIt;
				for(dIt = gConfig->deities.begin() ; dIt != gConfig->deities.end() ; dIt++) {
					if(monster->isDeityAggro((*dIt).second->getId(), false)) {
						maybeAggro = true;
						break;
					}
				}
			}

			if(maybeAggro)
				oStr << "^y";
			else
				oStr << "^g";
		}

		oStr << "Slot " << std::setw(2) << (*it).first+1 << ": " << monster->getName() << " "
			 << "[" << monType::getName(monster->getType()) << ":" << monType::getHitdice(monster->getType()) << "HD]\n"
			 << "         ^x[I:" << monster->info.str() << " L:" << monster->getLevel()
			 << " X:" << monster->getExperience() << " G:" << monster->coins[GOLD]
			 << " H:" << monster->hp.getMax() << " M:" << monster->mp.getMax()
			 << " N:" << (monster->getNumWander() ? monster->getNumWander() : 1)
			 << (monster->getAlignment() > 0 ? "^g" : "") << (monster->getAlignment() < 0 ? "^r" : "")
			 << " A:" << monster->getAlignment()
			 << "^x D:" << monster->damage.average() << "]\n";

		free_crt(monster);
	}

	if(!found)
		oStr << "    No random monsters currently come in this " << type << ".";

	player->printColor("%s\n", oStr.str().c_str());
}

int dmMobList(Player* player, cmd* cmnd) {

	if(!player->checkBuilder(player->getUniqueRoomParent())) {
		player->print("Error: Room number not inside any of your alotted ranges.\n");
		return(0);
	}

	player->print("Random monsters which come in this room:\n");

	if(player->inUniqueRoom())
		showMobList(player, &player->getUniqueRoomParent()->wander, "room");
	else if(player->inAreaRoom()) {

		Area* area = player->getAreaRoomParent()->area;
		std::list<AreaZone*>::iterator it;
		AreaZone *zone=0;

		for(it = area->zones.begin() ; it != area->zones.end() ; it++) {
			zone = (*it);
			if(zone->inside(area, &player->getAreaRoomParent()->mapmarker)) {
				player->print("Zone: %s\n", zone->name.c_str());
				showMobList(player, &zone->wander, "zone");
			}
		}

		TileInfo* tile = area->getTile(area->getTerrain(0, &player->getAreaRoomParent()->mapmarker, 0, 0, 0, true),
				area->getSeasonFlags(&player->getAreaRoomParent()->mapmarker));
		if(tile && tile->wander.getTraffic()) {
			player->print("Tile: %s\n", tile->getName().c_str());
			showMobList(player, &tile->wander, "tile");
		}
	}

	return(0);
}

//*********************************************************************
//						dmWrap
//*********************************************************************
// dmWrap will  either wrap the short or long desc of a room to the
// specified length, for sanity, a range of  60 - 78 chars is the limit.

int dmWrap(Player* player, cmd* cmnd) {
	UniqueRoom	*room = player->getUniqueRoomParent();
	int		wrap=0;
	bool err=false, which=false;

	bstring text = "";

	if(!needUniqueRoom(player))
		return(0);
	if(!player->checkBuilder(player->getUniqueRoomParent())) {
		player->print("Room is not in any of your alotted room ranges.\n");
		return(0);
	}


	// parse the input command for syntax/range problems
	if(cmnd->num < 2)
		err = true;
	else {
		switch(cmnd->str[1][0]) {
		case 'l':
			which = true;
			text = room->getLongDescription();
			break;
		case 's':
			which = false;
			text = room->getShortDescription();
			break;
		default:
			err = true;
			break;
		}
		if(!err) {
			wrap = cmnd->val[1];
			if(wrap < 60 || wrap > 78)
				err = true;
		}
	}
	if(err) {
		player->print("*wrap <s | l> <len> where len is between 60 and 78 >\n");
		return(0);
	}

	if((!which && room->getShortDescription() == "") || (which && room->getLongDescription() == "")) {
		player->print("No text to wrap!\n");
		return(0);
	}

	// adjust!
	wrap++;

	// replace!
	if(!which)
		room->setShortDescription(wrapText(room->getShortDescription(), wrap));
	else
		room->setLongDescription(wrapText(room->getLongDescription(), wrap));

	player->print("Text wrapped.\n");
	player->getUniqueRoomParent()->escapeText();
	log_immort(false, player, "%s wrapped the description in room %s.\n", player->getCName(), player->getRoomParent()->fullName().c_str());
	return(0);
}



//*********************************************************************
//						dmDeleteAllExits
//*********************************************************************

int dmDeleteAllExits(Player* player, cmd* cmnd) {

	if(player->getRoomParent()->exits.empty()) {
		player->print("No exits to delete.\n");
		return(0);
	}

	player->getRoomParent()->clearExits();

	// sorry, can't delete exits in overland
	if(player->inAreaRoom())
		player->getAreaRoomParent()->updateExits();

	player->print("All exits deleted.\n");

	log_immort(true, player, "%s deleted all exits in room %s.\n", player->getCName(), player->getRoomParent()->fullName().c_str());
	room_track(player);
	return(0);
}

//*********************************************************************
//							exit_ordering
//*********************************************************************
// 1 mean exit2 goes in front of exit1
// 0 means keep looking

int exit_ordering(const char *exit1, const char *exit2) {

	// always skip if they're the same name
	if(!strcmp(exit1, exit2)) return(0);

	// north east south west
	if(!strcmp(exit1, "north")) return(0);
	if(!strcmp(exit2, "north")) return(1);

	if(!strcmp(exit1, "east")) return(0);
	if(!strcmp(exit2, "east")) return(1);

	if(!strcmp(exit1, "south")) return(0);
	if(!strcmp(exit2, "south")) return(1);

	if(!strcmp(exit1, "west")) return(0);
	if(!strcmp(exit2, "west")) return(1);

	// northeast northwest southeast southwest
	if(!strcmp(exit1, "northeast")) return(0);
	if(!strcmp(exit2, "northeast")) return(1);

	if(!strcmp(exit1, "northwest")) return(0);
	if(!strcmp(exit2, "northwest")) return(1);

	if(!strcmp(exit1, "southeast")) return(0);
	if(!strcmp(exit2, "southeast")) return(1);

	if(!strcmp(exit1, "southwest")) return(0);
	if(!strcmp(exit2, "southwest")) return(1);

	if(!strcmp(exit1, "up")) return(0);
	if(!strcmp(exit2, "up")) return(1);

	if(!strcmp(exit1, "down")) return(0);
	if(!strcmp(exit2, "down")) return(1);

	// alphabetic
	return strcmp(exit1, exit2) > 0 ? 1 : 0;
}


//*********************************************************************
//						dmArrangeExits
//*********************************************************************
bool exitCompare( const Exit* left, const Exit* right ){
	return(exit_ordering(left->getCName(), right->getCName()));
}
void BaseRoom::arrangeExits(Player* player) {

	if(exits.size() <= 1) {
		if(player)
			player->print("No exits to rearrange!\n");
		return;
	}

	exits.sort(exitCompare);

	if(player)
		player->print("Exits rearranged!\n");
}

int dmArrangeExits(Player* player, cmd* cmnd) {
	if(!player->checkBuilder(player->getUniqueRoomParent())) {
		player->print("Error: Room number not inside any of your alotted ranges.\n");
		return(0);
	}

	player->getRoomParent()->arrangeExits(player);
	return(0);
}


//*********************************************************************
//							link_rom
//*********************************************************************
// from this room to unique room
void link_rom(BaseRoom* room, Location l, bstring str) {

	const char* dir = str.c_str();
	for(Exit* ext : room->exits) {
		if(ext->getName() == dir) {
			ext->target = l;
			return;
		}
	}

	Exit* exit = new Exit;

    exit->setRoom(room);

	exit->setName( dir);
	exit->target = l;

	room->exits.push_back(exit);
}

void link_rom(BaseRoom* room, short tonum, bstring str) {
	Location l;
	l.room.id = tonum;
	link_rom(room, l, str);
}
void link_rom(BaseRoom* room, CatRef cr, bstring str) {
	Location l;
	l.room = cr;
	link_rom(room, l, str);
}
void link_rom(BaseRoom* room, MapMarker *mapmarker, bstring str) {
	Location l;
	l.mapmarker = *mapmarker;
	link_rom(room, l, str);
}




//*********************************************************************
//						dmFix
//*********************************************************************

int dmFix(Player* player, cmd* cmnd, bstring name, char find, char replace) {
	Exit	*exit=0;
	int		i=0;
	bool	fixed=false;

	if(cmnd->num < 2) {
		player->print("Syntax: *%sup <exit>\n", name.c_str());
		return(0);
	}

	exit = findExit(player, cmnd);
	if(!exit) {
		player->print("You don't see that exit.\n");
		return(0);
	}
	bstring newName = exit->getName();
	for(i=newName.length(); i>0; i--) {
		if(newName[i] == find) {
			newName[i] = replace;
			fixed = true;
		}
	}

	if(fixed) {
		exit->setName(newName);
		log_immort(true, player, "%s %sed the exit '%s' in room %s.\n",
			player->getCName(), name.c_str(), exit->getCName(), player->getRoomParent()->fullName().c_str());
		player->print("Done.\n");
	} else
		player->print("Couldn't find any underscores.\n");

	return(0);
}

//*********************************************************************
//						dmUnfixExit
//*********************************************************************
int dmUnfixExit(Player* player, cmd* cmnd) {
	return(dmFix(player, cmnd, "unfix", ' ', '_'));
}

//*********************************************************************
//						dmFixExit
//*********************************************************************
int dmFixExit(Player* player, cmd* cmnd) {
	return(dmFix(player, cmnd, "fix", '_', ' '));
}


//*********************************************************************
//						dmRenameExit
//*********************************************************************

int dmRenameExit(Player* player, cmd* cmnd) {
	Exit	*exit=0;

	if(!player->checkBuilder(player->getUniqueRoomParent())) {
		player->print("Room is not in any of your alotted room ranges.\n");
		return(0);
	}

	if(cmnd->num < 3) {
		player->print("Syntax: *xrename <old exit>[#] <new exit>\n");
		return(0);
	}


	exit = findExit(player, cmnd);
	if(!exit) {
		player->print("There is no exit here by that name.\n");
		return(0);
	}

	bstring newName = getFullstrText(cmnd->fullstr, 2);

	if(newName.getLength() > 20) {
		player->print("New exit name must be 20 characters or less in length.\n");
		return(0);
	}


	player->printColor("Exit \"%s^x\" renamed to \"%s^x\".\n", exit->getCName(), newName.c_str());
	log_immort(false, player, "%s renamed exit %s^g to %s^g in room %s.\n",
		player->getCName(), exit->getCName(), newName.c_str(), player->getRoomParent()->fullName().c_str());
	room_track(player);

	if(getDir(newName) != NoDirection)
		exit->setDirection(NoDirection);

	exit->setName( newName.c_str());
	return(0);
}


//*********************************************************************
//						dmDestroyRoom
//*********************************************************************

int dmDestroyRoom(Player* player, cmd* cmnd) {
	if(!player->inUniqueRoom()) {
		player->print("Error: You need to be in a unique room to do that.\n");
		return(0);
	}
	if(!player->checkBuilder(player->getUniqueRoomParent())) {
		player->print("Room is not in any of your alotted room ranges.\n");
		return(0);
	}
	if(	player->getUniqueRoomParent()->info.isArea("test") &&
		player->getUniqueRoomParent()->info.id == 1
	) {
		player->print("Sorry, you cannot destroy this room.\n");
		player->print("It is the Builder Waiting Room.\n");
		return(0);
	}
	if(player->bound.room == player->currentLocation.room) {
		player->print("Sorry, you cannot destroy this room.\n");
		player->print("It is your bound room.\n");
		return(0);
	}

	std::map<bstring, StartLoc*>::iterator sIt;
	for(sIt = gConfig->start.begin() ; sIt != gConfig->start.end() ; sIt++) {
		if(	player->getUniqueRoomParent()->info == (*sIt).second->getBind().room ||
			player->getUniqueRoomParent()->info == (*sIt).second->getRequired().room
		) {
			player->print("Sorry, you cannot destroy this room.\n");
			player->print("It is important to starting locations.\n");
			return(0);
		}
	}

	std::list<CatRefInfo*>::const_iterator crIt;
	for(crIt = gConfig->catRefInfo.begin() ; crIt != gConfig->catRefInfo.end() ; crIt++) {
		if(player->getUniqueRoomParent()->info.isArea((*crIt)->getArea())) {
			if((*crIt)->getLimbo() == player->getUniqueRoomParent()->info.id) {
				player->print("Sorry, you cannot destroy this room.\n");
				player->print("It is a Limbo room.\n");
				return(0);
			}
			if((*crIt)->getRecall() == player->getUniqueRoomParent()->info.id) {
				player->print("Sorry, you cannot destroy this room.\n");
				player->print("It is a recall room.\n");
				return(0);
			}
		}
	}

	log_immort(true, player, "%s destroyed room %s.\n",
		player->getCName(), player->getRoomParent()->fullName().c_str());
	player->getUniqueRoomParent()->destroy();
	return(0);
}


//*********************************************************************
//							findRoomsWithFlag
//*********************************************************************

void findRoomsWithFlag(const Player* player, Range range, int flag) {
	Async async;
	if(async.branch(player, CHILD_PRINT) == AsyncExternal) {
		std::ostringstream oStr;
		bool found = false;

		if(range.low.id <= range.high) {
			UniqueRoom* room=0;
			CatRef cr;
			int high = range.high;

			cr.setArea(range.low.area);
			cr.id = range.low.id;
			if(range.low.id == -1 && range.high == -1) {
				cr.id = 1;
				high = RMAX;
			}

			for(; cr.id < high; cr.id++) {
				if(!loadRoom(cr, &room))
					continue;

				if(room->flagIsSet(flag)) {
					if(player->isStaff())
						oStr << room->info.rstr() << " - ";
					oStr << room->getName() << "^x\n";
					found = true;
				}
			}
		}

		printf("^YLocations found:^x\n");
		if(!found) {
			printf("No available locations were found.");
		} else {
			bstring output = oStr.str();
			printf("%s", output.c_str());
		}
		exit(0);
	}
}

void findRoomsWithFlag(const Player* player, CatRef area, int flag) {
	Async async;
	if(async.branch(player, CHILD_PRINT) == AsyncExternal) {
		struct	dirent *dirp=0;
		DIR		*dir=0;
		std::ostringstream oStr;
		bool	found = false;
		char	filename[250];
		UniqueRoom	*room=0;

		// This tells us just to get the path, not the file,
		// and tells loadRoomFromFile to ignore the CatRef
		area.id = -1;
		bstring path = roomPath(area);

		if((dir = opendir(path.c_str())) != NULL) {
			while((dirp = readdir(dir)) != NULL) {
				// is this a room file?
				if(dirp->d_name[0] == '.')
					continue;

				sprintf(filename, "%s/%s", path.c_str(), dirp->d_name);
				if(!loadRoomFromFile(area, &room, filename))
					continue;

				if(room->flagIsSet(flag)) {
					if(player->isStaff())
						oStr << room->info.rstr() << " - ";
					oStr << room->getName() << "^x\n";
					found = true;
				}
				// TODO: Memleak (even though it is forked), room is not deleted
			}
		}

		printf("^YLocations found:^x\n");
		if(!found) {
			printf("No available locations were found.");
		} else {
			bstring output = oStr.str();
			printf("%s", output.c_str());
		}
		exit(0);
	}
}


//*********************************************************************
//						dmFind
//*********************************************************************

int dmFind(Player* player, cmd* cmnd) {
	bstring type = getFullstrText(cmnd->fullstr, 1);
	CatRef cr;

	if(player->inUniqueRoom())
		cr = player->getUniqueRoomParent()->info;
	else
		cr.setArea("area");

	if(type == "r")
		type = "room";
	else if(type == "o")
		type = "object";
	else if(type == "m")
		type = "monster";

	if(type == "" || (type != "room" && type != "object" && type != "monster")) {
		if(type != "")
			player->print("\"%s\" is not a valid type.\n", type.c_str());
		player->print("Search for next available of the following: room, object, monster.\n");
		return(0);
	}

	if(!player->checkBuilder(cr)) {
		player->print("Error: this area is out of your range; you cannot *find here.\n");
		return(0);
	}

	if(type == "monster" && !player->canBuildMonsters()) {
		player->print("Error: you cannot work with monsters.\n");
		return(0);
	}
	if(type == "object" && !player->canBuildObjects()) {
		player->print("Error: you cannot work with objects.\n");
		return(0);
	}


	Async async;
	if(async.branch(player, CHILD_PRINT) == AsyncExternal) {
		cr = findNextEmpty(type, cr.area);

		printf("^YNext available %s in area %s:^x\n", type.c_str(), cr.area.c_str());
		if(cr.id == -1)
			printf("No empty %ss found.", type.c_str());
		else {
			bstring output = cr.rstr();
			printf("%s", output.c_str());
		}
		exit(0);
	} else {
		player->printColor("Searching for next available %s in ^W%s^x.\n", type.c_str(), cr.area.c_str());
	}

	return(0);
}


//*********************************************************************
//							findNextEmpty
//*********************************************************************
// searches for the next empty room/object/monster in the area

CatRef findNextEmpty(bstring type, bstring area) {
	CatRef cr;
	cr.setArea(area);

	if(type == "room") {
		UniqueRoom* room=0;
		
		for(cr.id = 1; cr.id < RMAX; cr.id++)
			if(!loadRoom(cr, &room))
				return(cr);
	} else if(type == "object") {
		Object *object=0;

		for(cr.id = 1; cr.id < OMAX; cr.id++) {
			if(!loadObject(cr, &object))
				return(cr);
			else
				delete object;
		}
	} else if(type == "monster") {
		Monster *monster=0;

		for(cr.id = 1; cr.id < MMAX; cr.id++) {
			if(!loadMonster(cr, &monster))
				return(cr);
			else
				free_crt(monster);
		}
	}

	// -1 indicates failure
	cr.id = -1;
	return(cr);
}