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/
/*
 * movement.cpp
 *	 Functions dealing with player movement.
 *   ____            _
 *  |  _ \ ___  __ _| |_ __ ___  ___
 *  | |_) / _ \/ _` | | '_ ` _ \/ __|
 *  |  _ <  __/ (_| | | | | | | \__ \
 *  |_| \_\___|\__,_|_|_| |_| |_|___/
 *
 * 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 <string>
#include "guilds.h"
#include "property.h"
#include "move.h"
#include "unique.h"
#include "calendar.h"

//*********************************************************************
//						tooFarAway
//*********************************************************************

bool Move::tooFarAway(BaseRoom* pRoom, BaseRoom* tRoom, bool track) {
	if(pRoom == tRoom)
		return(false);
	if(!pRoom || !tRoom || pRoom->flagIsSet(R_JAIL) || tRoom->flagIsSet(R_JAIL))
		return(true);

	const CatRefInfo*	pr = gConfig->getCatRefInfo(pRoom);
	if(!pr)
		return(true);
	const CatRefInfo*	tr = gConfig->getCatRefInfo(tRoom);
	if(!tr)
		return(true);

	if(pr->getTeleportZone() != tr->getTeleportZone())
		return(true);
	if(track && pr->getTrackZone() != tr->getTrackZone())
		return(true);

	return(false);
}

bool Move::tooFarAway(Creature *player, BaseRoom* room) {
	if(player->isStaff())
		return(false);

	if(!room || Move::tooFarAway(player->getRoomParent(), room, false)) {
		player->print("That location is too far away.\n");
		return(true);
	}

	return(false);
}

bool Move::tooFarAway(Creature *player, Creature *target, bstring action) {
	if(player->isStaff())
		return(false);

	BaseRoom* tRoom = target->getRoomParent();
	// target is offline, so we need to load their room
	if(!tRoom) {
		UniqueRoom *room=0;
		if(target->currentLocation.room.id && loadRoom(target->currentLocation.room, &room))
			tRoom = room;
	}

	if(!tRoom || Move::tooFarAway(player->getRoomParent(), tRoom, action == "track")) {
		player->print("%M is too far away to %s.\n", target, action.c_str());
		return(true);
	}

	return(false);
}

//*********************************************************************
//						broadcast
//*********************************************************************

void Move::broadcast(Creature* player, Container* container, bool ordinal, bstring exit, bool hiddenExit) {
	bstring strAction = Move::getString(player, ordinal, exit);
	bool noShow = (player->pFlagIsSet(P_DM_INVIS));

	if(!noShow) {
		if(player->isMonster() || (player->isPlayer() && !player->isEffected("mist"))) {
			if(hiddenExit)
				::broadcast(player->getSock(), container, "%M slips out of sight.", player);
			else
				::broadcast(player->getSock(), container, "%M %s %s^x.", player, strAction.c_str(), exit.c_str());
		} else if(player->isEffected("mist") && !player->flagIsSet(P_DM_INVIS)) {
			if(hiddenExit)
				::broadcast(player->getSock(), container, "A light mist slips out of sight.");
			else
				::broadcast(player->getSock(), container, "A light mist %s %s^x.", strAction.c_str(), exit.c_str());
		}
	}

	if(noShow || hiddenExit) {
		if(player->isDm())
			::broadcast(isDm, player->getSock(), container, "*STAFF* %M %s %s^x.", player, strAction.c_str(), exit.c_str());
		if(player->getClass() == CARETAKER)
			::broadcast(isCt, player->getSock(), container, "*STAFF* %M %s %s^x.", player, strAction.c_str(), exit.c_str());
		if(!player->isCt())
			::broadcast(isStaff, player->getSock(), container, "*STAFF* %M %s %s^x.", player, strAction.c_str(), exit.c_str());
	}
}


//*********************************************************************
//						formatFindExit
//*********************************************************************
// This function allows for multi-word desc-only exits.

bstring Move::formatFindExit(cmd* cmnd) {
	bstring str;
	int		i=0;

	str = "";
	for(i=1; i<cmnd->num; i++) {
		if(cmnd->str[i][0]) {
			str += cmnd->str[i];
			str += " ";
		}
	}
	if(str.length()>1)
		str = str.substr(0, str.length() - 1);
	return(str);
}


//*********************************************************************
//						isSneaking
//*********************************************************************
// if we're sneaking

bool Move::isSneaking(cmd* cmnd) {
	return(!strncmp(cmnd->str[0], "sn", 2));
}

//*********************************************************************
//						isOrdinal
//*********************************************************************
// if it's an ordinal move

bool Move::isOrdinal(cmd* cmnd) {
	// Ordinal if not go, enter, exit, or sneak
	return(	strncmp(cmnd->str[0], "go", 2) &&
			strncmp(cmnd->str[0], "en", 2) &&
			strncmp(cmnd->str[0], "ex", 2) &&
			!Move::isSneaking(cmnd) );
}

//*********************************************************************
//						recycle
//*********************************************************************
// given the room we just left (everyone is out) this code handles the
// recycling of the room

AreaRoom *Move::recycle(AreaRoom* room, Exit* exit) {
	if(room->canDelete()) {
		room->setMapMarker(&exit->target.mapmarker);
		room->recycle();
	} else {
		room = new AreaRoom(room->area, &exit->target.mapmarker);
		room->area->rooms[room->mapmarker.str()] = room;
	}
	return(room);
}

//*********************************************************************
//						update
//*********************************************************************
// updates some settings on the character once they move

void Move::update(Player* player) {
	if(	player->getRoomParent()->isUnderwater() && !player->flagIsSet(P_FREE_ACTION)) {
		player->lasttime[LT_MOVED].ltime = time(0);
		player->lasttime[LT_MOVED].interval = 2L;
	}

	if(player->flagIsSet(P_SITTING))
		player->stand();

	player->clearFlag(P_JUST_TRAINED);

	player->lasttime[LT_MOVED].misc++;
}


//*********************************************************************
//						broadMove
//*********************************************************************
// announces to the room we're leaving that we're leaving

void Move::broadMove(Creature* player, Exit* exit, cmd* cmnd, bool sneaking) {

	if(exit->getEnter() != "")
		player->printColor("%s\n", exit->getEnter().c_str());

	// sneak failure is decided earlier
	if(!sneaking) {
		Move::broadcast(
			player,
			player->getRoomParent(),
			Move::isOrdinal(cmnd),
			exit->getName(),
			exit->flagIsSet(X_SECRET) || exit->isConcealed() || exit->flagIsSet(X_DESCRIPTION_ONLY)
		);
	} else {
		if(player->isDm())
			::broadcast(isDm, player->getSock(), player->getRoomParent(), "*DM* %M snuck to the %s^x.", player, exit->getCName());
		if(player->getClass() == CARETAKER)
			::broadcast(isCt, player->getSock(), player->getRoomParent(), "*DM* %M snuck to the %s^x.", player, exit->getCName());
		if(!player->isCt())
			::broadcast(isStaff, player->getSock(), player->getRoomParent(), "*DM* %M snuck to the %s^x.", player, exit->getCName());
		player->checkImprove("sneak", true);
	}
}



//*********************************************************************
//						track
//*********************************************************************
// takes care of any tracks we leave

bool Move::track(UniqueRoom* room, MapMarker *mapmarker, Exit* exit, Player* player, bool reset) {
	if(room && room->flagIsSet(R_PERMENANT_TRACKS))
		return(reset);
	if(exit->flagIsSet(X_DESCRIPTION_ONLY) || exit->flagIsSet(X_PORTAL))
		return(reset);

	if(	player &&
		!player->isEffected("fly") &&
		!player->isEffected("levitate") &&
		!player->isEffected("pass-without-trace") &&
		!player->isStaff())
	{
		Track *track=0;

		if(room)
			track = &room->track;
		else if(mapmarker) {

			Area *area = gConfig->getArea(mapmarker->getArea());
			if(area) {
				track = area->getTrack(mapmarker);
				// only make tracks if they will last longer than 0 minutes
				if(!track && area->getTrackDuration(mapmarker)) {
					AreaTrack *aTrack = new AreaTrack;

					*&aTrack->mapmarker = *mapmarker;
					aTrack->setDuration(area->getTrackDuration(mapmarker));

					area->addTrack(aTrack);
					track = &aTrack->track;
				}
			}
		}

		if(track) {
			if(!reset) {
				track->setNum(0);
				track->setSize(NO_SIZE);
			}
			track->setDirection(exit->getName());
			if(player->getSize() > track->getSize())
				track->setSize(player->getSize());
			track->setNum(track->getNum()-1);
			return(true);
		}
	}
	return(reset);
}

void Move::track(UniqueRoom* room, MapMarker *mapmarker, Exit* exit, Player *leader, std::list<Creature*> *followers) {
	std::list<Creature*>::iterator it;
	bool	reset=false;

	if(!room && !mapmarker)
		return;
	if(room && room->flagIsSet(R_PERMENANT_TRACKS))
		return;
	if(exit->flagIsSet(X_DESCRIPTION_ONLY))
		return;

	// the leader is not stored in the list
	reset = Move::track(room, mapmarker, exit, leader, reset);

	for(it = followers->begin() ; it != followers->end() ; it++)
		reset = Move::track(room, mapmarker, exit, (*it)->getAsPlayer(), reset);
}


//*********************************************************************
//						sneak
//*********************************************************************
// handles our attempted sneaking

bool Move::sneak(Player* player, bool sneaking) {

	if(sneaking && player->isEffected("mist"))
		player->setFlag(P_SNEAK_WHILE_MISTED);

	if(!player->isStaff() && !player->isEffected("mist")) {

		// see if they failed sneaking or not
		if(sneaking && (
				!player->flagIsSet(P_HIDDEN) ||
				mrand(1, 100) > player->getSneakChance()
			)
		) {
			player->checkImprove("sneak", false);
			player->print("You failed to sneak.\n");
			sneaking = false;
		}

	}

	if(!sneaking)
		player->unhide();

	return(sneaking);
}

//*********************************************************************
//						canEnter
//*********************************************************************
// this function determines if we're allowed to go in the exit provided
// it should print the reason for failure

bool Move::canEnter(Player* player, Exit* exit, bool leader) {
	// this also keeps builders out of overland rooms
	if(!player->checkBuilder(exit->target.room))
		return(false);

	// sending true to this function will print the reason
	// why they can't enter
	if(!player->canEnter(exit, true))
		return(false);
	for(Monster* pet : player->pets) {
		if(pet && !pet->canEnter(exit) && !player->checkStaff("%M cannot go that way.\n", pet))
			return(false);
	}

	if(player->isStaff())
		return(true);

	// This is handled here and not in canEnter. If it were in canEnter,
	// they wouldn't be able to flee past the monster.
	for(Monster* mons : player->getRoomParent()->monsters) {
        if( mons->flagIsSet(M_BLOCK_EXIT) && mons->isEnemy(player) && mons->canSee(player)) {
            player->print("%M blocks your exit.\n", mons);
            return(false);
        }
	}

	if(	(exit->flagIsSet(X_NEEDS_CLIMBING_GEAR) || exit->flagIsSet(X_CLIMBING_GEAR_TO_REPEL)) &&
	    !player->isEffected("levitate") &&
		!player->isEffected("mist"))
	{
		int fall = (exit->flagIsSet(X_DIFFICULT_CLIMB) ? 50 : 0) + 50 - player->getFallBonus();

		if(mrand(1, 100) < fall) {
			int dmg = mrand(5, 15 + fall / 10);

			player->printColor("You fell and hurt yourself for %s%d^x damage.\n", player->customColorize("*CC:DAMAGE*").c_str(), dmg);
			::broadcast(player->getSock(), player->getParent(), "%M fell down.", player);
			broadcastGroup(false, player, "%M fell and took *CC:DAMAGE*%d^x damage, %s%s\n",
				player, dmg, player->heShe(), player->getStatusStr(dmg));

			player->hp.decrease(dmg);
			if(player->hp.getCur() <= 0) {
				player->print("You fell to your death.\n");
				player->hp.setCur(0);
				::broadcast(player->getSock(), player->getParent(), "%M died from the fall.\n", player);
				player->die(FALL);
				return(false);
			}

			if(exit->flagIsSet(X_NEEDS_CLIMBING_GEAR)) {
				player->print("You need climbing gear to go that way.\n");
				return(false);
			}
		}
	}

	// don't follow someone to an incorrect bound room
	// only if you aren't trying to walk yourself through
	if(	!leader &&
		exit->flagIsSet(X_TO_BOUND_ROOM) &&
		player->getGroup() &&
		player->getGroup()->getLeader() &&
		player->getGroup()->getLeader()->getAsPlayer() &&
		player->bound != player->getGroup()->getLeader()->getAsPlayer()->bound
	)
		return(false);

	return(true);
}


//*********************************************************************
//						canMove
//*********************************************************************
// this runs the checks to see if we're even able to go anywhere
// failure should print a message

bool Move::canMove(Player* player, cmd* cmnd) {

	player->clearFlag(P_AFK);

	if(!player->ableToDoCommand())
		return(false);

	if(player->isStaff())
		return(true);

	if(player->isEffected("hold-person")) {
		player->print("You are unable to move right now.\n");
		return(false);
	}

	if(	!player->getRoomParent()->flagIsSet(R_LIMBO) && (
			player->getTotalBulk() > player->getMaxBulk() ||
			player->getWeight() > player->maxWeight()
		)
	) {
		player->print("You are carrying too many things to move!\n");
		return(false);
	}

	if(!player->flagIsSet(P_STUNNED))
		if(player->checkConfusion())
			return(false);

	if(Move::isSneaking(cmnd)) {
		if(!player->knowsSkill("sneak")) {
	 		player->print("You don't know how to sneak effectively.\n");
	 		return(false);
		}
	 	if(!player->flagIsSet(P_HIDDEN) && !player->isEffected("mist")) {
	 		player->print("You need to hide first.\n");
	 		return(false);
	 	}
		if(player->getRoomParent()->isUnderwater()) {
			player->print("You can't sneak here! You're too busy swimming!\n");
			return(false);
		}
		if(player->inCombat()) {
			player->print("How do you expect to sneak in the middle of battle?!\n");
			return(false);
		}
		if(player->flagIsSet(P_SITTING)) {
			player->print("You have to stand up first.\n");
			return(false);
		}
	}


	int		chance=0, moves=0;
	long	t = time(0);
	long	s = LT(player, LT_SPELL);
	long	u = LT(player, LT_MOVED);


	if(u - t > 0 && player->getRoomParent()->isUnderwater() && !player->flagIsSet(P_FREE_ACTION)) {
		player->pleaseWait(u - t);
		return(0);
	}

	if(Move::isSneaking(cmnd)) {
		if(t < s) {
			player->pleaseWait(s - t);
			return(0);
		}
		if(!player->checkAttackTimer())
			return(0);
	} else {
		chance = MAX(1, ((5+(player->dexterity.getCur()/10)*3) - player->getArmorWeight() + player->strength.getCur()));

		if(player->getClass() == RANGER || player->getClass() == DRUID)
			chance += 5;
		if(player->isEffected("levitate"))
			chance += 5;

		if(	player->getRoomParent()->flagIsSet(R_DIFFICULT_TO_MOVE) &&
			!player->isEffected("fly") &&
			!player->isEffected("mist") &&
			!player->flagIsSet(P_FREE_ACTION)
		)
			moves = 1;
		else
			moves = 3;

		// check for speed move

		// TODO: Tweak this check
		if(	player->lasttime[LT_MOVED].ltime == t || (
				player->getRoomParent()->flagIsSet(R_DIFFICULT_TO_MOVE) &&
				!player->isEffected("fly") &&
				!player->flagIsSet(P_FREE_ACTION) &&
				!player->isEffected("mist") &&
				mrand(1,100) > chance
			))
		{
			if(player->lasttime[LT_MOVED].misc > moves) {
				bool wait = false;
				if(moves == 1) {
					if(player->getRoomParent()->flagIsSet(R_EARTH_BONUS)) {
						player->print("You are stuck in the mud!\n");
						::broadcast(player->getSock(), player->getParent(), "%M got stuck in the mud!", player);
					} else if(player->getRoomParent()->flagIsSet(R_AIR_BONUS)) {
						player->print("The strong wind knocks you from your feet!\n");
						::broadcast(player->getSock(), player->getParent(), "%M got blown over by the wind!", player);
					} else if(player->getRoomParent()->flagIsSet(R_FIRE_BONUS)) {
						player->print("You are knocked down by heat waves!\n");
						::broadcast(player->getSock(), player->getParent(), "%M got knocked down by heat waves!", player);
					} else if(player->getRoomParent()->flagIsSet(R_WATER_BONUS)) {
						player->print("Strong water currents make you lose balance!\n");
						::broadcast(player->getSock(), player->getParent(), "%M got knocked down by the current!", player);
					} else if(player->getRoomParent()->flagIsSet(R_COLD_BONUS) || player->getRoomParent()->isWinter()) {
						player->print("You are stuck in the ice and snow!\n");
						::broadcast(player->getSock(), player->getParent(), "%M got stuck in the ice and snow!", player);
					} else if(player->getRoomParent()->flagIsSet(R_ELEC_BONUS)) {
						player->print("Strong magnetic forces knock you down!\n");
						::broadcast(player->getSock(), player->getParent(), "%M got knocked down by magnetic forces!", player);
					} else {
						player->print("You are stuck!!\n");
						::broadcast(player->getSock(), player->getParent(), "%M got stuck!", player);
					}
					wait = true;
				} else {
					// Speedwalking is fine as long as there are no aggros in the room; if there are
					// there's a chance to trip them up
					for(Monster* monster : player->getParent()->monsters) {
						if(monster->willAggro(player) || monster->isEnemy(player)) {
							*player << ColorOn << setf(CAP) << monster << " startles you.\n" << ColorOff;
							wait = true;
							break;
						}
					}
				}

				if(wait) {
					player->pleaseWait(1);
					return(false);
				}
			}
		} else {
			player->lasttime[LT_MOVED].ltime = t;
			player->lasttime[LT_MOVED].misc = 1;
		}


		if(player->lasttime[LT_PLAYER_STUNNED].ltime+(player->lasttime[LT_PLAYER_STUNNED].interval) > t) {
			player->pleaseWait((player->lasttime[LT_PLAYER_STUNNED].interval + 1) - t + player->lasttime[LT_PLAYER_STUNNED].ltime - 1);
			return(0);
		}

		// fix walking bug here
		if(player->getLTAttack()+3 > t) {
			player->pleaseWait(3-t+player->getLTAttack());
			return(false);
		// and here
		} else if(player->lasttime[LT_SPELL].ltime+3 > t) {
			player->pleaseWait(3-t+player->lasttime[LT_SPELL].ltime);
			return(false);
		// this fixes walking steal
		} else if(player->lasttime[LT_STEAL].ltime+3 > t) {
			player->pleaseWait(3-t+player->lasttime[LT_STEAL].ltime);
			return(false);
		}

	}

	return(true);
}

//*********************************************************************
//						getExit
//*********************************************************************
// this looks through the exits in the room and finds the one we want
// to go to

Exit *Move::getExit(Creature* player, cmd* cmnd) {
	BaseRoom* room = player->getRoomParent();
	Exit	*exit=0;

	if(!room)
	    return(NULL);

	if(player->isPet())
		player = player->getMaster();

	if(Move::isOrdinal(cmnd)) {
		for(Exit* ext : room->exits) {
			if(	ext->getName() == cmnd->str[1] &&
				player->canSee(ext) &&
				!(!player->isStaff() && ext->flagIsSet(X_DESCRIPTION_ONLY)))
			{
				exit = ext;
				break;
			}
		}
	} else {
		if(cmnd->fullstr.length() < 3)
			return(0);

		exit = findExit(player, Move::formatFindExit(cmnd), cmnd->val[cmnd->num-1], room);
	}
	return(exit);
}

//*********************************************************************
//						drunkenStumble
//*********************************************************************
// whether or not a person should stumble when drunk

bool drunkenStumble(const EffectInfo* effect) {
	AlcoholState state = getAlcoholState(effect);
	switch(state) {
	case ALCOHOL_TIPSY:
		// 33%
		return(!mrand(0,2));
	case ALCOHOL_DRUNK:
		// 75%
		return(!!mrand(0,3));
	case ALCOHOL_INEBRIATED:
		// 100%
		return(true);
	default:
		// 0%
		return(false);
	}
}

//*********************************************************************
//						getString
//*********************************************************************
// gives us the text of the movement string

bstring Move::getString(Creature* creature, bool ordinal, bstring exit) {
	bstring str = "";
	int		num=0;

	if(creature->getAsPlayer()) {

		if(creature->isStaff())
			str = "wanders to the";
		else if(creature->getRoomParent()->isUnderwater())
			str = "swam to the";
		else if(creature->isEffected("fly"))
			str = "flew to the";
		else if(creature->isEffected("levitate") || creature->isEffected("mist"))
			str = "floats to the";
		else if(creature->isEffected("confusion") || drunkenStumble(creature->getEffect("drunkenness")))
			str = "stumbles to the";
		// Not ordinal, not up, not down, and not out
		else if( !ordinal &&
			exit != "up" &&
			exit != "down" &&
			exit != "out"
		)
			str = "went to the";
		else
			str = "leaves";

	} else {

		if(	creature->movetype[0][0] ||
			creature->movetype[1][0] ||
			creature->movetype[2][0]
		) {

			do {
				num = mrand(1, 3);
				if(creature->movetype[num-1][0])
					str = creature->movetype[num-1];
			} while(!creature->movetype[num-1][0]);

		} else if(creature->getRoomParent()->isUnderwater())
			str = "swam to the";
		else if(creature->isEffected("fly"))
			str = "flew to the";
		else if(creature->isEffected("levitate"))
			str = "floats to the";
		else
			str = "wanders";

	}

	return(str);
}



//*********************************************************************
//						checkFollowed
//*********************************************************************
// this sees if any monsters plan on following the people leaving the
// room, adding them to the list if they want to tag along for the ride

void Move::checkFollowed(Player* player, Exit* exit, BaseRoom* room, std::list<Creature*> *followers) {
	Monster *target=0;
	if(!room)
		return;

	MonsterSet::iterator mIt = room->monsters.begin();
	while(mIt != room->monsters.end()) {
		target = (*mIt++);

		if(!target->flagIsSet(M_FOLLOW_ATTACKER) || target->flagIsSet(M_DM_FOLLOW))
			continue;
		if(!target->hasEnemy())
			continue;

		Creature* crt = target->getTarget(false);
		if(!crt || crt != player) continue;

		// Protect the newbie areas from aggro-dragging
		if(target->flagIsSet(M_AGGRESSIVE) && exit->flagIsSet(X_PASSIVE_GUARD))
			continue;

		if(!target->canSee(player))
			continue;
		if(!target->canEnter(exit))
			continue;

		if(mrand(1,20) > 10 - (player->dexterity.getCur()/10) + target->dexterity.getCur()/10)
			continue;

		player->print("%M followed you.\n", target);
		::broadcast(player->getSock(), room, "%M follows %N.", target, player);

		// prevent players from continuously creating new monsters
		if(target->flagIsSet(M_PERMENANT_MONSTER))
			target->diePermCrt();

		target->clearFlag(M_PERMENANT_MONSTER);
		gServer->addActive(target);

		target->deleteFromRoom();

		followers->push_back(target);
	}

}



//*********************************************************************
//						finish
//*********************************************************************
// this finishes up all the work of adding people to the room

void Move::finish(Creature* creature, BaseRoom* room, bool self, std::list<Creature*> *followers) {
	Player	*player = creature->getAsPlayer();
	Monster *monster = creature->getAsMonster();

	if(player)
		player->addToRoom(room);
	else
		monster->addToRoom(room);

	if(followers && !followers->empty()) {
		while(!followers->empty()) {
			Move::finish(followers->front(), room, false, 0);
			followers->pop_front();
		}
		followers->clear();
	}

	if(player && room->isUniqueRoom())
		player->checkTraps(room->getAsUniqueRoom(), self);
}


//*********************************************************************
//						getRoom
//*********************************************************************
// loads up the room. handles moving into the room through an exit
// (walk/flee), looking into the room (scout/yell), and appearing in
// the room (teleport)
// creature is allowed to be null if we're just trying to load the room
// out of the exit.

bool Move::getRoom(Creature* creature, const Exit* exit, BaseRoom **newRoom, bool justLooking, MapMarker* teleport, bool recycle) {

	Player* player = 0;
	if(creature)
	   player = creature->getAsPlayer();
	Location l;

	// pets can go where players can go
	if(!player && creature && creature->isPet())
		player = creature->getPlayerMaster();

	// find out where the exit actually points

	if(creature && exit && (
		exit->flagIsSet(X_TO_BOUND_ROOM) ||
		exit->flagIsSet(X_TO_PREVIOUS) ||
		exit->flagIsSet(X_TO_STORAGE_ROOM)
	) )
	{
		// flags can cause us to completely ignore where the exit points
		if(exit->flagIsSet(X_TO_PREVIOUS))
			l = creature->previousRoom;
		else if(player && exit->flagIsSet(X_TO_STORAGE_ROOM)) {
			l.room = gConfig->getSingleProperty(player, PROP_STORAGE);
			if(!l.room.id) {
				player->print("You must first purchase a storage room to go to this exit.\n");
				return(0);
			} else if(l.room.id == -1) {
				player->print("You may not enter that room when you are affiliated with more than one\n");
				player->print("storage room. Type \"property\" for instructions on how to remove yourself\n");
				player->print("as partial owner from other storage rooms if you no longer wish to be\n");
				player->print("affiliated with them.\n");
				return(0);
			}
		} else if(player && exit->flagIsSet(X_TO_BOUND_ROOM))
			l = player->getBound();
		else
			l.room = creature->currentLocation.room;
	} else if(!exit || teleport) {
		// if we're teleporting
		l.mapmarker = *teleport;
	} else if(exit->target.mapmarker.getArea()) {
		l.mapmarker = exit->target.mapmarker;
	} else {
		l.room = exit->target.room;
	}

	if(l.mapmarker.getArea()) {
		Area *area = gConfig->getArea(l.mapmarker.getArea());
		if(!area) {
			if(!teleport && creature) {
				creature->print("Off the map in that direction.\n");
				if(creature->isStaff())
					creature->printColor("^eMove::getRoom failed on an area room.\n");
			}
			return(false);
		}
		// if we're in a unique room, don't activate any unique-room triggers on
		// the overland unless we're teleporting
		if(teleport || !creature || !creature->inUniqueRoom())
			l.room = area->getUnique(&l.mapmarker);
		if(!l.room.id) {
			// don't recycle if we are teleporting or leaving
			// a unique room
			if(	teleport ||
				(creature && creature->inAreaRoom() && creature->getAreaRoomParent()->unique.id)
			)
				recycle = false;
			// if we're only looking, don't use the function that will create a new room
			// if one doesnt already exit
			if(justLooking)
				(*newRoom) = area->getRoom(&l.mapmarker);
			else
				(*newRoom) = area->loadRoom(creature, &l.mapmarker, recycle);
			if(!*newRoom)
				return(false);
			// getUnique gets called again later, so skip the decrement process here
			// or the compass will use 2 shots
			if(teleport || !creature || !creature->inUniqueRoom())
				l.room = (*newRoom)->getAsAreaRoom()->getUnique(creature, true);
		}
	}

	if(l.room.id) {
		UniqueRoom* uRoom = 0;
		if(	(creature && creature->inUniqueRoom() && l.room == creature->currentLocation.room) ||
			!loadRoom(l.room, &uRoom) )
		{
			if(!teleport && creature)
				creature->print("Off map in that direction.\n");
			return(false);
		}
		*newRoom = uRoom;
		if(creature) {
			// sending true to this function tells the player why
			// they can't cant enter the room: if teleporting send false
			if(!creature->canEnter(uRoom, !teleport)) {
				(*newRoom)=0;
				return(false);
			}

			// the rest of these rules are for players only
			// exclude pets
			if(player && !creature->isPet()) {
				for(Monster*pet : player->pets) {
					if(pet && !pet->canEnter(uRoom, !teleport)) {
						if(!teleport)
							player->print("%M won't follow you there.\n", pet);
						(*newRoom)=0;
						return(false);
					}
				}
				// if they're leaving limbo
				if(!justLooking && player->getLocation() == player->getLimboRoom())
					player->clearFlag(P_KILLED_BY_MOB);

				if(player->inUniqueRoom() && player->getUniqueRoomParent()->info.isArea("stor"))
					gConfig->saveStorage(player->getUniqueRoomParent());

			}

		}
	}


	BaseRoom* room = (*newRoom);

	// when entering the room, we may have to unmist them
	if( !justLooking &&
		player &&
		player->isEffected("mist") &&
		!player->isStaff() &&
		(room->flagIsSet(R_DISPERSE_MIST) || room->flagIsSet(R_ETHEREAL_PLANE) || room->isUnderwater()) )
	{
		if(room->isUnderwater())
			player->print("Water currents disperse your mist.\n");
		else {
			player->print("Swirling vapors disperse your mist.\n");
			player->stun(mrand(5,8));
		}
		player->unmist();
	}

	return(true);
}


//*********************************************************************
//						start
//*********************************************************************
// this function starts everyone moving - it finds the room, finds
// the exit, sees if they can enter, then removes them from the current
// room. the final step is completed in Move::finish

BaseRoom* Move::start(Creature* creature, cmd* cmnd, Exit **gExit, bool leader, std::list<Creature*> *followers, int* numPeople, bool& roomPurged) {
	BaseRoom *oldRoom = creature->getRoomParent(), *newRoom=0;
//	UniqueRoom	*uRoom=0;
//	AreaRoom* aRoom=0;
	Exit	*exit=0;
	Player	*player = creature->getAsPlayer();
	Monster* monster = creature->getAsMonster();
	bool	sneaking=false, wasSneaking=false, mem=false;

	if(player) {
		if(!Move::canMove(player, cmnd))
			return(NULL);
		// only players sneak
		sneaking = wasSneaking = Move::isSneaking(cmnd);

		if(sneaking && player->isStaff() && !player->flagIsSet(P_HIDDEN))
			player->setFlag(P_HIDDEN);
	}

	exit = Move::getExit(creature, cmnd);
	if(!exit) {
		creature->print("You don't see that exit.\n");
		if(monster && monster->getMaster())
			monster->getMaster()->print("Your pet doesn't see that exit.\n");
		return(NULL);
	}


	if(player && !Move::canEnter(player, exit, leader))
		return(NULL);

	if(!Move::getRoom(creature, exit, &newRoom))
		return(NULL);

	// Rangers and F/T can't sneak with heavy armor on!
	if(sneaking && player && player->checkHeavyRestrict("sneak"))
		return(NULL);

	if(newRoom->isAreaRoom()) {
		mem = newRoom->getAsAreaRoom()->getStayInMemory();
		newRoom->getAsAreaRoom()->setStayInMemory(true);
	}
	// we have to run a manual isFull check here because nobody is
	// in the room yet
	(*numPeople)++;
	if(!monster && !player->isStaff()) {
		if(newRoom->maxCapacity() && (newRoom->countVisPly() + *numPeople) > newRoom->maxCapacity()) {
			creature->print("That room is full.\n");
			// reset stayInMemory
			if(newRoom->isAreaRoom())
				newRoom->getAsAreaRoom()->setStayInMemory(mem);
			return(NULL);
		}
	}

	// were they killed by exit effect damage?
	if(exit->doEffectDamage(creature))
		return(NULL);

	// save a copy of the exit for the track function
	if(leader) {
		*gExit = exit;
	}


	if(player) {
		Move::update(player);
		sneaking = Move::sneak(player, sneaking);
	}
	Move::broadMove(creature, exit, cmnd, sneaking);

	// Was the area room purged?  If so we have no monsters to follow is in it so
	// no need to check that later
	int del = creature->deleteFromRoom(false);
	if(del & DEL_ROOM_DESTROYED) {
		// Room was purged and oldRoom is no longer valid
		roomPurged = true;
		exit = 0;
		oldRoom = 0;
	}

	// the leader doesn't use the list
	if(!leader)
		followers->push_back(creature);


	// sneaking means only your pet will follow you, even if you fail
	// also do this for destroyed portals
	bool portal = false;
	if(!exit)
		portal = true;
	if(!portal && player && exit->flagIsSet(X_PORTAL))
		portal = Move::usePortal(player, oldRoom, exit);

	if(exit)
		exit->checkReLock(creature, sneaking);

	if(player && (wasSneaking || portal)) {
		// TODO: Make pet unhide during failed sneak
		for(Monster*pet : player->pets) {
			if(pet && oldRoom == pet->getRoomParent())
				Move::start(pet, cmnd, 0, 0, followers, numPeople, roomPurged);
		}
		// reset stayInMemory
		if(newRoom->isAreaRoom())
			newRoom->getAsAreaRoom()->setStayInMemory(mem);
		if(portal && oldRoom && exit)
			Move::deletePortal(oldRoom, exit, leader ? player : player->getGroupLeader(), followers);
		// portal owners close once they exit the room
		if(player && player->flagIsSet(P_PORTAL))
			Move::deletePortal(oldRoom, player->getCName(), leader ? player : player->getGroupLeader());
		return(newRoom);
	}


	Group* group = creature->getGroup();
	if(group && creature->getGroupStatus() == GROUP_LEADER && !creature->pFlagIsSet(P_DM_INVIS)) {
		for(Creature* follower : group->members) {
		    if(follower == creature || follower->getGroupStatus() < GROUP_MEMBER)
		        continue;

			if(oldRoom == follower->getRoomParent())
				Move::start(follower, cmnd, 0, 0, followers, numPeople, roomPurged);
			if(roomPurged)
				oldRoom = NULL;
		}
	}
	if(leader && (!group || creature->getGroupStatus() != GROUP_LEADER)) {
        for(Monster* pet : creature->pets) {
            if(oldRoom == pet->getRoomParent())
                Move::start(pet, cmnd, 0, 0, followers, numPeople, roomPurged);
            if(roomPurged)
                oldRoom = NULL;
        }
	}
	if(player && !sneaking && !roomPurged && oldRoom)
		Move::checkFollowed(player, exit, oldRoom, followers);

	// reset stayInMemory
	if(newRoom->isAreaRoom())
		newRoom->getAsAreaRoom()->setStayInMemory(mem);

	// portal owners close once they exit the room, but this means their group can follow
	if(player && player->flagIsSet(P_PORTAL))
		Move::deletePortal(oldRoom, player->getCName(), leader ? player : player->getGroupLeader(), followers);
	return(newRoom);
}



//*********************************************************************
//						cmdGo
//*********************************************************************
// the base go command - it is called by the leader and causes everyone
// following to run the Move::start and Move::finish functions

int cmdGo(Player* player, cmd* cmnd) {
	MapMarker *oldMarker=0;
	UniqueRoom	*oldRoom = player->getUniqueRoomParent();
	Exit*	exit;
	int		numPeople=0;

	if(!player->getRoomParent()) {
		player->print("You must be in a room to go anywhere!\n");
		return(0);
	}

	if(cmnd->num < 2) {
		player->print("Go where?\n");
		return(0);
	}

	// keep track of this room
	AreaRoom* aRoom = player->getAreaRoomParent();
	std::list<Creature*> followers;

	if(aRoom) {
		oldMarker = new MapMarker;
		*oldMarker = aRoom->mapmarker;
	}

	bool roomPurged = false;
	// this removes everyone from the room
	BaseRoom* newRoom = 0;
	if(!(newRoom = Move::start(player, cmnd, &exit, true, &followers, &numPeople, roomPurged)))
		return(0);

	if(!roomPurged)
		Move::track(oldRoom, oldMarker, exit, player, &followers);

	if(oldMarker)
		delete oldMarker;

	// for display reasons, we only need to kill objects in
	// the room they're entering
	newRoom->killMortalObjectsOnFloor();

	// loadRoom is telling us the room the player used to be in is
	// a candidate for recycling
	if(newRoom->isAreaRoom() && aRoom == newRoom)
		newRoom = Move::recycle(aRoom, exit);

	// this adds everyone to the room
	Move::finish(player, newRoom, true, &followers);
	// we killed objects already, we can skip those this time around
	player->getRoomParent()->killMortalObjects(false);
	return(0);
}


//*********************************************************************
//						cmdMove
//*********************************************************************
// This function attempts to move a player in one of the
// cardinal directions (n,s,e,w,u,d).

int cmdMove(Player* player, cmd* cmnd) {
	bstring dir = "", str = "";

	player->clearFlag(P_AFK);

	// find the ordinal direction
	str = cmnd->str[0];
	if(!player->ableToDoCommand())
		return(0);

	if(str == "sw" || !strncmp(str.c_str(), "southw", 6))
		dir = "southwest";
	else if(str == "nw" || !strncmp(str.c_str(), "northw", 6))
		dir = "northwest";
	else if(str == "se" || !strncmp(str.c_str(), "southe", 6))
		dir = "southeast";
	else if(str == "ne" || !strncmp(str.c_str(), "northe", 6))
		dir = "northeast";

	/*	else if(!strcmp(str, "aft")
			strcpy(tempstr, "aft");
		else if(!strcmp(str, "fore")
			strcpy(tempstr, "fore");
		else if(!strcmp(str, "starb")
			strcpy(tempstr, "starboard");
		else if(!strcmp(str, "port")
			strcpy(tempstr, "port");  */

	else {
		switch(str[0]) {
		case 'n':
			dir = "north";
			break;
		case 's':
			dir = "south";
			break;
		case 'e':
			dir = "east";
			break;
		case 'w':
			dir = "west";
			break;
		case 'u':
			dir = "up";
			break;
		case 'd':
			dir = "down";
			break;
		case 'o':
		case 'l':
			dir = "out";
			break;
		}
	}

	// fake a "go north" type thing
	cmnd->num = 2;
	strcpy(cmnd->str[1], dir.c_str());
	cmnd->val[1] = 1;

	return(cmdGo(player, cmnd));
}


//*********************************************************************
//						cmdFlee
//*********************************************************************
// wrapper which allows a player to flee

int cmdFlee(Player* player, cmd* cmnd) {
	player->flee();
	return(0);
}


//*********************************************************************
//						cmdOpen
//*********************************************************************
// This function allows a player to open a door if it is not already
// open and if it is not locked.

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

	player->clearFlag(P_AFK);


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

	if(player->flagIsSet(P_SITTING)) {
		player->print("You have to stand up first.\n");
		return(0);
	}

	if(cmnd->num < 2) {
		player->print("Open what?\n");
		return(0);
	}

	exit = findExit(player, cmnd);


	if(!exit) {
		player->print("You don't see that openable exit.\n");
		return(0);
	}

	if(!exit->flagIsSet(X_CLOSED) && !exit->flagIsSet(X_CLOSABLE)) {
		player->print("You don't see that openable exit.\n");
		return(0);
	}

	if(exit->flagIsSet(X_TOLL_TO_PASS) && player->getRoomParent()->getTollkeeper() && !player->isCt()) {
		player->printColor("You must pay a toll of %ld gold coins to go through the %s^x.\n", tollcost(player, exit, 0), exit->getCName());
		return(0);
	}

	const Monster* guard = player->getRoomParent()->getGuardingExit(exit, player);
	if(guard && !player->checkStaff("%M %s.\n", guard, guard->flagIsSet(M_FACTION_NO_GUARD) ? "doesn't like you enough to let you open it" : "won't let you open it"))
		return(0);

	if(exit->flagIsSet(X_LOCKED) && !player->checkStaff("It's locked.\n"))
		return(0);

	exit->clearFlag(X_LOCKED);

	if(!exit->flagIsSet(X_CLOSED)) {
		player->print("It's already open.\n");
		return(0);
	}

	player->unhide();

	if(exit->isWall("wall-of-force")) {
		player->printColor("The %s^x is blocked by a wall of force.\n", exit->getCName());
		return(0);
	}
	// were they killed by exit effect damage?
	if(exit->doEffectDamage(player))
		return(0);
	
	exit->clearFlag(X_CLOSED);
	exit->ltime.ltime = time(0);

	player->printColor("You open the %s^x.\n", exit->getCName());
	broadcast(player->getSock(), player->getParent(), "%M opens the %s^x.", player, exit->getCName());

	Hooks::run(player, "openExit", exit, "openByCreature");

	if(exit->getOpen() != "") {
		if(exit->flagIsSet(X_ONOPEN_PLAYER)) {
			player->print("%s.\n", exit->getOpen().c_str());
		} else {
			broadcast(0, player->getRoomParent(), exit->getOpen().c_str());
		}
	}

	return(0);
}

//*********************************************************************
//						cmdClose
//*********************************************************************
// This function allows a player to close a door, if the door is not
// already closed, and if indeed it is a door.

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

	player->clearFlag(P_AFK);


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

	if(cmnd->num < 2) {
		player->print("Close what?\n");
		return(0);
	}

	if(player->flagIsSet(P_SITTING)) {
		player->print("You have to stand up first.\n");
		return(0);
	}

	exit = findExit(player, cmnd);

	if(!exit) {
		player->print("You don't see that closeable exit.\n");
		return(0);
	}

	if(!exit->flagIsSet(X_CLOSABLE)) {
		player->print("You don't see that closeable exit.\n");
		return(0);
	}

	if(exit->flagIsSet(X_CLOSED)) {
		player->print("It's already closed.\n");
		return(0);
	}

	for(Player* ply : player->getRoomParent()->players) {
		if(ply->inCombat()) {
			player->print("You cannot do that right now.\n");
			return(0);
		}
	}

	if(exit->isWall("wall-of-force")) {
		player->printColor("The %s^x is blocked by a wall of force.\n", exit->getCName());
		return(0);
	}
	// were they killed by exit effect damage?
	if(exit->doEffectDamage(player))
		return(0);
	
	player->unhide();

	exit->setFlag(X_CLOSED);
	Hooks::run(player, "closeExit", exit, "closeByCreature");

	player->printColor("You close the %s^x.\n", exit->getCName());
	broadcast(player->getSock(), player->getParent(), "%M closes the %s^x.", player, exit->getCName());

	return(0);

}

//*********************************************************************
//						cmdUnlock
//*********************************************************************
// This function allows a player to unlock a door if they have the correct
// key, and it is locked.

unsigned short Object::getKey() const { return(keyVal); }
void Object::setKey(unsigned short k) { keyVal = k; }

int cmdUnlock(Player* player, cmd* cmnd) {
	Object	*object=0;
	Exit	*exit=0;

	player->clearFlag(P_AFK);


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



	if(player->flagIsSet(P_SITTING)) {
		player->print("You have to stand up first.\n");
		return(0);
	}

	if(cmnd->num < 2) {
		player->print("Unlock what?\n");
		return(0);
	}

	exit = findExit(player, cmnd);

	if(!exit) {
		player->print("Unlock what?\n");
		return(0);
	}

	const Monster* guard = player->getRoomParent()->getGuardingExit(exit, player);
	if(guard && !player->checkStaff("%M %s.\n", guard, guard->flagIsSet(M_FACTION_NO_GUARD) ? "doesn't like you enough to let you unlock it" : "won't let you unlock it"))
		return(0);

	if(!exit->flagIsSet(X_LOCKED) && !player->checkStaff("It's not locked.\n"))
		return(0);

	if(player->inJail()) {
		if(player->getUniqueRoomParent()->countVisPly() > 1) {
			player->print("You can't do that! Someone else might get out!\n");
			return(0);
		}
	}

	if(exit->flagIsSet(X_TO_STORAGE_ROOM)) {
		CatRef	sr = gConfig->getSingleProperty(player, PROP_STORAGE);
		if(!sr.id) {
			player->print("You must first purchase a storage room to unlock this exit.\n");
			return(0);
		}
	}

	if(exit->flagIsSet(X_WATCHER_UNLOCK) && (player->isWatcher() || player->isCt())) {
		player->unhide();

		exit->clearFlag(X_LOCKED);
		exit->ltime.ltime = time(0);
		player->print("Click.\n");

		broadcast(player->getSock(), player->getParent(), "%M unlocks the %s^x.", player, exit->getCName());

		broadcast(isWatcher, "^C%s unlocked the %s^x exit in room %s.\n",
			player->getCName(), exit->getCName(), player->getRoomParent()->fullName().c_str());

		logn("log.watchers", "%s unlocked the %s exit in room %s.\n",
			player->getCName(), exit->getCName(), player->getRoomParent()->fullName().c_str());

		return(0);
	}

	if(cmnd->num < 3) {
		player->print("Unlock it with what?\n");
		return(0);
	}

	object = player->findObject(player->getAsConstPlayer(), cmnd, 2);

	if(!object) {
		player->print("You don't have that.\n");
		return(0);
	}


	if(object->getType() == WAND && object->getMagicpower() == S_KNOCK)
		return(cmdUseWand(player, cmnd));
	if(object->getType() != KEY) {
		player->print("That's not a key.\n");
		return(0);
	}

	if(object->getShotsCur() < 1) {
		player->printColor("%O is broken.\n", object);
		return(0);
	}

	if(exit->isWall("wall-of-force")) {
		player->printColor("The %s^x is blocked by a wall of force.\n", exit->getCName());
		return(0);
	}
	// were they killed by exit effect damage?
	if(exit->doEffectDamage(player))
		return(0);
	
	if(!object->isKey(player->getUniqueRoomParent(), exit)) {
		player->print("Wrong key.\n");
		return(0);
	}

	player->unhide();

	exit->clearFlag(X_LOCKED);
	exit->ltime.ltime = time(0);
	object->decShotsCur();

	if(object->use_output[0])
		player->print("%s\n", object->use_output);
	else
		player->print("Click.\n");
	broadcast(player->getSock(), player->getParent(), "%M unlocks the %s^x.", player, exit->getCName());

	Hooks::run(player, "unlockExit", exit, "unlockByCreature");

	if(object->getShotsCur() < 1 && Unique::isUnique(object)) {
		player->delObj(object, true);
		delete object;
	}
	return(0);
}

//*********************************************************************
//						cmdLock
//*********************************************************************
// This function allows a player to lock a door with the correct key.

int cmdLock(Player* player, cmd* cmnd) {
	Object	*object=0;
	Exit	*exit=0;

	player->clearFlag(P_AFK);

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

	if(cmnd->num < 2) {
		player->print("Lock what?\n");
		return(0);
	}

	if(player->flagIsSet(P_SITTING)) {
		player->print("You have to stand up first.\n");
		return(0);
	}

	exit = findExit(player, cmnd);

	if(!exit) {
		player->print("Lock what?\n");
		return(0);
	}

	const Monster* guard = player->getRoomParent()->getGuardingExit(exit, player);
	if(guard && !player->checkStaff("%M %s.\n", guard, guard->flagIsSet(M_FACTION_NO_GUARD) ? "doesn't like you enough to let you lock it" : "won't let you lock it"))
		return(0);

	if(exit->flagIsSet(X_LOCKED)) {
		player->print("It's already locked.\n");
		return(0);
	}

	if(cmnd->num < 3) {
		player->print("Lock it with what?\n");
		return(0);
	}

	object = player->findObject(player, cmnd, 2);

	if(!object) {
		player->print("You don't have that.\n");
		return(0);
	}

	if(object->getType() != KEY) {
		player->printColor("%O is not a key.\n", object);
		return(0);
	}

	if(!exit->flagIsSet(X_LOCKABLE)) {
		player->print("You can't lock it.\n");
		return(0);
	}

	if(!exit->flagIsSet(X_CLOSED)) {
		player->print("You have to close it first.\n");
		return(0);
	}

	if(object->getShotsCur() < 1) {
		player->printColor("%O is broken.\n", object);
		return(0);
	}

	if(exit->isWall("wall-of-force")) {
		player->printColor("The %s^x is blocked by a wall of force.\n", exit->getCName());
		return(0);
	}
	// were they killed by exit effect damage?
	if(exit->doEffectDamage(player))
		return(0);
	
	if(!object->isKey(player->getUniqueRoomParent(), exit)) {
		player->print("Wrong key.\n");
		return(0);
	}

	player->unhide();
	exit->setFlag(X_LOCKED);

	player->print("Click.\n");
	broadcast(player->getSock(), player->getParent(), "%M locks the %s^x.", player, exit->getCName());

	Hooks::run(player, "lockExit", exit, "lockByCreature");

	if(object->getShotsCur() < 1 && Unique::isUnique(object)) {
		player->delObj(object, true);
		delete object;
	}
	return(0);
}


//*********************************************************************
//						getCatRef
//*********************************************************************
// we're given a string and expected to find a) the area and
// b) the room id.

void getCatRef(bstring str, CatRef* cr, const Creature* target) {
	cr->setDefault(target);

	// chop off the end!
	bstring::size_type pos = str.Find(" ");
	if(pos != bstring::npos)
		str = str.substr(0, pos);

	pos = str.Find(".");
	if(pos == bstring::npos)
		pos = str.Find(":");
	if(pos != bstring::npos) {
		cr->setArea(str.substr(0, pos));
		str.erase(0, pos+1);
	}

	if(str != "" && isdigit(str.getAt(0)))
		cr->id = atoi(str.c_str());
	else {
		if(str != "")
			cr->setArea(str);
		cr->id = -1;
	}
}

//*********************************************************************
//						getDestination
//*********************************************************************
// we're given a string and expected to find a) a MapMarker or
// b) a CatRef. the string will start at the point we wish
// to search, but may contain extra crap at the end

void getDestination(bstring str, Location* l, const Creature* target) {
	l->mapmarker.reset();
	l->room.id = 0;
	getDestination(str, &l->mapmarker, &l->room, target);
}

void getDestination(bstring str, MapMarker* mapmarker, CatRef* cr, const Creature* target) {
	bstring::size_type pos=0;
	char	*txt;
	int		x=0, y=0, z=0;

	// chop off the end!
	pos = str.find(" ", 0);
	if(pos != bstring::npos)
		str = str.substr(0, pos);

	// 1.-10.7
	txt = (char*)strstr(str.c_str(), ".");
	if(txt && isdigit(str.getAt(0))) {
		txt++;
		x = atoi(txt);

		txt = strstr(txt, ".");
		if(txt) {
			txt++;
			y = atoi(txt);
			txt = strstr(txt, ".");
			if(txt) {
				txt++;
				z = atoi(txt);
			}
		}

		mapmarker->set(atoi(str.c_str()), x, y, z);
	} else
		getCatRef(str, cr, target);
}