/*
* property.h
* Code dealing with properties
* ____ _
* | _ \ ___ __ _| |_ __ ___ ___
* | |_) / _ \/ _` | | '_ ` _ \/ __|
* | _ < __/ (_| | | | | | | \__ \
* |_| \_\___|\__,_|_|_| |_| |_|___/
*
* 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 "property.h"
#include "commands.h"
#include "guilds.h"
#include "dm.h"
#include "login.h"
#include "move.h"
#include <sstream>
#include <iomanip>
#include <locale>
//*********************************************************************
// PartialOwner
//*********************************************************************
PartialOwner::PartialOwner() {
name = "";
zero(flags, sizeof(flags));
}
void PartialOwner::defaultFlags(PropType type) {
switch(type) {
case PROP_SHOP:
setFlag(PROP_SHOP_CAN_STOCK);
break;
default:
break;
}
}
bstring PartialOwner::getName() const { return(name); }
void PartialOwner::setName(bstring str) { name = str; }
bool PartialOwner::flagIsSet(int flag) const {
return(flags[flag/8] & 1<<(flag%8));
}
void PartialOwner::setFlag(int flag) {
flags[flag/8] |= 1<<(flag%8);
}
void PartialOwner::clearFlag(int flag) {
flags[flag/8] &= ~(1<<(flag%8));
}
bool PartialOwner::toggleFlag(int flag) {
if(flagIsSet(flag))
clearFlag(flag);
else
setFlag(flag);
return(flagIsSet(flag));
}
void PartialOwner::load(xmlNodePtr rootNode) {
xml::copyPropToBString(name, rootNode, "Name");
loadBits(rootNode, flags);
}
void PartialOwner::save(xmlNodePtr rootNode) const {
xmlNodePtr curNode = xml::newStringChild(rootNode, "Owner") ;
xml::newProp(curNode, "Name", name.c_str());
for(int i=0; i<32; i++) {
if(BIT_ISSET(flags, i))
saveBit(curNode, i);
}
}
//*********************************************************************
// Property
//*********************************************************************
Property::Property() {
guild = 0;
owner = dateFounded = location = "";
logType = LOG_PARTIAL;
type = PROP_NONE;
zero(flags, sizeof(flags));
}
bool Property::flagIsSet(int flag) const {
return(flags[flag/8] & 1<<(flag%8));
}
void Property::setFlag(int flag) {
flags[flag/8] |= 1<<(flag%8);
}
void Property::clearFlag(int flag) {
flags[flag/8] &= ~(1<<(flag%8));
}
bool Property::toggleFlag(int flag) {
if(flagIsSet(flag))
clearFlag(flag);
else
setFlag(flag);
return(flagIsSet(flag));
}
int Property::getGuild() const { return(guild); }
bstring Property::getOwner() const { return(owner); }
bstring Property::getName() const { return(name); }
bstring Property::getDateFounded() const { return(dateFounded); }
bstring Property::getLocation() const { return(location); }
PropType Property::getType() const { return(type); }
bstring Property::getTypeStr() const { return(getTypeStr(type)); }
PropLog Property::getLogType() const { return(logType); }
void Property::setGuild(int g) { guild = g; }
void Property::setOwner(bstring str) { owner = str; }
void Property::setName(bstring str) { name = str; }
void Property::setDateFounded() {
long t = time(0);
dateFounded = ctime(&t);
dateFounded.trim();
}
void Property::setLocation(bstring str) { location = str; }
void Property::setType(PropType t) { type = t; }
void Property::setLogType(PropLog t) { logType = t; }
//*********************************************************************
// load
//*********************************************************************
void Property::load(xmlNodePtr rootNode) {
xmlNodePtr childNode, curNode = rootNode->children;
bstring temp = "";
while(curNode) {
if(NODE_NAME(curNode, "Owner")) xml::copyToBString(owner, curNode);
else if(NODE_NAME(curNode, "Name")) xml::copyToBString(name, curNode);
else if(NODE_NAME(curNode, "DateFounded")) xml::copyToBString(dateFounded, curNode);
else if(NODE_NAME(curNode, "Location")) xml::copyToBString(location, curNode);
else if(NODE_NAME(curNode, "Guild")) xml::copyToNum(guild, curNode);
else if(NODE_NAME(curNode, "Type")) type = (PropType)xml::toNum<int>(curNode);
else if(NODE_NAME(curNode, "Flags")) loadBits(curNode, flags);
else if(NODE_NAME(curNode, "Ranges")) {
childNode = curNode->children;
while(childNode) {
if(NODE_NAME(childNode, "Range")) {
Range r;
r.load(childNode);
ranges.push_back(r);
}
childNode = childNode->next;
}
}
else if(NODE_NAME(curNode, "PartialOwners")) {
childNode = curNode->children;
while(childNode) {
if(NODE_NAME(childNode, "Owner")) {
PartialOwner po;
po.load(childNode);
if(po.getName() != "")
partialOwners.push_back(po);
}
childNode = childNode->next;
}
}
else if(NODE_NAME(curNode, "LogType")) logType = (PropLog)xml::toNum<int>(curNode);
else if(NODE_NAME(curNode, "Log")) {
childNode = curNode->children;
while(childNode) {
if(NODE_NAME(childNode, "Entry")) {
xml::copyToBString(temp, childNode);
log.push_back(temp);
}
childNode = childNode->next;
}
}
curNode = curNode->next;
}
}
//*********************************************************************
// save
//*********************************************************************
void Property::save(xmlNodePtr rootNode) const {
xmlNodePtr childNode, curNode = xml::newStringChild(rootNode, "Property");
xml::saveNonNullString(curNode, "Owner", owner.c_str());
xml::saveNonNullString(curNode, "Name", name.c_str());
xml::saveNonNullString(curNode, "DateFounded", dateFounded.c_str());
xml::saveNonNullString(curNode, "Location", location.c_str());
xml::saveNonZeroNum(curNode, "Guild", guild);
xml::saveNonZeroNum(curNode, "Type", (int)type);
saveBits(curNode, "Flags", 32, flags);
if(!ranges.empty()) {
std::list<Range>::const_iterator rt;
childNode = xml::newStringChild(curNode, "Ranges");
for(rt = ranges.begin() ; rt != ranges.end() ; rt++)
(*rt).save(childNode, "Range");
}
if(!partialOwners.empty()) {
std::list<PartialOwner>::const_iterator pt;
childNode = xml::newStringChild(curNode, "PartialOwners");
for(pt = partialOwners.begin() ; pt != partialOwners.end() ; pt++)
(*pt).save(childNode);
}
xml::saveNonZeroNum(curNode, "LogType", logType);
if(!log.empty()) {
std::list<bstring>::const_iterator it;
childNode = xml::newStringChild(curNode, "Log");
for(it = log.begin() ; it != log.end() ; it++) {
xml::saveNonNullString(childNode, "Entry", (*it).c_str());
}
}
}
//*********************************************************************
// getTypeStr
//*********************************************************************
bstring Property::getTypeStr(PropType propType) {
switch(propType) {
case PROP_STORAGE:
return("storage room");
case PROP_SHOP:
return("shop");
case PROP_GUILDHALL:
return("guild hall");
case PROP_HOUSE:
return("personal house");
default:
break;
}
return("unknown type");
}
//*********************************************************************
// getTypeArea
//*********************************************************************
bstring Property::getTypeArea(PropType propType) {
switch(propType) {
case PROP_SHOP:
return("shop");
case PROP_STORAGE:
return("stor");
case PROP_GUILDHALL:
return("guild");
case PROP_HOUSE:
return("house");
default:
break;
}
return("");
}
//*********************************************************************
// getLogTypeStr
//*********************************************************************
bstring Property::getLogTypeStr() const {
switch(logType) {
case LOG_NONE:
return("This property is not recording actions.");
break;
case LOG_PARTIAL:
return("This property is recording all actions of partial owners.");
break;
case LOG_ALL:
default:
break;
}
return("This property is recording all actions.");
}
//*********************************************************************
// addRange
//*********************************************************************
void Property::addRange(CatRef cr) {
// first of all, let us intelligently try to update existing ranges
std::list<Range>::iterator rt;
for(rt = ranges.begin() ; rt != ranges.end() ; rt++) {
if((*rt).isArea(cr.area) && (*rt).high == cr.id - 1) {
(*rt).high = cr.id;
return;
}
}
Range r;
r.low = cr;
r.high = r.low.id;
ranges.push_back(r);
}
void Property::addRange(bstring area, int low, int high) {
// first of all, let us intelligently try to update existing ranges
std::list<Range>::iterator rt;
for(rt = ranges.begin() ; rt != ranges.end() ; rt++) {
if((*rt).isArea(area) && (*rt).high == low - 1) {
(*rt).high = high;
return;
}
}
Range r;
r.low.setArea(area);
r.low.id = low;
r.high = high;
ranges.push_back(r);
}
//*********************************************************************
// isOwner
//*********************************************************************
bool Property::isOwner(bstring str) const {
return(owner == str);
}
//*********************************************************************
// belongs
//*********************************************************************
bool Property::belongs(CatRef cr) const {
std::list<Range>::const_iterator rt;
for(rt = ranges.begin() ; rt != ranges.end() ; rt++)
if((*rt).belongs(cr))
return(true);
return(false);
}
//*********************************************************************
// getFlagList
//*********************************************************************
std::map<int, MudFlag>* Property::getFlagList() {
switch(type) {
case PROP_STORAGE:
return(&gConfig->propStorFlags);
case PROP_SHOP:
return(&gConfig->propShopFlags);
case PROP_GUILDHALL:
return(&gConfig->propGuildFlags);
case PROP_HOUSE:
return(&gConfig->propHouseFlags);
default:
break;
}
return(0);
}
//*********************************************************************
// viewLogFlag
//*********************************************************************
int Property::viewLogFlag() const {
switch(type) {
case PROP_STORAGE:
return(PROP_STOR_VIEW_LOG);
case PROP_SHOP:
return(PROP_SHOP_VIEW_LOG);
default:
// other proprties do not use this function
break;
}
return(-1);
}
//*********************************************************************
// usePropFlags
//*********************************************************************
bool Property::usePropFlags(PropType propType) {
return(propType == PROP_HOUSE || propType == PROP_GUILDHALL);
}
//*********************************************************************
// canEnter
//*********************************************************************
bool Property::canEnter(const Player* player, const Room* room, bool p) {
// staff may always go anywhere
int staff = player->isStaff();
if(room->info.isArea(getTypeArea(PROP_STORAGE))) {
CatRef cr = gConfig->getSingleProperty(player, PROP_STORAGE);
if(cr != room->info) {
if(p) player->checkStaff("You are not permited in there.\n");
if(!staff) return(false);
}
if(player->getLevel() < 10) {
if(p) player->checkStaff("You must be level 10 to go there.\n");
if(!staff) return(false);
}
}
if( room->info.isArea(getTypeArea(PROP_GUILDHALL)) &&
!room->flagIsSet(R_GUILD_OPEN_ACCESS)
) {
Guild *guild=0, *pGuild=0;
if(player && player->getGuild())
guild = gConfig->getGuild(player->getGuild());
Property *prop = gConfig->getProperty(room->info);
if(prop && prop->getGuild())
pGuild = gConfig->getGuild(prop->getGuild());
if(!pGuild) {
if(p) player->checkStaff("Sorry, that guildhall is broken.\n");
if(!staff) return(false);
}
if( (!guild || guild->num != pGuild->num) &&
(!prop || !prop->flagIsSet(PROP_GUILD_PUBLIC))
) {
if(p) player->checkStaff("You must belong to %s to go that way.\n", pGuild->name.c_str());
if(!staff) return(false);
}
}
if(room->info.isArea(getTypeArea(PROP_HOUSE))) {
Property *prop = gConfig->getProperty(room->info);
if( prop &&
prop->flagIsSet(PROP_HOUSE_PRIVATE) &&
!prop->isOwner(player->name) &&
!prop->isPartialOwner(player->name)
) {
if(p) player->checkStaff("That personal house is private property.\n");
if(!staff) return(false);
}
}
return(true);
}
//*********************************************************************
// goodNameDesc
//*********************************************************************
bool Property::goodNameDesc(const Player* player, bstring str, bstring fail, bstring disallow) {
if(str == "") {
player->print("%s\n", fail.c_str());
return(false);
}
if(str.getLength() > 79) {
player->print("Too long.\n");
return(false);
}
if(Pueblo::is(str)) {
player->print("That %s is not allowed.\n", disallow.c_str());
return(false);
}
return(true);
}
//*********************************************************************
// goodExit
//*********************************************************************
bool Property::goodExit(const Player* player, const BaseRoom* room, const char *type, bstring xname) {
if(xname.getLength() > 19) {
player->print("%s exit name is too long!\n", type);
return(false);
}
if(xname.getLength() < 3) {
player->print("%s exit name is too short!\n", type);
return(false);
}
if(isCardinal(xname)) {
player->print("Exits cannot be made cardinal directions.\n");
return(false);
}
int len = xname.getLength();
for(int i=0; i<len; i++) {
if(isupper(xname.getAt(i))) {
player->print("Do not use capital letters in exit names.\n");
return(false);
}
if(xname.getAt(i) == '^') {
player->print("Carets (^) are not allowed in exit names.\n");
return(false);
}
}
xtag* xp = room->first_ext;
while(xp) {
if(!strcmp(xp->ext->name, xname.c_str())) {
player->print("An exit with that name already exists in this room.\n");
return(false);
}
xp = xp->next_tag;
}
return(true);
}
//*********************************************************************
// destroy
//*********************************************************************
void Property::destroy() {
BaseRoom* outside=0;
AreaRoom* aRoom=0;
Room *room=0, *uRoom=0;
xtag* xp=0, *oxp=0;
std::list<Range>::iterator rt;
for(rt = ranges.begin() ; rt != ranges.end() ; rt++) {
for(; (*rt).low.id <= (*rt).high; (*rt).low.id++) {
if(loadRoom((*rt).low, &room)) {
// for every property besides storage rooms, we have to delete the
// exit leading to the propery
if(type != PROP_STORAGE) {
// handle the exits that point outside
xp = room->first_ext;
while(xp) {
if( xp->ext->target.mapmarker.getArea() ||
!xp->ext->target.room.isArea(room->info.area)
) {
outside = xp->ext->target.loadRoom();
if(outside) {
uRoom = outside->getUniqueRoom();
aRoom = outside->getAreaRoom();
// get rid of all exits
oxp = outside->first_ext;
while(oxp) {
if(oxp->ext->target.room == room->info) {
broadcast(NULL, outside, "%s closes its doors.", name.c_str());
del_exit(outside, oxp->ext);
oxp = outside->first_ext;
}
oxp = oxp->next_tag;
}
// reset flags
if(uRoom) {
if(type == PROP_SHOP && uRoom->flagIsSet(R_WAS_BUILD_SHOP)) {
uRoom->clearFlag(R_WAS_BUILD_SHOP);
uRoom->setFlag(R_BUILD_SHOP);
} else if(type == PROP_GUILDHALL && uRoom->flagIsSet(R_WAS_BUILD_GUILDHALL)) {
uRoom->clearFlag(R_WAS_BUILD_GUILDHALL);
uRoom->setFlag(R_BUILD_GUILDHALL);
}
uRoom->saveToFile(0);
} else if(aRoom) {
aRoom->save();
}
}
}
xp = xp->next_tag;
}
}
broadcast(NULL, room, "%s closes its doors.", name.c_str());
room->destroy();
}
}
}
delete this;
}
//*********************************************************************
// destroyProperties
//*********************************************************************
// This function is called when a player is destroyed and everything
// they own must be destroyed as well.
void Config::destroyProperties(bstring pOwner) {
std::list<Property*>::iterator it;
// giving guildhalls a new owner occurs when the player leaves the guild
for(it = properties.begin() ; it != properties.end() ; ) {
if((*it)->isOwner(pOwner)) {
(*it)->destroy();
it = properties.erase(it);
continue;
} else if((*it)->isPartialOwner(pOwner)) {
(*it)->unassignPartialOwner(pOwner);
(*it)->appendLog(pOwner, "%s has resigned as partial owner.", pOwner.c_str());
}
it++;
}
saveProperties();
}
//*********************************************************************
// destroyProperty
//*********************************************************************
void Config::destroyProperty(Property *p) {
std::list<Property*>::iterator it;
for(it = properties.begin() ; it != properties.end() ; it++) {
if((*it) == p) {
p->destroy();
properties.erase(it);
saveProperties();
return;
}
}
}
//*********************************************************************
// partial owner functions
//*********************************************************************
// guildhalls do not use the partialowner list: they rely on getting
// their information from the guild
PartialOwner* Property::getPartialOwner(bstring pOwner) {
std::list<PartialOwner>::iterator it;
for(it = partialOwners.begin() ; it != partialOwners.end() ; it++) {
if(pOwner == (*it).getName())
return(&(*it));
}
return(0);
}
bool Property::isPartialOwner(bstring pOwner) {
// shops will also check guild membership, if they're assigned to a guild
if(type == PROP_SHOP) {
if(!!getPartialOwner(pOwner))
return(true);
} else if(type != PROP_GUILDHALL)
return(!!getPartialOwner(pOwner));
if(!guild)
return(false);
Guild *g = gConfig->getGuild(guild);
return(g && g->isMember(pOwner));
}
void Property::assignPartialOwner(bstring pOwner) {
if(type == PROP_GUILDHALL)
return;
PartialOwner po;
po.defaultFlags(type);
po.setName(pOwner);
partialOwners.push_back(po);
}
void Property::unassignPartialOwner(bstring pOwner) {
std::list<PartialOwner>::iterator it;
for(it = partialOwners.begin() ; it != partialOwners.end() ; it++) {
if(pOwner == (*it).getName()) {
partialOwners.erase(it);
return;
}
}
}
//*********************************************************************
// addProperty
//*********************************************************************
void Config::addProperty(Property* p) {
properties.push_back(p);
saveProperties();
}
//*********************************************************************
// loadProperties
//*********************************************************************
bool Config::loadProperties() {
xmlDocPtr xmlDoc;
xmlNodePtr curNode;
Property *p=0;
xmlDoc = xml::loadFile(CONFPATH "properties.xml", "Properties");
if(xmlDoc == NULL)
return(false);
curNode = xmlDocGetRootElement(xmlDoc);
curNode = curNode->children;
while(curNode && xmlIsBlankNode(curNode))
curNode = curNode->next;
if(curNode == 0) {
xmlFreeDoc(xmlDoc);
return(false);
}
clearProperties();
while(curNode != NULL) {
if(NODE_NAME(curNode, "Property")) {
p = new Property;
p->load(curNode);
properties.push_back(p);
}
curNode = curNode->next;
}
xmlFreeDoc(xmlDoc);
xmlCleanupParser();
return(true);
}
//*********************************************************************
// saveProperties
//*********************************************************************
bool Config::saveProperties() const {
std::list<Property*>::const_iterator it;
xmlDocPtr xmlDoc;
xmlNodePtr rootNode;
char filename[80];
xmlDoc = xmlNewDoc(BAD_CAST "1.0");
rootNode = xmlNewDocNode(xmlDoc, NULL, BAD_CAST "Properties", NULL);
xmlDocSetRootElement(xmlDoc, rootNode);
for(it = properties.begin() ; it != properties.end() ; it++) {
(*it)->save(rootNode);
}
sprintf(filename, "%s/properties.xml", CONFPATH);
xml::saveFile(filename, xmlDoc);
xmlFreeDoc(xmlDoc);
return(true);
}
//*********************************************************************
// getSingleProperty
//*********************************************************************
// cr.id = 0 means they have no property
// cr.id = -1 means they have too many properties
// otherwise, cr points to the property's first room
Property* Config::getProperty(const Player* player, PropType type) {
std::list<Property*>::iterator it;
Property* p=0;
CatRef cr;
for(it = gConfig->properties.begin() ; it != gConfig->properties.end() ; it++) {
if((*it)->getType() != type)
continue;
if(type == PROP_STORAGE && !(*it)->isOwner(player->name) && !(*it)->isPartialOwner(player->name))
continue;
if(type == PROP_GUILDHALL && player->getGuild() != (*it)->getGuild())
continue;
if(cr.id) {
p = 0;
cr.id = -1;
} else {
p = (*it);
cr = p->ranges.front().low;
}
}
return(p);
}
CatRef Config::getSingleProperty(const Player* player, PropType type) {
std::list<Property*>::iterator it;
CatRef cr;
for(it = gConfig->properties.begin() ; it != gConfig->properties.end() ; it++) {
if((*it)->getType() != type)
continue;
if(type == PROP_STORAGE && !(*it)->isOwner(player->name) && !(*it)->isPartialOwner(player->name))
continue;
if(type == PROP_GUILDHALL && player->getGuild() != (*it)->getGuild())
continue;
if(cr.id)
cr.id = -1;
else
cr = (*it)->ranges.front().low;
}
return(cr);
}
//*********************************************************************
// expelOnRemove
//*********************************************************************
bool Property::expelOnRemove() const {
return( type == PROP_STORAGE ||
type == PROP_GUILDHALL ||
(type == PROP_HOUSE && flagIsSet(PROP_HOUSE_PRIVATE))
);
}
//*********************************************************************
// expelToExit
//*********************************************************************
// can safely call this function; it will decide if the player needs
// to be kicked out or not
void Property::expelToExit(Player* player, bool offline) {
if(!expelOnRemove() || player->area_room || !belongs(player->room))
return;
AreaRoom* aRoom=0;
Room* uRoom=0;
BaseRoom* newRoom=0;
if(loadRoom(ranges.front().low, &uRoom)) {
xtag *xp = uRoom->first_ext;
uRoom = 0;
while(xp) {
newRoom = xp->ext->target.loadRoom(player);
if(newRoom)
break;
xp = xp->next_tag;
}
}
if(!newRoom)
newRoom = player->bound.loadRoom(player);
if(!newRoom)
newRoom = abortFindRoom(player, "expelToExit");
uRoom = newRoom->getUniqueRoom();
aRoom = newRoom->getAreaRoom();
if(offline) {
if(uRoom)
player->room = uRoom->info;
else
player->area_room = aRoom;
} else {
player->print("You are escorted off the premises.\n");
broadcast(player->getSock(), player->parent_rom, "%M is escorted off the premises.", player);
player->deleteFromRoom();
player->addToRoom(newRoom);
player->doPetFollow();
}
player->save();
}
//*********************************************************************
// propCommands
//*********************************************************************
void propCommands(Player* player, Property *p) {
player->print("Commands for this property:\n");
if(p->isOwner(player->name)) {
// example showing width
//player->printColor(" <commands> - description\n");
switch(p->getType()) {
case PROP_STORAGE:
case PROP_HOUSE:
player->printColor(" property ^e<^xassign^e>^x ^e<^cplayer^e>^x ^e-^x add a partial owner\n");
player->printColor(" ^e<^xunassign^e>^x ^e<^cplayer^e>^x ^e-^x remove a partial owner\n");
break;
case PROP_SHOP:
player->printColor(" property ^e<^xhire^e>^x ^e<^cplayer^e>^x ^e-^x add a partial owner\n");
player->printColor(" ^e<^xfire^e>^x ^e<^cplayer^e>^x ^e-^x remove a partial owner\n");
if(player->getGuild())
player->printColor(" ^e<^xguild^e>^x ^e<^xassign^e|^xremove^e>\n");
break;
case PROP_GUILDHALL:
player->printColor(" property ^e<^xdestroy^e>^x ^e-^x close this property down\n");
break;
default:
player->print(" None.\n");
return;
}
if(Property::usePropFlags(p->getType()))
player->printColor(" ^e<^xflags^e>^x ^e-^x view / set flags on property\n");
else
player->printColor(" ^e<^xflags^e>^x ^e-^x view / set flags on partial owners\n");
if(p->getType() != PROP_GUILDHALL) {
player->printColor(" ^e<^xlog^e>^x ^e<^xdelete^e|^xrecord^e>^x ^e-^x view / delete / set log type\n");
player->printColor(" ^e<^xdestroy^e>^x ^e-^x close this property down\n");
}
player->printColor(" ^e<^xhelp^e>\n");
} else {
PartialOwner *po = p->getPartialOwner(player->name);
if(!po)
player->print(" none.\n");
else {
player->printColor(" property ^e<^xunassign^e>\n");
if(p->getType() != PROP_GUILDHALL) {
player->printColor(" ^e<^xflags^e>\n");
if( p->getType() != PROP_HOUSE &&
po &&
po->flagIsSet(p->viewLogFlag())
)
player->printColor(" ^e<^xlog^e>\n");
}
}
}
player->print("\n");
}
//*********************************************************************
// propLog
//*********************************************************************
// handles property logging
void propLog(Player* player, cmd* cmnd, Property *p) {
if(!cmnd || cmnd->num == 2) {
player->print("Property Log For %s:\n", p->getName().c_str());
player->printColor("^b--------------------------------------------------------------\n");
player->printColor("%s\n", p->getLog().c_str());
} else if(!strcmp(cmnd->str[2], "delete")) {
player->print("Property log deleted.\n");
p->clearLog();
gConfig->saveProperties();
} else if(!strcmp(cmnd->str[2], "record")) {
if(cmnd->num == 3) {
player->print("%s\n", p->getLogTypeStr().c_str());
player->printColor("To change this, type ^yproperty log record <all|partial|none>^x.\n");
} else {
if(!strncmp(cmnd->str[3], "all", strlen(cmnd->str[3])))
p->setLogType(LOG_ALL);
else if(!strncmp(cmnd->str[3], "partial", strlen(cmnd->str[3])))
p->setLogType(LOG_PARTIAL);
else if(!strncmp(cmnd->str[3], "none", strlen(cmnd->str[3])))
p->setLogType(LOG_NONE);
else {
player->printColor("Log recording type not understood.\nType ^yproperty log record <all|partial|none>^x.\n");
return;
}
player->print("Property log type set.\n");
gConfig->saveProperties();
}
} else {
player->print("Command not understood.\n");
propCommands(player, p);
}
return;
}
//*********************************************************************
// propDestroy
//*********************************************************************
// handles property destruction
void propDestroy(Player* player, cmd* cmnd, Property *p) {
if(strcmp(cmnd->str[2], "confirm")) {
player->printColor("^rAre you sure you wish to destroy this property?\n");
player->printColor("Type \"property destroy confirm\" to destroy it.\n");
} else {
gConfig->destroyProperty(p);
}
}
//*********************************************************************
// propAssignUnassign
//*********************************************************************
// handles assigning/unassigning of partial owners
void propAssignUnassign(Player* player, cmd* cmnd, Property *p, bool assign) {
if(p->getType() == PROP_GUILDHALL) {
player->print("Partial ownership of guildhalls is handled by guild membership.\n");
return;
}
bstring name = "";
bool offline=false;
// find out who they're trying to work with
if(cmnd->num < 3) {
player->print("Player does not exist.\n");
return;
}
cmnd->str[2][0] = up(cmnd->str[2][0]);
Player* target = gServer->findPlayer(cmnd->str[2]);
if(!target) {
if(!loadPlayer(cmnd->str[2], &target)) {
player->print("Player does not exist.\n");
return;
}
offline = true;
}
if((target->isStaff() && !player->isStaff()) || target == player) {
player->print("That player is not a valid target.\n");
if(offline) free_crt(target);
return;
}
// is this action even valid?
if(assign && p->getPartialOwner(target->name)) {
player->print("%s is already a partial owner of this property.\n", target->name);
if(offline) free_crt(target);
return;
} else if(!assign && !p->getPartialOwner(target->name)) {
player->print("%s is not a partial owner of this property.\n", target->name);
if(offline) free_crt(target);
return;
}
// their action is valid: let's continue
if(assign) {
if(Move::tooFarAway(player, target, "appoint as partial owner of this property")) {
if(offline) free_crt(target);
return;
}
// people can only belong to one storage room
if(p->getType() == PROP_STORAGE) {
CatRef cr = gConfig->getSingleProperty(target, PROP_STORAGE);
if(cr.id) {
player->print("%s may only be affiliated with one storage room at a time.\n", target->name);
if(offline) free_crt(target);
return;
}
}
p->assignPartialOwner(target->name);
p->appendLog(target->name, "%s is now a partial owner.", target->name);
player->print("%s is now a partial owner of this property.\n", target->name);
if(!offline) {
target->printColor( "^c%s has appointed you partial owner of %s.\n",
player->name, p->getName().c_str());
target->print("Type \"property\" for instructions on how to remove yourself\nif you do not wish to be partial owner.\n");
}
} else {
p->unassignPartialOwner(target->name);
p->appendLog(target->name, "%s is no longer a partial owner.", target->name);
player->print("%s is no longer a partial owner of this property.\n", target->name);
if(!offline) {
target->printColor( "^c%s has removed you as partial owner of %s.\n",
player->name, p->getName().c_str());
}
p->expelToExit(target, offline);
}
gConfig->saveProperties();
if(offline) free_crt(target);
}
//*********************************************************************
// propFlags
//*********************************************************************
// handles viewing/setting of flags on partial owners
void propFlags(Player* player, cmd* cmnd, Property *p) {
std::map<int, MudFlag>* list = p->getFlagList();
std::map<int, MudFlag>::iterator it;
PartialOwner *po=0;
MudFlag f;
bool propFlag = Property::usePropFlags(p->getType());
if(cmnd->num == 2) {
player->printColor("^yFlags for %sproperty type: %s^x\n\n",
propFlag ? "" : "Partial Owners for ", p->getTypeStr().c_str());
for(it = list->begin(); it != list->end() ; it++) {
f = (*it).second;
player->printColor("^c#%-2d^x %-20s ^e-^x %s\n",
f.id, f.name.c_str(), f.desc.c_str());
}
player->print("\nThese commands allow you to change flags on %s:\n",
propFlag ? "this property" : "partial owners");
player->printColor(" property flags %s^e<^xview^e>\n",
propFlag ? "" : "^e<^cplayer^e>^x ");
player->printColor(" property flags %s^e<^xset^e|^xclear^e|^xtoggle^e>^x ^e<^cflag num^e>^x\n\n",
propFlag ? "" : "^e<^cplayer^e>^x ");
} else if(propFlag) {
bstring action = cmnd->str[2];
if(action == "" || action == "view") {
player->printColor("^yFlags set for this property:^x\n\n");
for(it = list->begin(); it != list->end() ; it++) {
f = (*it).second;
if(p->flagIsSet(f.id-1))
player->printColor("^c#%-2d^x %s\n", f.id, f.name.c_str());
}
player->print("\n");
} else if(action == "set" || action == "clear" || action == "toggle") {
int num = atoi(getFullstrText(cmnd->fullstr, 3).c_str());
bool found=false;
for(it = list->begin(); it != list->end() ; it++) {
f = (*it).second;
if(num == f.id) {
found = true;
break;
}
}
if(!found) {
player->print("Invalid flag!\n");
return;
}
num--;
if(action == "set")
p->setFlag(num);
else if(action == "clear")
p->clearFlag(num);
else
p->toggleFlag(num);
if(p->flagIsSet(num))
player->printColor("Flag ^c""%s""^x set on this property.\n", f.name.c_str());
else
player->printColor("Flag ^c""%s""^x cleared on this property.\n", f.name.c_str());
gConfig->saveProperties();
} else {
player->print("Command not understood.\n");
propCommands(player, p);
}
} else {
cmnd->str[2][0] = up(cmnd->str[2][0]);
po = p->getPartialOwner(cmnd->str[2]);
if(!po) {
player->print("%s is not a partial owner of this property!\n", cmnd->str[2]);
return;
}
bstring action = cmnd->str[3];
if(action == "" || action == "view") {
// if viewing self, show property name instead
bstring title = po->getName();
if(title == player->name)
title = p->getName();
player->printColor("^yFlags set for %s:^x\n\n", title.c_str());
for(it = list->begin(); it != list->end() ; it++) {
f = (*it).second;
if(po->flagIsSet(f.id-1))
player->printColor("^c#%-2d^x %s\n", f.id, f.name.c_str());
}
player->print("\n");
} else if(action == "set" || action == "clear" || action == "toggle") {
int num = atoi(getFullstrText(cmnd->fullstr, 4).c_str());
bool found=false;
for(it = list->begin(); it != list->end() ; it++) {
f = (*it).second;
if(num == f.id) {
found = true;
break;
}
}
if(!found) {
player->print("Invalid flag!\n");
return;
}
num--;
if(action == "set")
po->setFlag(num);
else if(action == "clear")
po->clearFlag(num);
else
po->toggleFlag(num);
Player* target = gServer->findPlayer(po->getName().c_str());
if(po->flagIsSet(num)) {
player->printColor("Flag ^c""%s""^x set on %s.\n", f.name.c_str(),
po->getName().c_str());
if(target)
target->printColor("Flag ^c""%s""^x has been set on you for %s.\n",
f.name.c_str(), p->getName().c_str());
} else {
player->printColor("Flag ^c""%s""^x cleared on %s.\n", f.name.c_str(),
po->getName().c_str());
if(target)
target->printColor("Flag ^c""%s""^x has been cleared on you for %s.\n",
f.name.c_str(), p->getName().c_str());
}
gConfig->saveProperties();
} else {
player->print("Command not understood.\n");
propCommands(player, p);
}
}
}
//*********************************************************************
// propRemove
//*********************************************************************
// handles assigning/unassigning of partial owners
void propRemove(Player* player, Property *p) {
if(p->getType() == PROP_GUILDHALL) {
player->print("Partial ownership of guildhalls is handled by guild membership.\n");
return;
}
p->unassignPartialOwner(player->name);
p->appendLog(player->name, "%s has resigned as partial owner.", player->name);
Player* owner = gServer->findPlayer(p->getOwner().c_str());
if(owner) {
owner->printColor("^c%s has resigned as partial owner of %s.\n",
player->name, p->getName().c_str());
}
player->print("You are no longer a partial owner of %s.\n", p->getName().c_str());
gConfig->saveProperties();
}
//*********************************************************************
// propHelp
//*********************************************************************
void propHelp(Player* player, cmd* cmnd, PropType propType) {
if(propType == PROP_GUILDHALL)
strcpy(cmnd->str[1], "guildhalls");
else if(propType == PROP_HOUSE)
strcpy(cmnd->str[1], "houses");
else
strcpy(cmnd->str[1], "property");
cmdHelp(player, cmnd);
}
//*********************************************************************
// cmdProperties
//*********************************************************************
int cmdProperties(Player* player, cmd* cmnd) {
Property *p=0;
PartialOwner *po=0;
if(player->parent_rom)
p = gConfig->getProperty(player->parent_rom->info);
int len = strlen(cmnd->str[1]);
if(len && !strncmp(cmnd->str[1], "help", len)) {
propHelp(player, cmnd, p ? p->getType() : PROP_NONE);
return(0);
}
if(p)
po = p->getPartialOwner(player->name);
// they only want to see their properties
if(cmnd->num < 2) {
player->printColor("Properties You Own\n");
player->printColor("^b--------------------------------------------------------------\n");
gConfig->showProperties(player, player);
if(!p || (!p->isOwner(player->name) && !p->isPartialOwner(player->name)))
return(0);
propCommands(player, p);
} else if(!p) {
// not in a property, trying to do stuff
if(!strcmp(cmnd->str[1], "remove")) {
int i=0;
std::list<Property*>::iterator it;
for(it = gConfig->properties.begin() ; it != gConfig->properties.end() ; it++) {
if((*it)->isPartialOwner(player->name))
i++;
if(i == cmnd->val[1]) {
propRemove(player, *it);
return(0);
}
}
player->print("Property not found!\n");
} else {
player->print("Command not understood.\n");
player->print("To manage your own properties, you must be on the premises.\n");
player->print("Type \"property remove [num]\" to remove yourself as partial owner from a property.\n");
}
} else if(p->isOwner(player->name)) {
// they want to do something with this property
bool found=true, assign=false;
// figure out what they're trying to do
if(!strcmp(cmnd->str[1], "destroy")) {
propDestroy(player, cmnd, p);
} else if(!strcmp(cmnd->str[1], "log") && p->getType() != PROP_HOUSE) {
propLog(player, cmnd, p);
} else if(p->getType() == PROP_SHOP && !strcmp(cmnd->str[1], "guild") && cmnd->num > 2) {
// give them a cross-command shortcut
cmdShop(player, cmnd);
} else if(!strcmp(cmnd->str[1], "flags")) {
propFlags(player, cmnd, p);
} else {
switch(p->getType()) {
case PROP_STORAGE:
case PROP_HOUSE:
if(!strcmp(cmnd->str[1], "assign"))
assign = true;
else if(strcmp(cmnd->str[1], "unassign"))
found = false;
break;
case PROP_SHOP:
if(!strcmp(cmnd->str[1], "hire"))
assign = true;
else if(strcmp(cmnd->str[1], "fire"))
found = false;
break;
default:
found = false;
break;
}
if(!found) {
player->print("Command not understood.\n");
propCommands(player, p);
return(0);
}
propAssignUnassign(player, cmnd, p, assign);
}
} else if(po) {
if( !strcmp(cmnd->str[1], "log") &&
p->viewLogFlag() != -1 &&
po->flagIsSet(p->viewLogFlag())
) {
propLog(player, 0, p);
} else if(!strcmp(cmnd->str[1], "flags") && p->getType() != PROP_HOUSE) {
cmnd->num = 4;
strcpy(cmnd->str[2], player->name);
strcpy(cmnd->str[3], "view");
propFlags(player, cmnd, p);
} else if(!strcmp(cmnd->str[1], "unassign")) {
propRemove(player, p);
p->expelToExit(player, false);
} else {
player->print("Command not understood.\n");
propCommands(player, p);
}
}
return(0);
}
//*********************************************************************
// dmProperties
//*********************************************************************
int dmProperties(Player* player, cmd* cmnd) {
bstring id = getFullstrText(cmnd->fullstr, 1);
if(id == "") {
player->printColor("Properties: Type ^c*propery [num]^x to view more info.\n");
player->printColor("Properties: Type ^c*propery [type]^x to filter the list.\n");
player->printColor("Properties: Type ^c*propery destroy [num] [owner]^x to destroy a property.\n");
player->printColor("^b--------------------------------------------------------------\n");
gConfig->showProperties(player, 0);
} else if(!strcmp(cmnd->str[1], "destroy")) {
int num = cmnd->val[1];
int i=1;
if(!strlen(cmnd->str[2])) {
player->printColor("You must specify the owner of the property you wish to destroy.\nType ^c*propery destroy [num] [owner]^x\n");
return(0);
}
cmnd->str[2][0] = up(cmnd->str[2][0]);
player->print("Looking to destroy propery #%d owned by %s.\n", num, cmnd->str[2]);
std::list<Property*>::iterator it;
for(it = gConfig->properties.begin() ; it != gConfig->properties.end() ; it++) {
if(num == i++ && (*it)->isOwner(cmnd->str[2])) {
player->printColor("^rProperty found...^x\n\n%s\n^rProperty destroyed!\n", (*it)->show().c_str());
gConfig->destroyProperty(*it);
return(0);
}
}
player->print("Property not found!\n");
} else if(!isdigit(id.getAt(0))) {
PropType propType = PROP_NONE, i;
int len = strlen(cmnd->str[1]);
for(i = PROP_NONE; i < PROP_END && propType == PROP_NONE; i = (PropType)((int)i+1)) {
if( !strncmp(cmnd->str[1], Property::getTypeStr(i).c_str(), len) ||
(i == PROP_HOUSE && !strncmp(cmnd->str[1], "house", len))
)
propType = i;
}
player->printColor("Properties filtered on type: %s.\n", Property::getTypeStr(propType).c_str());
player->printColor("^b--------------------------------------------------------------\n");
gConfig->showProperties(player, 0, propType);
} else {
int num = atoi(id.c_str());
int i=1;
player->print("Property #%d\n", num);
player->printColor("^b--------------------------------------------------------------\n");
std::list<Property*>::iterator it;
for(it = gConfig->properties.begin() ; it != gConfig->properties.end() ; it++) {
if(num == i++) {
player->printColor("%s\n", (*it)->show().c_str());
return(0);
}
}
player->print("Property not found!\n");
}
return(0);
}
//*********************************************************************
// show
//*********************************************************************
bstring Property::show(bool isOwner, bstring player, int *i) {
std::ostringstream oStr;
oStr.setf(std::ios::left, std::ios::adjustfield);
oStr << "Name: ^e" << name;
if(player != "" && !!getPartialOwner(player))
oStr << " (#" << (*i)++ << ")";
oStr << "^x\n"
<< "Location: ^c" << location << "^x\n"
<< "Founded: ^c" << std::setw(30) << dateFounded << "^x";
if(!isOwner)
oStr << " Owner: ^c" << std::setw(15) << owner << "^x";
oStr << "Type: ^c" << getTypeStr() << "^x\n";
// show them who else is part of this property
if(guild) {
Guild *g = gConfig->getGuild(guild);
if(!guild) {
if(type == PROP_GUILDHALL)
oStr << "This guildhall is broken: guild ID " << guild << " does not exist.\n";
} else {
oStr << "Guild: ^c#" << guild << " ^c" << g->name << "^x\n";
std::list<bstring>::iterator pt;
bool initial=false;
oStr << "Guild Members: ^c";
for(pt = g->members.begin() ; pt != g->members.end() ; pt++) {
if(initial)
oStr << ", ";
initial = true;
if(!isOwner && player == (*pt))
oStr << "^e";
oStr << (*pt);
if(!isOwner && player == (*pt))
oStr << "^c";
}
oStr << "^x\n";
}
}
if(!partialOwners.empty()) {
std::list<PartialOwner>::const_iterator pt;
bool initial=false;
oStr << "Partial Owners: ^c";
for(pt = partialOwners.begin() ; pt != partialOwners.end() ; pt++) {
if(initial)
oStr << ", ";
initial = true;
if(!isOwner && player == (*pt).getName())
oStr << "^m";
oStr << (*pt).getName();
if(!isOwner && player == (*pt).getName())
oStr << "^c";
}
oStr << "^x\n";
}
// admin info
if(player == "") {
oStr << "Ranges:\n";
std::list<Range>::const_iterator rt;
for(rt = ranges.begin() ; rt != ranges.end() ; rt++)
oStr << "Low: ^c" << (*rt).low.str() << "^x High: ^c" << (*rt).high << "^x\n";
oStr << "Log Type: ^c" << getLogTypeStr() << "^x\n";
if(!log.empty()) {
oStr << "Log:\n^b-----------------^x\n";
oStr << getLog();
}
}
return(oStr.str());
}
//*********************************************************************
// showProperties
//*********************************************************************
// player=0 means we're using the admin version
void Config::showProperties(Player* viewer, Player* player, PropType propType) {
std::list<Property*>::iterator it;
std::ostringstream oStr;
Property *p=0;
bstring name = "";
bool isOwner=false, partialOwnerList=false;
int i=0;
oStr.setf(std::ios::left, std::ios::adjustfield);
if(player)
name = player->name;
for(it = properties.begin() ; it != properties.end() ; it++) {
p = (*it);
if(player) {
isOwner = p->isOwner(name);
// guild halls don't count toward the partial owner list, since they can't
// remove themselves with the property interface
if(!partialOwnerList && player && p->getType() != PROP_GUILDHALL)
partialOwnerList = !!p->getPartialOwner(name);
if(!isOwner && !p->isPartialOwner(name))
continue;
}
// when filtering, we still increment so we can show the correct id
i++;
if(propType != PROP_NONE && p->getType() != propType)
continue;
if(!player) {
oStr << "^c#" << std::setw(3) << i << "^x Name: ^y" << std::setw(50) << p->getName()
<< "^x Range: ^c" << p->ranges.front().str() << "^x\n";
continue;
}
oStr << p->show(isOwner, name, &i) << "\n";
}
if(partialOwnerList) {
oStr << "To remove yourself as partial owner from a property,\n"
<< "type \"property remove [num]\".\n\n";
}
viewer->printColor("%s", oStr.str().c_str());
}
//*********************************************************************
// clearProperties
//*********************************************************************
void Config::clearProperties() {
Property* p;
while(!properties.empty()) {
p = properties.front();
delete p;
properties.pop_front();
}
properties.clear();
}
//*********************************************************************
// getProperties
//*********************************************************************
Property* Config::getProperty(CatRef cr) {
std::list<Property*>::iterator it;
for(it = properties.begin() ; it != properties.end() ; it++) {
if((*it)->belongs(cr))
return(*it);
}
return(0);
}
//*********************************************************************
// getAvailableProperty
//*********************************************************************
CatRef Config::getAvailableProperty(PropType type, int numRequired) {
std::list<Property*>::iterator it;
std::list<Range>::iterator rt;
std::map<int, bool> sort;
std::map<int, bool>::iterator st;
Property *p;
CatRef cr;
int i=0;
cr.setArea(Property::getTypeArea(type));
if(cr.isArea(""))
return(cr);
for(it = properties.begin() ; it != properties.end() ; it++) {
p = (*it);
if(p->getType() != type)
continue;
for(rt = p->ranges.begin() ; rt != p->ranges.end() ; rt++) {
if(!cr.isArea((*rt).low.area))
continue;
for(cr.id = (*rt).low.id; cr.id <= (*rt).high; cr.id++)
sort[cr.id] = true;
}
}
// if a entire-area range is found, we fail
if(sort.find(-1) != sort.end()) {
cr.id = 0;
return(cr);
}
cr.id = 1;
for(st = sort.begin() ; st != sort.end() ; st++) {
i = (*st).first;
// this room is used, keep looking
if(cr.id == i) {
cr.id++;
continue;
}
// we've jumped over some empty rooms. we need to know if there's
// enough space to fit this property
if(i - cr.id >= numRequired)
return(cr);
// not enough room; keep looking
cr.id = i + 1;
}
if(!validRoomId(cr))
cr.id = 0;
return(cr);
}
//*********************************************************************
// rename
//*********************************************************************
void storageName(Room* room, char* name);
void setupShop(Player* player, Guild* guild, Room* shop, Room* storage);
void Property::rename(Player *player) {
Room *shop=0, *storage=0;
setOwner(player->name);
if(type == PROP_STORAGE) {
if(loadRoom(ranges.front().low, &storage)) {
storageName(storage, player->name);
setName(storage->name);
storage->saveToFile(0);
}
} else if(type == PROP_SHOP) {
CatRef cr = ranges.front().low;
if(loadRoom(cr, &shop)) {
cr.id++;
if(loadRoom(cr, &storage)) {
// only rename shops that aren't affiliated with a guild
if(!guild) {
setupShop(player, 0, shop, storage);
setName(shop->name);
}
}
}
}
}
//*********************************************************************
// renamePropertyOwner
//*********************************************************************
void Config::renamePropertyOwner(bstring oldName, Player *player) {
std::list<Property*>::iterator it;
for(it = properties.begin() ; it != properties.end() ; it++) {
if((*it)->getOwner() == oldName)
(*it)->rename(player);
}
saveProperties();
}
//*********************************************************************
// getLog
//*********************************************************************
bstring Property::getLog() const {
if(log.empty())
return("No entries in the log.\n");
bstring str = "";
std::list<bstring>::const_iterator it;
for(it = log.begin() ; it != log.end() ; it++) {
str += (*it);
str += "\n";
}
return(str);
}
//*********************************************************************
// appendLog
//*********************************************************************
void Property::appendLog(bstring user, const char *fmt,...) {
if(logType == LOG_NONE)
return;
if(logType == LOG_PARTIAL && isOwner(user))
return;
char *str;
va_list ap;
va_start(ap, fmt);
if(vasprintf(&str, fmt, ap) == -1) {
printf("Error in Property::appendLog\n");
return;
}
va_end(ap);
long t = time(0);
bstring txt;
txt = "^c";
txt += ctime(&t);
txt.trim();
txt += ":^x ";
txt += str;
log.push_front(txt);
while(log.size() > PROP_LOG_SIZE)
log.pop_back();
gConfig->saveProperties();
}
//*********************************************************************
// clearLog
//*********************************************************************
void Property::clearLog() {
log.clear();
}
//*********************************************************************
// guildRoomSetup
//*********************************************************************
// this sets up the room to be part of the guild, then deletes it
void Property::guildRoomSetup(Room *room, const Guild* guild, bool outside) {
sprintf(room->name, "%s's Guild Hall", guild->name.c_str());
if(!outside) {
room->setFlag(R_INDOORS);
room->setFlag(R_SAFE_ROOM);
}
room->setShortDescription("You are in the ");
room->appendShortDescription(guild->name);
room->appendShortDescription("'s guild hall.");
room->saveToFile(0);
delete room;
}
//*********************************************************************
// houseRoomSetup
//*********************************************************************
// this sets up the room as a personal house, then deletes it
void Property::houseRoomSetup(Room *room, const Player* player, bool outside) {
sprintf(room->name, "%s's Personal House", player->name);
if(!outside) {
room->setFlag(R_INDOORS);
room->setFlag(R_SAFE_ROOM);
}
room->setShortDescription("You are in ");
room->appendShortDescription(player->name);
room->appendShortDescription("'s personal house.");
room->saveToFile(0);
delete room;
}
//*********************************************************************
// roomSetup
//*********************************************************************
void Property::roomSetup(Room *room, PropType propType, const Player* player, const Guild* guild, bool outside) {
if(propType == PROP_GUILDHALL)
guildRoomSetup(room, guild, outside);
else if(propType == PROP_HOUSE)
houseRoomSetup(room, player, outside);
}
//*********************************************************************
// linkRoom
//*********************************************************************
void Property::linkRoom(BaseRoom* inside, BaseRoom* outside, bstring xname) {
Room* uRoom = outside->getUniqueRoom();
AreaRoom* aRoom = outside->getAreaRoom();
if(uRoom) {
link_rom(inside, uRoom->info, xname.c_str());
uRoom->saveToFile(0);
} else {
link_rom(inside, &aRoom->mapmarker, xname.c_str());
aRoom->save();
}
uRoom = inside->getUniqueRoom();
aRoom = inside->getAreaRoom();
if(uRoom) {
link_rom(outside, uRoom->info, "out");
uRoom->saveToFile(0);
} else {
link_rom(outside, &aRoom->mapmarker, "out");
aRoom->save();
}
}
//*********************************************************************
// makeNextRoom
//*********************************************************************
Room* Property::makeNextRoom(Room* r1, PropType propType, CatRef cr, bool exits, const Player* player, const Guild* guild, BaseRoom* room, bstring xname, const char *go, const char *back, bool save) {
Room *r2 = new Room;
r2->info = cr;
if(r1) {
link_rom(r1, r2->info, go);
link_rom(r2, r1->info, back);
}
if(exits)
linkRoom(room, r2, xname);
if(save) {
roomSetup(r2, propType, player, guild);
return(0);
}
return(r2);
}
//*********************************************************************
// rotateHouse
//*********************************************************************
int Property::rotateHouse(char *dir1, char *dir2, int rotation) {
if(!rotation)
strcpy(dir1, "west");
else if(rotation == 1)
strcpy(dir1, "north");
else if(rotation == 2)
strcpy(dir1, "east");
else if(rotation == 3)
strcpy(dir1, "south");
opposite_exit_name(dir1, dir2);
return((rotation + 1) % 4);
}
//*********************************************************************
// isInside
//*********************************************************************
bool Property::isInside(const Player* player, const Room* room, Property** p) {
if(!(*p) || !room)
return(false);
if((*p)->getType() == PROP_GUILDHALL) {
if( !(*p)->getGuild() ||
!player->getGuild() ||
player->getGuild() != (*p)->getGuild()
)
return(false);
}
return(true);
}
//*********************************************************************
// requireInside
//*********************************************************************
bool Property::requireInside(const Player* player, const Room* room, Property** p, PropType propType) {
if(room)
(*p) = gConfig->getProperty(room->info);
if(!isInside(player, room, p)) {
player->print("You must be inside your %s to perform this action.\n", getTypeStr(propType).c_str());
return(false);
}
return(true);
}
//*********************************************************************
// descEdit
//*********************************************************************
bstring postText(char* str);
void Property::descEdit(Socket* sock, char *str) {
bstring outstr = "";
char outcstr[160];
int ff=0;
Player* ply = sock->getPlayer();
if((str[0] == '.' || str[0] == '*') && !str[1]) {
ply->clearFlag(P_READING_FILE);
sock->restoreState();
Property *p=0;
if(!Property::requireInside(ply, ply->parent_rom, &p))
return;
FILE* fp = fopen(sock->tempstr[0], "r");
if(!fp) {
ply->print("Error replacing room description.\n");
return;
}
ply->parent_rom->setLongDescription("");
while(!feof(fp)) {
fgets(outcstr, sizeof(outcstr), fp);
ply->parent_rom->appendLongDescription(outcstr);
strcpy(outcstr, "");
}
ply->parent_rom->setLongDescription(ply->parent_rom->getLongDescription().left(ply->parent_rom->getLongDescription().getLength()-1));
fclose(fp);
unlink(sock->tempstr[0]);
ply->print("Room description replaced.\n");
ply->parent_rom->escapeText();
ply->parent_rom->saveToFile(0);
return;
}
if(str[0] == '\\' && !str[1]) {
unlink(sock->tempstr[0]);
ply->clearFlag(P_READING_FILE);
ply->print("Description editting cancelled.\n");
sock->restoreState();
return;
}
ff = open(sock->tempstr[0], O_CREAT | O_APPEND | O_RDWR, ACC);
if(ff < 0)
merror("Property::descEdit", FATAL);
outstr = postText(str);
write(ff, outstr.c_str(), outstr.getLength());
close(ff);
ply->print("-: ");
gServer->processOutput();
sock->intrpt &= ~1;
}
//*********************************************************************
// cmdHouse
//*********************************************************************
int cmdHouse(Player* player, cmd* cmnd) {
Property::manage(player, cmnd, PROP_HOUSE, 1);
return(0);
}
bool Property::houseCanBuild(AreaRoom* aRoom, BaseRoom* room) {
if(room->flagIsSet(R_BUILD_HOUSE))
return(true);
return(false);
}
//*********************************************************************
// manage
//*********************************************************************
// This function handles the commands for editting a property. It
// currently supports:
// PROP_GUILDHALL
// PROP_HOUSE
void Property::manage(Player* player, cmd* cmnd, PropType propType, int x) {
AreaRoom* aRoom = player->area_room;
Room* uRoom = player->parent_rom;
BaseRoom* room = player->getRoom();
Guild* guild=0;
CatRef cr;
int len = strlen(cmnd->str[2-x]);
bool canBuild=false;
const char *syntax;
if(player->getClass() == BUILDER) {
player->print("Builders have no need to manage property.");
return;
}
if(propType == PROP_GUILDHALL) {
canBuild = room->flagIsSet(R_BUILD_GUILDHALL);
syntax = "Syntax: guild hall ^e<^xsurvey^e>^x\n"
"Shortcut: ^cgh ^e<^xfound^e> <^cexit name^e> <^croom #^e>^x\n"
" ^e<^xname^e> <^croom name^e>^x\n"
" ^e<^xshort^e> <^cshort description^e>^x\n"
" ^e<^xdesc^e>^x - will enter editor\n"
" ^e<^xrename^e> <^cexit name^e> <^cnew name^e>^x\n"
" ^e<^xextend^e> <^cobject^e> <^cexit name^e>^x\n"
" ^e<^xhelp^e>^x\n\n";
} else if(propType == PROP_HOUSE) {
// TODO: can-do rules will be complicated
syntax = "Syntax: house ^e<^xsurvey^e>^x\n"
" ^e<^xfound^e> <^cexit name^e> <^croom #^e>^x\n"
" ^e<^xname^e> <^croom name^e>^x\n"
" ^e<^xshort^e> <^cshort description^e>^x\n"
" ^e<^xdesc^e>^x - will enter editor\n"
" ^e<^xrename^e> <^cexit name^e> <^cnew name^e>^x\n"
" ^e<^xextend^e> <^cobject^e> <^cexit name^e>^x\n"
" ^e<^xhelp^e>^x\n\n";
}
if(!len) {
player->printColor(syntax);
return;
}
if(!strncmp(cmnd->str[2-x], "help", len)) {
propHelp(player, cmnd, propType);
return;
}
if(!strncmp(cmnd->str[2-x], "survey", len)) {
if(!canBuild)
player->print("You are unable to build a %s here.\n", getTypeStr(propType).c_str());
else
player->print("This site is open for a %s to be constructed.\n", getTypeStr(propType).c_str());
return;
}
bstring xNameType = getTypeStr(propType);
// caps!
xNameType.setAt(0, toupper(xNameType.getAt(0)));
if(propType == PROP_GUILDHALL) {
if(player->getGuild())
guild = gConfig->getGuild(player->getGuild());
if(!guild) {
player->printColor("^yYou are not a member of a guild.\n");
player->printColor(syntax);
return;
}
// everything else requires a guildmaster
if(player->getGuildRank() != GUILD_MASTER) {
player->print("You are not the leader of a guild.\n");
return;
}
} else if(propType == PROP_HOUSE) {
if(player->getLevel() < 15) {
player->print("You must be atleast level 15 to build a personal house.\n");
return;
}
}
if(!strncmp(cmnd->str[2-x], "found", len)) {
Object *deed=0, *oHidden=0, *oConceal=0, *oInvis=0, *oFoyer=0;
if(!canBuild) {
player->print("You can't build a %s here!\n", getTypeStr(propType).c_str());
return;
}
cr = gConfig->getSingleProperty(player, propType);
if(cr.id) {
if(propType == PROP_GUILDHALL)
player->print("Your guild already owns a guild hall.\nOnly one is allowed per guild.\n");
else if(propType == PROP_HOUSE)
player->print("You already own a house!\n");
return;
}
otag* op = player->first_obj;
bstring oName;
while(op) {
// find their property related objects
oName = op->obj->name;
if( (propType == PROP_GUILDHALL && oName.left(10) == "guildhall ") ||
(propType == PROP_HOUSE && oName.left(6) == "house ")
) {
if( !deed && (
( propType == PROP_GUILDHALL &&
oName.left(14) == "guildhall deed" &&
( (uRoom && op->obj->deed.belongs(uRoom->info)) ||
(aRoom && op->obj->deed.isArea("area"))
)
) || (
propType == PROP_HOUSE &&
oName.left(10) == "house deed"
)
) ) {
deed = op->obj;
// see if they want the entrance hidden
} else if(oName.right(15) == "hidden entrance") {
oHidden = op->obj;
// see if they want the entrance concealed
} else if(oName.right(18) == "concealed entrance") {
oConceal = op->obj;
// see if they want the entrance invisible
} else if(oName.right(18) == "invisible entrance") {
oInvis = op->obj;
// see if they want a foyer
} else if(oName.right(5) == "foyer") {
oFoyer = op->obj;
}
}
op = op->next_tag;
}
if(!deed) {
player->print("You need a deed to build a %s!\nVisit a real estate office.\n", getTypeStr(propType).c_str());
return;
}
bstring xname = getFullstrText(cmnd->fullstr, 3-x);
if(xname == "") {
player->print("Please enter an exit name followed by the number of the room you wish to connect to.\n");
return;
}
int roomId=0;
for(int i = xname.getLength()-1; i>0; i--) {
if(xname.getAt(i) == ' ') {
roomId = atoi(xname.right(xname.getLength()-i).c_str());
xname = xname.left(i);
break;
}
}
xname.Replace("_", " ");
if(!Property::goodExit(player, room, xNameType.c_str(), xname))
return;
// determine what layout we're using!
bstring layout = deed->name;
bstring orientation = "";
int extra=0;
int req=0;
if(layout.right(12) == "5 room cross") {
layout = layout.right(12);
req = 5;
} else if(layout.right(13) == "4 room square") {
layout = layout.right(13);
req = 4;
} else if(layout.right(5) == "tower") {
// 5 room tower
layout = layout.right(12);
req = atoi(layout.left(1).c_str());
layout = layout.right(5);
} else if(layout.right(11) == "small house") {
layout = layout.right(11);
roomId = req = 1;
} else if(layout.right(12) == "medium house") {
orientation = getFullstrText(layout, 3).left(1);
if(orientation != "n" && orientation != "e")
orientation = "n";
layout = layout.right(12);
req = 2;
} else if(layout.right(11) == "large house") {
orientation = getFullstrText(layout, 3).left(1);
if(orientation == "n")
extra = 0;
else if(orientation == "e")
extra = 1;
else if(orientation == "s")
extra = 2;
else {
orientation = "w";
extra = 3;
}
layout = layout.right(11);
req = 4;
} else {
player->print("The layout specified by your %s deed '%s' could not be determined!\n", getTypeStr(propType).c_str(), deed->name);
return;
}
if(!roomId) {
player->print("That is not a valid room number to connect to.\n");
return;
}
cr = gConfig->getAvailableProperty(propType, req);
if(cr.id < 1) {
player->print("Sorry, you can't build a new %s right now; try again later!\n", getTypeStr(propType).c_str());
return;
}
if( roomId < 1 ||
roomId > req ||
(layout == "5 room cross" && roomId == 1)
) {
player->print("That is not a valid room number to connect to.\n");
return;
}
Property *p = new Property;
p->setOwner(player->name);
p->setDateFounded();
p->setLocation(player->parent_rom->name);
p->setType(propType);
// foyer needs 1 extra room
p->addRange(cr.area, cr.id, cr.id + req - (oFoyer ? 0 : 1));
if(propType == PROP_GUILDHALL) {
p->setGuild(player->getGuild());
p->setName(guild->name + "'s Guild Hall");
} else if(propType == PROP_HOUSE) {
{
bstring pName = player->name;
pName += "'s House";
p->setName(pName);
}
}
// if they have a foyer, make one
Room* rFoyer=0;
bstring xPropName = xname;
if(oFoyer) {
rFoyer = makeNextRoom(0, propType, cr, true, player, guild, room, xname, "", "", false);
if(propType == PROP_GUILDHALL) {
rFoyer->setFlag(R_GUILD_OPEN_ACCESS);
xname = "guild";
} else if(propType == PROP_HOUSE)
xname = "house";
room = rFoyer;
cr.id++;
}
// this is where the work of creating the rooms is done.
if(layout == "5 room cross") {
Room* r1 = makeNextRoom(0, propType, cr, false, player, guild, room, xname, "", "", false);
cr.id++; // north
makeNextRoom(r1, propType, cr, roomId == 2, player, guild, room, xname, "north", "south", true);
cr.id++; // east
makeNextRoom(r1, propType, cr, roomId == 3, player, guild, room, xname, "east", "west", true);
cr.id++; // south
makeNextRoom(r1, propType, cr, roomId == 4, player, guild, room, xname, "south", "north", true);
cr.id++; // west
makeNextRoom(r1, propType, cr, roomId == 5, player, guild, room, xname, "west", "east", true);
roomSetup(r1, propType, player, guild);
} else if(layout == "4 room square") {
Room* nw = makeNextRoom(0, propType, cr, roomId == 1, player, guild, room, xname, "", "", false);
cr.id++; // northeast
Room* ne = makeNextRoom(nw, propType, cr, roomId == 2, player, guild, room, xname, "east", "west", false);
cr.id++; // southwest
Room* sw = makeNextRoom(nw, propType, cr, roomId == 3, player, guild, room, xname, "south", "north", false);
cr.id++; // southeast
Room* se = makeNextRoom(ne, propType, cr, roomId == 4, player, guild, room, xname, "south", "north", false);
link_rom(se, sw->info, "west");
link_rom(sw, se->info, "east");
nw->arrangeExits();
roomSetup(nw, propType, player, guild);
ne->arrangeExits();
roomSetup(ne, propType, player, guild);
se->arrangeExits();
roomSetup(se, propType, player, guild);
sw->arrangeExits();
roomSetup(sw, propType, player, guild);
} else if(layout == "tower") {
Room* r1 = makeNextRoom(0, propType, cr, roomId == 1, player, guild, room, xname, "", "", false);
int i=1;
while(i < req) {
i++;
cr.id++;
Room* r2 = makeNextRoom(r1, propType, cr, roomId == i, player, guild, room, xname, "up", "down", false);
r1->arrangeExits();
roomSetup(r1, propType, player, guild);
r1 = r2;
}
r1->arrangeExits();
roomSetup(r1, propType, player, guild);
} else if(layout == "small house") {
makeNextRoom(0, propType, cr, true, player, guild, room, xname, "", "", true);
} else if(layout == "medium house") {
Room* r1 = makeNextRoom(0, propType, cr, roomId == 1, player, guild, room, xname, "", "", false);
cr.id++;
if(orientation == "n")
makeNextRoom(r1, propType, cr, roomId == 2, player, guild, room, xname, "south", "north", true);
else
makeNextRoom(r1, propType, cr, roomId == 2, player, guild, room, xname, "east", "west", true);
roomSetup(r1, propType, player, guild);
} else if(layout == "large house") {
char dir1[10], dir2[10];
Room* r1 = makeNextRoom(0, propType, cr, roomId == 1, player, guild, room, xname, "", "", false);
cr.id++;
extra = rotateHouse(dir1, dir2, extra);
makeNextRoom(r1, propType, cr, roomId == 2, player, guild, room, xname, dir1, dir2, true);
cr.id++;
extra = rotateHouse(dir1, dir2, extra);
makeNextRoom(r1, propType, cr, roomId == 3, player, guild, room, xname, dir1, dir2, true);
cr.id++;
extra = rotateHouse(dir1, dir2, extra);
makeNextRoom(r1, propType, cr, roomId == 4, player, guild, room, xname, dir1, dir2, true);
r1->arrangeExits();
roomSetup(r1, propType, player, guild);
} else {
if(rFoyer)
delete rFoyer;
player->print("There was an error in construction of your %s!\n", getTypeStr(propType).c_str());
delete p;
return;
}
if(rFoyer)
roomSetup(rFoyer, propType, player, guild, true);
gConfig->addProperty(p);
if(propType == PROP_GUILDHALL) {
player->print("Congratulations! Your guild is now the owner of a brand new guild hall.\n");
if(!player->flagIsSet(P_DM_INVIS)) {
broadcast(player->getSock(), player->getRoom(), "%M just opened a guild hall!", player);
broadcast("### %s, leader of %s, just opened a guild hall!", player->name, guild->name.c_str());
}
} else if(propType == PROP_HOUSE) {
player->print("Congratulations! You are now the owner of a brand new house.\n");
if(!player->flagIsSet(P_DM_INVIS))
broadcast(player->getSock(), player->getRoom(), "%M just built a house!", player);
}
player->delObj(deed, true, false, true, false);
delete deed;
Exit* exit = findExit(player, xPropName, 1);
if(oHidden) {
if(exit) {
player->delObj(oHidden, true, false, true, false);
delete oHidden;
exit->setFlag(X_SECRET);
player->print("Exit '%s' is now hidden.\n", exit->name);
} else {
player->print("There was an error making the %s entrance hidden!\n", getTypeStr(propType).c_str());
}
}
if(oConceal) {
if(exit) {
player->delObj(oConceal, true, false, true, false);
delete oConceal;
exit->setFlag(X_CONCEALED);
player->print("Exit '%s' is now concealed.\n", exit->name);
} else {
player->print("There was an error making the %s entrance concealed!\n", getTypeStr(propType).c_str());
}
}
if(oInvis) {
if(exit) {
player->delObj(oInvis, true, false, true, false);
delete oInvis;
exit->setFlag(X_INVISIBLE);
player->print("Exit '%s' is now invisible.\n", exit->name);
} else {
player->print("There was an error making the %s entrance invisible!\n", getTypeStr(propType).c_str());
}
}
if(oFoyer) {
player->delObj(oFoyer, true, false, true, false);
delete oFoyer;
}
player->checkDarkness();
if(propType == PROP_GUILDHALL) {
player->parent_rom->clearFlag(R_BUILD_GUILDHALL);
player->parent_rom->setFlag(R_WAS_BUILD_GUILDHALL);
} else if(propType == PROP_HOUSE) {
player->parent_rom->clearFlag(R_BUILD_HOUSE);
player->parent_rom->setFlag(R_WAS_BUILD_HOUSE);
}
player->parent_rom->saveToFile(0);
return;
}
Property *p=0;
if(!Property::requireInside(player, player->parent_rom, &p, propType))
return;
if(!strncmp(cmnd->str[2-x], "extend", len)) {
Object* obj = findObject(player, player->first_obj, cmnd, 3-x);
if(!obj) {
player->print("Object not found.\nYou need to purchase %s extensions from a realty office.\n", getTypeStr(propType).c_str());
return;
}
bstring layout = obj->name;
if( (propType == PROP_GUILDHALL && layout.left(19) != "guildhall extension") ||
(propType == PROP_HOUSE && layout.left(15) != "house extension")
) {
player->printColor("%O is not a %s extension permit.\nYou need to purchase %s extensions from a realty office.\n",
obj, getTypeStr(propType).c_str(), getTypeStr(propType).c_str());
return;
}
layout = layout.right(layout.getLength() - 21);
bstring xname = getFullstrText(cmnd->fullstr, 4-x);
if(xname != "" && isdigit(xname.getAt(0)))
xname = getFullstrText(xname, 1);
xname.Replace("_", " ");
if(!Property::goodExit(player, room, xNameType.c_str(), xname))
return;
cr = gConfig->getAvailableProperty(propType, 1);
if(cr.id < 1) {
player->print("Sorry, you can't expand your %s right now, try again later!\n", getTypeStr(propType).c_str());
return;
}
Room* target = new Room;
bool outside=false;
if(layout == "normal room") {
// normal rooms are acceptable
} else if(layout == "outdoors") {
outside = true;
} else if(layout == "clinic") {
target->setFlag(R_FAST_HEAL);
} else if(layout == "post office") {
target->setFlag(R_POST_OFFICE);
} else if(layout == "recycling room") {
target->setFlag(R_DUMP_ROOM);
} else if(layout == "magic atrium") {
target->setFlag(R_FAST_HEAL);
target->setFlag(R_MAGIC_BONUS);
} else {
player->print("The extension type for this permit could not be determined!\n");
delete target;
return;
}
target->info = cr;
linkRoom(player->parent_rom, target, xname);
roomSetup(target, propType, player, guild, outside);
player->delObj(obj, true);
delete obj;
player->print("Extension has been added to your property!\n");
p->addRange(cr.area, cr.id, cr.id);
gConfig->saveProperties();
} else if(!strncmp(cmnd->str[2-x], "desc", len)) {
char file[80];
int ff=0;
checkDirExists(POSTPATH, true);
switch(propType) {
case PROP_GUILDHALL:
sprintf(file, "%s/%s_guildhall.txt", POSTPATH, player->name);
break;
case PROP_HOUSE:
sprintf(file, "%s/%s_house.txt", POSTPATH, player->name);
break;
default:
player->print("Error: this property type is not supported.\n");
return;
}
ff = open(file, O_RDONLY, 0);
close(ff);
if(file_exists(file)) {
player->print("Room description so far:\n\n");
viewLoginFile(player->getSock(), file);
player->print("\n\n");
}
player->print("You may edit this room's long description now.\nType '.' or '*' on a line by itself to finish or '\\' to cancel.\nEach line should be NO LONGER THAN 80 CHARACTERS.\n-: ");
strcpy(player->getSock()->tempstr[0], file);
player->setFlag(P_READING_FILE);
gServer->processOutput();
player->getSock()->setState(CON_EDIT_PROPERTY);
player->getSock()->intrpt &= ~1;
} else if(!strncmp(cmnd->str[2-x], "short", len)) {
bstring desc = getFullstrText(cmnd->fullstr, 3-x);
if(!Property::goodNameDesc(player, desc, "Set room short description to what?", "description string"))
return;
player->parent_rom->setShortDescription(desc);
player->print("Property short description set to '%s'.\n", desc.c_str());
player->parent_rom->saveToFile(0);
} else if(!strncmp(cmnd->str[2-x], "name", len)) {
bstring name = getFullstrText(cmnd->fullstr, 3-x);
if(!Property::goodNameDesc(player, name, "Rename this room to what?", "room name"))
return;
strcpy(player->parent_rom->name, name.c_str());
player->print("Room renamed to '%s'.\n", player->parent_rom->name);
player->parent_rom->saveToFile(0);
} else if(!strncmp(cmnd->str[2-x], "rename", len)) {
bstring origExit = cmnd->str[3-x];
bstring newExit = getFullstrText(cmnd->fullstr, 4-x);
if(origExit == "" || newExit == "") {
player->print("Rename which exit to what?\n");
return;
}
if(isCardinal(newExit)) {
player->print("Exits cannot be made cardinal directions.\n");
return;
}
if(!Property::goodExit(player, room, xNameType.c_str(), newExit))
return;
xtag *xp = player->parent_rom->first_ext, *found=0;
bool unique=true;
while(xp) {
// exact match
if(!strcmp(xp->ext->name, origExit.c_str())) {
unique = true;
found = xp;
break;
}
if(!strncmp(xp->ext->name, origExit.c_str(), origExit.getLength())) {
if(found)
unique = false;
found = xp;
}
xp = xp->next_tag;
}
if(!unique) {
player->print("That exit name is not unique.\n");
return;
}
if(!found) {
player->print("That exit was not found in this room.\n");
return;
}
if(isCardinal(found->ext->name)) {
player->print("Cardinal directions cannot be renamed.\n");
return;
}
strcpy(found->ext->name, newExit.c_str());
player->parent_rom->arrangeExits();
player->print("Exit renamed to '%s'.\n", newExit.c_str());
player->parent_rom->saveToFile(0);
}
}