/* * raceData.cpp * Race data storage file * ____ _ * | _ \ ___ __ _| |_ __ ___ ___ * | |_) / _ \/ _` | | '_ ` _ \/ __| * | _ < __/ (_| | | | | | | \__ \ * |_| \_\___|\__,_|_|_| |_| |_|___/ * * 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" //********************************************************************* // raceCount //********************************************************************* unsigned short Config::raceCount() { return(races.size()); } //********************************************************************* // getPlayableRaceCount //********************************************************************* unsigned short Config::getPlayableRaceCount() { RaceData* curRace; int numPlayableRaces = 0; for(std::pair<int, RaceData*> rp : races) { curRace = rp.second; if(!curRace) continue; if(curRace->isPlayable()) numPlayableRaces++; } return(numPlayableRaces); } //********************************************************************* // RaceData //********************************************************************* RaceData::RaceData(xmlNodePtr rootNode) { Sex sex; Dice dice; char sizeTxt[20]; int i=0; short n=0; bstring str; id = 0; name = adjective = abbr = ""; infra = bonus = 0; startAge = parentRace = porphyriaResistance = 0; size = NO_SIZE; isParentRace = false; playable = gendered = true; zero(stats, sizeof(stats)); zero(saves, sizeof(saves)); zero(classes, sizeof(classes)); zero(multiClerDeities, sizeof(multiClerDeities)); zero(clerDeities, sizeof(clerDeities)); zero(palDeities, sizeof(palDeities)); zero(dkDeities, sizeof(dkDeities)); id = xml::getIntProp(rootNode, "id"); xml::copyPropToBString(name, rootNode, "name"); xmlNodePtr subNode, childNode, curNode = rootNode->children; while(curNode) { if(NODE_NAME(curNode, "Adjective")) xml::copyToBString(adjective, curNode); else if(NODE_NAME(curNode, "Abbr")) xml::copyToBString(abbr, curNode); else if(NODE_NAME(curNode, "NotPlayable")) playable = false; else if(NODE_NAME(curNode, "ParentRace")) { xml::copyToNum(parentRace, curNode); if(gConfig->races.find(parentRace) != gConfig->races.end()) gConfig->races[parentRace]->makeParent(); } else if(NODE_NAME(curNode, "Data")) { childNode = curNode->children; while(childNode) { if(NODE_NAME(childNode, "Size")) { xml::copyToCString(sizeTxt, childNode); size = ::getSize(sizeTxt); } else if(NODE_NAME(childNode, "BaseHeight")) { sex = (Sex)xml::getIntProp(childNode, "Sex"); xml::copyToNum(n, childNode); baseHeight[sex] = n; } else if(NODE_NAME(childNode, "BaseWeight")) { sex = (Sex)xml::getIntProp(childNode, "Sex"); xml::copyToNum(n, childNode); baseWeight[sex] = n; } else if(NODE_NAME(childNode, "VarHeight")) { sex = (Sex)xml::getIntProp(childNode, "Sex"); dice.load(childNode); varHeight[sex] = dice; } else if(NODE_NAME(childNode, "VarWeight")) { sex = (Sex)xml::getIntProp(childNode, "Sex"); dice.load(childNode); varWeight[sex] = dice; } else if(NODE_NAME(childNode, "Gendered")) xml::copyToBool(gendered, childNode); else if(NODE_NAME(childNode, "BonusStat")) { xml::copyToNum(i, childNode); if(i) bonus = true; } else if(NODE_NAME(childNode, "StartAge")) xml::copyToNum(startAge, childNode); else if(NODE_NAME(childNode, "PorphyriaResistance")) xml::copyToNum(porphyriaResistance, childNode); else if(NODE_NAME(childNode, "Infravision")) xml::copyToBool(infra, childNode); else if(NODE_NAME(childNode, "Stats")) { subNode = childNode->children; while(subNode) { if(NODE_NAME(subNode, "Stat")) { xml::copyPropToBString(str, subNode, "id"); xml::copyToNum(stats[gConfig->stattoNum(str)], subNode); } subNode = subNode->next; } } else if(NODE_NAME(childNode, "Classes")) { subNode = childNode->children; while(subNode) { if(NODE_NAME(subNode, "Class")) { xml::copyPropToBString(str, subNode, "id"); classes[gConfig->classtoNum(str)] = true; } subNode = subNode->next; } } else if(NODE_NAME(childNode, "MultiClericDeities")) { subNode = childNode->children; while(subNode) { if(NODE_NAME(subNode, "Deity")) { xml::copyPropToBString(str, subNode, "id"); multiClerDeities[gConfig->deitytoNum(str)] = true; } subNode = subNode->next; } } else if(NODE_NAME(childNode, "ClericDeities")) { subNode = childNode->children; while(subNode) { if(NODE_NAME(subNode, "Deity")) { xml::copyPropToBString(str, subNode, "id"); clerDeities[gConfig->deitytoNum(str)] = true; } subNode = subNode->next; } } else if(NODE_NAME(childNode, "PaladinDeities")) { subNode = childNode->children; while(subNode) { if(NODE_NAME(subNode, "Deity")) { xml::copyPropToBString(str, subNode, "id"); palDeities[gConfig->deitytoNum(str)] = true; } subNode = subNode->next; } } else if(NODE_NAME(childNode, "DeathknightDeities")) { subNode = childNode->children; while(subNode) { if(NODE_NAME(subNode, "Deity")) { xml::copyPropToBString(str, subNode, "id"); dkDeities[gConfig->deitytoNum(str)] = true; } subNode = subNode->next; } } else if(NODE_NAME(childNode, "SavingThrows")) { subNode = childNode->children; while(subNode) { if(NODE_NAME(subNode, "SavingThrow")) { xml::copyPropToBString(str, subNode, "id"); xml::copyToNum(saves[gConfig->savetoNum(str)], subNode); } subNode = subNode->next; } } else if(NODE_NAME(childNode, "Effects")) { subNode = childNode->children; while(subNode) { if(NODE_NAME(subNode, "Effect")) { xml::copyToBString(str, subNode); effects.push_back(str); } subNode = subNode->next; } } else if(NODE_NAME(childNode, "Skills")) { xmlNodePtr skillNode = childNode->children; while(skillNode) { if(NODE_NAME(skillNode, "Skill")) { SkillGain* skillGain = new SkillGain(skillNode); baseSkills.push_back(skillGain); } skillNode = skillNode->next; } } childNode = childNode->next; } } curNode = curNode->next; } } RaceData::~RaceData() { for(SkillGain* skGain : baseSkills) { delete skGain; } baseSkills.clear(); } //********************************************************************* // stattoNum //********************************************************************* int Config::stattoNum(bstring str) { if(str == "Str") return(STR); if(str == "Con") return(CON); if(str == "Dex") return(DEX); if(str == "Int") return(INT); if(str == "Pty") return(PTY); return(0); } //********************************************************************* // savetoNum //********************************************************************* int Config::savetoNum(bstring str) { if(str == "Poi") return(POI); if(str == "Dea") return(DEA); if(str == "Bre") return(BRE); if(str == "Men") return(MEN); if(str == "Spl") return(SPL); return(0); } //********************************************************************* // classtoNum //********************************************************************* int Config::classtoNum(bstring str) { if(str == "Assassin") return(ASSASSIN); if(str == "Berserker") return(BERSERKER); if(str == "Cleric") return(CLERIC); if(str == "Fighter") return(FIGHTER); if(str == "Mage") return(MAGE); if(str == "Paladin") return(PALADIN); if(str == "Ranger") return(RANGER); if(str == "Thief") return(THIEF); if(str == "Pureblood") return(PUREBLOOD); if(str == "Monk") return(MONK); if(str == "Deathknight") return(DEATHKNIGHT); if(str == "Druid") return(DRUID); if(str == "Lich") return(LICH); if(str == "Werewolf") return(WEREWOLF); if(str == "Bard") return(BARD); if(str == "Rogue") return(ROGUE); if(str == "Fighter/Mage") return(MULTI_BASE+0); if(str == "Fighter/Thief") return(MULTI_BASE+1); if(str == "Cleric/Assassin") return(MULTI_BASE+2); if(str == "Mage/Thief") return(MULTI_BASE+3); if(str == "Thief/Mage") return(MULTI_BASE+4); if(str == "Cleric/Fighter") return(MULTI_BASE+5); if(str == "Mage/Assassin") return(MULTI_BASE+6); return(0); } //********************************************************************* // racetoNum //********************************************************************* int Config::racetoNum(bstring str) { for(unsigned i=0; i<races.size(); i++) { if(!races[i]) continue; if(str == races[i]->getName() || str == races[i]->getAdjective()) return(i); } return(-1); } //********************************************************************* // deitytoNum //********************************************************************* int Config::deitytoNum(bstring str) { for(unsigned i=0; i<deities.size(); i++) { if(!deities[i]) continue; if(str == deities[i]->getName()) return(i); } return(-1); } //********************************************************************** // loadRaces //********************************************************************** bool Config::loadRaces() { xmlDocPtr xmlDoc; xmlNodePtr curNode; int i=0; char filename[80]; snprintf(filename, 80, "%s/races.xml", Path::Game); xmlDoc = xml::loadFile(filename, "Races"); 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); } clearRaces(); while(curNode != NULL) { if(NODE_NAME(curNode, "Race")) { i = xml::getIntProp(curNode, "id"); if(races.find(i) == races.end()) races[i] = new RaceData(curNode); } curNode = curNode->next; } xmlFreeDoc(xmlDoc); xmlCleanupParser(); return(true); } //********************************************************************** // getRace //********************************************************************** const RaceData* Config::getRace(bstring race) const { std::map<int, RaceData*>::const_iterator it; RaceData* data=0; int len = race.getLength(); race = race.toLower(); for(it = races.begin() ; it != races.end() ; it++) { // exact match if((*it).second->getName().toLower() == race) return((*it).second); if((*it).second->getName().substr(0, len) == race) { // not unique if(data) return(0); data = (*it).second; } } return(data); } //********************************************************************** // getRace //********************************************************************** const RaceData* Config::getRace(int id) const { std::map<int, RaceData*>::const_iterator it = races.find(id); if(it == races.end()) it = races.begin(); return((*it).second); } //********************************************************************* // dmShowRaces //********************************************************************* int dmShowRaces(Player* player, cmd* cmnd) { std::map<int, RaceData*>::const_iterator it; RaceData* data=0; bool all = player->isDm() && cmnd->num > 1 && !strcmp(cmnd->str[1], "all"); std::map<Sex, short>::const_iterator bIt; std::map<Sex, Dice>::const_iterator dIt; player->printColor("Displaying Races:%s\n", player->isDm() && !all ? " Type ^y*racelist all^x to view all information." : ""); for(it = gConfig->races.begin() ; it != gConfig->races.end() ; it++) { data = (*it).second; player->printColor("Id: ^c%-2d^x Name: ^c%s\n", data->getId(), data->getName().c_str()); if(all) { if(data->bonusStat()) player->printColor(" ^mThis race picks a bonus and a penalty stat.\n"); else player->printColor(" Str: ^m%-2d^x Dex: ^m%-2d^x Con: ^m%-2d^x Int: ^m%-2d^x Pty: ^m%-2d^x\n", data->getStatAdj(STR), data->getStatAdj(DEX), data->getStatAdj(CON), data->getStatAdj(INT), data->getStatAdj(PTY)); player->printColor(" Infravision: %-3s^x Size: %s isParent: %-3s\n", data->hasInfravision() ? "^gYes" : "^rNo", getSizeName(data->getSize()).c_str(), data->isParent() ? "^gYes" : "^rNo"); if(data->getParentRace()) player->print(" Parent Race: %s", gConfig->getRace(data->getParentRace())->getName().c_str()); if(data->getPorphyriaResistance()) player->print(" Porphyria Resistance: %d", data->getPorphyriaResistance()); player->printColor(" Playable: %s\n", data->isPlayable() ? "^gYes" : "^rNo"); // base weight for all genders for(bIt = data->baseHeight.begin() ; bIt != data->baseHeight.end() ; bIt++) { player->printColor(" BaseHeight: %s: ^c%d", getSexName((*bIt).first).c_str(), (*bIt).second); } if(data->baseHeight.size()) player->print("\n"); // variable weight for all genders for(dIt = data->varHeight.begin() ; dIt != data->varHeight.end() ; dIt++) { player->printColor(" VarHeight: %s: ^c%s", getSexName((*dIt).first).c_str(), (*dIt).second.str().c_str()); } if(data->varHeight.size()) player->print("\n"); // base height for all genders for(bIt = data->baseWeight.begin() ; bIt != data->baseWeight.end() ; bIt++) { player->printColor(" BaseWeight: %s: ^c%d", getSexName((*bIt).first).c_str(), (*bIt).second); } if(data->baseWeight.size()) player->print("\n"); // variable height for all genders for(dIt = data->varWeight.begin() ; dIt != data->varWeight.end() ; dIt++) { player->printColor(" VarWeight: %s: ^c%s", getSexName((*dIt).first).c_str(), (*dIt).second.str().c_str()); } if(data->varWeight.size()) player->print("\n"); } } player->print("\n"); return(0); } //********************************************************************** // clearRaces //********************************************************************** void Config::clearRaces() { std::map<int, RaceData*>::iterator it; for(it = races.begin() ; it != races.end() ; it++) { RaceData* r = (*it).second; delete r; } races.clear(); } //********************************************************************* // getAdjective //********************************************************************* bstring RaceData::getAdjective() const { return(adjective); } //********************************************************************* // getAbbr //********************************************************************* bstring RaceData::getAbbr() const { return(abbr); } //********************************************************************* // getParentRace //********************************************************************* int RaceData::getParentRace() const { return(parentRace); } //********************************************************************* // getPorphyriaResistance //********************************************************************* short RaceData::getPorphyriaResistance() const { return(porphyriaResistance); } //********************************************************************* // makeParent //********************************************************************* void RaceData::makeParent() { isParentRace = true; } //********************************************************************* // isParent //********************************************************************* bool RaceData::isParent() const { return(isParentRace); } //********************************************************************* // isPlayable //********************************************************************* bool RaceData::isPlayable() const { return(playable); } //********************************************************************* // isGendered //********************************************************************* bool RaceData::isGendered() const { return(gendered); } //********************************************************************* // hasInfravision //********************************************************************* bool RaceData::hasInfravision() const { return(infra); } //********************************************************************* // bonusStat //********************************************************************* bool RaceData::bonusStat() const { return(bonus); } //********************************************************************* // getSize //********************************************************************* Size RaceData::getSize() const { return(size); } //********************************************************************* // getStartAge //********************************************************************* int RaceData::getStartAge() const { return(startAge); } //********************************************************************* // getStatAdj //********************************************************************* int RaceData::getStatAdj(int stat) const { return(stats[stat]); } //********************************************************************* // getSave //********************************************************************* int RaceData::getSave(int save) const { return(saves[save]); } //********************************************************************* // allowedClass //********************************************************************* bool RaceData::allowedClass(int cls) const { return(classes[cls]); } //********************************************************************* // allowedDeity //********************************************************************* bool RaceData::allowedDeity(int cls, int cls2, int dty) const { return( (cls == CLERIC && !cls2 && allowedClericDeity(dty)) || (cls == PALADIN && !cls2 && allowedPaladinDeity(dty)) || (cls == DEATHKNIGHT && !cls2 && allowedDeathknightDeity(dty)) || (cls == CLERIC && cls2 && allowedMultiClericDeity(dty)) ); } //********************************************************************* // allowedMultiClericDeity //********************************************************************* bool RaceData::allowedMultiClericDeity(int dty) const { return(multiClerDeities[dty]); } //********************************************************************* // allowedClericDeity //********************************************************************* bool RaceData::allowedClericDeity(int dty) const { return(clerDeities[dty]); } //********************************************************************* // allowedPaladinDeity //********************************************************************* bool RaceData::allowedPaladinDeity(int dty) const { return(palDeities[dty]); } //********************************************************************* // allowedDeathknightDeity //********************************************************************* bool RaceData::allowedDeathknightDeity(int dty) const { return(dkDeities[dty]); } //********************************************************************* // getId //********************************************************************* int RaceData::getId() const { return(id); } //********************************************************************* // getName //********************************************************************* bstring RaceData::getName(int tryToShorten) const { if(!tryToShorten) return(name); bstring txt = name; if(txt.length() > (unsigned)tryToShorten) txt.Replace("Half-", "H-"); return(txt); } //********************************************************************* // getSkillBegin //********************************************************************* std::list<SkillGain*>::const_iterator RaceData::getSkillBegin() const { return(baseSkills.begin()); } //********************************************************************* // getSkillEnd //********************************************************************* std::list<SkillGain*>::const_iterator RaceData::getSkillEnd() const { return(baseSkills.end()); }