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/
/*
 * area.cpp
 *   Everything related to the overland map
 *   ____            _
 *  |  _ \ ___  __ _| |_ __ ___  ___
 *  | |_) / _ \/ _` | | '_ ` _ \/ __|
 *  |  _ <  __/ (_| | | | | | | \__ \
 *  |_| \_\___|\__,_|_|_| |_| |_|___/
 *
 * 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 "effects.h"
#include "calendar.h"
#include <math.h>
#include <dirent.h>
#include <fstream>

//*********************************************************************
//						AreaTrack
//*********************************************************************

AreaTrack::AreaTrack() {
	duration = 0;
}

int AreaTrack::getDuration() const { return(duration); }
void AreaTrack::setDuration(int dur) { duration = dur; }

//*********************************************************************
//						MapMarker
//*********************************************************************

MapMarker::MapMarker() {
	reset();
}

short MapMarker::getArea() const { return(area); }
short MapMarker::getX() const { return(x); }
short MapMarker::getY() const { return(y); }
short MapMarker::getZ() const { return(z); }


void MapMarker::setArea(short n) { area = n; }
void MapMarker::setX(short n) { x = n; }
void MapMarker::setY(short n) {y  = n; }
void MapMarker::setZ(short n) { z = n; }
void MapMarker::set(short _area, short _x, short _y, short _z) {
	area = _area;
	x = _x;
	y = _y;
	z = _z;
}
void MapMarker::add(short _x, short _y, short _z) {
	x += _x;
	y += _y;
	z += _z;
}


void MapMarker::reset() {
	area = x = y = z = 0;
}

MapMarker& MapMarker::operator=(const MapMarker& m) {
	area = m.getArea();
	x = m.getX();
	y = m.getY();
	z = m.getZ();
	return(*this);
}
bool MapMarker::operator==(const MapMarker& m) const {
	return(area == m.getArea() &&
		x == m.getX() &&
		y == m.getY() &&
		z == m.getZ());
}
bool MapMarker::operator!=(const MapMarker& m) const {
	return(!(*this == m));
}

//*********************************************************************
//						load
//*********************************************************************

void MapMarker::load(bstring str) {
	// trim
	str = str.right(str.getLength()-3);
	str = str.left(str.getLength()-1);

	area = atoi(str.c_str());
	x = atoi(getFullstrText(str, 1, ':').c_str());
	y = atoi(getFullstrText(str, 2, ':').c_str());
	z = atoi(getFullstrText(str, 3, ':').c_str());
}

//*********************************************************************
//						load
//*********************************************************************

void MapMarker::load(xmlNodePtr curNode) {
	xmlNodePtr childNode = curNode->children;
	reset();

	while(childNode) {
		if(NODE_NAME(childNode, "Area")) xml::copyToNum(area, childNode);
		else if(NODE_NAME(childNode, "X")) xml::copyToNum(x, childNode);
		else if(NODE_NAME(childNode, "Y")) xml::copyToNum(y, childNode);
		else if(NODE_NAME(childNode, "Z")) xml::copyToNum(z, childNode);

		childNode = childNode->next;
	}
}

//*********************************************************************
//						save
//*********************************************************************

void MapMarker::save(xmlNodePtr curNode) const {
	xml::saveNonZeroNum(curNode, "Area", area);
	xml::saveNonZeroNum(curNode, "X", x);
	xml::saveNonZeroNum(curNode, "Y", y);
	xml::saveNonZeroNum(curNode, "Z", z);
}

//*********************************************************************
//						str
//*********************************************************************

bstring MapMarker::str(bool color) const {
	std::ostringstream oStr;
	bstring sep = ":";
	if(color) {
		sep = "^b:^w";
		oStr << "^b";
	} else
		oStr << "(";

	oStr  << "A" << sep << (int)area << sep << (int)x << sep << (int)y << sep << (int)z;

	if(!color)
		oStr << ")";
	return(oStr.str());
}


//*********************************************************************
//						direction
//*********************************************************************
// judges the direction to the target

bstring MapMarker::direction(const MapMarker *mapmarker) const {
	if(area != mapmarker->area || *this == *mapmarker)
		return("nowhere");

	// the 360 degrees have been divided into 24 sections, 15 degrees each
	bstring dir = "";
	double dY = y - mapmarker->y;
	double dX = x - mapmarker->x;
	double dZ = z - mapmarker->z;
	double angle = 0.0;

	// do the standard plane
	if(x == mapmarker->getX()) {
		if(y > mapmarker->getY()) {
			dir = "south";
		} else if(y < mapmarker->getY()) {
			dir = "north";
		}
	} else if(y == mapmarker->getY()) {
		if(x > mapmarker->getX()) {
			dir = "west";
		} else if(x < mapmarker->getX()) {
			dir = "east";
		}
	} else {

		angle = atan(dY / dX) * RADIAN;

		if(y > mapmarker->getY()) {

			if(angle < -75)
				dir = "south";
			else if(angle < -60)
				dir = "south southeast";
			else if(angle < -30)
				dir = "southeast";
			else if(angle < -15)
				dir = "east southeast";
			else if(angle < 0)
				dir = "east";
			else if(angle < 15)
				dir = "west";
			else if(angle < 30)
				dir = "west southwest";
			else if(angle < 60)
				dir = "southwest";
			else if(angle < 75)
				dir = "south southwest";
			else
				dir = "south";

		} else if(y < mapmarker->getY()) {

			if(angle < -75)
				dir = "north";
			else if(angle < -60)
				dir = "north northwest";
			else if(angle < -30)
				dir = "northwest";
			else if(angle < -15)
				dir = "west northwest";
			else if(angle < 0)
				dir = "west";
			else if(angle < 15)
				dir = "east";
			else if(angle < 30)
				dir = "east northeast";
			else if(angle < 60)
				dir = "northeast";
			else if(angle < 75)
				dir = "north northeast";
			else
				dir = "north";

		}
	}

	// now do elevation
	if(z != mapmarker->getZ()) {

		if(y == mapmarker->getY() && x == mapmarker->getX()) {
			if(z > mapmarker->getZ())
				dir = "straight down";
			else
				dir = "straight up";
		} else {

			dir += ", ";
			// Pythagorean theorem
			angle = abs((int)(atan(dZ / pow(pow(dX, 2) + pow(dY, 2), 0.5)) * RADIAN));

			if(angle < 15)
				dir += "slight";
			else if(angle < 30)
				dir += "gradual";
			else if(angle < 60)
				dir += "moderate";
			else
				dir += "steep";

			dir += " ";

			if(z > mapmarker->getZ())
				dir += "descent";
			else
				dir += "ascent";

		}
	}

	return(dir);
}

//*********************************************************************
//						distance
//*********************************************************************
// estimates the distance between two points

bstring MapMarker::distance(const MapMarker *mapmarker) const {
	// we don't understand distance in these scenarios
	if(area != mapmarker->getArea() || *this == *mapmarker)
		return("");

	int distance = (MAX(abs(z - mapmarker->getZ()), MAX(abs(x - mapmarker->getX()), abs(y - mapmarker->getY()))));

	// The target is...
	if(distance <= 3)
		return("very close by");
	else if(distance <= 9)
		return("close by");
	else if(distance <= 15)
		return("in the vicinity");
	else if(distance <= 25)
		return("a ways away");
	else if(distance <= 45)
		return("a good distance away");
	else if(distance <= 90)
		return("far away");
	else if(distance <= 120)
		return("very far away");
	return("far, far away");
}

//*********************************************************************
//						filename
//*********************************************************************
// returns the filename this mapmarker would use

bstring MapMarker::filename() const {
	std::ostringstream oStr;
	bstring str;
	oStr << "m" << (int)x << "." << (int)y << "." << (int)z << ".xml";
	str = oStr.str();
	// svn doesnt like -, so we'll just use underscore
	str.Replace("-", "_");
	return(str);
}

//*********************************************************************
//						AreaZone
//*********************************************************************

AreaZone::AreaZone() {
	name = display = fishing = "";
	zero(terRestrict, sizeof(terRestrict));
	zero(mapRestrict, sizeof(mapRestrict));

	min.setX(1000);
	min.setY(1000);
	min.setZ(1000);
	max.setX(-1000);
	max.setY(-1000);
	max.setZ(-1000);

	zero(flags, sizeof(flags));
}

AreaZone::~AreaZone() {
	std::map<int, MapMarker*>::iterator it;
	MapMarker *mapmarker;

	for(it = coords.begin() ; it != coords.end() ; it++) {
		mapmarker = (*it).second;
		delete mapmarker;
	}
	coords.clear();
}



//*********************************************************************
//						inside
//*********************************************************************
// Point in polygon code adapted for Realms

// Copyright 2001, softSurfer (www.softsurfer.com)
// This code may be freely used and modified for any purpose providing that this
// copyright notice is included with it. SoftSurfer makes no warranty for this code,
// and cannot be held liable for any real or imagined damage resulting from its use.
// Users of this code must verify correctness for their application.

// cn_PnPoly(): crossing number test for a point in a polygon
//	Input:	P = a point,
//				V[] = vertex points of a polygon V[n+1] with V[n]=V[0]
//	Return:	0 = outside, 1 = inside
// This code is patterned after [Franklin, 2000]

bool AreaZone::inside(const Area *area, const MapMarker *mapmarker) const {
	MapMarker *vi=0, *vn=0;
	// the crossing number counter
	int		cn = 0;
	int		i=0, n = coords.size();
	float	vt = 0.0;

	// if they aren't even within the established region, don't do the work
	if(	mapmarker->getX() > max.getX() || mapmarker->getY() > max.getY() || mapmarker->getZ() > max.getZ() ||
		mapmarker->getX() < min.getX() || mapmarker->getY() < min.getY() || mapmarker->getZ() < min.getZ()
	) {
		return(false);
	}

	if(	!inRestrict(area->getTerrain(0, mapmarker, 0, 0, 0, true), terRestrict) ||
		!inRestrict(area->getTerrain(0, mapmarker, 0, 0, 0, false), mapRestrict)
	) {
		return(false);
	}


	// loop through all edges of the polygon
	// edge from V[i] to V[i+1]
	for(i=0; i<n; i++) {
		vi = (*coords.find(i)).second;
		vn = (*coords.find(i+1 < n ? i+1 : 0)).second;
		if(	(vi->getY() <= mapmarker->getY() && vn->getY() > mapmarker->getY()) ||	// an upward crossing
			(vi->getY() > mapmarker->getY() && vn->getY() <= mapmarker->getY())	// a downward crossing
		) {
			// compute the actual edge-ray intersect x-coordinate
			vt = (float)(mapmarker->getY() - vi->getY());
			vt /= (float)(vn->getY() - vi->getY());
			vt *= (float)(vn->getX() - vi->getX());
			vt += (float)vi->getX();
			// P.x < intersect
			if(mapmarker->getX() <= vt) {
				// a valid crossing of y=P.y right of P.x
				++cn;
			}
		}
	}
	// 0 if even (out), and 1 if odd (in)
	return(cn&1);
}

//*********************************************************************
//						inRestrict
//*********************************************************************

bool AreaZone::inRestrict(char tile, const char *list) const {
	int i=strlen(list);
	if(!i)
		return(true);
	for(;i>=0;i--)
		if(tile == list[i])
			return(true);
	return(false);
}

void AreaZone::save(xmlNodePtr curNode) const {
	xmlNodePtr subNode, childNode;

	xml::saveNonNullString(curNode, "Name", name);
	xml::saveNonNullString(curNode, "Display", display);
	xml::saveNonNullString(curNode, "Fishing", fishing);
	xml::saveNonNullString(curNode, "TerRestrict", terRestrict);
	xml::saveNonNullString(curNode, "MapRestrict", mapRestrict);
	unique.save(curNode, "Unique", false);
	saveBits(curNode, "Flags", MAX_ROOM_FLAGS, flags);
	wander.save(curNode);

	childNode = xml::newStringChild(curNode, "Coords");
	std::map<int, MapMarker*>::const_iterator it;
	for(it = coords.begin() ; it != coords.end() ; it++) {
		subNode = xml::newStringChild(childNode, "MapMarker");
		(*it).second->save(subNode);
	}
}


//*********************************************************************
//						load
//*********************************************************************

void AreaZone::load(xmlNodePtr curNode) {
	xmlNodePtr mNode, childNode = curNode->children;
	MapMarker *mapmarker=0;
	int		i=0;

	while(childNode) {
		if(NODE_NAME(childNode, "Name")) { xml::copyToBString(name, childNode); }
		else if(NODE_NAME(childNode, "Fishing")) { xml::copyToBString(fishing, childNode); }
		else if(NODE_NAME(childNode, "Display")) { xml::copyToBString(display, childNode); }
		else if(NODE_NAME(childNode, "TerRestrict")) xml::copyToCString(terRestrict, childNode);
		else if(NODE_NAME(childNode, "MapRestrict")) xml::copyToCString(mapRestrict, childNode);
		else if(NODE_NAME(childNode, "Unique")) unique.load(childNode);
		else if(NODE_NAME(childNode, "Flags")) loadBits(childNode, flags);
		else if(NODE_NAME(childNode, "Wander")) wander.load(childNode);
		else if(NODE_NAME(childNode, "Coords")) {
			mNode = childNode->children;
			while(mNode) {
				if(NODE_NAME(mNode, "MapMarker")) {
					mapmarker = new MapMarker;
					mapmarker->load(mNode);

					// load min/max while running through the coords
					if(mapmarker->getX() > max.getX())
						max.setX(mapmarker->getX());
					if(mapmarker->getX() < min.getX())
						min.setX(mapmarker->getX());

					if(mapmarker->getY() > max.getY())
						max.setY(mapmarker->getY());
					if(mapmarker->getY() < min.getY())
						min.setY(mapmarker->getY());

					if(mapmarker->getZ() > max.getZ())
						max.setZ(mapmarker->getZ());
					if(mapmarker->getZ() < min.getZ())
						min.setZ(mapmarker->getZ());

					coords[i++] = mapmarker;
				}
				mNode = mNode->next;
			}
		}

		childNode = childNode->next;
	}
}

bool AreaZone::flagIsSet(int flag) const {
	return(flags[flag/8] & 1<<(flag%8));
}

//*********************************************************************
//						TileInfo
//*********************************************************************

TileInfo::TileInfo() {
	id = style = display = 0;
	name = description = fishing = "";
	trackDur = cost = fly = 0;
	vision = 0.0;
	zero(flags, sizeof(flags));
	water = road = false;
	herbs.clear();
}

void TileInfo::load(xmlNodePtr curNode) {
	xmlNodePtr	childNode = curNode->children, subNode;
	Season	s;
	char	temp[2];

	while(childNode) {
			 if(NODE_NAME(childNode, "Name")) xml::copyToBString(name, childNode);
		else if(NODE_NAME(childNode, "Fishing")) xml::copyToBString(fishing, childNode);
		else if(NODE_NAME(childNode, "Description")) xml::copyToBString(description, childNode);
		else if(NODE_NAME(childNode, "Id")) {
			xml::copyToCString(temp, childNode);
			id = temp[0];
		}
		else if(NODE_NAME(childNode, "Cost")) xml::copyToNum(cost, childNode);
		else if(NODE_NAME(childNode, "Vision")) xml::copyToNum(vision, childNode);
		else if(NODE_NAME(childNode, "TrackDur")) xml::copyToNum(trackDur, childNode);
		else if(NODE_NAME(childNode, "Wander")) wander.load(childNode);
		else if(NODE_NAME(childNode, "Style")) {
			xml::copyToCString(temp, childNode);
			style = temp[0];
		}
		else if(NODE_NAME(childNode, "Display")) {
			xml::copyToCString(temp, childNode);
			display = temp[0];
		}
		else if(NODE_NAME(childNode, "Flags")) loadBits(childNode, flags);
		else if(NODE_NAME(childNode, "Fly")) xml::copyToNum(fly, childNode);
		else if(NODE_NAME(childNode, "Water")) water = true;
		else if(NODE_NAME(childNode, "Road")) road = true;
		else if(NODE_NAME(childNode, "Herbs")) {
			subNode = childNode->children;
			while(subNode) {
				if(NODE_NAME(subNode, "Herb")) {
					CatRef cr;
					cr.load(subNode);
					herbs.push_back(cr);
				}
				subNode = subNode->next;
			}
		}
		else if(NODE_NAME(childNode, "Seasons")) {
			subNode = childNode->children;
			while(subNode) {
				if(NODE_NAME(subNode, "Season")) {
					s = (Season)xml::getIntProp(subNode, "Id");
					xml::copyToCString(temp, subNode);
					season[s] = temp[0];
				}
				subNode = subNode->next;
			}
		}

		childNode = childNode->next;
	}
}

void TileInfo::save(xmlNodePtr curNode) const {
	xmlNodePtr childNode, subNode;

	xml::saveNonNullString(curNode, "Name", name);
	xml::saveNonNullString(curNode, "Description", description);
	xml::saveNonNullString(curNode, "Fishing", fishing);
	xml::saveNonNullString(curNode, "Id", (bstring)id);

	xml::saveNonZeroNum(curNode, "Cost", cost);
	xml::saveNonZeroNum(curNode, "Vision", vision);
	xml::saveNonZeroNum(curNode, "TrackDur", trackDur);
	wander.save(curNode);

	xml::saveNonNullString(curNode, "Style", (bstring)style);
	xml::saveNonNullString(curNode, "Display", (bstring)display);

	saveBits(curNode, "Flags", MAX_ROOM_FLAGS, flags);

	xml::saveNonZeroNum(curNode, "Fly", fly);
	xml::saveNonZeroNum(curNode, "Road", road);
	xml::saveNonZeroNum(curNode, "Water", water);

	std::list<CatRef>::const_iterator it;
	if(!herbs.empty()) {
		childNode = xml::newStringChild(curNode, "Herbs");
		for(it = herbs.begin() ; it != herbs.end() ; it++) {
			(*it).save(childNode, "Herb", true);
		}
	}

	std::map<Season,char>::const_iterator st;
	if(!season.empty()) {
		childNode = xml::newStringChild(curNode, "Seasons");
		for(st = season.begin() ; st != season.end() ; st++) {
			subNode = xml::newStringChild(childNode, "Season", (bstring)(*st).second);
			xml::newNumProp(subNode, "Id", (int)(*st).first);
		}
	}
}


char TileInfo::getId() const { return(id); }
bstring TileInfo::getName() const { return(name); }
bstring TileInfo::getDescription() const { return(description); }
short TileInfo::getCost() const { return(cost); }
float TileInfo::getVision() const { return(vision); }
char TileInfo::getDisplay() const { return(display); }
short TileInfo::getTrackDur() const { return(trackDur); }
bool TileInfo::flagIsSet(int flag) const {
	return(flags[flag/8] & 1<<(flag%8));
}
bool TileInfo::isWater() const { return(water); }
bool TileInfo::isRoad() const { return(road); }
short TileInfo::getFly() const { return(fly); }

//*********************************************************************
//						getStyle
//*********************************************************************

char TileInfo::getStyle(const Player* player) const {
	if(player && player->flagIsSet(P_INVERT_AREA_COLOR)) {
		// toggle case
		if(isupper(style))
			return(tolower(style));
		else if(islower(style))
			return(toupper(style));
	}
	return(style);
}


//*********************************************************************
//						AreaData
//*********************************************************************

AreaData::AreaData() {
	area = 0;
	isTerrain = true;
}

char AreaData::get(short x, short y, short z) const {
	return(data[z][y][x]);
}

void AreaData::setArea(Area* a) { area = a; }
void AreaData::setTerrain(bool t) { isTerrain = t; }

//*********************************************************************
//						Area
//*********************************************************************

Area::Area() {
	id = 0;
	width = height = depth = minDepth = 0;
	name = "";
	defaultTerrain = 0;
	critical_x = critical_y = critical_z = 0;
	zero_offset_x = zero_offset_y = zero_offset_z = 0;
	flightPower = 0;

	aTerrain.setArea(this);
	aMap.setArea(this);
	aMap.setTerrain(false);
	aSeason.setArea(this);
	aSeason.setTerrain(false);
}

Area::~Area() {
	AreaZone *zone=0;
	AreaTrack *aTrack=0;
	std::map<char, TileInfo*>::iterator tt;
	std::map<bstring, AreaRoom*>::iterator it;
	TileInfo* tile=0;
	AreaRoom* room=0;

	for(it = rooms.begin() ; it != rooms.end() ; it++) {
		room = (*it).second;
		delete room;
	}
	rooms.clear();

	while(!zones.empty()) {
		zone = zones.front();
		delete zone;
		zones.pop_front();
	}
	zones.clear();



	for(tt = ter_tiles.begin() ; tt != ter_tiles.end() ; tt++) {
		tile = (*tt).second;
		delete tile;
	}
	ter_tiles.clear();

	for(tt = map_tiles.begin() ; tt != map_tiles.end() ; tt++) {
		tile = (*tt).second;
		delete tile;
	}
	map_tiles.clear();


	while(!tracks.empty()) {
		aTrack = tracks.front();
		delete aTrack;
		tracks.pop_front();
	}
	tracks.clear();
}


//*********************************************************************
//						getUnique
//*********************************************************************

CatRef Area::getUnique(const MapMarker *mapmarker) const {
	std::list<AreaZone*>::const_iterator it;
	AreaZone *zone=0;

	for(it = zones.begin() ; it != zones.end() ; it++) {
		zone = (*it);
		if(zone->unique.id && zone->inside(this, mapmarker))
			return(zone->unique);
	}
	CatRef	cr;
	return(cr);
}


//*********************************************************************
//						move
//*********************************************************************

// this function does not check rules for moving
bool Area::move(Player* player, MapMarker *mapmarker) {
	bool	mem=false;
	// does it already exist?
	AreaRoom* room = getRoom(mapmarker);

	if(!room) {
		room = player->getAreaRoomParent();

		if(room && room->canDelete()) {
			room->setMapMarker(mapmarker);
			room->recycle();
		} else {
			room = new AreaRoom(this, mapmarker);
		}
	}

	// the room we are going to (could be the same room!) should not be
	// deleted after everyone leaves
	mem = room->getStayInMemory();
	room->setStayInMemory(true);

	// everyone leaves room
	BaseRoom* old_room = player->getRoomParent();
	player->deleteFromRoom();

	room->killMortalObjects();

	// everyone enters room
	player->addToRoom(room);
	player->doFollow(old_room);

	// back to normal
	room->setStayInMemory(mem);
	return(true);
}

//*********************************************************************
//						remove
//*********************************************************************

void Area::remove(AreaRoom* room) {
	if(!room)
		return;
	rooms.erase(room->mapmarker.str());
	delete room;
}

//*********************************************************************
//						getRoom
//*********************************************************************
// only checks to see if the room is in memory or on disk

AreaRoom *Area::getRoom(const MapMarker *mapmarker) {
	AreaRoom* room=0;
	MapMarker m = *mapmarker;

	// this will modify the mapmarker, but if it's pointing to invalid rooms,
	// this is desired behavior
	checkCycle(&m);

	if(rooms.find(m.str()) != rooms.end())
		return(rooms[m.str()]);

	char			filename[256];
	sprintf(filename, "%s/%d/%s", Path::AreaRoom, id, m.filename().c_str());

	if(file_exists(filename)) {
		xmlDocPtr	xmlDoc;
		xmlNodePtr	rootNode;

		if((xmlDoc = xml::loadFile(filename, "AreaRoom")) == NULL)
			merror("Unable to read arearoom file", FATAL);

		rootNode = xmlDocGetRootElement(xmlDoc);

		room = new AreaRoom(this);
		room->load(rootNode);

		xmlFreeDoc(xmlDoc);
		xmlCleanupParser();

		return(room);
	}

	return(0);
}


//*********************************************************************
//						loadRoom
//*********************************************************************
// loads the room, including options for recycling, if needed.
// creature is allowed to be null.

AreaRoom *Area::loadRoom(Creature* creature, const MapMarker* mapmarker, bool recycle, bool p) {

	// we only care about this if we have a creature
	if(creature) {
		// impassable terrain
		if(!canPass(creature, mapmarker, true)) {
			if(!(creature->isPet() && creature->getConstMaster()->isStaff())) {
				if(p) creature->checkStaff("You can't go there!\n");
				if(!creature->isStaff()) return(0);
			}
		}
	}

	// does it already exist?
	AreaRoom* room = getRoom(mapmarker);

	// because an AreaRoom can only be a candidate for recycling once
	// it is completely empty, we'll just use this room for now
	if(!room && creature && recycle)
		room = creature->getAreaRoomParent();
	if(!room)
		room = new AreaRoom(this, mapmarker);

	return(room);
}


//*********************************************************************
//						checkCycle
//*********************************************************************
// this set of functions forces us to uses a bounded map that cycles
// when we get too far away from the center

void Area::checkCycle(MapMarker *mapmarker) const {
	mapmarker->setX(checkCycle(mapmarker->getX(), critical_x));
	mapmarker->setY(checkCycle(mapmarker->getY(), critical_y));
	mapmarker->setZ(checkCycle(mapmarker->getZ(), critical_z));
}

short Area::checkCycle(short vector, short critical) const {
	if(critical) {
		if(vector > critical)
			vector = vector % critical - critical - 1;
		else if(vector < (critical * -1))
			vector = vector % critical + critical + 1;
	}
	return(vector);
}

//*********************************************************************
//						canPass
//*********************************************************************
// Tells if the creature can pass through this terrain. Send a null
// creature to simulate a normal person

bool Area::canPass(const Creature* creature, const MapMarker *mapmarker, bool adjust) const {
	int fly = 0;
	TileInfo* tile=0;

	short x = mapmarker->getX();
	short y = mapmarker->getY();
	short z = mapmarker->getZ();

	if(adjust)
		adjustCoords(&x, &y, &z);

	if(outOfBounds(x, y, z))
		tile = getTile(defaultTerrain, 0);
	else if(isRoad(x, y, z))
		return(true);
	else
		tile = getTile(aTerrain.get(x,y,z), aSeason.get(x,y,z));

	if(!tile)
		return(false);

	if(creature && creature->isEffected("fly"))
		fly = creature->getEffect("fly")->getStrength();

	// code swimming rules later
	if(tile->isWater() && !fly)
		return(false);

	// can they fly over it?
	if(tile->getFly() == -1)
		return(false);

	return(fly >= tile->getFly());
}


//*********************************************************************
//						isRoad
//*********************************************************************

bool Area::isRoad(short x, short y, short z, bool adjust) const {
	if(adjust)
		adjustCoords(&x, &y, &z);
	if(outOfBounds(x, y, z))
		return(false);
	TileInfo* tile = getTile(aMap.get(x,y,z), 0);
	return(tile && tile->isRoad());
}


//*********************************************************************
//						isWater
//*********************************************************************

bool Area::isWater(short x, short y, short z, bool adjust) const {
	if(adjust)
		adjustCoords(&x, &y, &z);
	TileInfo* tile = 0;
	if(outOfBounds(x, y, z))
		tile = getTile(defaultTerrain, 0);
	else
		tile = getTile(aTerrain.get(x,y,z), aSeason.get(x,y,z));
	return(tile && tile->isWater());
}

//*********************************************************************
//						getTile
//*********************************************************************
// when getting a tile stored in memory, we may get a different tile
// during a particular season. this is how rivers freeze during the winter

TileInfo *Area::getTile(char grid, char seasonFlags, Season season, bool checkSeason) const {
	// figure out rules for season
	if(checkSeason)
		season = gConfig->getCalendar()->whatSeason();

	// pow() turns season (1-4) to binary (1,2,4,8)
	// if the season's flag isnt set, then we don't look for alternate season info
	if(season != NO_SEASON && !(seasonFlags & (int)pow(2, (double)season-1)))
		season = NO_SEASON;

	std::map<char, TileInfo*>::const_iterator it = ter_tiles.find(grid);
	if(it != ter_tiles.end()) {
		TileInfo* tile = (*it).second;
		if(season != NO_SEASON) {
			std::map<Season,char>::const_iterator st = tile->season.find(season);
			if(st != tile->season.end()) {
				it = ter_tiles.find((*st).second);
				// only switch if we can find the new tile
				if(it != ter_tiles.end())
					tile = (*it).second;
			}
		}
		return(tile);
	}
	it = map_tiles.find(grid);
	if(it != map_tiles.end())
		return((*it).second);
	return(0);
}

//*********************************************************************
//						getTerrain
//*********************************************************************

char Area::getTerrain(const Player* player, const MapMarker *mapmarker, short y, short x, short z, bool terOnly) const {
	std::list<AreaRoom*>::iterator it;
	AreaRoom* room=0;
	bool	staff = player ? player->isStaff() : false;
	bool	found=false;

	// If given a player, vision may be distorted based on properties
	// on the player
	if(player) {
		MapMarker m = *mapmarker;

		// adjust our mapmarker
		m.add(x, y, z);

		if(rooms.find(m.str()) != rooms.end()) {
			room = (*rooms.find(m.str())).second;

			if(!room->players.empty() || !room->monsters.empty()) {
				if(!staff && room->isMagicDark())
					return('#');

				// can they see anybody in the room?
				for(Player* ply : room->players) {
				    if(player == ply || (player->canSee(ply) && (staff || !ply->flagIsSet(P_HIDDEN)))) {
				        found = true;
				        break;
				    }
				}
				if(!found) {
				    for(Monster* mons : room->monsters) {
				        if( player->canSee(mons) && (staff || !mons->flagIsSet(M_HIDDEN))) {
				            found = true;
				            break;
				        }
				    }
				}

				// if so, show them the creatue symbol
				if(found)
					return('@');
			}
		}
	}

	// adjust our coordinates
	x += mapmarker->getX();
	y += mapmarker->getY();
	z += mapmarker->getZ();

	adjustCoords(&x, &y, &z);

	if(outOfBounds(x, y, z))
		return(defaultTerrain);

	if(!terOnly && aMap.get(x,y,z) != ' ')
		return(aMap.get(x,y,z));
	return(aTerrain.get(x,y,z));
}

//*********************************************************************
//						getSeasonFlags
//*********************************************************************

char Area::getSeasonFlags(const MapMarker *mapmarker, short y, short x, short z) const {
	// adjust our coordinates
	x += mapmarker->getX();
	y += mapmarker->getY();
	z += mapmarker->getZ();

	adjustCoords(&x, &y, &z);

	if(outOfBounds(x, y, z))
		return(0);
	return(aSeason.get(x,y,z));
}


//*********************************************************************
//						getLosPower
//*********************************************************************

float Area::getLosPower(const Player* player, int xVision, int yVision) const {
	// default
	float power = xVision - 0.5;
	// elves have great vision
	if(player->getRace())
		power += 0.5;
	// drow and vampires can see well at night
	if(	!isDay() &&
		(player->getRace() == DARKELF || player->isEffected("vampirism"))
	)
		power += 0.5;
	return(power);
}


//*********************************************************************
//						getGridText
//*********************************************************************

void Area::getGridText(char grid[][80], int height, const MapMarker *mapmarker, int maxWidth) const {
	std::list<AreaZone*>::const_iterator it;
	AreaZone *zone=0;

	TileInfo *tile = getTile(getTerrain(0, mapmarker, 0, 0, 0, true), getSeasonFlags(mapmarker));
	bstring desc = tile ? tile->getDescription() : "";
	if(maxWidth < 80)
		desc = wrapText(desc, maxWidth);
	if(desc == "\n")
		desc = "";

	int		i=0, n=0, k=0, lines=1, offset=0;
	bool	displayed = (desc == "");

	for(i=0; i<height; i++)
		strcpy(grid[i], "");

	// see if there is any zone text to add
	for(it = zones.begin() ; it != zones.end() ; it++) {
		zone = (*it);
		if(zone->display != "" && zone->inside(this, mapmarker)) {
			if(!displayed) {
				desc += "\n";
				displayed = true;
			}
			desc += "\n";
			if(maxWidth < 80)
				desc += wrapText(zone->display, maxWidth);
			else
				desc += zone->display;
		}
	}

	// if we know how many lines the text will take up, we can adjust its
	// starting point based on how much room we have
	for(i = desc.size()-1; i>=0; i--)
		if(desc.at(i) == '\n')
			lines++;

	// time to move the description into the array
	offset = MAX(0, (height - lines) / 2);
	k = desc.size()-1;
	n=0;

	for(i=0; i<=k && offset<height; i++) {
		if(desc.at(i) == '\n' || i==k) {
			if(i==k)
				i++;
			strncpy(grid[offset++], desc.substr(n, i-n).c_str(), 79);
			n = i+1;
		}
	}
}

//*********************************************************************
//						showGrid
//*********************************************************************

bstring Area::showGrid(const Player* player, const MapMarker *mapmarker, bool compass) const {
	std::list<AreaZone*>::const_iterator it;
	bstring border = "";
	std::ostringstream grid;
	int		xVision = player->getVision();
	int		yVision = xVision * 2 / 3;
	bool	staff = player->isStaff();
	int		my = yVision*2+1, mx = xVision*2+1, y=0, x=0, i=0;
	int		zx=0, zy=0, zi=0;
	char	gridText[my][80];
	char	seasonFlags;
	Season	season = gConfig->getCalendar()->whatSeason();
	TileInfo *tile=0;
	MapMarker m = *mapmarker;

	zero(gridText, sizeof(gridText));
	getGridText(gridText, my, &m, player->getSock()->getTermCols()-mx-5);

	// line of sight
	float	losGrid[my][mx];
	float	losPower = getLosPower(player, xVision, yVision);
	makeLosGrid(*losGrid, player, my, mx, mapmarker);

	// these are only for staff who want to see zones
	bool	zInside=false;
	int		zZone = 0, zHigh = 0;
	char	zColor[] = {'r', 'w', 'm', 'M', 'W', 'R'};
	int		zNum = sizeof(zColor) / sizeof(char);
	bool	resetBlink=false;

	border += ".";
	i=0;
	for(y = yVision; y >= yVision * -1; y--) {
		grid << "|";
		for(x = xVision * -1; x <= xVision; x++) {
			if(!y)
				border += "-";

			// This lets them see the edges of mountains, even though they
			// can't see over the mountains themselves.
			zy = y;
			zx = x;
			if(!staff && losPower < losGrid[zy+yVision][zx+xVision]) {
				zi++;
				losCloser(&zy, &zx, 0, 0, 0);
			}

			if(!staff && losPower < losGrid[zy+yVision][zx+xVision])
				grid << " ";
			else {
				seasonFlags = getSeasonFlags(mapmarker, y, x);
				tile = getTile(getTerrain(player, mapmarker, y, x, 0, false), seasonFlags, season, false);

				if(!tile)
					grid << " ";
				else {
					grid << "^";

					// if they want to view zones, we're going to have to look
					// at every zone for every square of the map
					if(player->isCt() && player->flagIsSet(P_VIEW_ZONES)) {
						m.add(x, y, 0);
						zInside = false;
						zZone = 0;
						for(it = zones.begin() ; it != zones.end() ; it++) {
							zZone++;
							if((*it)->inside(this, mapmarker)) {
								zHigh = zZone;
								zInside = true;
							}
						}
						if(!zInside)
							grid << tile->getStyle(player);
						else
							grid << zColor[(zHigh-1)%zNum];
						m.add(x*-1, y*-1, 0);
					} else {
						if(tile->getDisplay() == '@' && !x && !y) {
							bstring self = player->customColorize("*CC:SELF*", false);
							if(self == "#") {
								grid << "x^";
								resetBlink = true;
							}
							grid << self;
						} else {
							if(resetBlink) {
								grid << "x^";
								resetBlink = false;
							}
							grid << tile->getStyle(player);
						}
					}


					grid << tile->getDisplay();

					// escape character
					if(tile->getDisplay() == '^')
						grid << "^";
				}
			}
		}
		grid << "^w|   ";
		grid << gridText[i++];
		grid << "\n";
	}
	border += ".\n";

	grid << border;
	//player->printColor("%s%s%s", border.c_str(), grid.str().c_str(), border.c_str());

	// if they have a compass equipped
	if(compass)
		for(i=0; i<MAXWEAR; i++)
			if(player->ready[i] && player->ready[i]->compass)
				grid << player->ready[i]->getCompass(player, true);

	border += grid.str();
	return(border);
}

//*********************************************************************
//						outOfBounds
//*********************************************************************
// don't access outside of the array

bool Area::outOfBounds(short x, short y, short z) const {
	return(y < 0 || x < 0 || z < 0 || y >= height || x >= width || z >= depth);
}

//*********************************************************************
//						adjustCoords
//*********************************************************************
// adjust the points from map to container

void Area::adjustCoords(short* x, short* y, short* z) const {
	(*x) += zero_offset_x;
	(*y) *= -1;
	(*y) += zero_offset_y;
	(*z) += zero_offset_z;
}

//*********************************************************************
//						getTrack
//*********************************************************************
// search, return if found

Track* Area::getTrack(MapMarker* mapmarker) const {
	std::list<AreaTrack*>::const_iterator it;
	AreaTrack *aTrack=0;

	for(it = tracks.begin() ; it != tracks.end() ; it++) {
		aTrack = (*it);
		if(*&aTrack->mapmarker == *mapmarker)
			return(&aTrack->track);
	}
	return(0);
}


//*********************************************************************
//						addTrack
//*********************************************************************
// add, pop from end if too many

void Area::addTrack(AreaTrack *aTrack) {
	tracks.push_front(aTrack);
	while(tracks.size() > MAX_AREA_TRACK)
		tracks.pop_back();
}


//*********************************************************************
//						getTrackDuration
//*********************************************************************
// how long will the tracks last?

int Area::getTrackDuration(const MapMarker* mapmarker) const {
	TileInfo *tile = getTile(getTerrain(0, mapmarker, 0, 0, 0, true), getSeasonFlags(mapmarker));
	return(tile ? tile->getTrackDur() : 0);
}


//*********************************************************************
//						updateTrack
//*********************************************************************
// make sure tracks don't stay around for too long

void Area::updateTrack(int t) {
	std::list<AreaTrack*>::iterator it = tracks.begin();
	AreaTrack *aTrack=0;

	while(it != tracks.end()) {
		std::list<AreaTrack*>::iterator next = it;
		++next;
		aTrack = (*it);
		aTrack->setDuration(aTrack->getDuration() - t);
		if(aTrack->getDuration() <= 0) {
			delete aTrack;
			tracks.erase(it);
		}
		it = next;
	}
}

void Area::save(xmlNodePtr curNode, bool saveRooms) const {
	xmlNodePtr childNode, subNode;

	xml::newNumProp(curNode, "id", id);
	xml::saveNonNullString(curNode, "Name", name);
	xml::saveNonNullString(curNode, "DataFile", dataFile);

	xml::saveNonZeroNum(curNode, "Width", width);
	xml::saveNonZeroNum(curNode, "Height", height);
	xml::saveNonZeroNum(curNode, "Depth", depth);
	xml::saveNonZeroNum(curNode, "MinDepth", minDepth);

	xml::saveNonZeroNum(curNode, "CriticalX", critical_x);
	xml::saveNonZeroNum(curNode, "CriticalY", critical_y);
	xml::saveNonZeroNum(curNode, "CriticalZ", critical_z);

	xml::saveNonZeroNum(curNode, "FlightPower", flightPower);
	xml::saveNonNullString(curNode, "DefaultTerrain", defaultTerrain);

	std::list<AreaZone*>::const_iterator zIt;
	childNode = xml::newStringChild(curNode, "Zones");
	for(zIt = zones.begin() ; zIt != zones.end() ; zIt++) {
		subNode = xml::newStringChild(childNode, "Zone");
		(*zIt)->save(subNode);
	}


	std::map<char, TileInfo*>::const_iterator tIt;
	childNode = xml::newStringChild(curNode, "TerrainInfo");
	for(tIt = ter_tiles.begin() ; tIt != ter_tiles.end() ; tIt++) {
		subNode = xml::newStringChild(childNode, "Terrain");
		(*tIt).second->save(subNode);
	}

	childNode = xml::newStringChild(curNode, "MapInfo");
	for(tIt = map_tiles.begin() ; tIt != map_tiles.end() ; tIt++) {
		subNode = xml::newStringChild(childNode, "Map");
		(*tIt).second->save(subNode);
	}

	if(saveRooms) {
		std::map<bstring, AreaRoom*>::const_iterator rIt;
		for(rIt = rooms.begin() ; rIt != rooms.end() ; rIt++) {
			(*rIt).second->save();
		}
	}
}

//*********************************************************************
//						load
//*********************************************************************

void Area::load(xmlNodePtr curNode) {
	xmlNodePtr childNode = curNode->children;
	char	temp[2];

	id = xml::getIntProp(curNode, "id");

	while(childNode) {
		if(NODE_NAME(childNode, "Name")) xml::copyToBString(name, childNode);
		else if(NODE_NAME(childNode, "DataFile")) xml::copyToCString(dataFile, childNode);

		else if(NODE_NAME(childNode, "Width")) xml::copyToNum(width, childNode);
		else if(NODE_NAME(childNode, "Height")) xml::copyToNum(height, childNode);
		else if(NODE_NAME(childNode, "Depth")) xml::copyToNum(depth, childNode);
		else if(NODE_NAME(childNode, "MinDepth")) xml::copyToNum(minDepth, childNode);

		else if(NODE_NAME(childNode, "Id")) xml::copyToNum(id, childNode);
		else if(NODE_NAME(childNode, "CriticalX")) xml::copyToNum(critical_x, childNode);
		else if(NODE_NAME(childNode, "CriticalY")) xml::copyToNum(critical_y, childNode);
		else if(NODE_NAME(childNode, "CriticalZ")) xml::copyToNum(critical_z, childNode);

		else if(NODE_NAME(childNode, "FlightPower")) xml::copyToNum(flightPower, childNode);

		else if(NODE_NAME(childNode, "DefaultTerrain")) {
			xml::copyToCString(temp, childNode);
			defaultTerrain = temp[0];
		}
		else if(NODE_NAME(childNode, "Zones")) loadZones(childNode);

		else if(NODE_NAME(childNode, "TerrainInfo")) loadTiles(childNode, true);
		else if(NODE_NAME(childNode, "MapInfo")) loadTiles(childNode, false);
		childNode = childNode->next;
	}

	loadTerrain(minDepth);
	loadRooms();
}

//*********************************************************************
//						loadZones
//*********************************************************************

void Area::loadZones(xmlNodePtr curNode) {
	xmlNodePtr	childNode = curNode->children;
	AreaZone	*zone=0;

	while(childNode) {
		if(NODE_NAME(childNode, "Zone")) {
			zone = new AreaZone();
			zone->load(childNode);
			zones.push_back(zone);
		}
		childNode = childNode->next;
	}
}

//*********************************************************************
//						checkFileSize
//*********************************************************************
// if the terrain, map, and season files are different sizes, that
// is a bad thing.

void Area::checkFileSize(int& size, const char* filename) const {
	struct stat f_stat;
	if(stat(filename, &f_stat))
		return;
	if(!size) {
		size = f_stat.st_size;
		return;
	}
	if(size != f_stat.st_size) {
		printf("Area file sizes are not the same. Aborting.\n");
		crash(-1);
	}
}

//*********************************************************************
//						loadTerrain
//*********************************************************************

void Area::loadTerrain(int minDepth) {
	bool	gotOffset=false;
	int		i=0, n=0, k=minDepth, len=0, size=0;
	char	filename[256];
	char	storage[MAX(height, width)+1];

	while(k < depth) {
		sprintf(filename, "%s/%s.%d.ter", Path::AreaData, dataFile, k);
		if(!file_exists(filename))
			return;
		checkFileSize(size, filename);
		std::fstream t(filename, std::ios::in);
		i=0;
		std::vector< std::vector<char> > vTer;
		while(!t.eof() && i < height) {
			std::vector<char> v;

			t.getline(storage, width+1);
			len = strlen(storage);

			for(n=0; n<len; n++)
				v.push_back(storage[n]);
			vTer.push_back(v);
			i++;
		}
		t.close();
		aTerrain.data.push_back(vTer);

		sprintf(filename, "%s/%s.%d.map", Path::AreaData, dataFile, k);
		std::vector< std::vector<char> > vMap;
		if(file_exists(filename)) {
			checkFileSize(size, filename);
			std::fstream m(filename, std::ios::in);
			i=0;
			while(!m.eof() && i < height) {
				std::vector<char> v;

				m.getline(storage, width+1);
				len = strlen(storage);

				for(n=0; n<len; n++) {
					if(storage[n] == '*') {
						v.push_back(' ');
						if(!gotOffset) {
							zero_offset_x = n;
							zero_offset_y = i;
							zero_offset_z = k - minDepth;
							gotOffset = true;
						}
					} else {
						v.push_back(storage[n]);
					}
				}
				vMap.push_back(v);
				i++;
			}
			m.close();
		} else {
			// if we don't get a file, fill with emptyness
			for(i=0; i<height; i++) {
				std::vector<char> v;
				for(n=0; n<width; n++)
					v.push_back(' ');
				vMap.push_back(v);
			}
		}
		aMap.data.push_back(vMap);

		sprintf(filename, "%s/%s.%d.sn", Path::AreaData, dataFile, k);
		std::vector< std::vector<char> > vSn;
		if(file_exists(filename)) {
			checkFileSize(size, filename);
			std::fstream s(filename, std::ios::in);
			i=0;
			while(!s.eof() && i < height) {
				std::vector<char> v;

				s.getline(storage, width+1);
				len = strlen(storage);

				// convert from ascii numbers to actual numbers
				for(n=0; n<len; n++)
					v.push_back(storage[n]-48);
				vSn.push_back(v);
				i++;
			}
			s.close();
		} else {
			// if we don't get a file, fill with emptyness
			for(i=0; i<height; i++) {
				std::vector<char> v;
				for(n=0; n<width; n++)
					v.push_back(0);
				vSn.push_back(v);
			}
		}
		aSeason.data.push_back(vSn);

		k++;
	}
}


//*********************************************************************
//						loadRooms
//*********************************************************************

void Area::loadRooms() {
	struct dirent *dirp=0;
	DIR			*dir=0;
	AreaRoom	*room=0;
	xmlDocPtr	xmlDoc;
	xmlNodePtr	rootNode;
	char		filename[256];

	sprintf(filename, "%s/%d", Path::AreaRoom, id);

	if((dir = opendir(filename)) == NULL)
		return;

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

		sprintf(filename, "%s/%d/%s", Path::AreaRoom, id, dirp->d_name);

		if((xmlDoc = xml::loadFile(filename, "AreaRoom")) == NULL)
			continue;

		rootNode = xmlDocGetRootElement(xmlDoc);

		room = new AreaRoom(this);
		room->setVersion(xml::getProp(rootNode, "Version"));
		room->load(rootNode);

		xmlFreeDoc(xmlDoc);
	}

	xmlCleanupParser();
	closedir(dir);
}


//*********************************************************************
//						loadTiles
//*********************************************************************

void Area::loadTiles(xmlNodePtr curNode, bool ter) {
	xmlNodePtr	childNode = curNode->children;
	TileInfo	*tile=0;

	while(childNode) {
		tile = new TileInfo();
		tile->load(childNode);

		// TODO: Dom: why does this happen?
		if(tile->getName() == "")
			delete tile;
		else {
			if(ter)
				ter_tiles[tile->getId()] = tile;
			else
				map_tiles[tile->getId()] = tile;
		}
		childNode = childNode->next;
	}
}

//*********************************************************************
//						flagIsSet
//*********************************************************************

bool Area::flagIsSet(int flag, const MapMarker* mapmarker) const {
	TileInfo *tile = getTile(getTerrain(0, mapmarker, 0, 0, 0, true), getSeasonFlags(mapmarker));
	if(!tile)
		return(false);
	if(tile->flagIsSet(flag))
		return(true);

	std::list<AreaZone*>::const_iterator it;
	AreaZone *zone=0;

	for(it = zones.begin() ; it != zones.end() ; it++) {
		zone = (*it);
		if(zone->flagIsSet(flag) && zone->inside(this, mapmarker))
			return(true);
	}
	return(false);
}


//*********************************************************************
//						dmListArea
//*********************************************************************

// a prototype needed for only this function: from dmroom.cpp
void showMobList(Player* player, WanderInfo *wander, bstring type);

int dmListArea(Player* player, cmd* cmnd) {
	std::list<Area*>::iterator it;
	std::map<bstring, AreaRoom*>::iterator rt;
	Area	*area=0;
	AreaRoom* room=0;
	int		a=0;
	bstring str = getFullstrText(cmnd->fullstr, 1);

	if(str != "")
		a = atoi(str.c_str());
	if(!a)
		player->printColor("You may type ^y*arealist [num]^x to display only a specific area.\n\n");

	bool empty = cmnd->num >= 2 && !strcmp(cmnd->str[1], "all");
	bool tiles = cmnd->num >= 2 && !strcmp(cmnd->str[1], "tile");
	bool zones = cmnd->num >= 2 && !strcmp(cmnd->str[1], "zones");
	bool coords = cmnd->num >= 3 && !strcmp(cmnd->str[1], "zones") && !strcmp(cmnd->str[2], "coords");
	bool wander = cmnd->num >= 3 && (!strcmp(cmnd->str[1], "zones") || !strcmp(cmnd->str[1], "tile")) && !strcmp(cmnd->str[2], "wander");
	bool track = cmnd->num >= 2 && !strcmp(cmnd->str[1], "track");

	for(it = gConfig->areas.begin() ; it != gConfig->areas.end() ; it++) {
		area = (*it);
		if(a && a != area->id)
			continue;
		player->printColor("Area: ^C%s\n", area->name.c_str());
		player->printColor("  ID: ^c%d^x, DataFile: ^c%s\n", area->id, area->dataFile);
		player->printColor("  Critical Cycle X: ^c%d^x, Y: ^c%d^x, Z: ^c%d\n", area->critical_x, area->critical_y, area->critical_z);
		player->printColor("  Zero Coord Offset X: ^c%d^x, Y: ^c%d^x, Z: ^c%d\n", area->zero_offset_x, area->zero_offset_y, area->zero_offset_z);
		player->printColor("  Height: ^c%d^x, Width: ^c%d^x, Depth: ^c%d\n", area->height, area->width, area->depth);
		player->printColor("  DefaultTerrain: %c  FlightPower: ^c%d\n", area->defaultTerrain, area->flightPower);

		player->printColor("  Rooms Available: ^c%d\n", area->height * area->width);
		player->printColor("  Rooms In Memory: ^c%d\n", area->rooms.size());
		if(!empty) {
			if(a)
				player->printColor("     ^yTo show empty rooms in memory, type \"*arealist %d all\".\n", a);
			else
				player->printColor("     ^yTo show empty rooms in memory, type \"*arealist all\".\n");
		}

		for(rt = area->rooms.begin() ; rt != area->rooms.end() ; rt++) {
			room = (*rt).second;
			if(!room->players.empty() || !room->monsters.empty() || !room->objects.empty() || empty) {
				player->printColor("     %-16s Ply: %s^x  Mon: %s^x  Obj: %s\n",
					room->fullName().c_str(), !room->players.empty() ? "^gy" : "^rn",
					!room->monsters.empty() ? "^gy" : "^rn", !room->objects.empty() ? "^gy" : "^rn");
			}
		}
		player->print("\n");

		if(tiles) {
			std::map<char, TileInfo*>::iterator tt;
			TileInfo *tile=0;

			if(!wander) {
				if(a)
					player->printColor("^yTo show tile wander info, type \"*arealist %d tile wander\".\n", a);
				else
					player->printColor("^yTo show tile wander info, type \"*arealist tile wander\".\n");
			}

			for(tt = area->ter_tiles.begin() ; tt != area->ter_tiles.end() ; tt++) {
				tile = (*tt).second;
				player->print("Ter Tile: %-17s ID: %c  ", tile->getName().c_str(), tile->getId());
				if(wander) {
					player->print("\n");
					showMobList(player, &tile->wander, "tile");
				} else {
					player->printColor("^%cStyle: %c^x   ", tile->getStyle(), tile->getStyle());
					player->printColor("Display: %c  Cost: %d  Vision: %-4.1f  TrackDur: %-4d Fly: %2d",
						tile->getDisplay(), tile->getCost(), tile->getVision(), tile->getTrackDur(),
						tile->getFly());
					if(tile->isWater())
						player->printColor("  ^BWater: ^x(^c%s^x)", tile->getFishing().c_str());
					player->print("\n");
					showRoomFlags(player, 0, tile, 0);
				}
			}

			if(!wander) {
				player->print("\n");

				for(tt = area->map_tiles.begin() ; tt != area->map_tiles.end() ; tt++) {
					tile = (*tt).second;
					player->print("Map Tile: %-17s ID: %c   ", tile->getName().c_str(), tile->getId());
					player->printColor("^%cStyle: %c^x  ", tile->getStyle(), tile->getStyle());
					player->printColor("Display: %c%s\n", tile->getDisplay(), tile->isRoad() ? "  ^WRoad" : "");
				}
			}

		} else {
			if(a)
				player->printColor("^yTo show tile information, type \"*arealist %d tile\".\n", a);
			else
				player->printColor("^yTo show tile information, type \"*arealist tile\".\n");
		}

		if(zones) {
			std::map<int, MapMarker*>::iterator zIt;
			std::list<AreaZone*>::iterator zt;
			AreaZone *zone=0;
			MapMarker *mapmarker=0;

			if(!coords) {
				if(a)
					player->printColor("^yTo show zone coordinates, type \"*arealist %d zones coords\".\n", a);
				else
					player->printColor("^yTo show zone coordinates, type \"*arealist zones coords\".\n");
			}
			if(!wander) {
				if(a)
					player->printColor("^yTo show zone wander info, type \"*arealist %d zones wander\".\n", a);
				else
					player->printColor("^yTo show zone wander info, type \"*arealist zones wander\".\n");
			}

			for(zt = area->zones.begin() ; zt != area->zones.end() ; zt++) {
				zone = (*zt);
				player->printColor("Name: ^c%s\n", zone->name.c_str());
				if(wander) {
					showMobList(player, &zone->wander, "zone");
				} else {
					player->printColor("   Display: ^c%s\n", zone->display.c_str());
					player->printColor("   TerRestrict: ^c%s^x  MapRestrict: ^c%s\n",
						zone->terRestrict, zone->mapRestrict);
					player->printColor("   Unique: ^c%s\n", zone->unique.str().c_str());
					player->printColor("   Min: (X:^c%d^x Y:^c%d^x Z:^c%d^x) Max: (X:^c%d^x Y:^c%d^x Z:^c%d^x)\n",
						zone->min.getX(), zone->min.getY(), zone->min.getZ(),
						zone->max.getX(), zone->max.getY(), zone->max.getZ());
					if(zone->getFishing() != "")
						player->printColor("   Fishing: ^c%s\n", zone->getFishing().c_str());
					player->print("   ");
					showRoomFlags(player, 0, 0, zone);

					if(coords) {
						player->print("   Coords:\n");
						for(zIt = zone->coords.begin() ; zIt != zone->coords.end() ; zIt++) {
							mapmarker = (*zIt).second;
							player->printColor("      (X:^c%d^x Y:^c%d^x Z:^c%d^x)\n",
								mapmarker->getX(), mapmarker->getY(), mapmarker->getZ());
						}
					}
				}
			}
			player->print("\n");

		} else {
			if(a)
				player->printColor("^yTo show zone information, type \"*arealist %d zones\".\n", a);
			else
				player->printColor("^yTo show zone information, type \"*arealist zones\".\n");
		}

		if(track) {
			std::list<AreaTrack*>::iterator at;
			AreaTrack *aTrack=0;

			player->printColor("Track Objects: ^c%d\n", area->tracks.size());

			for(at = area->tracks.begin() ; at != area->tracks.end() ; at++) {
				aTrack = (*at);
				player->print("   MapMarker: %s  Dur: %d   Dir: %s \n",
					aTrack->mapmarker.str().c_str(), aTrack->getDuration(), aTrack->track.getDirection().c_str());
			}

		} else {
			if(a)
				player->printColor("^yTo show track information, type \"*arealist %d track\".\n", a);
			else
				player->printColor("^yTo show track information, type \"*arealist track\".\n");
		}
	}
	player->print("\n");
	return(0);
}

//*********************************************************************
//						areaInit
//*********************************************************************

void Config::areaInit(Creature* player, xmlNodePtr curNode) {
	MapMarker mapmarker;
	mapmarker.load(curNode);
	areaInit(player, mapmarker);
}

void Config::areaInit(Creature* player, MapMarker mapmarker) {
	Area *area = getArea(mapmarker.getArea());
	if(area)
		player->currentLocation.mapmarker = mapmarker;
//	AreaRoom* aRoom=0;
//	if(area)
//		aRoom = area->loadRoom(0, &mapmarker, false);
//	player->area_room = aRoom;
}



//*********************************************************************
//						loadAreas
//*********************************************************************

bool Config::loadAreas() {
	char filename[80];
	xmlDocPtr	xmlDoc;
	xmlNodePtr	rootNode;
	xmlNodePtr	curNode;
	Area	*area=0;

	sprintf(filename, "%s/areas.xml", Path::AreaData);

	if(!file_exists(filename))
		return(false);

	if((xmlDoc = xml::loadFile(filename, "Areas")) == NULL)
		return(false);

	rootNode = xmlDocGetRootElement(xmlDoc);
	curNode = rootNode->children;

	clearAreas();
	while(curNode) {
		if(NODE_NAME(curNode, "Area")) {
			area = new Area;
			area->load(curNode);
			areas.push_back(area);
		}
		curNode = curNode->next;
	}

	xmlFreeDoc(xmlDoc);
	xmlCleanupParser();
	return(true);
}


//*********************************************************************
//						losCloser
//*********************************************************************
// navigating our grid, move closer to the goal

void Area::losCloser(int *x, int *y, int me_x, int me_y, int i) const {
	if(	(abs(me_y - (*y)) == abs(me_x - (*x))) || (
			i%2 &&
			!((*y)==me_y || (*x)==me_x)
		)
	) {
		// do some diagonal movement!
		(*y) += (me_y<(*y) ? -1 : 1);
		(*x) += (me_x<(*x) ? -1 : 1);
	} else {
		// do some straight movement
		if(abs(me_y - (*y)) > abs(me_x - (*x)))
			(*y) += (me_y<(*y) ? -1 : 1);
		else
			(*x) += (me_x<(*x) ? -1 : 1);
	}
}

//*********************************************************************
//						lineOfSight
//*********************************************************************
// recursive function to place values in our grid

float Area::lineOfSight(float *grid, const Player* player, int width, int *y, int *x, int me_y, int me_x, int *i, const MapMarker *mapmarker) const {
	int		og_y = (*y), og_x = (*x);
	float	*g, *h, cost=0.0;
	TileInfo *tile=0;

	// Calculate the position in the array you're going after
	g = grid + (*y) * width + (*x);
	h = grid + og_y * width + og_x;

	// check to see if float is empty
	if((int)(*h))
		return(*h);

	if((*y)==me_y && (*x)==me_x) {
		(*g) = 0;
	} else {

		// see how much this piece of terrain costs
		tile = getTile(getTerrain(0, mapmarker, og_y - me_y, og_x - me_x, 0, true), getSeasonFlags(mapmarker, og_y - me_y, og_x - me_x));
		cost = tile ? tile->getVision() : 0;
		if(isRoad(og_x - me_x + mapmarker->getX(), og_y - me_y + mapmarker->getY(), mapmarker->getZ(), true))
			cost /= 4 ;

		// Profiling: Check flightpower before checking effect. Flightpower is less expensive
		// flying people can see further
		if(flightPower && player->isEffected("fly"))
			cost /= flightPower;

		// base cost for distance
		cost += 1;

		// move closer to the goal
		(*i)++;
		losCloser(y, x, me_y, me_x, *i);

		// diagonal
		if((*y)!=og_y && (*x)!=og_x)
			cost += 0.5;
		// we need to make an oval because our display grid is not square
		if((*y)!=og_y && (*x)==og_x)
			cost += 0.5;

		// have we already calculated this location?
		// check to see if float is empty
		if((int)(*g))
			(*h) = cost + (*g);
		else
			(*h) = cost + lineOfSight(grid, player, width, y, x, me_y, me_x, i, mapmarker);
	}

	return(*h);
}

//*********************************************************************
//						makeLosGrid
//*********************************************************************
// generates line of sight values in supplied grid

void Area::makeLosGrid(float *grid, const Player* player, int height, int width, const MapMarker *mapmarker) const {
	int		x=0, y=0, me_x = (width-1)/2, me_y = (height-1)/2;
	int		zx=0, zy=0, zi=0;
	float	*g;
	MapMarker m = *mapmarker;

	zero(grid, height*width*sizeof(float));

	for(y=0; y<height; y++) {
		zi = 1;
		zx = 0;
		zy = y;
		lineOfSight(grid, player, width, &zy, &zx, me_y, me_x, &zi, &m);
		zi = 1;
		zx = width-1;
		zy = y;
		lineOfSight(grid, player, width, &zy, &zx, me_y, me_x, &zi, &m);

		if(y == 0 || y == height-1) {
			for(x=0; x<width-1; x++) {
				zi = 1;
				zx = x;
				zy = y;
				lineOfSight(grid, player, width, &zy, &zx, me_y, me_x, &zi, &m);
			}
		}

	}
	// verification
	for(y=0; y<height; y++) {
		for(x=0; x<width-1; x++) {
			g = grid + y * width + x;
			if((int)(*g))
				continue;
			zi = 1;
			zx = x;
			zy = y;
			lineOfSight(grid, player, width, &zy, &zx, me_y, me_x, &zi, &m);
		}
	}
}

//*********************************************************************
//						clearAreas
//*********************************************************************

void Config::clearAreas() {
	Area	*area=0;

	while(!areas.empty()) {
		area = areas.front();
		delete area;
		areas.pop_front();
	}
	areas.clear();
}

//*********************************************************************
//						getArea
//*********************************************************************

Area *Config::getArea(int id) {
	std::list<Area*>::iterator it;
	Area	*area=0;

	for(it = areas.begin() ; it != areas.end() ; it++) {
		area = (*it);
		if(area->id == id)
			return(area);
	}
	return(0);
}

//*********************************************************************
//						saveAreas
//*********************************************************************

void Config::saveAreas(bool saveRooms) const {
	std::list<Area*>::const_iterator it;
	xmlDocPtr	xmlDoc;
	xmlNodePtr		rootNode, curNode;
	char			filename[80];

	xmlDoc = xmlNewDoc(BAD_CAST "1.0");
	rootNode = xmlNewDocNode(xmlDoc, NULL, BAD_CAST "Areas", NULL);
	xmlDocSetRootElement(xmlDoc, rootNode);

	for(it = areas.begin() ; it != areas.end() ; it++) {
		curNode = xml::newStringChild(rootNode, "Area");
		(*it)->save(curNode, saveRooms);
	}

	sprintf(filename, "%s/areas2.xml", Path::Config);
	xml::saveFile(filename, xmlDoc);
	xmlFreeDoc(xmlDoc);
}

//*********************************************************************
//						cleanUpRooms
//*********************************************************************

void Config::cleanUpAreas() {
	std::list<Area*>::iterator it;

	for(it = areas.begin() ; it != areas.end() ; it++) {
		(*it)->cleanUpRooms();
	}
}

//*********************************************************************
//						cleanUpRooms
//*********************************************************************

void Area::cleanUpRooms() {
	std::map<bstring, AreaRoom*>::iterator it;
	AreaRoom* room=0;

	for(it = rooms.begin() ; it != rooms.end() ; ) {
		room = (*it).second;
		it++;

		if(room->canDelete()) {
			rooms.erase(room->mapmarker.str());
			delete room;
		}
	}
}