/*
* rooms.cpp
* Room routines.
* ____ _
* | _ \ ___ __ _| |_ __ ___ ___
* | |_) / _ \/ _` | | '_ ` _ \/ __|
* | _ < __/ (_| | | | | | | \__ \
* |_| \_\___|\__,_|_|_| |_| |_|___/
*
* Permission to use, modify and distribute is granted via the
* Creative Commons - Attribution - Non Commercia4 - 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 "move.h"
#include <sstream>
BaseRoom::BaseRoom() {
tempNoKillDarkmetal = false;
memset(misc, 0, sizeof(misc));
hooks.setParent(this);
}
bstring BaseRoom::getVersion() const { return(version); }
void BaseRoom::setVersion(bstring v) { version = v; }
bstring BaseRoom::fullName() const {
const UniqueRoom* uRoom = getAsConstUniqueRoom();
const AreaRoom* aRoom = getAsConstAreaRoom();
std::ostringstream oStr;
if(uRoom) {
oStr << uRoom->info.str() << "(" << uRoom->getName() << ")";
} else if(aRoom) {
oStr << aRoom->mapmarker.str();
} else
oStr << "invalid room";
return(oStr.str());
}
//*********************************************************************
// Room
//*********************************************************************
UniqueRoom::UniqueRoom() {
short_desc = "";
long_desc = "";
lowLevel = highLevel = maxmobs = trap = trapweight = trapstrength = 0;
memset(flags, 0, sizeof(flags));
roomExp = 0;
size = NO_SIZE;
beenhere = 0;
memset(last_mod, 0, sizeof(last_mod));
memset(lastModTime, 0, sizeof(lastModTime));
memset(lastPly, 0, sizeof(lastPly));
memset(lastPlyTime, 0, sizeof(lastPlyTime));
}
bool UniqueRoom::operator< (const UniqueRoom& t) const {
if(this->info.area[0] == t.info.area[0]) {
return(this->info.id < t.info.id);
} else {
return(this->info.area < t.info.area);
}
}
bstring UniqueRoom::getShortDescription() const { return(short_desc); }
bstring UniqueRoom::getLongDescription() const { return(long_desc); }
short UniqueRoom::getLowLevel() const { return(lowLevel); }
short UniqueRoom::getHighLevel() const { return(highLevel); }
short UniqueRoom::getMaxMobs() const { return(maxmobs); }
short UniqueRoom::getTrap() const { return(trap); }
CatRef UniqueRoom::getTrapExit() const { return(trapexit); }
short UniqueRoom::getTrapWeight() const { return(trapweight); }
short UniqueRoom::getTrapStrength() const { return(trapstrength); }
bstring UniqueRoom::getFaction() const { return(faction); }
long UniqueRoom::getBeenHere() const { return(beenhere); }
int UniqueRoom::getRoomExperience() const { return(roomExp); }
Size UniqueRoom::getSize() const { return(size); }
void UniqueRoom::setShortDescription(const bstring& desc) { short_desc = desc; }
void UniqueRoom::setLongDescription(const bstring& desc) { long_desc = desc; }
void UniqueRoom::appendShortDescription(const bstring& desc) { short_desc += desc; }
void UniqueRoom::appendLongDescription(const bstring& desc) { long_desc += desc; }
void UniqueRoom::setLowLevel(short lvl) { lowLevel = MAX(0, lvl); }
void UniqueRoom::setHighLevel(short lvl) { highLevel = MAX(0, lvl); }
void UniqueRoom::setMaxMobs(short m) { maxmobs = MAX(0, m); }
void UniqueRoom::setTrap(short t) { trap = t; }
void UniqueRoom::setTrapExit(CatRef t) { trapexit = t; }
void UniqueRoom::setTrapWeight(short weight) { trapweight = MAX(0, weight); }
void UniqueRoom::setTrapStrength(short strength) { trapstrength = MAX(0, strength); }
void UniqueRoom::setFaction(bstring f) { faction = f; }
void UniqueRoom::incBeenHere() { beenhere++; }
void UniqueRoom::setRoomExperience(int exp) { roomExp = MAX(0, exp); }
void UniqueRoom::setSize(Size s) { size = s; }
//*********************************************************************
// deconstructors
//*********************************************************************
void BaseRoom::BaseDestroy() {
clearExits();
ObjectSet::iterator it;
Object* obj;
for(it = objects.begin() ; it != objects.end() ; ) {
obj = (*it++);
delete obj;
}
objects.clear();
MonsterSet::iterator mIt = monsters.begin();
while(mIt != monsters.end()) {
Monster* mons = (*mIt++);
free_crt(mons);
}
monsters.clear();
players.clear();
effects.removeAll();
removeEffectsIndex();
gServer->removeDelayedActions(this);
}
UniqueRoom::~UniqueRoom() {
BaseDestroy();
}
AreaRoom::~AreaRoom() {
BaseDestroy();
reset();
}
bool AreaRoom::operator< (const AreaRoom& t) const {
if(this->mapmarker.getX() == t.mapmarker.getX()) {
if(this->mapmarker.getY() == t.mapmarker.getY()) {
return(this->mapmarker.getZ() < t.mapmarker.getZ());
} else {
return(this->mapmarker.getY() < t.mapmarker.getY());
}
} else {
return(this->mapmarker.getX() < t.mapmarker.getX());
}
}
//*********************************************************************
// reset
//*********************************************************************
void AreaRoom::reset() {
area = 0;
decCompass = needsCompass = stayInMemory = false;
ExitList::iterator xit;
for(xit = exits.begin() ; xit != exits.end(); ) {
Exit* exit = (*xit++);
delete exit;
}
exits.clear();
}
//*********************************************************************
// AreaRoom
//*********************************************************************
AreaRoom::AreaRoom(Area *a, const MapMarker *m) {
reset();
area = a;
if(m) {
setMapMarker(m);
recycle();
}
}
Size AreaRoom::getSize() const { return(SIZE_COLOSSAL); }
bool AreaRoom::getNeedsCompass() const { return(needsCompass); }
bool AreaRoom::getDecCompass() const { return(decCompass); }
bool AreaRoom::getStayInMemory() const { return(stayInMemory); }
void AreaRoom::setNeedsCompass(bool need) { needsCompass = need; }
void AreaRoom::setDecCompass(bool dec) { decCompass = dec; }
void AreaRoom::setStayInMemory(bool stay) { stayInMemory = stay; }
//*********************************************************************
// recycle
//*********************************************************************
void AreaRoom::recycle() {
ObjectSet::iterator it;
Object* obj;
for(it = objects.begin() ; it != objects.end() ; ) {
obj = (*it++);
delete obj;
}
objects.clear();
updateExits();
}
//*********************************************************************
// setMapMarker
//*********************************************************************
void AreaRoom::setMapMarker(const MapMarker* m) {
unRegisterMo();
setId("-1");
bstring str = mapmarker.str();
area->rooms.erase(str);
*&mapmarker = *m;
str = mapmarker.str();
area->rooms[str] = this;
setId(bstring("R") + str);
registerMo();
}
//*********************************************************************
// updateExits
//*********************************************************************
bool AreaRoom::updateExit(bstring dir) {
if(dir == "north") {
mapmarker.add(0, 1, 0);
link_rom(this, &mapmarker, dir);
mapmarker.add(0, -1, 0);
} else if(dir == "east") {
mapmarker.add(1, 0, 0);
link_rom(this, &mapmarker, dir);
mapmarker.add(-1, 0, 0);
} else if(dir == "south") {
mapmarker.add(0, -1, 0);
link_rom(this, &mapmarker, dir);
mapmarker.add(0, 1, 0);
} else if(dir == "west") {
mapmarker.add(-1, 0, 0);
link_rom(this, &mapmarker, dir);
mapmarker.add(1, 0, 0);
} else if(dir == "northeast") {
mapmarker.add(1, 1, 0);
link_rom(this, &mapmarker, dir);
mapmarker.add(-1, -1, 0);
} else if(dir == "northwest") {
mapmarker.add(-1, 1, 0);
link_rom(this, &mapmarker, dir);
mapmarker.add(1, -1, 0);
} else if(dir == "southeast") {
mapmarker.add(1, -1, 0);
link_rom(this, &mapmarker, dir);
mapmarker.add(-1, 1, 0);
} else if(dir == "southwest") {
mapmarker.add(-1, -1, 0);
link_rom(this, &mapmarker, dir);
mapmarker.add(1, 1, 0);
} else
return(false);
return(true);
}
void AreaRoom::updateExits() {
updateExit("north");
updateExit("east");
updateExit("south");
updateExit("west");
updateExit("northeast");
updateExit("northwest");
updateExit("southeast");
updateExit("southwest");
}
//*********************************************************************
// exitNameByOrder
//*********************************************************************
const char *exitNameByOrder(int i) {
if(i==0) return("north");
if(i==1) return("east");
if(i==2) return("south");
if(i==3) return("west");
if(i==4) return("northeast");
if(i==5) return("northwest");
if(i==6) return("southeast");
if(i==7) return("southwest");
return("");
}
//*********************************************************************
// canSave
//*********************************************************************
bool AreaRoom::canSave() const {
int i=0;
if(unique.id)
return(true);
if(needsEffectsIndex())
return(true);
if(!exits.empty()) {
for(Exit* ext : exits) {
if(ext->getName() != exitNameByOrder(i++))
return(true);
// doesnt check rooms leading to other area rooms
if(ext->target.room.id)
return(true);
}
if(i != 8)
return(true);
}
return(false);
}
//*********************************************************************
// getRandomWanderInfo
//*********************************************************************
WanderInfo* AreaRoom::getRandomWanderInfo() {
std::list<AreaZone*>::iterator it;
AreaZone *zone=0;
TileInfo* tile=0;
std::map<int, WanderInfo*> w;
int i=0;
// we want to randomly pick one of the zones
for(it = area->zones.begin() ; it != area->zones.end() ; it++) {
zone = (*it);
if(zone->wander.getTraffic() && zone->inside(area, &mapmarker)) {
w[i++] = &zone->wander;
}
}
tile = area->getTile(area->getTerrain(0, &mapmarker, 0, 0, 0, true), area->getSeasonFlags(&mapmarker));
if(tile && tile->wander.getTraffic())
w[i++] = &tile->wander;
if(!i)
return(0);
return(w[mrand(0,i-1)]);
}
//*********************************************************************
// getWanderInfo
//*********************************************************************
WanderInfo* BaseRoom::getWanderInfo() {
UniqueRoom* uRoom = getAsUniqueRoom();
if(uRoom)
return(&uRoom->wander);
AreaRoom* aRoom = getAsAreaRoom();
if(aRoom)
return(aRoom->getRandomWanderInfo());
return(0);
}
//*********************************************************************
// save
//*********************************************************************
void AreaRoom::save(Player* player) const {
char filename[256];
sprintf(filename, "%s/%d/", Path::AreaRoom, area->id);
Path::checkDirExists(filename);
strcat(filename, mapmarker.filename().c_str());
if(!canSave()) {
if(file_exists(filename)) {
if(player)
player->print("Restoring this room to generic status.\n");
unlink(filename);
} else {
if(player)
player->print("There is no reason to save this room!\n\n");
}
return;
}
// record rooms saved during swap
if(gConfig->swapIsInteresting(this))
gConfig->swapLog((bstring)"a" + mapmarker.str(), false);
xmlDocPtr xmlDoc;
xmlNodePtr rootNode, curNode;
xmlDoc = xmlNewDoc(BAD_CAST "1.0");
rootNode = xmlNewDocNode(xmlDoc, NULL, BAD_CAST "AreaRoom", NULL);
xmlDocSetRootElement(xmlDoc, rootNode);
unique.save(rootNode, "Unique", false);
xml::saveNonZeroNum(rootNode, "NeedsCompass", needsCompass);
xml::saveNonZeroNum(rootNode, "DecCompass", decCompass);
effects.save(rootNode, "Effects");
hooks.save(rootNode, "Hooks");
curNode = xml::newStringChild(rootNode, "MapMarker");
mapmarker.save(curNode);
curNode = xml::newStringChild(rootNode, "Exits");
saveExitsXml(curNode);
xml::saveFile(filename, xmlDoc);
xmlFreeDoc(xmlDoc);
if(player)
player->print("Room saved.\n");
}
//*********************************************************************
// delExit
//*********************************************************************
bool BaseRoom::delExit(Exit *exit) {
if(exit) {
ExitList::iterator xit = std::find(exits.begin(), exits.end(), exit);
if(xit != exits.end()) {
exits.erase(xit);
delete exit;
return(true);
}
}
return(false);
}
bool BaseRoom::delExit( bstring dir) {
for(Exit* ext : exits) {
if(ext->getName() == dir.c_str()) {
exits.remove(ext);
delete ext;
return(true);
}
}
return(false);
}
void BaseRoom::clearExits() {
ExitList::iterator xit;
for(xit = exits.begin() ; xit != exits.end(); ) {
Exit* exit = (*xit++);
if(exit->flagIsSet(X_PORTAL)) {
BaseRoom* target = exit->target.loadRoom();
Move::deletePortal(target, exit->getPassPhrase());
}
delete exit;
}
exits.clear();
}
//*********************************************************************
// load
//*********************************************************************
void AreaRoom::load(xmlNodePtr rootNode) {
xmlNodePtr childNode = rootNode->children;
clearExits();
while(childNode) {
if(NODE_NAME(childNode, "Exits"))
readExitsXml(childNode);
else if(NODE_NAME(childNode, "MapMarker")) {
mapmarker.load(childNode);
area->rooms[mapmarker.str()] = this;
}
else if(NODE_NAME(childNode, "Unique")) unique.load(childNode);
else if(NODE_NAME(childNode, "NeedsCompass")) xml::copyToBool(needsCompass, childNode);
else if(NODE_NAME(childNode, "DecCompass")) xml::copyToBool(decCompass, childNode);
else if(NODE_NAME(childNode, "Effects")) effects.load(childNode);
else if(NODE_NAME(childNode, "Hooks")) hooks.load(childNode);
childNode = childNode->next;
}
addEffectsIndex();
}
//*********************************************************************
// canDelete
//*********************************************************************
bool AreaRoom::canDelete() {
// don't delete unique rooms
if(stayInMemory || unique.id)
return(false);
if(!monsters.empty())
return(false);
if(!players.empty())
return(false);
for(Object* obj : objects) {
if(!obj->flagIsSet(O_DISPOSABLE))
return(false);
}
// any room effects?
if(needsEffectsIndex())
return(false);
if(!exits.empty()) {
int i=0;
for(Exit* ext : exits) {
if(ext->getName() != exitNameByOrder(i++))
return(false);
// doesnt check rooms leading to other area rooms
if(ext->target.room.id)
return(false);
}
if(i != 8)
return(false);
}
return(true);
}
//*********************************************************************
// isInteresting
//*********************************************************************
// This will determine whether the looker will see anything of
// interest in the room. This is used by cmdScout.
bool AreaRoom::isInteresting(const Player *viewer) const {
int i=0;
if(unique.id)
return(true);
for(Player* ply : players) {
if(!ply->flagIsSet(P_HIDDEN) && viewer->canSee(ply))
return(true);
}
for(Monster* mons : monsters) {
if(!mons->flagIsSet(M_HIDDEN) && viewer->canSee(mons))
return(true);
}
i = 0;
for(Exit* ext : exits) {
if(i < 7 && ext->getName() != exitNameByOrder(i))
return(true);
if(i >= 8) {
if(viewer->showExit(ext))
return(true);
}
i++;
}
// Fewer than 8 exits
if( i < 7 )
return(true);
// xp = first_ext;
// for(i=0; i<8; i++) {
// if(!xp)
// return(true);
// if(strcmp(xp->ext->name, exitNameByOrder(i)))
// return(true);
// xp = xp->next_tag;
// }
//
// check out the remaining exits
// while(xp) {
// if(viewer->showExit(xp->ext))
// return(true);
// xp = xp->next_tag;
// }
return(false);
}
bool AreaRoom::flagIsSet(int flag) const {
MapMarker m = mapmarker;
return(area->flagIsSet(flag, &m));
}
void AreaRoom::setFlag(int flag) {
std::cout << "Trying to set a flag on an area room!" << std::endl;
return;
}
bool UniqueRoom::flagIsSet(int flag) const {
return(flags[flag/8] & 1<<(flag%8));
}
void UniqueRoom::setFlag(int flag) {
flags[flag/8] |= 1<<(flag%8);
}
void UniqueRoom::clearFlag(int flag) {
flags[flag/8] &= ~(1<<(flag%8));
}
bool UniqueRoom::toggleFlag(int flag) {
if(flagIsSet(flag))
clearFlag(flag);
else
setFlag(flag);
return(flagIsSet(flag));
}
//*********************************************************************
// isMagicDark
//*********************************************************************
bool BaseRoom::isMagicDark() const {
if(flagIsSet(R_MAGIC_DARKNESS))
return(true);
// check for darkness spell
for(Player* ply : players) {
// darkness spell on staff does nothing
if(ply->isEffected("darkness") && !ply->isStaff())
return(true);
if(ply->flagIsSet(P_DARKNESS) && !ply->flagIsSet(P_DM_INVIS))
return(true);
}
for(Monster* mons : monsters) {
if(mons->isEffected("darkness"))
return(true);
if(mons->flagIsSet(M_DARKNESS))
return(true);
}
for(Object *obj : objects) {
if(obj->flagIsSet(O_DARKNESS))
return(true);
}
return(false);
}
//*********************************************************************
// isNormalDark
//*********************************************************************
bool BaseRoom::isNormalDark() const {
if(flagIsSet(R_DARK_ALWAYS))
return(true);
if(!isDay() && flagIsSet(R_DARK_AT_NIGHT))
return(true);
return(false);
}
void BaseRoom::addExit(Exit *ext) {
ext->setRoom(this);
exits.push_back(ext);
}
//
// This function checks the status of the exits in a room. If any of
// the exits are closable or lockable, and the correct time interval
// has occurred since the last opening/unlocking, then the doors are
// re-shut/re-closed.
//
void BaseRoom::checkExits() {
long t = time(0);
for(Exit* ext : exits) {
if( ext->flagIsSet(X_LOCKABLE) && (ext->ltime.ltime + ext->ltime.interval) < t)
{
ext->setFlag(X_LOCKED);
ext->setFlag(X_CLOSED);
} else if( ext->flagIsSet(X_CLOSABLE) && (ext->ltime.ltime + ext->ltime.interval) < t)
ext->setFlag(X_CLOSED);
}
}
//*********************************************************************
// deityRestrict
//*********************************************************************
// if our particular flag is set, fail = 0
// else if ANY OTHER flags are set, fail = 1
bool BaseRoom::deityRestrict(const Creature* creature) const {
// if no flags are set
if( !flagIsSet(R_ARAMON) &&
!flagIsSet(R_CERIS) &&
!flagIsSet(R_ENOCH) &&
!flagIsSet(R_GRADIUS) &&
!flagIsSet(R_KAMIRA) &&
!flagIsSet(R_ARES) &&
!flagIsSet(R_ARACHNUS) &&
!flagIsSet(R_LINOTHAN) )
return(false);
// if the deity flag is set and they match, they pass
if( (flagIsSet(R_ARAMON) && creature->getDeity() == ARAMON) ||
(flagIsSet(R_CERIS) && creature->getDeity() == CERIS) ||
(flagIsSet(R_ENOCH) && creature->getDeity() == ENOCH) ||
(flagIsSet(R_GRADIUS) && creature->getDeity() == GRADIUS) ||
(flagIsSet(R_KAMIRA) && creature->getDeity() == KAMIRA) ||
(flagIsSet(R_ARES) && creature->getDeity() == ARES) ||
(flagIsSet(R_ARACHNUS) && creature->getDeity() == ARACHNUS) ||
(flagIsSet(R_LINOTHAN) && creature->getDeity() == LINOTHAN) )
return(false);
return(true);
}
//*********************************************************************
// maxCapacity
//*********************************************************************
// 0 means no limit
int BaseRoom::maxCapacity() const {
if(flagIsSet(R_ONE_PERSON_ONLY))
return(1);
if(flagIsSet(R_TWO_PEOPLE_ONLY))
return(2);
if(flagIsSet(R_THREE_PEOPLE_ONLY))
return(3);
return(0);
}
//*********************************************************************
// isFull
//*********************************************************************
// Can the player fit in the room?
bool BaseRoom::isFull() const {
return(maxCapacity() && countVisPly() >= maxCapacity());
}
//*********************************************************************
// countVisPly
//*********************************************************************
// This function counts the number of (non-DM-invisible) players in a
// room and returns that number.
int BaseRoom::countVisPly() const {
int num = 0;
for(Player* ply : players) {
if(!ply->flagIsSet(P_DM_INVIS))
num++;
}
return(num);
}
//*********************************************************************
// countCrt
//*********************************************************************
// This function counts the number of creatures in a
// room and returns that number.
int BaseRoom::countCrt() const {
int num = 0;
for(Monster* mons : monsters) {
if(!mons->isPet())
num++;
}
return(num);
}
//*********************************************************************
// getMaxMobs
//*********************************************************************
int BaseRoom::getMaxMobs() const {
const UniqueRoom* room = getAsConstUniqueRoom();
if(!room)
return(MAX_MOBS_IN_ROOM);
return(room->getMaxMobs() ? room->getMaxMobs() : MAX_MOBS_IN_ROOM);
}
//*********************************************************************
// vampCanSleep
//*********************************************************************
bool BaseRoom::vampCanSleep(Socket* sock) const {
// not at night
if(!isDay()) {
sock->print("Your thirst for blood keeps you from sleeping.\n");
return(false);
}
// during the day, under certain circumstances
if(isMagicDark())
return(true);
if( flagIsSet(R_DARK_ALWAYS) ||
flagIsSet(R_INDOORS) ||
flagIsSet(R_LIMBO) ||
flagIsSet(R_VAMPIRE_COVEN) ||
flagIsSet(R_UNDERGROUND) )
return(true);
sock->print("The sunlight prevents you from sleeping.\n");
return(false);
}
//*********************************************************************
// isCombat
//*********************************************************************
// checks to see if there is any time of combat going on in this room
bool BaseRoom::isCombat() const {
for(Player* ply : players) {
if(ply->inCombat(true))
return(true);
}
for(Monster* mons : monsters) {
if(mons->inCombat(true))
return(true);
}
return(false);
}
//*********************************************************************
// isUnderwater
//*********************************************************************
bool BaseRoom::isUnderwater() const {
return(flagIsSet(R_UNDER_WATER_BONUS));
}
//*********************************************************************
// isOutdoors
//*********************************************************************
bool BaseRoom::isOutdoors() const {
return(!flagIsSet(R_INDOORS) && !flagIsSet(R_VAMPIRE_COVEN) && !flagIsSet(R_SHOP_STORAGE) && !flagIsSet(R_LIMBO));
}
//*********************************************************************
// magicBonus
//*********************************************************************
bool BaseRoom::magicBonus() const {
if(!this)
return(false);
return(flagIsSet(R_MAGIC_BONUS));
}
//*********************************************************************
// isForest
//*********************************************************************
bool BaseRoom::isForest() const {
return(flagIsSet(R_FOREST_OR_JUNGLE));
}
//*********************************************************************
// isWater
//*********************************************************************
// drowning in area rooms is different than drowning in unique water rooms,
// therefore we need a different isWater function
bool AreaRoom::isWater() const {
return(area->isWater(mapmarker.getX(), mapmarker.getY(), mapmarker.getZ(), true));
}
//*********************************************************************
// isRoad
//*********************************************************************
bool AreaRoom::isRoad() const {
return(area->isRoad(mapmarker.getX(), mapmarker.getY(), mapmarker.getZ(), true));
}
//*********************************************************************
// getUnique
//*********************************************************************
// creature is allowed to be null
CatRef AreaRoom::getUnique(Creature *creature, bool skipDec) {
if(!needsCompass)
return(unique);
CatRef cr;
if(!creature)
return(cr);
bool pet = creature->isPet();
Player* player = creature->getPlayerMaster();
if( !player ||
!player->ready[HELD-1] ||
player->ready[HELD-1]->getShotsCur() < 1 ||
!player->ready[HELD-1]->compass ||
*player->ready[HELD-1]->compass != *&mapmarker
)
return(cr);
// no need to decrement twice if their pet is going through
if(!skipDec && decCompass && !pet) {
player->ready[HELD-1]->decShotsCur();
player->breakObject(player->ready[HELD-1], HELD);
}
return(unique);
}
bool BaseRoom::isDropDestroy() const {
if(!flagIsSet(R_DESTROYS_ITEMS))
return(false);
const AreaRoom* aRoom = getAsConstAreaRoom();
if(aRoom && aRoom->area->isRoad(aRoom->mapmarker.getX(), aRoom->mapmarker.getY(), aRoom->mapmarker.getZ(), true))
return(false);
return(true);
}
bool BaseRoom::hasRealmBonus(Realm realm) const {
return(flagIsSet(R_ROOM_REALM_BONUS) && flagIsSet(getRealmRoomBonusFlag(realm)));
}
bool BaseRoom::hasOppositeRealmBonus(Realm realm) const {
return(flagIsSet(R_OPPOSITE_REALM_BONUS) && flagIsSet(getRealmRoomBonusFlag(getOppositeRealm(realm ))));
}
Monster* BaseRoom::getTollkeeper() const {
for(Monster* mons : monsters) {
if(!mons->isPet() && mons->flagIsSet(M_TOLLKEEPER))
return(mons->getAsMonster());
}
return(0);
}
//*********************************************************************
// isSunlight
//*********************************************************************
// the first version is isSunlight is only called when an AreaRoom
// does not exist - when rogues scout, for example
bool Area::isSunlight(const MapMarker* mapmarker) const {
if(!isDay())
return(false);
// if their room has any of the following flags, no kill
if( flagIsSet(R_DARK_ALWAYS, mapmarker) ||
flagIsSet(R_UNDERGROUND, mapmarker) ||
flagIsSet(R_INDOORS, mapmarker) ||
flagIsSet(R_LIMBO, mapmarker) ||
flagIsSet(R_VAMPIRE_COVEN, mapmarker) ||
flagIsSet(R_ETHEREAL_PLANE, mapmarker) ||
flagIsSet(R_IS_STORAGE_ROOM, mapmarker) ||
flagIsSet(R_MAGIC_DARKNESS, mapmarker)
)
return(false);
return(true);
}
// this version of isSunlight is called when a room exists
bool BaseRoom::isSunlight() const {
// !this - for offline players. make it not sunny so darkmetal isnt destroyed
if(!this)
return(false);
if(!isDay() || !isOutdoors() || isMagicDark())
return(false);
const UniqueRoom* uRoom = getAsConstUniqueRoom();
const CatRefInfo* cri=0;
if(uRoom)
cri = gConfig->getCatRefInfo(uRoom->info.area);
// be nice - no killing darkmetal in special rooms
if(cri && (
!uRoom->info.id ||
uRoom->info.id == cri->getRecall() ||
uRoom->info.id == cri->getLimbo()
) )
return(false);
// if their room has any of the following flags, no kill
if( flagIsSet(R_DARK_ALWAYS) ||
flagIsSet(R_UNDERGROUND) ||
flagIsSet(R_ETHEREAL_PLANE) ||
flagIsSet(R_IS_STORAGE_ROOM)
)
return(false);
return(true);
}
//*********************************************************************
// destroy
//*********************************************************************
void UniqueRoom::destroy() {
saveToFile(0, LS_BACKUP);
char filename[256];
strcpy(filename, roomPath(info));
unlink(filename);
expelPlayers(true, true, true);
gConfig->delRoomQueue(info);
delete this;
}
//*********************************************************************
// expelPlayers
//*********************************************************************
void BaseRoom::expelPlayers(bool useTrapExit, bool expulsionMessage, bool expelStaff) {
Player* target=0;
BaseRoom* newRoom=0;
UniqueRoom* uRoom=0;
// allow them to be sent to the room's trap exit
if(useTrapExit) {
uRoom = getAsUniqueRoom();
if(uRoom) {
CatRef cr = uRoom->getTrapExit();
uRoom = 0;
if(cr.id && loadRoom(cr, &uRoom))
newRoom = uRoom;
}
}
PlayerSet::iterator pIt = players.begin();
PlayerSet::iterator pEnd = players.end();
while(pIt != pEnd) {
target = (*pIt++);
if(!expelStaff && target->isStaff())
continue;
if(expulsionMessage) {
target->printColor("^rYou are expelled from this room as it collapses in on itself!\n");
broadcast(target->getSock(), this, "^r%M is expelled from the room as it collapses in on itself!", target);
} else {
target->printColor("^CShifting dimensional forces displace you from this room.\n");
broadcast(target->getSock(), this, "^CShifting dimensional forces displace %N from this room.", target);
}
if(!useTrapExit || !newRoom) {
newRoom = target->bound.loadRoom(target);
if(!newRoom || newRoom == this)
newRoom = abortFindRoom(target, "expelPlayers");
}
target->deleteFromRoom();
target->addToRoom(newRoom);
target->doPetFollow();
}
}
//*********************************************************************
// getSpecialArea
//*********************************************************************
Location getSpecialArea(int (CatRefInfo::*toCheck), CatRef cr) {
return(getSpecialArea(toCheck, 0, cr.area, cr.id));
}
Location getSpecialArea(int (CatRefInfo::*toCheck), const Creature* creature, bstring area, short id) {
Location l;
if(creature) {
if(creature->inAreaRoom()) {
l.room.setArea("area");
l.room.id = creature->getConstAreaRoomParent()->area->id;
} else {
l.room.setArea(creature->currentLocation.room.area);
}
}
if(area != "")
l.room.setArea(area);
if(id)
l.room.id = id;
const CatRefInfo* cri = gConfig->getCatRefInfo(l.room.area, l.room.id, true);
if(!cri)
cri = gConfig->getCatRefInfo(gConfig->defaultArea);
if(cri) {
l.room.setArea(cri->getArea());
l.room.id = (cri->*toCheck);
} else {
// failure!
l.room.setArea(gConfig->defaultArea);
l.room.id = 0;
}
return(l);
}
//*********************************************************************
// getLimboRoom
//*********************************************************************
Location Creature::getLimboRoom() const {
return(getSpecialArea(&CatRefInfo::limbo, this, "", 0));
}
//*********************************************************************
// getRecallRoom
//*********************************************************************
Location Creature::getRecallRoom() const {
const Player* player = getAsConstPlayer();
if(player && (player->flagIsSet(P_T_TO_BOUND) || player->getClass() == BUILDER))
return(player->bound);
return(getSpecialArea(&CatRefInfo::recall, this, "", 0));
}
//*********************************************************************
// print
//*********************************************************************
bool hearBroadcast(Creature* target, Socket* ignore1, Socket* ignore2, bool showTo(Socket*));
void BaseRoom::print(Socket* ignore, const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
doPrint(0, ignore, NULL, fmt, ap);
va_end(ap);
}
void BaseRoom::print(Socket* ignore1, Socket* ignore2, const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
doPrint(0, ignore1, ignore2, fmt, ap);
va_end(ap);
}
//*********************************************************************
// doPrint
//*********************************************************************
void BaseRoom::doPrint(bool showTo(Socket*), Socket* ignore1, Socket* ignore2, const char *fmt, va_list ap) {
for(Player* ply : players) {
if(!hearBroadcast(ply, ignore1, ignore2, showTo))
continue;
if(ply->flagIsSet(P_UNCONSCIOUS))
continue;
ply->vprint(ply->customColorize(fmt).c_str(), ap);
}
}
//*********************************************************************
// isOutlawSafe
//*********************************************************************
bool BaseRoom::isOutlawSafe() const {
return( flagIsSet(R_OUTLAW_SAFE) ||
flagIsSet(R_VAMPIRE_COVEN) ||
flagIsSet(R_LIMBO) ||
whatTraining()
);
}
//*********************************************************************
// isOutlawSafe
//*********************************************************************
bool BaseRoom::isPkSafe() const {
return( flagIsSet(R_SAFE_ROOM) ||
flagIsSet(R_VAMPIRE_COVEN) ||
flagIsSet(R_LIMBO)
);
}
//*********************************************************************
// isOutlawSafe
//*********************************************************************
bool BaseRoom::isFastTick() const {
return( flagIsSet(R_FAST_HEAL) ||
flagIsSet(R_LIMBO)
);
}