/*
* start.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-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 "magic.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->room = player->bound.room;
player->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, char* 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]);
initialBind(player, location);
// 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 VAMPIRE:
// 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");
// options.remove("ruhrdan");
options.push_back("ocean");
// 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());
// 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->getPlayer();
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->getRoom()->findCreature(pPlayer, cmnd->str[2], cmnd->val[2], false);
if(creature)
target = creature->getPlayer();
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->parent_rom && pPlayer->parent_rom->info.id)
location = gConfig->getStartLocByReq(pPlayer->parent_rom->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->area_room && pPlayer->area_room->mapmarker != location->getRequired().mapmarker) ||
(pPlayer->parent_rom && pPlayer->parent_rom->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->getRoom(), "%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->name);
return(0);
}
pPlayer->print("Bind cast on %s.\n%s is now bound to %s.\n",
target->name, target->name, 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->getRoom(),
"%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;
xmlDoc = xml::loadFile(CONFPATH "start.xml", "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);
}
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", CONFPATH);
xml::saveFile(filename, xmlDoc);
xmlFreeDoc(xmlDoc);
}