roh/conf/area/
roh/game/talk/
roh/help/
roh/monsters/ocean/
roh/objects/ocean/
roh/player/
roh/rooms/area/1/
roh/rooms/misc/
roh/rooms/ocean/
roh/src-2.44b/
/*
 * room.cpp
 *	 Room routines.
 *   ____            _
 *  |  _ \ ___  __ _| |_ __ ___  ___
 *  | |_) / _ \/ _` | | '_ ` _ \/ __|
 *  |  _ <  __/ (_| | | | | | | \__ \
 *  |_| \_\___|\__,_|_|_| |_| |_|___/
 *
 * 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-2009 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 <sstream>
#include "property.h"
#include "effects.h"

//*********************************************************************
//						addToSameRoom
//*********************************************************************
// This function is called to add a player to a room. It inserts the
// player into the room's linked player list, alphabetically. Also,
// the player's room pointer is updated.

void Player::addToSameRoom(Creature* target) {
	if(target->parent_rom)
		addToRoom(target->parent_rom);
	else
		addToRoom(target->area_room);
}



void Player::finishAddPlayer(BaseRoom* room) {
	ctag	*cg=0, *cp=0, *temp=0, *prev=0;
	otag	*op=0, *cop=0;

	wake("You awaken suddenly!");
	setFishing(false);

	cg = new ctag;
	if(!cg)
		merror("add_ply_rom", FATAL);
	cg->crt = this;
	cg->next_tag = 0;
	if(!gServer->isRebooting()) {

		if(!flagIsSet(P_DM_INVIS) && !flagIsSet(P_HIDDEN) && !flagIsSet(P_MISTED) ) {
			broadcast(getSock(), room, "%M just arrived.", this);
		} else if(flagIsSet(P_MISTED) && !flagIsSet(P_SNEAK_WHILE_MISTED)) {
			broadcast(getSock(), room, "A light mist just arrived.");
		} else {
			if(isDm())
				broadcast(::isDm, getSock(), getRoom(), "*DM* %M just arrived.", this);
			if(cClass == CARETAKER)
				broadcast(::isCt, getSock(), getRoom(), "*DM* %M just arrived.", this);
			if(!isCt())
				broadcast(::isStaff, getSock(), getRoom(), "*DM* %M just arrived.", this);
		}

		if(!isStaff()) {
			if((isEffected("darkness") || flagIsSet(P_DARKNESS)) && !getRoom()->flagIsSet(R_MAGIC_DARKNESS))
				broadcast(getSock(), room, "^DA globe of darkness just arrived.");
		}
	}

	if(flagIsSet(P_SNEAK_WHILE_MISTED))
		clearFlag(P_SNEAK_WHILE_MISTED);
	setLastPawn(0);

	op = first_obj;
	while(op) {
		if(op->obj->getType() == CONTAINER) {
			cop = op->obj->first_obj;
			while(cop) {
				if(cop->obj->flagIsSet(O_JUST_BOUGHT))
					cop->obj->clearFlag(O_JUST_BOUGHT);
				cop=cop->next_tag;
			}
		} else {
			if(op->obj->flagIsSet(O_JUST_BOUGHT))
				op->obj->clearFlag(O_JUST_BOUGHT);
		}
		op = op->next_tag;
	}

	clearFlag(P_JUST_TRAINED); // Player can train again.
	clearFlag(P_CAUGHT_SHOPLIFTING); // No longer in the act of shoplifting.
	clearFlag(P_LAG_PROTECTION_OPERATING); // Lag detection routines deactivated.

	if(flagIsSet(P_SITTING)) {
		clearFlag(P_SITTING);
		print("You stand up.\n");
		broadcast(getSock(), room, "%M stands up.", this);
	}


	cp = first_fol;
	while(cp) {
		if(cp->crt->isPet() && cp->crt->first_enm)
			cp->crt->first_enm = NULL;	// Clears pets' attack lists.
		cp = cp->next_tag;
	}

	if(!isCt())
		room->checkExits();



	if(!room->first_ply) {
		room->first_ply = cg;
		cp = room->first_mon;
		Monster *mon;
		while(cp) {
			mon = cp->crt->getMonster();
			gServer->addActive(mon);
			cp = cp->next_tag;
		}
		display_rom(this);
		hooks.execute("afterAddRoom", room);
		room->hooks.execute("afterAddRoom", this);
		return;
	}

	temp = room->first_ply;
	if(strcmp(temp->crt->name, name) > 0) {
		cg->next_tag = temp;
		room->first_ply = cg;
		display_rom(this);
		hooks.execute("afterAddRoom", room);
		room->hooks.execute("afterAddRoom", this);
		return;
	}

	while(temp) {
		if(strcmp(temp->crt->name, name) > 0)
			break;
		prev = temp;
		temp = temp->next_tag;
	}
	cg->next_tag = prev->next_tag;
	prev->next_tag = cg;

	display_rom(this);

	hooks.execute("afterAddRoom", room);
	room->hooks.execute("afterAddRoom", this);
}

void Player::addToRoom(BaseRoom* room) {
	AreaRoom* aRoom = room->getAreaRoom();

	if(aRoom)
		addToRoom(aRoom);
	else
		addToRoom(room->getUniqueRoom());
}

void Player::addToRoom(AreaRoom* aRoom) {
	hooks.execute("afterAddRoom", aRoom);
	aRoom->hooks.execute("afterAddRoom", this);
	area_room = aRoom;
	room.clear();
	parent_rom = 0;
	finishAddPlayer(aRoom);
}

void Player::addToRoom(Room* uRoom) {
	ctag	*cp=0;
	bool	builderInRoom=false;

	hooks.execute("beforeAddRoom", uRoom);
	uRoom->hooks.execute("beforeAddRoom", this);
	parent_rom = uRoom;
	*&room = uRoom->info;
	area_room = 0;

	// So we can see an accurate this count.
	if(!isStaff())
		uRoom->incBeenHere();

	if(uRoom->getRoomExperience()) {
		bool beenHere=false;
		std::list<CatRef>::iterator it;

		for(it = roomExp.begin() ; it != roomExp.end() ; it++) {
			if(uRoom->info == (*it)) {
				beenHere = true;
				break;
			}
		}

		if(!beenHere) {
			print("You %s experience for entering this place for the first time.\n", gConfig->isAprilFools() ? "lose" : "gain");
			if(!halftolevel()) {
				addExperience(uRoom->getRoomExperience());
				checkLevel();
			}
			roomExp.push_back(uRoom->info);
		}
	}


	// Builders cannot leave their areas unless escorted by a DM.
	// Any other time they leave their areas it will be logged.
	if(	cClass==BUILDER &&
		(	!following ||
			(following && !following->isDm()) ) &&
		checkRangeRestrict(uRoom->info)
	) {
		// only log if another builder is not in the room
		cp = uRoom->first_ply;
		while(cp) {
			if( cp->crt != this &&
				cp->crt->getClass() == BUILDER
			) {
				builderInRoom = true;
			}
			cp = cp->next_tag;
		}
		if(!builderInRoom) {
			checkBuilder(uRoom);
			printColor("^yYou are illegally out of your assigned area. This has been logged.\n");
			broadcast(::isCt, "^y### %s is illegally out of %s assigned area. (%s)",
				name, himHer(), uRoom->info.str().c_str());
			logn("log.builders", "%s illegally entered room %s - (%s).\n", name,
				uRoom->info.str().c_str(), uRoom->name);
		}
	}


	uRoom->addPermCrt();
	uRoom->addPermObj();
	uRoom->validatePerms();

	finishAddPlayer(uRoom);
	updateRooms(uRoom);
}

//*********************************************************************
//						deletePortal
//*********************************************************************

bool deletePortal(Player* player, BaseRoom* room, bstring name) {
	xtag	*xp = room->first_ext, *xprev=0;
	otag	*op = room->first_obj, *oprev=0;

	while(op) {
		if(op->obj->flagIsSet(O_PORTAL) && !strcmp(op->obj->use_output, name.c_str())) {
			if(oprev)
				oprev->next_tag = op->next_tag;
			else
				room->first_obj = op->next_tag;

			delete op->obj;
			delete op;
			break;
		}
		oprev = op;
		op = op->next_tag;
	}

	while(xp) {
		if(xp->ext->flagIsSet(X_PORTAL) && xp->ext->getPassPhrase() == name) {
			if(xprev)
				xprev->next_tag = xp->next_tag;
			else
				room->first_ext = xp->next_tag;

			player->print("The %s collapses into nothingness.\n", xp->ext->name);
			broadcast(player->getSock(), room, "The %s collapses into nothingness.", xp->ext->name);
			delete xp->ext;
			delete xp;

			Player* owner = gServer->findPlayer(name.c_str());
			if(owner)
				owner->clearFlag(P_PORTAL);
			return(true);
		}
		xprev = xp;
		xp = xp->next_tag;
	}
	return(false);
}

//*********************************************************************
//						deleteFromRoom
//*********************************************************************
// This function removes a player from a room's linked list of players.
// It also resets the player's room pointer.

int Player::deleteFromRoom(bool delPortal) {
	hooks.execute("beforeDeleteFromRoom", getRoom());
	getRoom()->hooks.execute("beforeDeleteFromRoom", this);
	if(parent_rom) {
		if(!parent_rom->flagIsSet(R_NO_PREVIOUS)) {
			previousRoom.room = parent_rom->info;
			previousRoom.mapmarker.reset();
		}
		return(deleteFromRoom(parent_rom, delPortal));
	} else if(area_room) {
		if(!area_room->flagIsSet(R_NO_PREVIOUS)) {
			previousRoom.room.id = 0;
			previousRoom.mapmarker = area_room->mapmarker;
		}
		AreaRoom* room = area_room;
		int i = deleteFromRoom(area_room, delPortal);
		if(room->canDelete()) {
			room->area->remove(room);
			i |= DEL_ROOM_DESTROYED;
		}
		return(i);
	}
	return(0);
}

int Player::deleteFromRoom(BaseRoom* room, bool delPortal) {
	ctag 	*cp, *temp, *prev;
	long	t=0;
	int		i=0;

	t = time(0);
	if(parent_rom && !isStaff()) {
		strcpy(parent_rom->lastPly, name);
		strcpy(parent_rom->lastPlyTime, ctime(&t));
	}

	parent_rom = 0;
	area_room = 0;

	if(delPortal && flagIsSet(P_PORTAL) && deletePortal(this, room, name))
		i |= DEL_PORTAL_DESTROYED;

	// when we're removing them from the room - AreaRoom, usually -
	// but they were never added to the player list. this happens
	// in dmMove for offline players.
	if(!room->first_ply) {
		hooks.execute("afterDeleteFromRoom", room);
		room->hooks.execute("afterDeleteFromRoom", this);
		return(i);
	}

	if(room->first_ply->crt == this) {
		temp = room->first_ply->next_tag;
		delete room->first_ply;
		room->first_ply = temp;

		if(!temp) {
			cp = room->first_mon;
			while(cp) {
				/*
				 * pets and fast wanderers are always active
				 *
				if(!cp->crt->flagIsSet(M_FAST_WANDER) && !cp->crt->isPet() && !cp->crt->flagIsSet(M_POISONED) &&
				!cp->crt->flagIsSet(M_FAST_TICK) && !cp->crt->flagIsSet(M_REGENERATES) && !cp->crt->flagIsSet(M_SLOW) &&
				!(cp->crt->flagIsSet(M_PERMENANT_MONSTER) && (cp->crt->hp.getCur() < cp->crt->hp.getMax())))
				*/
				if(	!cp->crt->flagIsSet(M_FAST_WANDER) &&
					!cp->crt->isPet() &&
					!cp->crt->isPoisoned() &&
					!cp->crt->flagIsSet(M_FAST_TICK) &&
					!cp->crt->flagIsSet(M_ALWAYS_ACTIVE) &&
					!cp->crt->flagIsSet(M_REGENERATES) &&
					!cp->crt->isEffected("slow") &&
					!cp->crt->flagIsSet(M_PERMENANT_MONSTER) &&
					!cp->crt->flagIsSet(M_AGGRESSIVE)
				)
					gServer->delActive(cp->crt->getMonster());
				cp = cp->next_tag;
			}
		}
		hooks.execute("afterDeleteFromRoom", room);
		room->hooks.execute("afterDeleteFromRoom", this);
		return(i);
	}

	prev = room->first_ply;
	temp = prev->next_tag;
	while(temp) {
		if(temp->crt == this) {
			prev->next_tag = temp->next_tag;
			delete temp;
			hooks.execute("afterDeleteFromRoom", room);
			room->hooks.execute("afterDeleteFromRoom", this);
			return(i);
		}
		prev = temp;
		temp = temp->next_tag;
	}

	hooks.execute("afterDeleteFromRoom", room);
	room->hooks.execute("afterDeleteFromRoom", this);
	return(i);
}

//*********************************************************************
//						add_obj_rom
//*********************************************************************
// This function adds the object pointed to by the first parameter to
// the object list of the room pointed to by the second parameter.
// The object is added alphabetically to the room.

void Object::addToRoom(BaseRoom* room) {
	otag	*op, *temp, *prev;

	ASSERTLOG(room);
	hooks.execute("beforeAddRoom", room);
	room->hooks.execute("beforeAddRoom", this);
	parent_room = room;
	parent_obj = 0;
	parent_crt = 0;
	clearFlag(O_KEEP);

	op = 0;
	op = new otag;
	if(!op)
		merror("add_obj_rom", FATAL);
	op->obj = this;
	op->next_tag = 0;

	if(!room->first_obj) {
		room->first_obj = op;
		room->killMortalObjects();
		hooks.execute("afterAddRoom", room);
		room->hooks.execute("afterAddRoom", this);
		return;
	}

	prev = temp = room->first_obj;
	if(strcmp(temp->obj->name, name) > 0 ||
	        (!strcmp(temp->obj->name, name) &&
	         (temp->obj->adjustment > adjustment
	          || (temp->obj->adjustment >= adjustment &&
	              temp->obj->shopValue > shopValue)))) {
		op->next_tag = temp;
		room->first_obj = op;
		room->killMortalObjects();
		hooks.execute("afterAddRoom", room);
		room->hooks.execute("afterAddRoom", this);
		return;
	}

	while(temp) {
		if(strcmp(temp->obj->name, name) > 0 ||
		        (!strcmp(temp->obj->name, name) &&
		         (temp->obj->adjustment > adjustment
		          || (temp->obj->adjustment >= adjustment &&
		              temp->obj->shopValue > shopValue))))
			break;
		prev = temp;
		temp = temp->next_tag;
	}
	op->next_tag = prev->next_tag;
	prev->next_tag = op;
	room->killMortalObjects();
	hooks.execute("afterAddRoom", room);
	room->hooks.execute("afterAddRoom", this);
}

//*********************************************************************
//						deleteFromRoom
//*********************************************************************
// This function removes the object pointer to by the first parameter
// from the room pointed to by the second.

void Object::deleteFromRoom() {
	if(!parent_room)
		return;
	BaseRoom* room = parent_room;

	hooks.execute("beforeDeleteFromRoom", room);
	room->hooks.execute("beforeDeleteFromRoom", this);

	otag	*op = room->first_obj, *prev=0;
	parent_room = 0;

	while(op) {
		if(op->obj == this) {
			if(prev)
				prev->next_tag = op->next_tag;
			else
				room->first_obj = op->next_tag;

			delete op;
			hooks.execute("afterDeleteFromRoom", room);
			room->hooks.execute("afterDeleteFromRoom", this);
			return;
		}
		prev = op;
		op = op->next_tag;
	}
	hooks.execute("afterDeleteFromRoom", room);
	room->hooks.execute("afterDeleteFromRoom", this);
}

//*********************************************************************
//						addToRoom
//*********************************************************************
// This function adds the monster pointed to by the first parameter to
// the room pointed to by the second. The third parameter determines
// how the people in the room should be notified. If it is equal to
// zero, then no one will be notified that a monster entered the room.
// If it is non-zero, then the room will be told that "num" monsters
// of that name entered the room.

void Monster::addToRoom(BaseRoom* room, int num) {
	hooks.execute("beforeAddRoom", room);
	room->hooks.execute("beforeAddRoom", this);
	addToRoom(room, room->getUniqueRoom(), room->getAreaRoom(), num);
}

void Monster::addToRoom(BaseRoom* room, Room* uRoom, AreaRoom* aRoom, int num) {
	ctag	*cg=NULL, *temp=NULL, *prev=NULL;
	char	str[160];

	if(uRoom) {
		parent_rom = uRoom;
		*&this->room = *&uRoom->info;
	} else
		area_room = aRoom;

	lasttime[LT_AGGRO_ACTION].ltime = time(0);
	killDarkmetal();

	cg = 0;
	cg = new ctag;
	if(!cg)
		merror("add_crt_rom", FATAL);
	cg->crt = this;
	cg->next_tag = 0;

	// Only show if num != 0 and it isn't a perm, otherwise we'll either
	// show to staff or players
	if(num != 0 && !flagIsSet(M_PERMENANT_MONSTER)) {
		if(!flagIsSet(M_NO_SHOW_ARRIVE) && !isInvisible()
			&& !flagIsSet(M_WAS_PORTED) )
		{
			sprintf(str, "%%%dM just arrived.", num);
			broadcast(getSock(), room, str, this);
		} else {
			sprintf(str, "*DM* %%%dM just arrived.", num);
			broadcast(::isStaff, getSock(), room, str, this);
		}
	}
	// Handle sneaking mobs
	if(flagIsSet(M_SNEAKING)) {
		setFlag(M_NO_SHOW_ARRIVE);
		clearFlag(M_SNEAKING);
	} else
		clearFlag(M_NO_SHOW_ARRIVE);

	// Handle random aggressive monsters
	if(!flagIsSet(M_AGGRESSIVE)) {
		if(loadAggro && (mrand(1,100) <= MAX(1, loadAggro)))
			setFlag(M_WILL_BE_AGGRESSIVE);
	}


	if(!room->first_mon) {
		room->first_mon = cg;
		hooks.execute("afterAddRoom", room);
		room->hooks.execute("afterAddRoom", this);
		return;
	}

	temp = room->first_mon;
	if(strcmp(temp->crt->name, name) > 0) {
		cg->next_tag = temp;
		room->first_mon = cg;
		hooks.execute("afterAddRoom", room);
		room->hooks.execute("afterAddRoom", this);
		return;
	}

	while(temp) {
		if(strcmp(temp->crt->name, name) > 0)
			break;
		prev = temp;
		temp = temp->next_tag;
	}
	cg->next_tag = prev->next_tag;
	prev->next_tag = cg;

	hooks.execute("afterAddRoom", room);
	room->hooks.execute("afterAddRoom", this);
}

//*********************************************************************
//						deleteFromRoom
//*********************************************************************
// This function removes the monster pointed to by the first parameter
// from the room pointed to by the second.
// Return Value: True if the area room was purged
// 				 False otherwise

int Monster::deleteFromRoom(bool delPortal) {
	hooks.execute("beforeDeleteFromRoom", getRoom());
	getRoom()->hooks.execute("beforeDeleteFromRoom", this);
	if(parent_rom)
		return(deleteFromRoom(parent_rom, delPortal));
	else if(area_room) {
		AreaRoom* room = area_room;
		int i = deleteFromRoom(area_room, delPortal);
		if(room->canDelete()) {
			room->area->remove(room);
			i |= DEL_ROOM_DESTROYED;
		}
		return(i);
	}
	return(0);
}

int Monster::deleteFromRoom(BaseRoom* room, bool delPortal) {
	ctag 	*temp, *prev;

	parent_rom = 0;
	area_room = 0;
	this->room.clear();

	if(!room)
		return(0);
	if(!room->first_mon)
		return(0);

	if(room->first_mon->crt == this) {
		temp = room->first_mon->next_tag;
		delete room->first_mon;
		room->first_mon = temp;
		hooks.execute("afterDeleteFromRoom", room);
		room->hooks.execute("afterDeleteFromRoom", this);
		return(0);
	}


	prev = room->first_mon;
	temp = prev->next_tag;
	while(temp) {
		if(temp->crt == this) {
			prev->next_tag = temp->next_tag;
			delete temp;
			hooks.execute("afterDeleteFromRoom", room);
			room->hooks.execute("afterDeleteFromRoom", this);
			return(0);
		}
		prev = temp;
		temp = temp->next_tag;
	}

	hooks.execute("afterDeleteFromRoom", room);
	room->hooks.execute("afterDeleteFromRoom", this);
	return(0);
}

//********************************************************************
//				addPermCrt
//********************************************************************
// This function checks a room to see if any permanent monsters need to
// be loaded. If so, the monsters are loaded to the room, and their
// permanent flag is set.

void Room::addPermCrt() {
	std::map<int, crlasttime>::iterator it, nt;
	crlasttime* crtm=0;
	std::map<int, bool> checklist;
	Monster	*creature=0;
	ctag	*cp=0;
	long	t = time(0);
	int		j=0, m=0, n=0;

	for(it = permMonsters.begin(); it != permMonsters.end() ; it++) {
		crtm = &(*it).second;

		if(checklist[(*it).first])
			continue;
		if(!crtm->cr.id)
			continue;
		if(crtm->ltime + crtm->interval > t)
			continue;

		n = 1;
		nt = it;
		nt++;
		for(; nt != permMonsters.end() ; nt++) {
			if(	crtm->cr == (*nt).second.cr &&
				((*nt).second.ltime + (*nt).second.interval) < t
			) {
				n++;
				checklist[(*nt).first] = 1;
			}
		}

		if(!loadMonster(crtm->cr, &creature))
			continue;

		cp = first_mon;
		m = 0;
		while(cp) {
			if(cp->crt->flagIsSet(M_PERMENANT_MONSTER) &&
			        !strcmp(cp->crt->name, creature->name))
				m++;
			cp = cp->next_tag;
		}

		free_crt(creature);

		for(j=0; j<n-m; j++) {

			if(!loadMonster(crtm->cr, &creature))
				continue;

			creature->initMonster();
			creature->setFlag(M_PERMENANT_MONSTER);
			creature->daily[DL_BROAD].cur = 20;
			creature->daily[DL_BROAD].max = 20;

			creature->validateAc();
			creature->addToRoom(this, 0);

			if(first_ply)
				gServer->addActive(creature);
		}
	}
}


//*********************************************************************
//						addPermObj
//*********************************************************************
// This function checks a room to see if any permanent objects need to
// be loaded.  If so, the objects are loaded to the room, and their
// permanent flag is set.

void Room::addPermObj() {
	std::map<int, crlasttime>::iterator it, nt;
	crlasttime* crtm=0;
	std::map<int, bool> checklist;
	Object	*object=0;
	otag	*op=0;
	long	t = time(0);
	int		j=0, m=0, n=0;

	for(it = permObjects.begin(); it != permObjects.end() ; it++) {
		crtm = &(*it).second;

		if(checklist[(*it).first])
			continue;
		if(!crtm->cr.id)
			continue;
		if(crtm->ltime + crtm->interval > t)
			continue;

		n = 1;
		nt = it;
		nt++;
		for(; nt != permObjects.end() ; nt++) {
			if(	crtm->cr == (*nt).second.cr &&
				((*nt).second.ltime + (*nt).second.interval) < t
			) {
				n++;
				checklist[(*nt).first] = 1;
			}
		}

		if(!loadObject(crtm->cr, &object))
			continue;

		op = first_obj;
		m = 0;
		while(op) {
			if(op->obj->flagIsSet(O_PERM_ITEM) && !strcmp(op->obj->name, object->name) && op->obj->info == object->info)
				m++;
			op = op->next_tag;
		}

		delete object;

		for(j=0; j<n-m; j++) {
			if(!loadObject(crtm->cr, &object))
				continue;
			if(object->flagIsSet(O_RANDOM_ENCHANT))
				object->randomEnchant();

			object->setFlag(O_PERM_ITEM);
			object->addToRoom(this);
		}
	}
}

//*********************************************************************
//						roomEffStr
//*********************************************************************

bstring roomEffStr(bstring effect, bstring str, const BaseRoom* room, bool detectMagic) {
	if(!room->isEffected(effect))
		return("");
	if(detectMagic) {
		EffectInfo* eff = room->getEffect(effect);
		if(eff->getOwner() != "") {
			str += " (cast by ";
			str += eff->getOwner();
			str += ")";
		}
	}
	str += ".\n";
	return(str);
}

//*********************************************************************
//						displayRoom
//*********************************************************************
// This function displays the descriptions of a room, all the players
// in a room, all the monsters in a room, all the objects in a room,
// and all the exits in a room.  That is, unless they are not visible
// or the room is dark.

void displayRoom(Player* player, BaseRoom* room, Room* uRoom, AreaRoom* aRoom, int magicShowHidden) {
	Room	*target=0;
	xtag	*xp=0;
	ctag	*cp=0;
	Player	*pCreature=0;
	Creature* creature=0;
	char	name[256];
	int		n=0, m=0, flags = player->displayFlags(), staff=0;
	std::ostringstream oStr;
	bstring str = "";
	bool	wallOfFire=false, wallOfThorns=false, canSee=false;

	strcpy(name, "");

	staff = player->isStaff();

	player->print("\n");

	if(	player->isEffected("petrification") &&
		!player->checkStaff("You can't see anything. You're petrified!")
	)
		return;

	if(!player->canSee(room, true))
		return;

	oStr << (!player->flagIsSet(P_NO_EXTRA_COLOR) && room->isSunlight() ? "^C" : "^c");

	if(uRoom) {

		if(staff)
			oStr << uRoom->info.str() << " - ";
		oStr << uRoom->name << "^x\n\n";

		if(!player->flagIsSet(P_NO_SHORT_DESCRIPTION) && uRoom->getShortDescription() != "")
			oStr << uRoom->getShortDescription() << "\n";

		if(!player->flagIsSet(P_NO_LONG_DESCRIPTION) && uRoom->getLongDescription() != "")
			oStr << uRoom->getLongDescription() << "\n";

	} else {

		if(aRoom->area->name != "") {
			oStr << aRoom->area->name;
			if(player->isCt())
				oStr << " " << aRoom->fullName();
			oStr << "^x\n\n";
		}

		oStr << aRoom->area->showGrid(player, &aRoom->mapmarker, player->area_room == aRoom);
	}

	oStr << "^g" << (staff ? "All" : "Obvious") << " exits: ";
	n=0;

	str = "";
	xp = room->first_ext;
	while(xp) {
		wallOfFire = xp->ext->isWall("wall-of-fire");
		wallOfThorns = xp->ext->isWall("wall-of-thorns");

		canSee = player->showExit(xp->ext, magicShowHidden);
		if(canSee) {
			if(n)
				oStr << "^g, ";

			// first, check P_NO_EXTRA_COLOR
			// putting loadRoom in the boolean statement means that, if
			// canEnter returns false, it will save us on running the
			// function every once in a while
			oStr << "^";
			if(wallOfFire) {
				oStr << "R";
			} else if(wallOfThorns) {
				oStr << "o";
			} else if(	!player->flagIsSet(P_NO_EXTRA_COLOR) &&
				(	!player->canEnter(xp->ext) || (
						xp->ext->target.room.id && (
							!loadRoom(xp->ext->target.room, &target) ||
							!target ||
							!player->canEnter(target)
						)
					)
				)
			) {
				oStr << "G";
			} else {
				oStr << "g";
			}
			oStr << xp->ext->name;

			if(xp->ext->flagIsSet(X_CLOSED) || xp->ext->flagIsSet(X_LOCKED)) {
				oStr << "[";
				if(xp->ext->flagIsSet(X_CLOSED))
					oStr << "c";
				if(xp->ext->flagIsSet(X_LOCKED))
					oStr << "l";
				oStr << "]";
			}

			if(staff) {
				if(xp->ext->flagIsSet(X_SECRET))
					oStr << "(h)";
				if(xp->ext->flagIsSet(X_NO_WANDER))
					oStr << "(nw)";
				if(xp->ext->flagIsSet(X_NO_SEE))
					oStr << "(dm)";
				if(xp->ext->flagIsSet(X_INVISIBLE))
					oStr << "(*)";
				if(xp->ext->isConcealed(player))
					oStr << "(c)";
				if(xp->ext->flagIsSet(X_DESCRIPTION_ONLY))
					oStr << "(desc)";
				if(xp->ext->flagIsSet(X_NEEDS_FLY))
					oStr << "(fly)";
			}

			n++;
		}

		if(wallOfFire)
			str += xp->ext->blockedByStr('R', "wall of fire", "wall-of-fire", flags & MAG, canSee);
		if(xp->ext->isWall("wall-of-force"))
			str += xp->ext->blockedByStr('s', "wall of force", "wall-of-force", flags & MAG, canSee);
		if(wallOfThorns)
			str += xp->ext->blockedByStr('o', "wall of thorns", "wall-of-thorns", flags & MAG, canSee);

		xp = xp->next_tag;
	}

	if(!n)
		oStr << "none";
	// change colors
	oStr << ".\n";


	// room auras
	if(player->isEffected("know-aura")) {
		if(!room->isEffected("unhallow"))
			oStr << roomEffStr("hallow", "^YA holy aura fills the room", room, flags & MAG);
		if(!room->isEffected("hallow"))
			oStr << roomEffStr("unhallow", "^DAn unholy aura fills the room", room, flags & MAG);
	}

	oStr << roomEffStr("dense-fog", "^WA dense fog fills the room", room, flags & MAG);
	oStr << roomEffStr("toxic-cloud", "^GA toxic cloud fills the room", room, flags & MAG);
	oStr << roomEffStr("globe-of-silence", "^DAn unnatural silence hangs in the air", room, flags & MAG);


	oStr << str << "^c";
	str = "";

	cp = room->first_ply;
	n=0;
	while(cp) {
		pCreature = cp->crt->getPlayer();
		cp = cp->next_tag;

		if(pCreature != player && player->canSee(pCreature)) {

			// other non-vis rules
			if(!staff) {
				if(pCreature->flagIsSet(P_HIDDEN)) {
					// if we're using magic to see hidden creatures
					if(!magicShowHidden)
						continue;
					if(pCreature->isEffected("resist-magic")) {
						// if resisting magic, we use the strength of each spell to
						// determine if they are seen
						EffectInfo* effect = pCreature->getEffect("resist-magic");
						if(effect->getStrength() >= magicShowHidden)
							continue;
					}
				}
			}


			if(n)
				oStr << ", ";
			else
				oStr << "You see ";

			oStr << pCreature->fullName();

			if(pCreature->isStaff())
				oStr << " the " << pCreature->getTitle();

			if(pCreature->flagIsSet(P_SLEEPING))
				oStr << "(sleeping)";
			else if(pCreature->flagIsSet(P_UNCONSCIOUS))
				oStr << "(unconscious)";
			else if(pCreature->flagIsSet(P_SITTING))
				oStr << "(sitting)";

			if(pCreature->flagIsSet(P_AFK))
				oStr << "(afk)";

			if(pCreature->isEffected("petrification"))
				oStr << "(statue)";

			if(staff) {
				if(pCreature->flagIsSet(P_HIDDEN))
					oStr << "(h)";
				if(pCreature->isInvisible())
					oStr << "(*)";
				if(pCreature->flagIsSet(P_MISTED))
					oStr << "(m)";
				if(pCreature->flagIsSet(P_OUTLAW))
					oStr << "(o)";
			}
			n++;
		}
	}

	if(n)
		oStr << ".\n";
	oStr << "^m";

	cp = room->first_mon;
	n=0;

	while(cp) {
		creature = cp->crt;
		cp = cp->next_tag;

		if(staff || (player->canSee(creature) && (!creature->flagIsSet(M_HIDDEN) || magicShowHidden))) {
			m=1;
			while(cp) {
				if(	!strcmp(cp->crt->name, creature->name) &&
					(staff || (player->canSee(cp->crt) && (!cp->crt->flagIsSet(M_HIDDEN) || magicShowHidden))) &&
					creature->isInvisible() == cp->crt->isInvisible()
				) {
					m++;
					cp = cp->next_tag;
				} else
					break;
			}

			if(n)
				oStr << ", ";
			else
				oStr << "You see ";

			oStr << crt_str(creature, m, flags);

			if(staff) {
				if(creature->flagIsSet(M_HIDDEN))
					oStr << "(h)";
				if(creature->isInvisible())
					oStr << "(*)";
			}

			n++;
		}
	}

	if(n)
		oStr << ".\n";

	str = listObjects(player, room->first_obj, false, 'y');
	if(str != "")
		oStr << "^yYou see " << str << ".^w\n";

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

	cp = room->first_mon;
	while(cp) {
		if(cp->crt->first_enm) {
			creature = find_exact_crt(player, room->first_ply, cp->crt->first_enm->enemy, 1);

			if(creature == player)
				player->printColor("^r%M is attacking you.\n", cp->crt);
			else if(creature)
				player->printColor("^r%M is attacking %N.\n", cp->crt, creature);
			oStr << str;
		}
		cp = cp->next_tag;
	}

	player->print("\n");
}

void display_rom(Player* player, Player *looker, int magicShowHidden) {
	if(!looker)
		looker = player;
	if(player->parent_rom)
		displayRoom(looker, player->parent_rom, player->parent_rom, 0, magicShowHidden);
	else
		displayRoom(looker, player->area_room, 0, player->area_room, magicShowHidden);
}

void display_rom(Player* player, BaseRoom* room) {
	displayRoom(player, room, room->getUniqueRoom(), room->getAreaRoom(), 0);
}

//*********************************************************************
//						doCheckTraps
//*********************************************************************

// This function checks a room to see if there are any traps and whether
// the player pointed to by the first parameter fell into any of them.


int Player::doCheckTraps(Room* room) {
	Player	*ply=0;
	BaseRoom *newRoom=0;
	Room	*uRoom=0;
	ctag	*cp=0;
	Creature* target=0;
	int		toHit=0, a=0, num=0, roll=0, saved=0;
	Location l;
	Damage trapDamage;


	if(isCt()) {
		printColor("^mTrap passed.\n");
		return(0);
	}
	if(flagIsSet(P_MISTED))
		return(0);


	if(room->getTrapExit().id)
		l.room = room->getTrapExit();
	else
		l = bound;


	switch(room->getTrap()) {
	case TRAP_PIT:
	case TRAP_DART:
	case TRAP_BLOCK:
	case TRAP_NAKED:
	case TRAP_ALARM:
	case TRAP_ARROW:
	case TRAP_SPIKED_PIT:
	case TRAP_WORD:
	case TRAP_FIRE:
	case TRAP_FROST:
	case TRAP_ELEC:
	case TRAP_ACID:
	case TRAP_ROCKS:
	case TRAP_ICE:
	case TRAP_SPEAR:
	case TRAP_CROSSBOW:
	case TRAP_GASP:
	case TRAP_GASB:
	case TRAP_GASS:
	case TRAP_MUD:
	case TRAP_WEB:
	case TRAP_BONEAV:
	case TRAP_PIERCER:
		if(flagIsSet(P_PREPARED) && mrand(1,20) < dexterity.getCur()/10) {
			clearFlag(P_PREPARED);
			return(0);
		}
		clearFlag(P_PREPARED);
		if(mrand(1,100) < dexterity.getCur()/10)
			return(0);

		break;
	case TRAP_FALL:
		if(flagIsSet(P_PREPARED) && mrand(1,30) < dexterity.getCur()/20) {
			clearFlag(P_PREPARED);
			return(0);
		}
		clearFlag(P_PREPARED);
		if(mrand(1,100) < dexterity.getCur()/20)
			return(0);
		break;

	case TRAP_DISP:
		if(cClass == MAGE || cClass == LICH || cClass == CLERIC || cClass == DRUID) {
			if(flagIsSet(P_PREPARED) && mrand(1,30) < ((intelligence.getCur()/10 + piety.getCur()/10)/2)) {
				clearFlag(P_PREPARED);
				return(0);
			}
			clearFlag(P_PREPARED);
			if(mrand(1,100) < intelligence.getCur()/10)
				return(0);
		} else if(flagIsSet(P_PREPARED) && (mrand(1,100)<=5)) {
			clearFlag(P_PREPARED);
			return(0);
		}
		break;
	case TRAP_MPDAM:
	case TRAP_RMSPL:
		if(flagIsSet(P_PREPARED) && mrand(1,30) < ((intelligence.getCur()/10 + piety.getCur()/10)/2)) {
			clearFlag(P_PREPARED);
			return(0);
		}
		clearFlag(P_PREPARED);
		if(mrand(1,100) < intelligence.getCur()/10)
			return(0);
		break;
	case TRAP_TPORT:
	case TRAP_ETHEREAL_TRAVEL:
		if(flagIsSet(P_PREPARED) && mrand(1,50) < (level/2+(intelligence.getCur()/10 + piety.getCur()/10)/2)) {
			clearFlag(P_PREPARED);
			return(0);
		}
		clearFlag(P_PREPARED);
		if(mrand(1,100) < intelligence.getCur()/10)
			return(0);
		break;

	default:
		return(0);
	}


	switch(room->getTrap()) {
	case TRAP_PIT:
		if(!isEffected("levitate")) {
			newRoom = l.loadRoom(this);
			if(!newRoom)
				return(0);

			print("You fell into a pit trap!\n");
			broadcast(getSock(), getRoom(), "%M fell into a pit trap!", this);

			deleteFromRoom(room);
			addToRoom(newRoom);
			doPetFollow();

			trapDamage.set(mrand(1,15));

			if(chkSave(DEA, this, 0)) {
				print("You managed to control your fall.\n");
				trapDamage.set(trapDamage.get() / 2);
			}
			print("You lost %d hit points.\n", trapDamage.get());
			hp.decrease(trapDamage.get());

			if(hp.getCur() < 1)
				die(PIT);
			else
				checkTraps(newRoom->getUniqueRoom());
		}
		break;

	case TRAP_FALL:
		if(!isEffected("levitate") && !isEffected("fly")) {
			newRoom = l.loadRoom(this);
			if(!newRoom)
				return(0);

			print("You tumble downward uncontrollably!\n");
			broadcast(getSock(), getRoom(), "%M tumbles downward uncontrollably!", this);
			deleteFromRoom(room);
			addToRoom(newRoom);
			doPetFollow();
			trapDamage.set(mrand(1,20));

			if(chkSave(DEA, this, -25)) {
				print("You manage slow your fall.\n");
				trapDamage.set(trapDamage.get() / 2);
			}
			print("You lost %d hit points.\n", trapDamage.get());
			hp.decrease(trapDamage.get());
			if(hp.getCur() < 1)
				die(SPLAT);
			else
				checkTraps(newRoom->getUniqueRoom());
		}
		break;

	case TRAP_DISP:

		if(chkSave(LCK, this, -10))
			return(0);
		newRoom = l.loadRoom(this);
		if(!newRoom)
			return(0);


		print("You are overcome by vertigo!\n");
		broadcast(getSock(), getRoom(), "%M vanishes!", this);
		deleteFromRoom(room);
		addToRoom(newRoom);
		doPetFollow();
		checkTraps(newRoom->getUniqueRoom());
		break;

	case TRAP_CHUTE:
		newRoom = l.loadRoom(this);
		if(!newRoom)
			return(0);

		print("You fall down a chute!\n");
		broadcast(getSock(), getRoom(), "%M fell down a chute!", this);
		deleteFromRoom(room);
		addToRoom(newRoom);
		doPetFollow();
		checkTraps(newRoom->getUniqueRoom());

		break;

	case TRAP_ROCKS:
		if(!isEffected("fly")) {
			print("You have triggered a rock slide!\n");
			broadcast(getSock(), getRoom(), "%M triggers a rock slide!", this);

			rock_slide(this);
		}
		break;

	case TRAP_BONEAV:
		newRoom = l.loadRoom(this);
		if(!newRoom)
			return(0);

		print("You have triggered an avalanche of bones!\n");
		print("You are knocked down by its immense weight!\n");
		broadcast(getSock(), getRoom(), "%M was crushed by an avalanche of bones!", this);
		deleteFromRoom(room);
		addToRoom(newRoom);
		doPetFollow();
		trapDamage.set(mrand(hp.getMax() / 4, hp.getMax() / 2));

		if(chkSave(DEA, this, -15)) {
			print("You manage to only be partially buried.\n");
			trapDamage.set(trapDamage.get() / 2);
		}
		print("You lost %d hit points.\n", trapDamage.get());
		hp.decrease(trapDamage.get());
		if(hp.getCur() < 1)
			die(BONES);
		else
			checkTraps(newRoom->getUniqueRoom());

		break;

	case TRAP_SPIKED_PIT:
		if(!isEffected("levitate")) {
			newRoom = l.loadRoom(this);
			if(!newRoom)
				return(0);

			print("You fell into a pit!\n");
			print("You are impaled by spikes!\n");
			broadcast(getSock(), getRoom(), "%M fell into a pit!", this);
			broadcast(getSock(), getRoom(), "It has spikes at the bottom!");
			deleteFromRoom(room);
			addToRoom(newRoom);
			doPetFollow();
			trapDamage.set(mrand(15,30));

			if(chkSave(DEA,this,0)) {
				print("The spikes barely graze you.\n");
				trapDamage.set(trapDamage.get() / 2);
			}
			if(isBrittle())
				trapDamage.set(trapDamage.get() * 3 / 2);
			print("You lost %d hit points.\n", trapDamage.get());
			hp.decrease(trapDamage.get());
			if(hp.getCur() < 1)
				die(SPIKED_PIT);
			else
				checkTraps(newRoom->getUniqueRoom());
		}
		break;

	case TRAP_DART:
		print("You triggered a hidden dart!\n");
		broadcast(getSock(), getRoom(), "%M gets hit by a hidden poisoned dart!", this);
		trapDamage.set(mrand(1,10));
		if(chkSave(DEA,this,-1)) {
			print("The dart barely scratches you.\n");
			trapDamage.set(trapDamage.get() / 2);
		}

		print("You lost %d hit points.\n", trapDamage.get());
		if(!immuneToPoison() && !chkSave(POI, this, -1)) {
			num = room->getTrapStrength() ? room->getTrapStrength() : 2;

			poison(0, num, standardPoisonDuration(num, constitution.getCur()));
			poisonedBy = "a poisoned dart";
		}
		hp.decrease(trapDamage.get());
		if(hp.getCur() < 1)
			die(DART);
		break;

	case TRAP_ARROW:
		print("You triggered an arrow trap!\n");
		print("A flight of arrows peppers you!\n");
		broadcast(getSock(), getRoom(), "%M gets hit by a flight of arrows!", this);
		trapDamage.set(mrand(15,20));

		if(chkSave(DEA,this,0)) {
			print("You manage to dive and take half damage.\n");
			trapDamage.set(trapDamage.get() / 2);
		}
		print("You lost %d hit points.\n", trapDamage.get());

		hp.decrease(trapDamage.get());

		if(hp.getCur() < 1)
			die(ARROW);
		break;

	case TRAP_SPEAR:
		print("You triggered a spear trap!\n");
		print("A giant spear impales you!\n");
		broadcast(getSock(), getRoom(), "%M gets hit by a giant spear!", this);
		trapDamage.set(mrand(10, 15));

		if(chkSave(DEA, this, 0)) {
			print("The glances off of you.\n");
			trapDamage.set(trapDamage.get() / 2);
		}
		print("You lost %d hit points.\n", trapDamage.get());

		hp.decrease(trapDamage.get());

		if(hp.getCur() < 1)
			die(SPEAR);
		break;

	case TRAP_CROSSBOW:
		print("You triggered a crossbow trap!\n");
		print("A giant poisoned crossbow bolt hits you in the chest!\n");
		broadcast(getSock(), getRoom(), "%M gets hit by a giant crossbow bolt!", this);
		trapDamage.set(mrand(20, 25));

		if(chkSave(DEA, this, 0)) {
			print("The bolt barely scratches you.\n");
			trapDamage.set(trapDamage.get() / 2);
		}


		if(!immuneToPoison() && !chkSave(POI, this, 0)) {
			num = room->getTrapStrength() ? room->getTrapStrength() : 7;

			poison(0, num, standardPoisonDuration(num, constitution.getCur()));
			poisonedBy = "a poisoned crossbow bolt";
		}

		print("You lost %d hit points.\n", trapDamage.get());

		hp.decrease(trapDamage.get());

		if(hp.getCur() < 1)
			die(CROSSBOW_TRAP);
		break;

	case TRAP_GASP:
		print("You triggered a gas trap!\n");
		print("Gas rapidly fills the room!\n");
		broadcast(getSock(), getRoom(), "Gas rapidly fills the room!");

		if(!immuneToPoison()) {
			if(!chkSave(POI, this, -15)) {
				print("The billowing green cloud surrounds you!\n");

				num = room->getTrapStrength() ? room->getTrapStrength() : 15;

				poison(0, num, standardPoisonDuration(num, constitution.getCur()));
				poisonedBy = "poison gas";
			} else {
				print("You managed to hold your breath.\n");
			}
		} else {
			print("The billowing cloud of green gas has no effect on you.\n");
		}

		break;

	case TRAP_GASB:
		print("You triggered a gas trap!\n");
		print("Gas rapidly fills the room!\n");
		broadcast(getSock(), getRoom(), "Gas rapidly fills the room!");

		if(!chkSave(DEA, this, 0)) {
			print("The billowing red cloud blinds your eyes!\n");
			addEffect("blindness", 120 - (constitution.getCur()/10), 1);
		}
		break;

	case TRAP_GASS:
		print("You triggered a gas trap!\n");
		print("Gas rapidly fills the room!\n");
		broadcast(getSock(), getRoom(), "Gas rapidly fills the room!");

		cp = getRoom()->first_ply;
		while(cp) {
			if(!cp->crt->chkSave(DEA,cp->crt,0)) {
				if(!cp->crt->flagIsSet(P_RESIST_STUN)) {
					cp->crt->print("Billowing white clouds surrounds you!\n");
					cp->crt->print("You are stunned!\n");
					cp->crt->stun(mrand(10, 18));
				} else {
					cp->crt->print("The billowing cloud of white gas has no effect on you.\n");
				}
			} else
				cp->crt->print("The billowing cloud of white gas has no effect on you.\n");
			cp = cp->next_tag;
		}
		break;

	case TRAP_MUD:
	case TRAP_WEB:

		if(room->getTrap() == TRAP_MUD) {
			print("You sink into chest high mud!\n");
			print("You are stuck!\n");
			broadcast(getSock(), getRoom(), "%M sank chest deep into some mud!", this);
		} else if(room->getTrap() == TRAP_WEB) {
			print("You brush against some large spider webs!\n");
			print("You are stuck!\n");
			broadcast(getSock(), getRoom(), "%M stuck to some large spider webs!", this);
		}

		if(chkSave(DEA, this , -1))
			stun(mrand(5,10));
		else
			stun(mrand(20,30));
		break;

	case TRAP_ETHEREAL_TRAVEL:

		if(checkDimensionalAnchor()) {
			printColor("^yYour dimensional-anchor protects you from the ethereal-travel trap!^w\n");
		} else if(!chkSave(SPL, this, -25)) {
			printColor("^BYou become tranlucent and fade away!\n");
			printColor("You've been transported to the ethereal plane!\n");
			broadcast(getSock(), getRoom(), "^B%N becomes translucent and fades away!", this);
			etherealTravel(this);
		} else {
			print("You feel a warm feeling all over.\n");
		}
		break;


	case TRAP_PIERCER:
		num = MAX(1,MIN(8,mrand((room->getTrapStrength()+1)/2, room->getTrapStrength()+1)));
		for(a=0;a<num;a++) {
			target = getRandomPlayer(room);
			if(!target)
				target = getRandomMonster(room);
			if(!target)
				continue;

			ply = target->getPlayer();

			if(ply && ply->flagIsSet(P_DM_INVIS))
				continue;

			trapDamage.reset();
			trapDamage.set(mrand(target->hp.getMax()/20, target->hp.getMax()/10));
			target->modifyDamage(0, PHYSICAL, trapDamage);

			toHit = 14 - (trapDamage.get()/10);
			if(target->isInvisible() && target->isPlayer())
				toHit= 20 - (trapDamage.get()/10);
			roll = mrand(1,20);

			if(roll >= toHit) {
				broadcast(target->getSock(), room, "A living stalagtite falls upon %N from above!", target);
				target->print("A living stalagtite falls on you from above!\n");
				saved = chkSave(DEA, this, 0);
				if(saved)
					target->print("It struck a glancing blow.\n");
				target->printColor("You are hit for %s%d^x damage.\n", target->customColorize("*CC:DAMAGE*"), trapDamage.get());

				target->hp.decrease(trapDamage.get());
				if(target->hp.getCur() < 1) {
					if(ply) {
						ply->die(PIERCER);
						continue;
					} else {
						Monster* mon = target->getMonster();
						broadcast(NULL,  room, "%M was killed!", mon);
						if(mon->flagIsSet(M_PERMENANT_MONSTER))
							mon->diePermCrt();
						mon->deleteFromRoom();

						free_crt(target);
						continue;
					}
				}
				continue;
			} else {
				target->print("A falling stalagtite almost hit you!\n");
				broadcast(target->getSock(), room, "A falling stalagtite almost hits %N!", target);
				continue;
			}

		}
		break;

	case TRAP_FIRE:
		print("You set off a fire trap!\n");
		print("Flames engulf you!\n");
		broadcast(getSock(), getRoom(), "%M is engulfed by flames!", this);
		trapDamage.set(mrand(20,40));

		if(chkSave(BRE, this, 0)) {
			trapDamage.set(trapDamage.get() / 2);
			print("You are only half hit by the explosion.\n");
		}

		if(isEffected("heat-protection") || isEffected("alwayswarm"))
			trapDamage.set(trapDamage.get() / 2);
		if(isEffected("resist-fire"))
			trapDamage.set(trapDamage.get() / 2);

		printColor("You are burned for %s%d^x damage.\n", customColorize("*CC:DAMAGE*"), trapDamage.get());

		hp.decrease(trapDamage.get());

		if(hp.getCur() < 1)
			die(FIRE_TRAP);
		break;

	case TRAP_FROST:
		print("You are hit by a blast of ice!\n");
		print("Frost envelopes you!\n");
		broadcast(getSock(), getRoom(), "%M is engulfed by a cloud of frost!", this);
		trapDamage.set(mrand(20,40));

		if(chkSave(BRE, this, 0)) {
			trapDamage.set(trapDamage.get() / 2);
			print("You are only partially frostbitten.\n");
		}

		if(isEffected("warmth") || isEffected("alwayscold"))
			trapDamage.set(trapDamage.get() / 2);
		if(isEffected("resist-cold"))
			trapDamage.set(trapDamage.get() / 2);

		printColor("You are frozen for %s%d^x damage.\n", customColorize("*CC:DAMAGE*"), trapDamage.get());

		hp.decrease(trapDamage.get());

		if(hp.getCur() < 1)
			die(FROST);
		break;

	case TRAP_ELEC:
		print("You are hit by a crackling blue bolt!\n");
		print("Electricity pulses through your body!\n");
		broadcast(getSock(), getRoom(), "%M is surrounded by crackling blue arcs!", this);
		trapDamage.set(mrand(20,40));

		if(chkSave(BRE, this, 0)) {
			trapDamage.set(trapDamage.get() / 2);
			print("The electricity only half engulfed you.\n");
		}

		if(isEffected("resist-electric"))
			trapDamage.set(trapDamage.get() / 2);

		printColor("You are electrocuted for %s%d^x damage.\n", customColorize("*CC:DAMAGE*"), trapDamage.get());

		hp.decrease(trapDamage.get());

		if(hp.getCur() < 1)
			die(ELECTRICITY);
		break;

	case TRAP_ACID:
		print("You are blasted by a jet of acid!\n");
		print("You are immersed in dissolving liquid.\n");
		broadcast(getSock(), getRoom(), "%M is immersed in acid!", this);
		trapDamage.set(mrand(20,30));

		if(chkSave(BRE, this, 0)) {
			trapDamage.set(trapDamage.get() / 2);
			print("The acid doesn't totally cover you.\n");
		} else {
			print("Your skin dissolves to the bone.\n");
			loseAcid();
		}


		printColor("You are burned for %s%d^x damage.\n", customColorize("*CC:DAMAGE*"), trapDamage.get());

		hp.decrease(trapDamage.get());


		if(hp.getCur() < 1)
			die(ACID);
		break;

	case TRAP_BLOCK:
		print("You triggered a falling block!\n");
		broadcast(getSock(), getRoom(), "A large block falls on %N.", this);
		trapDamage.set(hp.getMax() / 2);

		if(chkSave(DEA, this, -5)) {
			trapDamage.set(trapDamage.get() / 2);
			print("The block hits you in a glancing blow\nas you quickly dive out of the way..\n");
		}
		print("You lost %d hit points.\n", trapDamage.get());
		hp.decrease(trapDamage.get());
		if(hp.getCur() < 1)
			die(BLOCK);
		break;

	case TRAP_ICE:
		print("A giant icicle falls from above!\n");
		broadcast(getSock(), getRoom(), "%N's vibrations caused a giant icicle to drop!", this);
		trapDamage.set(hp.getMax() / 2);

		if(chkSave(DEA, this, -5)) {
			trapDamage.set(trapDamage.get() / 2);
			print("The icicle slashes you but doesn't impale you.\n");
		} else {
			print("You are impaled!\n");
			broadcast(getSock(), getRoom(), "%N is impaled by a huge icicle!", this);
		}
		print("You lost %d hit points.\n", trapDamage.get());
		hp.decrease(trapDamage.get());
		if(hp.getCur() < 1)
			die(ICICLE_TRAP);
		break;

	case TRAP_MPDAM:

		if(mp.getCur() <= 0)
			break;

		print("You feel an exploding force in your mind!\n");
		broadcast(getSock(), getRoom(), "An energy bolt strikes %N.", this);

		if(chkSave(MEN, this,0)) {
			trapDamage.set(MIN(mp.getCur(),mp.getMax() / 2));
			print("You lost %d magic points.\n", trapDamage.get());
		} else {
			trapDamage.set(mp.getCur());
			print("You feel the magical energy sucked from your mind.\n");
		}

		mp.decrease(trapDamage.get());
		break;

	case TRAP_RMSPL:
		print("A foul smelling charcoal cloud surrounds you.\n");

		broadcast(getSock(), getRoom(), "A charcoal cloud surrounds %N.", this);
		if(!chkSave(SPL, this,0)) {
			doDispelMagic();
			print("Your magic begins to dissolve.\n");
		}
		break;

	case TRAP_NAKED:
		printColor("^gYou are covered in oozing green slime.\n");

		if(chkSave(DEA, this, 0)) {
			broadcast(getSock(), getRoom(), "^gA foul smelling and oozing green slime envelops %N.", this);
			stun(mrand(1,10));
		} else {
			print("Your possessions begin to dissolve!\n");
			lose_all(this, false);
			stun(mrand(5,20));
		}
		break;

	case TRAP_TPORT:

		if(checkDimensionalAnchor()) {
			printColor("^yYour dimensional-anchor protects you from the teleportation trap!^w\n");
		} else if(!chkSave(SPL, this, 0)) {
			printColor("^yThe world changes rapidly around you!\n");
			printColor("You've been teleported!\n");
			broadcast(getSock(), getRoom(), "%N disappears!", this);
			teleport_trap(this);
		} else {
			print("You feel a strange tingling all over.\n");
		}
		break;



	case TRAP_WORD:

		if(checkDimensionalAnchor()) {
			printColor("^yYour dimensional-anchor protects you from the word-of-recall trap!^w\n");
		} else {
			printColor("^cYou phase in and out of existence.\n");
			broadcast(getSock(), getRoom(), "%N disappears.", this);

			newRoom = getRecallRoom().loadRoom(this);
			if(!newRoom)
				break;
			deleteFromRoom();
			addToRoom(newRoom);
			doPetFollow();
			break;
		}

	case TRAP_ALARM:
		print("You set off an alarm!\n");
		print("You hope there aren't any guards around.\n\n");
		broadcast(getSock(), getRoom(), "%M sets off an alarm!\n", this);

		CatRef	acr;

		if(room->getTrapExit().id)
			acr = room->getTrapExit();
		else {
			acr = room->info;
			acr.id++;
		}

		if(!loadRoom(acr, &uRoom))
			return(0);

		uRoom->addPermCrt();
		cp = uRoom->first_mon;
		Monster *tmp_crt=0;
		while(cp) {
			tmp_crt = (Monster*)cp->crt;
			cp = cp->next_tag;
			if(tmp_crt->flagIsSet(M_PERMENANT_MONSTER)) {
				if(uRoom->first_ply)
					broadcast(tmp_crt->getSock(), tmp_crt->getRoom(),
						"%M hears an alarm and leaves to investigate.",tmp_crt);
				else
					gServer->addActive(tmp_crt);
				tmp_crt->clearFlag(M_PERMENANT_MONSTER);
				tmp_crt->setFlag(M_AGGRESSIVE);
				tmp_crt->diePermCrt();
				tmp_crt->deleteFromRoom();
				tmp_crt->addToRoom(room);
				broadcast(tmp_crt->getSock(), tmp_crt->getRoom(),
					"%M comes to investigate the alarm.", tmp_crt);
			}
		}
		break;
	}
	return(2);
}


//*********************************************************************
//						createStorage
//*********************************************************************
// This function creates a storage room. Setting all the flags, and
// putting in a generic description

void storageName(Room* room, char* name) {
	sprintf(room->name, "%s's Personal Storage Room", name);
}

int createStorage(CatRef cr, char *name) {
	Room *newRoom;
	bstring desc = "";

	newRoom = new Room;
	if(!newRoom)
		merror("createStorage", FATAL);

	newRoom->info = cr;
	storageName(newRoom, name);

	newRoom->setShortDescription("You are in your personal storage room.");
	newRoom->setLongDescription("The realtor's office has granted you this space in order to store excess\nequipment. Since you have paid quite a bit for this room, you may keep it\nindefinitely, and you may access it from any storage room shop in the\ngame. However, you may occasionally have to buy a new key, which you may\npurchase in the main Office. The chest on the floor is for your use. It\nwill hold 100 items, including those of the same type. For now you may\nonly have one chest. The realtor's office will possibly offer more then\none chest at a future time. You will be informed when this occurs.");

	CatRef sr;
	sr.id = 2633;
	link_rom(newRoom, sr, "out");
	newRoom->first_ext->ext->setFlag(X_TO_PREVIOUS);

	// reuse catref
	cr.id = 1;
	newRoom->permObjects[0].cr =
	newRoom->permObjects[1].cr =
	newRoom->permObjects[2].cr =
	newRoom->permObjects[3].cr =
	newRoom->permObjects[4].cr =
	newRoom->permObjects[5].cr =
	newRoom->permObjects[6].cr =
	newRoom->permObjects[7].cr =
	newRoom->permObjects[8].cr =
	newRoom->permObjects[9].cr = cr;

	newRoom->permObjects[0].interval =
	newRoom->permObjects[1].interval =
	newRoom->permObjects[2].interval =
	newRoom->permObjects[3].interval =
	newRoom->permObjects[4].interval =
	newRoom->permObjects[5].interval =
	newRoom->permObjects[6].interval =
	newRoom->permObjects[7].interval =
	newRoom->permObjects[8].interval =
	newRoom->permObjects[9].interval = (long)10;

	newRoom->setFlag(R_IS_STORAGE_ROOM);
	newRoom->setFlag(R_FAST_HEAL);
	newRoom->setFlag(R_NO_SUMMON_OUT);
	newRoom->setFlag(R_NO_TELEPORT);
 	newRoom->setFlag(R_NO_TRACK_TO);
 	newRoom->setFlag(R_NO_SUMMON_TO);
	newRoom->setFlag(R_OUTLAW_SAFE);
	newRoom->setFlag(R_INDOORS);

	Property *p = new Property;
	p->setOwner(name);
	p->setName(newRoom->name);
	p->setDateFounded();
	p->setLocation("any realty office");
	p->addRange(newRoom->info);
	p->setType(PROP_STORAGE);
	gConfig->addProperty(p);

	if(newRoom->saveToFile(0) < 0)
		return(0);

	delete newRoom;
	return(0);
}

//*********************************************************************
//						validatePerms
//*********************************************************************

void Room::validatePerms() {
	std::map<int, crlasttime>::iterator it;
	crlasttime* crtm=0;
	long	t = time(0);

	for(it = permMonsters.begin(); it != permMonsters.end() ; it++) {
		crtm = &(*it).second;
		if(crtm->ltime > t) {
			crtm->ltime = t;
			logn("log.validate", "Perm #%d(%s) in Room %s (%s): Time has been revalidated.\n",
				(*it).first+1, crtm->cr.str().c_str(), info.str().c_str(), name);
			broadcast(isCt, "^yPerm Mob #%d(%s) in Room %s (%s) has been revalidated",
				(*it).first+1, crtm->cr.str().c_str(), info.str().c_str(), name);
		}
	}
	for(it = permObjects.begin(); it != permObjects.end() ; it++) {
		crtm = &(*it).second;
		if(crtm->ltime > t) {
			crtm->ltime = t;
			logn("log.validate", "Perm Obj #%d(%s) in Room %s (%s): Time has been revalidated.\n",
				(*it).first+1, crtm->cr.str().c_str(), info.str().c_str(), name);

			broadcast(isCt, "^yPerm Obj #%d(%s) in Room %s (%s) has been revalidated.",
				(*it).first+1, crtm->cr.str().c_str(), info.str().c_str(), name);
		}
	}
}

//*********************************************************************
//						doRoomHarms
//*********************************************************************

void doRoomHarms(BaseRoom *inRoom, Player* target) {
	int		roll=0, toHit=0, dmg=0;

	if(!inRoom || !target)
		return;

	// elven archers
	if(inRoom->flagIsSet(R_ARCHERS)) {
		if(	target->flagIsSet(P_HIDDEN) ||
			target->isInvisible() ||
			target->flagIsSet(P_MISTED) ||
			target->flagIsSet(P_DM_INVIS)
		) {
			return;
		}

		roll = mrand(1,20);
		toHit = 10 - target->getArmor()/10;
		toHit = MAX(MIN(toHit,20), 1);


		if(roll >= toHit) {
			dmg = mrand(1,8) + mrand(1,2);
			target->printColor("A deadly arrow strikes you from above for %s%d^x damage.\n", target->customColorize("*CC:DAMAGE*"), dmg);
			broadcast(target->getSock(), inRoom, "An arrow strikes %s from the trees above!", target->name);

			target->hp.decrease(dmg);
			if(target->hp.getCur() < 1) {
				target->print("The arrow killed you.\n");
				broadcast(target->getSock(), inRoom, "The arrow killed %s!", target->name);
				target->die(ELVEN_ARCHERS);
				return;
			}
		} else {
			target->print("An arrow whizzes past you from above!\n");
			broadcast(target->getSock(), inRoom, "An arrow whizzes past %s from above!", target->name);
		}
	}


	// deadly moss
	if(inRoom->flagIsSet(R_DEADLY_MOSS)) {
		if(target->flagIsSet(P_DM_INVIS) || target->getClass() == LICH)
			return;

		dmg = 15 - MIN(bonus((int)target->constitution.getCur()),2) + mrand(1,3);
		target->printColor("Deadly underdark moss spores envelope you for %s%d^x damage!\n", target->customColorize("*CC:DAMAGE*"), dmg);
		broadcast(target->getSock(), inRoom, "Spores from deadly underdark moss envelope %s!", target->name);

		target->hp.decrease(dmg);
		if(target->hp.getCur() < 1) {
			target->print("The spores killed you.\n");
			broadcast(target->getSock(), inRoom, "The spores killed %s!", target->name);
			target->die(DEADLY_MOSS);
		}
	}
}

//*********************************************************************
//						abortFindRoom
//*********************************************************************
// When this function is called, we're in trouble. Perhaps teleport searched
// too long, or some vital room (like The Origin) couldn't be loaded. We need
// to hurry up and find a room to take the player - if we can't, we need to
// crash the mud.

BaseRoom *abortFindRoom(Creature* player, const char from[15]) {
	BaseRoom* room=0;
	Room*	newRoom=0;

	player->print("Shifting dimensional forces direct your travel!\n");
	loge("Error: abortFindRoom called by %s in %s().\n", player->name, from);
	broadcast(isCt, "^yError: abortFindRoom called by %s in %s().", player->name, from);

	room = player->getRecallRoom().loadRoom(player->getPlayer());
	if(room)
		return(room);
	broadcast(isCt, "^yError: could not load Recall: %s.", player->getRecallRoom().str().c_str());

	room = player->getLimboRoom().loadRoom(player->getPlayer());
	if(room)
		return(room);
	broadcast(isCt, "^yError: could not load Limbo: %s.", player->getLimboRoom().str().c_str());

	Player *target=0;
	if(player)
		target = player->getPlayer();
	if(target) {
		room = target->bound.loadRoom(target);
		if(room)
			return(room);
		broadcast(isCt, "^yError: could not load player's bound room: %s.",
			target->bound.str().c_str());
	}

	if(loadRoom(0, &newRoom))
		return(newRoom);
	broadcast(isCt, "^yError: could not load The Void. Aborting.");
	crash(-1);

	// since we "technically" have to return something to make compiler happy
	return(room);
}

//*********************************************************************
//						countForWeightTrap
//*********************************************************************

bool Creature::countForWeightTrap() const {
	if(isMonster())
		return(!isEffected("levitate") && !isEffected("fly"));
	return(!isStaff() && !flagIsSet(P_MISTED) && !isEffected("levitate") && !isEffected("fly"));
}

//*********************************************************************
//						getWeight
//*********************************************************************

int Room::getWeight() {
	ctag	*cp=0;
	otag	*op=0;
	int		i=0;

	// count weight of all players in this
	cp = first_ply;
	while(cp) {
		if(cp->crt->countForWeightTrap())
			i += cp->crt->getWeight();
		cp = cp->next_tag;
	}

	// count weight of all objects in this
	op = first_obj;
	while(op) {
		i += op->obj->getActualWeight();
		op = op->next_tag;
	}

	// count weight of all monsters in this
	cp = first_mon;
	while(cp) {
		if(cp->crt->countForWeightTrap()) {
			op = cp->crt->first_obj;
			while(op) {
				i += op->obj->getWeight();
				op = op->next_tag;
			}
		}
		cp = cp->next_tag;
	}

	return(i);
}

//*********************************************************************
//						checkTraps
//*********************************************************************
// A wrapper for do_check_traps so we can have a trap spring
// on everyone in the room. self is whether or not they moved themselves
// into a room - followers of a leader ALL springing weight traps would be bad.

int Player::checkTraps(Room* room, bool self) {
	if(!room)
		return(0);

	ctag	*cp=0;
	Player	*target=0;

	if(!room->getTrap()) {
		clearFlag(P_PREPARED);
		return(0);
	}

	// if it's not weight-based, continue normally
	if(!room->getTrapWeight()) {
		doCheckTraps(room);
	} else if(
		self &&
		countForWeightTrap() &&
		room->getWeight() >= room->getTrapWeight()
	) {
		// some traps already do area-of-effect; if we do them in
		// this loop, it'd be bad. run them normally.
		switch(room->getTrap()) {
			case TRAP_ROCKS:
			case TRAP_GASS:
			case TRAP_PIERCER:
			case TRAP_ALARM:
			case TRAP_BONEAV:
				return(doCheckTraps(room));
			default:
				break;
		}

		// we loop through everyone in the room!
		cp = room->first_ply;
		while(cp) {
			target = cp->crt->getPlayer();
			cp = cp->next_tag;
			if(target)
				target->doCheckTraps(room);
		}
	}
	return(0);
}

//*********************************************************************
//						needUniqueRoom
//*********************************************************************

bool needUniqueRoom(const Creature* player) {
	if(!player->parent_rom) {
		player->print("You can't do that here.\n");
		return(false);
	}
	return(true);
}