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