/*
* startlocs.cpp
* Player starting location code.
* ____ _
* | _ \ ___ __ _| |_ __ ___ ___
* | |_) / _ \/ _` | | '_ ` _ \/ __|
* | _ < __/ (_| | | | | | | \__ \
* |_| \_\___|\__,_|_|_| |_| |_|___/
*
* Permission to use, modify and distribute is granted via the
* Creative Commons - Attribution - Non Commercial - Share Alike 3.0 License
* http://creativecommons.org/licenses/by-nc-sa/3.0/
*
* Copyright (C) 2007-2012 Jason Mitchell, Randi Mitchell
* Contributions by Tim Callahan, Jonathan Hseu
* Based on Mordor (C) Brooke Paul, Brett J. Vickers, John P. Freeman
*
*/
#include "mud.h"
#include "login.h"
#include "commands.h"
//*********************************************************************
// initialBind
//*********************************************************************
// setup the player for the first time they are bound
void initialBind(Player* player, bstring str) {
const StartLoc* location = gConfig->getStartLoc(str);
if(!location) {
broadcast(isCt, "Invalid start location: %s", str.c_str());
return;
}
CatRef cr = location->getStartingGuide();
if(str == "oceancrest") {
player->setFlag(P_HARDCORE);
player->setFlag(P_CHAOTIC);
player->setFlag(P_CHOSEN_ALIGNMENT);
}
player->bind(location);
player->currentLocation.room = player->bound.room;
player->currentLocation.room = player->getRecallRoom().room;
if(cr.id)
Create::addStartingItem(player, cr.area, cr.id, false, true);
}
//*********************************************************************
// startingChoices
//*********************************************************************
// This function will determine the player's starting location.
// choose = false return true if we can easily pick their location
// return false if they must choose
// choose = true return true if they made a valid selection
// return false if they made an invalid selection
bool startingChoices(Player* player, bstring str, char* location, bool choose) {
// if only one start location is defined, our choices are easy!
if(gConfig->start.size() == 1) {
std::map<bstring, StartLoc*>::iterator s = gConfig->start.begin();
sprintf(location, "%s", (*s).first.c_str());
location[0] = up(location[0]);
player->bind((*s).second);
return(true);
}
std::list<bstring> options;
int race = player->getRace();
// set race equal to their parent race, use player->getRace() if checking
// for subraces
const RaceData* r = gConfig->getRace(race);
if(r && r->getParentRace())
race = r->getParentRace();
if(player->getClass() == DRUID) {
// druidic order overrides all other starting locations
options.push_back("druidwood");
} else if(player->getDeity() == ENOCH || player->getRace() == SERAPH) {
// religious states
options.push_back("sigil");
} else if(player->getDeity() == ARAMON || player->getRace() == CAMBION) {
// religious states
options.push_back("caladon");
} else if(race == HUMAN && player->getClass() == CLERIC) {
// all other human clerics have to start in HP because
// Sigil and Caladon are very religious
options.push_back("highport");
} else if(race == GNOME || race == HALFLING) {
options.push_back("gnomebarrow");
} else if(race == HALFGIANT) {
options.push_back("schnai");
options.push_back("highport");
} else if(race == BARBARIAN) {
options.push_back("schnai");
} else if(race == DWARF) {
options.push_back("ironguard");
} else if(race == DARKELF) {
options.push_back("oakspire");
} else if(race == TIEFLING) {
options.push_back("highport");
options.push_back("caladon");
} else if(race == MINOTAUR) {
options.push_back("ruhrdan");
} else if(race == KATARAN) {
options.push_back("kataran");
} else if(race == HALFORC) {
// half-breeds can start in more places
options.push_back("highport");
options.push_back("caladon");
options.push_back("orc");
} else if(race == ORC) {
options.push_back("orc");
} else if(race == ELF || player->getDeity() == LINOTHAN) {
options.push_back("meadhil");
} else if(race == HALFELF || race == HUMAN) {
// humans and half-breeds can start in more places
options.push_back("highport");
options.push_back("sigil");
if(race == HUMAN)
options.push_back("caladon");
else if(race == HALFELF)
options.push_back("meadhil");
}
switch(player->getClass()) {
case RANGER:
options.push_back("druidwood");
break;
// even seraphs of these classes cannot start in Sigil
case ASSASSIN:
case LICH:
case THIEF:
case DEATHKNIGHT:
case PUREBLOOD:
case ROGUE:
case WEREWOLF:
options.remove("sigil");
break;
default:
break;
}
// needed:
// troll, goblin, kobold, ogre
// these areas arent finished yet
options.remove("caladon");
options.remove("meadhil");
options.remove("orc");
options.remove("schnai");
options.remove("ironguard");
options.remove("kataran");
// if the areas aren't open, give them the default starting location,
// which is the first one on the list
if(!options.size())
options.push_back(gConfig->getDefaultStartLoc()->getName());
// OCEANCREST: Dom: HC
//options.push_back("oceancrest");
// if they have no choice, we assign them a location and are done with it
if(options.size() == 1) {
sprintf(location, "%s", options.front().c_str());
location[0] = up(location[0]);
initialBind(player, options.front());
return(true);
}
std::list<bstring>::iterator it;
int i=0;
// we don't need to make any choices - we need to show them what they can choose
if(!choose) {
std::ostringstream oStr;
char opt = 'A';
for(it = options.begin(); it != options.end(); it++) {
if(i)
oStr << " ";
i++;
sprintf(location, "%s", (*it).c_str());
location[0] = up(location[0]);
oStr << "[^W" << (opt++) << "^x] " << location;
}
sprintf(location, "%s", oStr.str().c_str());
return(false);
}
// determine where they want to start
int choice = low(str[0]) - 'a' + 1;
for(it = options.begin(); it != options.end(); it++) {
if(++i == choice) {
sprintf(location, "%s", (*it).c_str());
location[0] = up(location[0]);
initialBind(player, *it);
return(true);
}
}
return(false);
}
//*********************************************************************
// dmStartLocs
//*********************************************************************
int dmStartLocs(Player* player, cmd* cmnd) {
player->print("Starting Locations:\n");
std::map<bstring, StartLoc*>::iterator it;
for(it = gConfig->start.begin(); it != gConfig->start.end() ; it++) {
player->print("%s\n", (*it).first.c_str());
}
return(0);
}
//*********************************************************************
// splBind
//*********************************************************************
// This function will change the player's bound room. If cast as a spell,
// it must belong to a list of rooms. If used from the floor, it can point
// to any room
int splBind(Creature* player, cmd* cmnd, SpellData* spellData) {
Creature* creature=0;
Player* target=0, *pPlayer = player->getAsPlayer();
const StartLoc* location=0;
if(!pPlayer)
return(0);
if(spellData->how == CAST) {
if(!pPlayer->isCt()) {
if( pPlayer->getClass() != MAGE &&
pPlayer->getClass() != LICH &&
pPlayer->getClass() != CLERIC &&
pPlayer->getClass() != DRUID
) {
pPlayer->print("Your class prevents you from casting that spell.\n");
return(0);
}
if(pPlayer->getLevel() < 13) {
pPlayer->print("You are not yet powerful enough to cast that spell.\n");
return(0);
}
}
if(cmnd->num < 2) {
pPlayer->print("Syntax: cast bind [target]%s.\n", pPlayer->isCt() ? " <location>" : "");
return(0);
}
if(cmnd->num == 2)
target = pPlayer;
else {
cmnd->str[2][0] = up(cmnd->str[2][0]);
creature = pPlayer->getParent()->findCreature(pPlayer, cmnd->str[2], cmnd->val[2], false);
if(creature)
target = creature->getAsPlayer();
if(!target) {
pPlayer->print("You don't see that person here.\n");
return(0);
}
if(checkRefusingMagic(player, target))
return(0);
}
if(pPlayer->isCt() && cmnd->num == 4)
location = gConfig->getStartLoc(cmnd->str[3]);
else if(pPlayer->inUniqueRoom() && pPlayer->getUniqueRoomParent()->info.id)
location = gConfig->getStartLocByReq(pPlayer->getUniqueRoomParent()->info);
if( !location ||
!location->getRequired().getId() ||
!location->getBind().getId()
) {
pPlayer->print("%s is not a valid location to bind someone!\n",
pPlayer->isCt() && cmnd->num == 4 ? "That" : "This");
return(0);
}
if(!pPlayer->isCt()) {
if( (pPlayer->inAreaRoom() && pPlayer->getAreaRoomParent()->mapmarker != location->getRequired().mapmarker) ||
(pPlayer->inUniqueRoom() && pPlayer->getUniqueRoomParent()->info != location->getRequired().room)
) {
pPlayer->print("To bind to this location, you must be at %s.\n", location->getRequiredName().c_str());
return(0);
}
if(!dec_daily(&pPlayer->daily[DL_TELEP])) {
pPlayer->print("You are too weak to bind again today.\n");
return(0);
}
}
if(pPlayer == target) {
pPlayer->print("Bind spell cast.\nYou are now bound to %s.\n", location->getBindName().c_str());
broadcast(pPlayer->getSock(), pPlayer->getRoomParent(), "%M casts a bind spell on %sself.", pPlayer, pPlayer->himHer());
} else {
// nosummon flag
if( target->flagIsSet(P_NO_SUMMON) &&
!pPlayer->checkStaff("The spell fizzles.\n%M's summon flag is not set.\n", target))
{
target->print("%s tried to bind you to this room!\nIf you wish to be bound, type \"set summon\".\n", pPlayer->getCName());
return(0);
}
pPlayer->print("Bind cast on %s.\n%s is now bound to %s.\n",
target->getCName(), target->getCName(), location->getBindName().c_str());
target->print("%M casts a bind spell on you.\nYou are now bound to %s.\n",
pPlayer, location->getBindName().c_str());
broadcast(player->getSock(), target->getSock(), pPlayer->getRoomParent(),
"%M casts a bind spell on %N.", pPlayer, target);
}
target->bind(location);
} else {
if(spellData->how != WAND) {
pPlayer->print("Nothing happens.\n");
return(0);
} else {
// we need more info from the item!
pPlayer->print("Nothing happens (for now).\n");
return(0);
}
}
return(1);
}
//*********************************************************************
// StartLoc
//*********************************************************************
StartLoc::StartLoc() {
name = requiredName = bindName = "";
primary = false;
}
//*********************************************************************
// load
//*********************************************************************
void StartLoc::load(xmlNodePtr curNode) {
xmlNodePtr childNode = curNode->children;
xml::copyPropToBString(name, curNode, "Name");
while(childNode) {
if(NODE_NAME(childNode, "BindName")) xml::copyToBString(bindName, childNode);
else if(NODE_NAME(childNode, "RequiredName")) xml::copyToBString(requiredName, childNode);
else if(NODE_NAME(childNode, "Bind")) bind.load(childNode);
else if(NODE_NAME(childNode, "Required")) required.load(childNode);
else if(NODE_NAME(childNode, "StartingGuide")) startingGuide.load(childNode);
childNode = childNode->next;
}
}
//*********************************************************************
// save
//*********************************************************************
void StartLoc::save(xmlNodePtr curNode) const {
xmlNodePtr childNode = xml::newStringChild(curNode, "Location");
xml::newProp(childNode, "Name", name);
xml::saveNonNullString(childNode, "BindName", bindName);
xml::saveNonNullString(childNode, "RequiredName", requiredName);
bind.save(childNode, "Bind");
required.save(childNode, "Required");
startingGuide.save(childNode, "StartingGuide", false);
}
bstring StartLoc::getName() const { return(name); }
bstring StartLoc::getBindName() const { return(bindName); }
bstring StartLoc::getRequiredName() const { return(requiredName); }
Location StartLoc::getBind() const { return(bind); }
Location StartLoc::getRequired() const { return(required); }
CatRef StartLoc::getStartingGuide() const { return(startingGuide); }
bool StartLoc::isDefault() const { return(primary); }
void StartLoc::setDefault() { primary = true; }
//*********************************************************************
// bind
//*********************************************************************
void Player::bind(const StartLoc* location) {
bound = location->getBind();
}
//*********************************************************************
// getStartLoc
//*********************************************************************
const StartLoc *Config::getStartLoc(bstring id) const {
std::map<bstring, StartLoc*>::const_iterator it = start.find(id);
if(it == start.end())
return(0);
return((*it).second);
}
//*********************************************************************
// getDefaultStartLoc
//*********************************************************************
const StartLoc *Config::getDefaultStartLoc() const {
std::map<bstring, StartLoc*>::const_iterator it;
for(it = start.begin(); it != start.end() ; it++) {
if((*it).second->isDefault())
return((*it).second);
}
return(0);
}
//*********************************************************************
// getStartLocByReq
//*********************************************************************
const StartLoc *Config::getStartLocByReq(CatRef cr) const {
std::map<bstring, StartLoc*>::const_iterator it;
StartLoc* s=0;
for(it = start.begin() ; it != start.end() ; it++) {
s = (*it).second;
if(cr == s->getRequired().room)
return(s);
}
return(0);
}
//*********************************************************************
// clearStartLoc
//*********************************************************************
void Config::clearStartLoc() {
std::map<bstring, StartLoc*>::iterator it;
for(it = start.begin() ; it != start.end() ; it++) {
StartLoc* s = (*it).second;
delete s;
}
start.clear();
}
//*********************************************************************
// loadStartLoc
//*********************************************************************
bool Config::loadStartLoc() {
xmlDocPtr xmlDoc;
xmlNodePtr curNode;
// first one loaded is the primary one
bool primary=true;
char filename[80];
snprintf(filename, 80, "%s/start.xml", Path::Game);
xmlDoc = xml::loadFile(filename, "Locations");
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);
}
clearStartLoc();
bstring loc = "";
while(curNode != NULL) {
if(NODE_NAME(curNode, "Location")) {
xml::copyPropToBString(loc, curNode, "Name");
if(loc != "" && start.find(loc) == start.end()) {
start[loc] = new StartLoc;
start[loc]->load(curNode);
if(primary) {
start[loc]->setDefault();
primary = false;
}
}
}
curNode = curNode->next;
}
xmlFreeDoc(xmlDoc);
xmlCleanupParser();
return(true);
}
//*********************************************************************
// saveStartLocs
//*********************************************************************
void Config::saveStartLocs() const {
std::map<bstring, StartLoc*>::const_iterator it;
xmlDocPtr xmlDoc;
xmlNodePtr rootNode;
char filename[80];
xmlDoc = xmlNewDoc(BAD_CAST "1.0");
rootNode = xmlNewDocNode(xmlDoc, NULL, BAD_CAST "Locations", NULL);
xmlDocSetRootElement(xmlDoc, rootNode);
for(it = start.begin() ; it != start.end() ; it++)
(*it).second->save(rootNode);
sprintf(filename, "%s/start.xml", Path::Game);
xml::saveFile(filename, xmlDoc);
xmlFreeDoc(xmlDoc);
}