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/
/*
 * exits.cpp
 *	 Exit routines.
 *   ____            _
 *  |  _ \ ___  __ _| |_ __ ___  ___
 *  | |_) / _ \/ _` | | '_ ` _ \/ __|
 *  |  _ <  __/ (_| | | | | | | \__ \
 *  |_| \_\___|\__,_|_|_| |_| |_|___/
 *
 * Permission to use, modify and distribute is granted via the
 *  Creative Commons - Attribution - Non Commercial - Share Alike 3.0 License
 *    http://creativecommons.org/licenses/by-nc-sa/3.0/
 *
 * 	Copyright (C) 2007-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 "effects.h"
#include <sstream>

//*********************************************************************
//						Exit
//*********************************************************************

Exit::Exit() {

	level = trap = 0;
	zero(desc_key, sizeof(desc_key));
	zero(clanFlags, sizeof(clanFlags));
	zero(classFlags, sizeof(classFlags));
	zero(raceFlags, sizeof(raceFlags));
	key = toll = 0;
	size = NO_SIZE;
	zero(flags, sizeof(flags));
	keyArea = passphrase = open = enter = description = "";
	passlang = 0;
	size = NO_SIZE;
	hooks.setParent(this);
    parentRoom = 0;
	direction = NoDirection;
}

Exit::~Exit() {
	if(effects.effectList.size()) {
		//BaseRoom* parent = effects.list.front()->getParentRoom();
		effects.removeAll();
		//parent->removeEffectsIndex();
	}
}

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

bool Exit::operator< (const MudObject& t) const {
    return(exit_ordering(this->getCName(), t.getCName()));
}

short Exit::getLevel() const { return(level); }
bstring Exit::getOpen() const { return(open); }
short Exit::getTrap() const { return(trap); }
short Exit::getKey() const { return(key); }
bstring Exit::getKeyArea() const { return(keyArea); }
short Exit::getToll() const { return(toll); }
bstring Exit::getPassPhrase() const { return(passphrase); }
short Exit::getPassLanguage() const { return(passlang); }
bstring Exit::getDescription() const { return(description); }
Size Exit::getSize() const { return(size); }
Direction Exit::getDirection() const { return(direction); }
bstring Exit::getEnter() const { return(enter); }
BaseRoom* Exit::getRoom() const { return(parentRoom); }

void Exit::setLevel(short lvl) { level = lvl; }
void Exit::setOpen(bstring o) { open = o; }
void Exit::setTrap(short t) { trap = t; }
void Exit::setKey(short k) { key = k; }
void Exit::setKeyArea(bstring k) { keyArea = k; }
void Exit::setToll(short t) { toll = t; }
void Exit::setPassPhrase(bstring phrase) { passphrase = phrase; }
void Exit::setPassLanguage(short lang) { passlang = lang; }
void Exit::setDescription(bstring d) { description = d; }
void Exit::setSize(Size s) { size = s; }
void Exit::setDirection(Direction d) { direction = d; }
void Exit::setEnter(bstring e) { enter = e; }
void Exit::setRoom(BaseRoom* room) { parentRoom = room; }

// Checks if the exit is flagged to relock after being used, and do as such

void Exit::checkReLock(Creature* creature, bool sneaking) {
	if(!this)
		return;
	if(flagIsSet(X_LOCK_AFTER_USAGE) && !creature->isCt()) {
		bool nowLocked = false;
		bool nowClosed = false;
		if(flagIsSet(X_CLOSABLE) && !flagIsSet(X_CLOSED)) {
			setFlag(X_CLOSED);
			nowClosed = true;
		}

		if(flagIsSet(X_LOCKABLE) && !flagIsSet(X_LOCKED)) {
			setFlag(X_LOCKED);
			nowLocked = true;
		}
		bstring exitAction;
		if(nowClosed && nowLocked) {
			exitAction = "closes and locks itself";
		} else if (nowClosed) {
			exitAction = "closes itself";
		} else if(nowLocked) {
			exitAction = "locks";
		}
		if(nowClosed || nowLocked) {
			if(!sneaking) {
				broadcast(creature->getSock(), parentRoom, "The %s %s.^x", getCName(), exitAction.c_str());
			} else {
				 broadcast(isCt, creature->getSock(), parentRoom, "*STAFF* The %s %s^x.", getCName(), exitAction.c_str());
			}
		}
	}
}

bool Exit::flagIsSet(int flag) const {
	return(flags[flag/8] & 1<<(flag%8));
}
void Exit::setFlag(int flag) {
	flags[flag/8] |= 1<<(flag%8);
}
void Exit::clearFlag(int flag) {
	flags[flag/8] &= ~(1<<(flag%8));
}
bool Exit::toggleFlag(int flag) {
	if(flagIsSet(flag))
		clearFlag(flag);
	else
		setFlag(flag);
	return(flagIsSet(flag));
}

//*********************************************************************
//						findExit
//*********************************************************************
// This function attempts to find the exit specified by the given string
// and value by looking through the exit list headed by the second para-
// meter.  If found, a pointer to the exit is returned.

Exit *findExit(Creature* creature, cmd* cmnd, int val, BaseRoom* room) {
	return(findExit(creature, cmnd->str[val], cmnd->val[val], room));
}

Exit *findExit(Creature* creature, bstring str, int val, BaseRoom* room) {
	int		match=0;
	bool	minThree = (creature->getAsPlayer() && !creature->isStaff() && str.length() < 3);
	str = removeColor(str);

	if(!room)
		if((room = creature->getRoomParent()) == NULL)
			return(NULL);

	for(Exit* exit : room->exits) {
		bstring name = removeColor(exit->getName());
		name = name.toLower();

		if(!creature->isStaff()) {
			if(!creature->canSee(exit))
				continue;
			if(	minThree &&
				(exit->flagIsSet(X_CONCEALED) || exit->flagIsSet(X_SECRET)) &&
				exit->getName().length() > 2
			)
				continue;
		}

		if(!exit->flagIsSet(X_DESCRIPTION_ONLY) || creature->isStaff()) {
			if(!strncmp(name.c_str(), str.c_str(), str.length()))
				match++;
		} else {
			if(name == str)
				match++;
		}

		if(match == val)
			return(exit);
	}

	return(0);
}

//*********************************************************************
//							raceRestrict
//*********************************************************************

bool Exit::raceRestrict(const Creature* creature) const {
	bool pass = false;

	// if no flags are set
	if(	!flagIsSet(X_SEL_DWARF) &&
		!flagIsSet(X_SEL_ELF) &&
		!flagIsSet(X_SEL_HALFELF) &&
		!flagIsSet(X_SEL_HALFLING) &&
		!flagIsSet(X_SEL_HUMAN) &&
		!flagIsSet(X_SEL_ORC) &&
		!flagIsSet(X_SEL_HALFGIANT) &&
		!flagIsSet(X_SEL_GNOME) &&
		!flagIsSet(X_SEL_TROLL) &&
		!flagIsSet(X_SEL_HALFORC) &&
		!flagIsSet(X_SEL_OGRE) &&
		!flagIsSet(X_SEL_DARKELF) &&
		!flagIsSet(X_SEL_GOBLIN) &&
		!flagIsSet(X_SEL_MINOTAUR) &&
		!flagIsSet(X_SEL_SERAPH) &&
		!flagIsSet(X_SEL_KOBOLD) &&
		!flagIsSet(X_SEL_CAMBION) &&
		!flagIsSet(X_SEL_BARBARIAN) &&
		!flagIsSet(X_SEL_KATARAN) &&
		!flagIsSet(X_SEL_TIEFLING) &&
		!flagIsSet(X_RSEL_INVERT) )
		return(false);

	// if the race flag is set and they match, they pass
	pass = (
		(flagIsSet(X_SEL_DWARF) && creature->isRace(DWARF)) ||
		(flagIsSet(X_SEL_ELF) && creature->isRace(ELF)) ||
		(flagIsSet(X_SEL_HALFELF) && creature->isRace(HALFELF)) ||
		(flagIsSet(X_SEL_HALFLING) && creature->isRace(HALFLING)) ||
		(flagIsSet(X_SEL_HUMAN) && creature->isRace(HUMAN)) ||
		(flagIsSet(X_SEL_ORC) && creature->isRace(ORC)) ||
		(flagIsSet(X_SEL_HALFGIANT) && creature->isRace(HALFGIANT)) ||
		(flagIsSet(X_SEL_GNOME) && creature->isRace(GNOME)) ||
		(flagIsSet(X_SEL_TROLL) && creature->isRace(TROLL)) ||
		(flagIsSet(X_SEL_HALFORC) && creature->isRace(HALFORC)) ||
		(flagIsSet(X_SEL_OGRE) && creature->isRace(OGRE)) ||
		(flagIsSet(X_SEL_DARKELF) && creature->isRace(DARKELF)) ||
		(flagIsSet(X_SEL_GOBLIN) && creature->isRace(GOBLIN)) ||
		(flagIsSet(X_SEL_MINOTAUR) && creature->isRace(MINOTAUR)) ||
		(flagIsSet(X_SEL_SERAPH) && creature->isRace(SERAPH)) ||
		(flagIsSet(X_SEL_KOBOLD) && creature->isRace(KOBOLD)) ||
		(flagIsSet(X_SEL_CAMBION) && creature->isRace(CAMBION)) ||
		(flagIsSet(X_SEL_BARBARIAN) && creature->isRace(BARBARIAN)) ||
		(flagIsSet(X_SEL_KATARAN) && creature->isRace(KATARAN)) ||
		(flagIsSet(X_SEL_TIEFLING) && creature->isRace(TIEFLING))
	);

	if(flagIsSet(X_RSEL_INVERT)) pass = !pass;

	return(!pass);
}

//*********************************************************************
//							classRestrict
//*********************************************************************

bool Exit::classRestrict(const Creature* creature) const {
	bool pass = false;

	// if no flags are set
	if(	!flagIsSet(X_SEL_ASSASSIN) &&
		!flagIsSet(X_SEL_BERSERKER) &&
		!flagIsSet(X_SEL_CLERIC) &&
		!flagIsSet(X_SEL_FIGHTER) &&
		!flagIsSet(X_SEL_MAGE) &&
		!flagIsSet(X_SEL_PALADIN) &&
		!flagIsSet(X_SEL_RANGER) &&
		!flagIsSet(X_SEL_THIEF) &&
		!flagIsSet(X_SEL_VAMPIRE) &&
		!flagIsSet(X_SEL_MONK) &&
		!flagIsSet(X_SEL_DEATHKNIGHT) &&
		!flagIsSet(X_SEL_DRUID) &&
		!flagIsSet(X_SEL_LICH) &&
		!flagIsSet(X_SEL_WEREWOLF) &&
		!flagIsSet(X_SEL_BARD) &&
		!flagIsSet(X_SEL_ROGUE) &&
		!flagIsSet(X_CSEL_INVERT) )
		return(false);

	// if the class flag is set and they match, they pass
	pass = (
		(flagIsSet(X_SEL_ASSASSIN) && creature->getClass() == ASSASSIN) ||
		(flagIsSet(X_SEL_BERSERKER) && creature->getClass() == BERSERKER) ||
		(flagIsSet(X_SEL_CLERIC) && creature->getClass() == CLERIC) ||
		(flagIsSet(X_SEL_FIGHTER) && creature->getClass() == FIGHTER) ||
		(flagIsSet(X_SEL_MAGE) && creature->getClass() == MAGE) ||
		(flagIsSet(X_SEL_PALADIN) && creature->getClass() == PALADIN) ||
		(flagIsSet(X_SEL_RANGER) && creature->getClass() == RANGER) ||
		(flagIsSet(X_SEL_THIEF) && creature->getClass() == THIEF) ||
		(flagIsSet(X_SEL_VAMPIRE) && creature->isEffected("vampirism")) ||
		(flagIsSet(X_SEL_MONK) && creature->getClass() == MONK) ||
		(flagIsSet(X_SEL_DEATHKNIGHT) && creature->getClass() == DEATHKNIGHT) ||
		(flagIsSet(X_SEL_DRUID) && creature->getClass() == DRUID) ||
		(flagIsSet(X_SEL_LICH) && creature->getClass() == LICH) ||
		(flagIsSet(X_SEL_WEREWOLF) && creature->isEffected("lycanthropy")) ||
		(flagIsSet(X_SEL_BARD) && creature->getClass() == BARD) ||
		(flagIsSet(X_SEL_ROGUE) && creature->getClass() == ROGUE)
	);

	if(flagIsSet(X_CSEL_INVERT)) pass = !pass;

	return(!pass);
}

//*********************************************************************
//						clanRestrict
//*********************************************************************

bool Exit::clanRestrict(const Creature* creature) const {

	// if the exit requires pledging, let any clan pass
	if(flagIsSet(X_PLEDGE_ONLY))
		return(creature->getClan()!=0);

	// if no flags are set
	if(	!flagIsSet(X_CLAN_1) &&
		!flagIsSet(X_CLAN_2) &&
		!flagIsSet(X_CLAN_3) &&
		!flagIsSet(X_CLAN_4) &&
		!flagIsSet(X_CLAN_5) &&
		!flagIsSet(X_CLAN_6) &&
		!flagIsSet(X_CLAN_7) &&
		!flagIsSet(X_CLAN_8) &&
		!flagIsSet(X_CLAN_9) &&
		!flagIsSet(X_CLAN_10) &&
		!flagIsSet(X_CLAN_11) &&
		!flagIsSet(X_CLAN_12) )
		return(false);

	// if the clan flag is set and they match, they pass
	if(	(flagIsSet(X_CLAN_1) && creature->getClan() == 1) ||
		(flagIsSet(X_CLAN_2) && creature->getClan() == 2) ||
		(flagIsSet(X_CLAN_3) && creature->getClan() == 3) ||
		(flagIsSet(X_CLAN_4) && creature->getClan() == 4) ||
		(flagIsSet(X_CLAN_5) && creature->getClan() == 5) ||
		(flagIsSet(X_CLAN_6) && creature->getClan() == 6) ||
		(flagIsSet(X_CLAN_7) && creature->getClan() == 7) ||
		(flagIsSet(X_CLAN_8) && creature->getClan() == 8) ||
		(flagIsSet(X_CLAN_9) && creature->getClan() == 9) ||
		(flagIsSet(X_CLAN_10) && creature->getClan() == 10) ||
		(flagIsSet(X_CLAN_11) && creature->getClan() == 11) ||
		(flagIsSet(X_CLAN_12) && creature->getClan() == 12) )
		return(false);

	return(true);
}

//*********************************************************************
//						alignRestrict
//*********************************************************************

bool Exit::alignRestrict(const Creature* creature) const {
	return(
		(flagIsSet(X_NO_GOOD_ALIGN) && creature->getAdjustedAlignment() > NEUTRAL) ||
		(flagIsSet(X_NO_EVIL_ALIGN) && creature->getAdjustedAlignment() < NEUTRAL) ||
		(flagIsSet(X_NO_NEUTRAL_ALIGN) && creature->getAdjustedAlignment() == NEUTRAL)
	);
}

//*********************************************************************
//						getReturnExit
//*********************************************************************
// Tries to find a return exit. Will only return an exit if there is
// only one unique exit back to the parent room.

Exit* Exit::getReturnExit(const BaseRoom* parent, BaseRoom** targetRoom) const {
	(*targetRoom) = target.loadRoom();
	if(!*targetRoom)
		return(0);

	Exit* exit=0;
	bool found = false;

	const AreaRoom* aRoom = parent->getAsConstAreaRoom();
	const UniqueRoom* uRoom = parent->getAsConstUniqueRoom();

	for(Exit* ext : (*targetRoom)->exits) {
		if(ext->target.mapmarker.getArea()) {
			if(aRoom && ext->target.mapmarker == aRoom->mapmarker) {
				if(found)
					return(0);
				exit = ext;
				found = true;
			}
		} else {
			if(uRoom && ext-> target.room == uRoom->info) {
				if(found)
					return(0);
				exit = ext;
				found = true;
			}
		}
	}

	return(exit);
}

//*********************************************************************
//						blockedByStr
//*********************************************************************

bstring Exit::blockedByStr(char color, bstring spell, bstring effectName, bool detectMagic, bool canSee) const {
	EffectInfo* effect = 0;
	std::ostringstream oStr;

	if(canSee)
		oStr << "^" << color << "The " << getName() << "^" << color << " is blocked by a " << spell;
	else
		oStr << "^" << color << "A " << spell << " stands in the room";

	if(detectMagic) {
		effect = getEffect(effectName);
		if(effect->getOwner() != "")
			oStr << " (cast by " << effect->getOwner() << ")";
	}

	oStr << ".\n";
	return(oStr.str());
}

//*********************************************************************
//						showExit
//*********************************************************************

bool Player::showExit(const Exit* exit, int magicShowHidden) const {
	if(isStaff())
		return(true);
	return( canSee(exit) &&
		(!exit->flagIsSet(X_SECRET) || magicShowHidden) &&
		!exit->isConcealed(this) &&
		!exit->flagIsSet(X_DESCRIPTION_ONLY) &&
		(isEffected("fly") ? 1:!exit->flagIsSet(X_NEEDS_FLY))
	);
}

//*********************************************************************
//						addEffectReturnExit
//*********************************************************************
// Add an effect to the given exit and the return exit (ie, an exit in
// the room it points to that points back to this room)

void Exit::addEffectReturnExit(bstring effect, long duration, int strength, const Creature* owner) {
	BaseRoom *targetRoom=0;

	addEffect(effect, duration, strength, NULL, true, owner);
	// switch the meaning of exit
	Exit* exit = getReturnExit(owner->getConstRoomParent(), &targetRoom);
	if(exit)
		exit->addEffect(effect, duration, strength, NULL, true, owner);
}

//*********************************************************************
//						removeEffectReturnExit
//*********************************************************************
// Add an effect to the given exit and the return exit (ie, an exit in
// the room it points to that points back to this room)

void Exit::removeEffectReturnExit(bstring effect, BaseRoom* rParent) {
	BaseRoom *targetRoom=0;

	removeEffect(effect, true, false);
	// switch the meaning of exit
	Exit* exit = getReturnExit(rParent, &targetRoom);
	if(exit)
		exit->removeEffect(effect, true, false);
}

//*********************************************************************
//						isWall
//*********************************************************************

bool Exit::isWall(bstring name) const {
	EffectInfo* effect = getEffect(name);
	if(!effect)
		return(false);
	// a wall of force with "extra" set is temporarily disabled
	if(effect->getExtra())
		return(false);
	return(true);
}

//*********************************************************************
//						isConcealed
//*********************************************************************

bool Exit::isConcealed(const Creature* viewer) const {
	if(flagIsSet(X_CONCEALED))
		return(true);
	if(isEffected("concealed") && (!viewer || !viewer->willIgnoreIllusion()))
		return(true);
	return(false);
}

//*********************************************************************
//						getDir
//*********************************************************************

Direction getDir(bstring str) {
	int n = str.getLength();
	if(!n)
		return(NoDirection);
	str = removeColor(str);

	if(!strncmp(str.c_str(), "north", n))
		return(North);
	else if(!strncmp(str.c_str(), "east", n))
		return(East);
	else if(!strncmp(str.c_str(), "south", n))
		return(South);
	else if(!strncmp(str.c_str(), "west", n))
		return(West);
	else if(str == "ne" || !strncmp(str.c_str(), "northeast", n))
		return(Northeast);
	else if(str == "nw" || !strncmp(str.c_str(), "northwest", n))
		return(Northwest);
	else if(str == "se" || !strncmp(str.c_str(), "southeast", n))
		return(Southeast);
	else if(str == "sw" || !strncmp(str.c_str(), "southwest", n))
		return(Southwest);
	return(NoDirection);
}

//*********************************************************************
//						getDirName
//*********************************************************************

bstring getDirName(Direction dir) {
	switch(dir) {
	case North:
		return("north");
	case East:
		return("east");
	case South:
		return("south");
	case West:
		return("west");
	case Northeast:
		return("northeast");
	case Northwest:
		return("northwest");
	case Southeast:
		return("southeast");
	case Southwest:
		return("southwest");
	default:
		return("none");
	}
}