roh/conf/area/
roh/game/talk/
roh/help/
roh/monsters/ocean/
roh/objects/ocean/
roh/player/
roh/rooms/area/1/
roh/rooms/misc/
roh/rooms/ocean/
roh/src-2.44b/
/*
 * 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-2009 Jason Mitchell, Randi Mitchell
 * 	   Contributions by Tim Callahan, Jonathan Hseu
 *  Based on Mordor (C) Brooke Paul, Brett J. Vickers, John P. Freeman
 *
 */
#include "mud.h"
#include "effects.h"
#include "magic.h"
#include <sstream>

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

Exit::Exit() {
	zero(name, sizeof(name));

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

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

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); }
Size Exit::getSize() const { return(size); }
bstring Exit::getEnter() const { return(enter); }

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::setSize(Size s) { size = s; }
void Exit::setEnter(bstring e) { enter = e; }

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, xtag *first_xt) {
	return(findExit(creature, cmnd->str[val], cmnd->val[val], first_xt));
}

Exit *findExit(Creature* creature, bstring str, int val, xtag *first_xt) {
	xtag	*xp = first_xt;
	Exit	*exit=0;
	int		match=0;
	bool	minThree = (creature->getPlayer() && !creature->isStaff() && str.length() < 3);

	if(!xp)
		xp = creature->getRoom()->first_ext;

	while(xp) {
		exit = xp->ext;
		xp = xp->next_tag;

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

		if(!exit->flagIsSet(X_DESCRIPTION_ONLY) || creature->isStaff()) {
			if(!strncmp(exit->name, str.c_str(), str.length()))
				match++;
		} else {
			if(!strcmp(exit->name, str.c_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);

	xtag* xp = (*targetRoom)->first_ext;
	Exit* exit=0;
	bool found = false;

	const AreaRoom* aRoom = parent->getConstAreaRoom();
	const Room* uRoom = parent->getConstUniqueRoom();

	while(xp) {
		if(xp->ext->target.mapmarker.getArea()) {
			if(aRoom && xp->ext->target.mapmarker == aRoom->mapmarker) {
				if(found)
					return(0);
				exit = xp->ext;
				found = true;
			}
		} else {
			if(uRoom && xp->ext-> target.room == uRoom->info) {
				if(found)
					return(0);
				exit = xp->ext;
				found = true;
			}
		}
		xp = xp->next_tag;
	}

	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 " << name << " 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, true, owner->getRoom(), owner);
	// switch the meaning of exit
	Exit* exit = getReturnExit(owner->getRoom(), &targetRoom);
	if(exit)
		exit->addEffect(effect, duration, strength, true, targetRoom, 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, rParent);
	// switch the meaning of exit
	Exit* exit = getReturnExit(rParent, &targetRoom);
	if(exit)
		exit->removeEffect(effect, true, false, targetRoom);
}

//*********************************************************************
//						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);
}