//////////////////////////////////////////////////////////////////////// // OpenTibia - an opensource roleplaying game //////////////////////////////////////////////////////////////////////// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . //////////////////////////////////////////////////////////////////////// #include "otpch.h" #include #include #include #include "vocation.h" #include "tools.h" Vocation Vocations::defVoc = Vocation(); void Vocations::clear() { for(VocationsMap::iterator it = vocationsMap.begin(); it != vocationsMap.end(); ++it) delete it->second; vocationsMap.clear(); } bool Vocations::reload() { clear(); return loadFromXml(); } bool Vocations::parseVocationNode(xmlNodePtr p) { std::string strValue; int32_t intValue; float floatValue; if(xmlStrcmp(p->name, (const xmlChar*)"vocation")) return false; if(!readXMLInteger(p, "id", intValue)) { std::cout << "[Error - Vocations::parseVocationNode] Missing vocation id." << std::endl; return false; } Vocation* voc = new Vocation(intValue); if(readXMLString(p, "name", strValue)) voc->setName(strValue); if(readXMLString(p, "description", strValue)) voc->setDescription(strValue); if(readXMLString(p, "needpremium", strValue)) voc->setNeedPremium(booleanString(strValue)); if(readXMLInteger(p, "gaincap", intValue) || readXMLInteger(p, "gaincapacity", intValue)) voc->setGainCap(intValue); if(readXMLInteger(p, "gainhp", intValue) || readXMLInteger(p, "gainhealth", intValue)) voc->setGain(GAIN_HEALTH, intValue); if(readXMLInteger(p, "gainmana", intValue)) voc->setGain(GAIN_MANA, intValue); if(readXMLInteger(p, "gainhpticks", intValue) || readXMLInteger(p, "gainhealthticks", intValue)) voc->setGainTicks(GAIN_HEALTH, intValue); if(readXMLInteger(p, "gainhpamount", intValue) || readXMLInteger(p, "gainhealthamount", intValue)) voc->setGainAmount(GAIN_HEALTH, intValue); if(readXMLInteger(p, "gainmanaticks", intValue)) voc->setGainTicks(GAIN_MANA, intValue); if(readXMLInteger(p, "gainmanaamount", intValue)) voc->setGainAmount(GAIN_MANA, intValue); if(readXMLFloat(p, "manamultiplier", floatValue)) voc->setMultiplier(MULTIPLIER_MANA, floatValue); if(readXMLInteger(p, "attackspeed", intValue)) voc->setAttackSpeed(intValue); if(readXMLInteger(p, "basespeed", intValue)) voc->setBaseSpeed(intValue); if(readXMLInteger(p, "soulmax", intValue)) voc->setGain(GAIN_SOUL, intValue); if(readXMLInteger(p, "gainsoulamount", intValue)) voc->setGainAmount(GAIN_SOUL, intValue); if(readXMLInteger(p, "gainsoulticks", intValue)) voc->setGainTicks(GAIN_SOUL, intValue); if(readXMLString(p, "attackable", strValue)) voc->setAttackable(booleanString(strValue)); if(readXMLInteger(p, "fromvoc", intValue) || readXMLInteger(p, "fromvocation", intValue)) voc->setFromVocation(intValue); if(readXMLInteger(p, "lessloss", intValue)) voc->setLessLoss(intValue); for(xmlNodePtr configNode = p->children; configNode; configNode = configNode->next) { if(!xmlStrcmp(configNode->name, (const xmlChar*)"skill")) { if(readXMLFloat(configNode, "fist", floatValue)) voc->setSkillMultiplier(SKILL_FIST, floatValue); if(readXMLInteger(configNode, "fistBase", intValue)) voc->setSkillBase(SKILL_FIST, intValue); if(readXMLFloat(configNode, "club", floatValue)) voc->setSkillMultiplier(SKILL_CLUB, floatValue); if(readXMLInteger(configNode, "clubBase", intValue)) voc->setSkillBase(SKILL_CLUB, intValue); if(readXMLFloat(configNode, "axe", floatValue)) voc->setSkillMultiplier(SKILL_AXE, floatValue); if(readXMLInteger(configNode, "axeBase", intValue)) voc->setSkillBase(SKILL_AXE, intValue); if(readXMLFloat(configNode, "sword", floatValue)) voc->setSkillMultiplier(SKILL_SWORD, floatValue); if(readXMLInteger(configNode, "swordBase", intValue)) voc->setSkillBase(SKILL_SWORD, intValue); if(readXMLFloat(configNode, "distance", floatValue) || readXMLFloat(configNode, "dist", floatValue)) voc->setSkillMultiplier(SKILL_DIST, floatValue); if(readXMLInteger(configNode, "distanceBase", intValue) || readXMLInteger(configNode, "distBase", intValue)) voc->setSkillBase(SKILL_DIST, intValue); if(readXMLFloat(configNode, "shielding", floatValue) || readXMLFloat(configNode, "shield", floatValue)) voc->setSkillMultiplier(SKILL_SHIELD, floatValue); if(readXMLInteger(configNode, "shieldingBase", intValue) || readXMLInteger(configNode, "shieldBase", intValue)) voc->setSkillBase(SKILL_SHIELD, intValue); if(readXMLFloat(configNode, "fishing", floatValue) || readXMLFloat(configNode, "fish", floatValue)) voc->setSkillMultiplier(SKILL_FISH, floatValue); if(readXMLInteger(configNode, "fishingBase", intValue) || readXMLInteger(configNode, "fishBase", intValue)) voc->setSkillBase(SKILL_FISH, intValue); if(readXMLFloat(configNode, "experience", floatValue) || readXMLFloat(configNode, "exp", floatValue)) voc->setSkillMultiplier(SKILL__LEVEL, floatValue); if(readXMLInteger(configNode, "id", intValue)) { skills_t skill = (skills_t)intValue; if(intValue < SKILL_FIRST || intValue >= SKILL__LAST) { std::cout << "[Error - Vocations::parseVocationNode] No valid skill id (" << intValue << ")." << std::endl; continue; } if(readXMLInteger(configNode, "base", intValue)) voc->setSkillBase(skill, intValue); if(readXMLFloat(configNode, "multiplier", floatValue)) voc->setSkillMultiplier(skill, floatValue); } } else if(!xmlStrcmp(configNode->name, (const xmlChar*)"formula")) { if(readXMLFloat(configNode, "meleeDamage", floatValue)) voc->setMultiplier(MULTIPLIER_MELEE, floatValue); if(readXMLFloat(configNode, "distDamage", floatValue) || readXMLFloat(configNode, "distanceDamage", floatValue)) voc->setMultiplier(MULTIPLIER_DISTANCE, floatValue); if(readXMLFloat(configNode, "wandDamage", floatValue) || readXMLFloat(configNode, "rodDamage", floatValue)) voc->setMultiplier(MULTIPLIER_WAND, floatValue); if(readXMLFloat(configNode, "magDamage", floatValue) || readXMLFloat(configNode, "magicDamage", floatValue)) voc->setMultiplier(MULTIPLIER_MAGIC, floatValue); if(readXMLFloat(configNode, "magHealingDamage", floatValue) || readXMLFloat(configNode, "magicHealingDamage", floatValue)) voc->setMultiplier(MULTIPLIER_HEALING, floatValue); if(readXMLFloat(configNode, "defense", floatValue)) voc->setMultiplier(MULTIPLIER_DEFENSE, floatValue); if(readXMLFloat(configNode, "magDefense", floatValue) || readXMLFloat(configNode, "magicDefense", floatValue)) voc->setMultiplier(MULTIPLIER_MAGICDEFENSE, floatValue); if(readXMLFloat(configNode, "armor", floatValue)) voc->setMultiplier(MULTIPLIER_ARMOR, floatValue); } else if(!xmlStrcmp(configNode->name, (const xmlChar*)"absorb")) { if(readXMLInteger(configNode, "percentAll", intValue)) { for(int32_t i = COMBAT_FIRST; i <= COMBAT_LAST; i++) voc->increaseAbsorb((CombatType_t)i, intValue); } if(readXMLInteger(configNode, "percentElements", intValue)) { voc->increaseAbsorb(COMBAT_ENERGYDAMAGE, intValue); voc->increaseAbsorb(COMBAT_FIREDAMAGE, intValue); voc->increaseAbsorb(COMBAT_EARTHDAMAGE, intValue); voc->increaseAbsorb(COMBAT_ICEDAMAGE, intValue); } if(readXMLInteger(configNode, "percentMagic", intValue)) { voc->increaseAbsorb(COMBAT_ENERGYDAMAGE, intValue); voc->increaseAbsorb(COMBAT_FIREDAMAGE, intValue); voc->increaseAbsorb(COMBAT_EARTHDAMAGE, intValue); voc->increaseAbsorb(COMBAT_ICEDAMAGE, intValue); voc->increaseAbsorb(COMBAT_HOLYDAMAGE, intValue); voc->increaseAbsorb(COMBAT_DEATHDAMAGE, intValue); } if(readXMLInteger(configNode, "percentEnergy", intValue)) voc->increaseAbsorb(COMBAT_ENERGYDAMAGE, intValue); if(readXMLInteger(configNode, "percentFire", intValue)) voc->increaseAbsorb(COMBAT_FIREDAMAGE, intValue); if(readXMLInteger(configNode, "percentPoison", intValue) || readXMLInteger(configNode, "percentEarth", intValue)) voc->increaseAbsorb(COMBAT_EARTHDAMAGE, intValue); if(readXMLInteger(configNode, "percentIce", intValue)) voc->increaseAbsorb(COMBAT_ICEDAMAGE, intValue); if(readXMLInteger(configNode, "percentHoly", intValue)) voc->increaseAbsorb(COMBAT_HOLYDAMAGE, intValue); if(readXMLInteger(configNode, "percentDeath", intValue)) voc->increaseAbsorb(COMBAT_DEATHDAMAGE, intValue); if(readXMLInteger(configNode, "percentLifeDrain", intValue)) voc->increaseAbsorb(COMBAT_LIFEDRAIN, intValue); if(readXMLInteger(configNode, "percentManaDrain", intValue)) voc->increaseAbsorb(COMBAT_MANADRAIN, intValue); if(readXMLInteger(configNode, "percentDrown", intValue)) voc->increaseAbsorb(COMBAT_DROWNDAMAGE, intValue); if(readXMLInteger(configNode, "percentPhysical", intValue)) voc->increaseAbsorb(COMBAT_PHYSICALDAMAGE, intValue); if(readXMLInteger(configNode, "percentHealing", intValue)) voc->increaseAbsorb(COMBAT_HEALING, intValue); if(readXMLInteger(configNode, "percentUndefined", intValue)) voc->increaseAbsorb(COMBAT_UNDEFINEDDAMAGE, intValue); } else if(!xmlStrcmp(configNode->name, (const xmlChar*)"reflect")) { if(readXMLInteger(configNode, "percentAll", intValue)) { for(int32_t i = COMBAT_FIRST; i <= COMBAT_LAST; i++) voc->increaseReflect(REFLECT_PERCENT, (CombatType_t)i, intValue); } if(readXMLInteger(configNode, "percentElements", intValue)) { voc->increaseReflect(REFLECT_PERCENT, COMBAT_ENERGYDAMAGE, intValue); voc->increaseReflect(REFLECT_PERCENT, COMBAT_FIREDAMAGE, intValue); voc->increaseReflect(REFLECT_PERCENT, COMBAT_EARTHDAMAGE, intValue); voc->increaseReflect(REFLECT_PERCENT, COMBAT_ICEDAMAGE, intValue); } if(readXMLInteger(configNode, "percentMagic", intValue)) { voc->increaseReflect(REFLECT_PERCENT, COMBAT_ENERGYDAMAGE, intValue); voc->increaseReflect(REFLECT_PERCENT, COMBAT_FIREDAMAGE, intValue); voc->increaseReflect(REFLECT_PERCENT, COMBAT_EARTHDAMAGE, intValue); voc->increaseReflect(REFLECT_PERCENT, COMBAT_ICEDAMAGE, intValue); voc->increaseReflect(REFLECT_PERCENT, COMBAT_HOLYDAMAGE, intValue); voc->increaseReflect(REFLECT_PERCENT, COMBAT_DEATHDAMAGE, intValue); } if(readXMLInteger(configNode, "percentEnergy", intValue)) voc->increaseReflect(REFLECT_PERCENT, COMBAT_ENERGYDAMAGE, intValue); if(readXMLInteger(configNode, "percentFire", intValue)) voc->increaseReflect(REFLECT_PERCENT, COMBAT_FIREDAMAGE, intValue); if(readXMLInteger(configNode, "percentPoison", intValue) || readXMLInteger(configNode, "percentEarth", intValue)) voc->increaseReflect(REFLECT_PERCENT, COMBAT_EARTHDAMAGE, intValue); if(readXMLInteger(configNode, "percentIce", intValue)) voc->increaseReflect(REFLECT_PERCENT, COMBAT_ICEDAMAGE, intValue); if(readXMLInteger(configNode, "percentHoly", intValue)) voc->increaseReflect(REFLECT_PERCENT, COMBAT_HOLYDAMAGE, intValue); if(readXMLInteger(configNode, "percentDeath", intValue)) voc->increaseReflect(REFLECT_PERCENT, COMBAT_DEATHDAMAGE, intValue); if(readXMLInteger(configNode, "percentLifeDrain", intValue)) voc->increaseReflect(REFLECT_PERCENT, COMBAT_LIFEDRAIN, intValue); if(readXMLInteger(configNode, "percentManaDrain", intValue)) voc->increaseReflect(REFLECT_PERCENT, COMBAT_MANADRAIN, intValue); if(readXMLInteger(configNode, "percentDrown", intValue)) voc->increaseReflect(REFLECT_PERCENT, COMBAT_DROWNDAMAGE, intValue); if(readXMLInteger(configNode, "percentPhysical", intValue)) voc->increaseReflect(REFLECT_PERCENT, COMBAT_PHYSICALDAMAGE, intValue); if(readXMLInteger(configNode, "percentHealing", intValue)) voc->increaseReflect(REFLECT_PERCENT, COMBAT_HEALING, intValue); if(readXMLInteger(configNode, "percentUndefined", intValue)) voc->increaseReflect(REFLECT_PERCENT, COMBAT_UNDEFINEDDAMAGE, intValue); if(readXMLInteger(configNode, "chanceAll", intValue)) { for(int32_t i = COMBAT_FIRST; i <= COMBAT_LAST; i++) voc->increaseReflect(REFLECT_CHANCE, (CombatType_t)i, intValue); } if(readXMLInteger(configNode, "chanceElements", intValue)) { voc->increaseReflect(REFLECT_CHANCE, COMBAT_ENERGYDAMAGE, intValue); voc->increaseReflect(REFLECT_CHANCE, COMBAT_FIREDAMAGE, intValue); voc->increaseReflect(REFLECT_CHANCE, COMBAT_EARTHDAMAGE, intValue); voc->increaseReflect(REFLECT_CHANCE, COMBAT_ICEDAMAGE, intValue); } if(readXMLInteger(configNode, "chanceMagic", intValue)) { voc->increaseReflect(REFLECT_CHANCE, COMBAT_ENERGYDAMAGE, intValue); voc->increaseReflect(REFLECT_CHANCE, COMBAT_FIREDAMAGE, intValue); voc->increaseReflect(REFLECT_CHANCE, COMBAT_EARTHDAMAGE, intValue); voc->increaseReflect(REFLECT_CHANCE, COMBAT_ICEDAMAGE, intValue); voc->increaseReflect(REFLECT_CHANCE, COMBAT_HOLYDAMAGE, intValue); voc->increaseReflect(REFLECT_CHANCE, COMBAT_DEATHDAMAGE, intValue); } if(readXMLInteger(configNode, "chanceEnergy", intValue)) voc->increaseReflect(REFLECT_CHANCE, COMBAT_ENERGYDAMAGE, intValue); if(readXMLInteger(configNode, "chanceFire", intValue)) voc->increaseReflect(REFLECT_CHANCE, COMBAT_FIREDAMAGE, intValue); if(readXMLInteger(configNode, "chancePoison", intValue) || readXMLInteger(configNode, "chanceEarth", intValue)) voc->increaseReflect(REFLECT_CHANCE, COMBAT_EARTHDAMAGE, intValue); if(readXMLInteger(configNode, "chanceIce", intValue)) voc->increaseReflect(REFLECT_CHANCE, COMBAT_ICEDAMAGE, intValue); if(readXMLInteger(configNode, "chanceHoly", intValue)) voc->increaseReflect(REFLECT_CHANCE, COMBAT_HOLYDAMAGE, intValue); if(readXMLInteger(configNode, "chanceDeath", intValue)) voc->increaseReflect(REFLECT_CHANCE, COMBAT_DEATHDAMAGE, intValue); if(readXMLInteger(configNode, "chanceLifeDrain", intValue)) voc->increaseReflect(REFLECT_CHANCE, COMBAT_LIFEDRAIN, intValue); if(readXMLInteger(configNode, "chanceManaDrain", intValue)) voc->increaseReflect(REFLECT_CHANCE, COMBAT_MANADRAIN, intValue); if(readXMLInteger(configNode, "chanceDrown", intValue)) voc->increaseReflect(REFLECT_CHANCE, COMBAT_DROWNDAMAGE, intValue); if(readXMLInteger(configNode, "chancePhysical", intValue)) voc->increaseReflect(REFLECT_CHANCE, COMBAT_PHYSICALDAMAGE, intValue); if(readXMLInteger(configNode, "chanceHealing", intValue)) voc->increaseReflect(REFLECT_CHANCE, COMBAT_HEALING, intValue); if(readXMLInteger(configNode, "chanceUndefined", intValue)) voc->increaseReflect(REFLECT_CHANCE, COMBAT_UNDEFINEDDAMAGE, intValue); } } vocationsMap[voc->getId()] = voc; return true; } bool Vocations::loadFromXml() { xmlDocPtr doc = xmlParseFile(getFilePath(FILE_TYPE_XML,"vocations.xml").c_str()); if(!doc) { std::cout << "[Warning - Vocations::loadFromXml] Cannot load vocations file." << std::endl; std::cout << getLastXMLError() << std::endl; return false; } xmlNodePtr p, root = xmlDocGetRootElement(doc); if(xmlStrcmp(root->name,(const xmlChar*)"vocations")) { std::cout << "[Error - Vocations::loadFromXml] Malformed vocations file." << std::endl; xmlFreeDoc(doc); return false; } for(p = root->children; p; p = p->next) parseVocationNode(p); xmlFreeDoc(doc); return true; } Vocation* Vocations::getVocation(uint32_t vocId) { VocationsMap::iterator it = vocationsMap.find(vocId); if(it != vocationsMap.end()) return it->second; std::cout << "[Warning - Vocations::getVocation] Vocation " << vocId << " not found." << std::endl; return &Vocations::defVoc; } int32_t Vocations::getVocationId(const std::string& name) { for(VocationsMap::iterator it = vocationsMap.begin(); it != vocationsMap.end(); ++it) { if(!strcasecmp(it->second->getName().c_str(), name.c_str())) return it->first; } return -1; } int32_t Vocations::getPromotedVocation(uint32_t vocationId) { for(VocationsMap::iterator it = vocationsMap.begin(); it != vocationsMap.end(); ++it) { if(it->second->getFromVocation() == vocationId && it->first != vocationId) return it->first; } return -1; } Vocation::~Vocation() { cacheMana.clear(); for(int32_t i = SKILL_FIRST; i < SKILL_LAST; ++i) cacheSkill[i].clear(); } void Vocation::reset() { memset(absorb, 0, sizeof(absorb)); memset(reflect[REFLECT_PERCENT], 0, sizeof(reflect[REFLECT_PERCENT])); memset(reflect[REFLECT_CHANCE], 0, sizeof(reflect[REFLECT_CHANCE])); needPremium = false; attackable = true; lessLoss = fromVocation = 0; gain[GAIN_SOUL] = 100; gainTicks[GAIN_SOUL] = 120; baseSpeed = 220; attackSpeed = 1500; name = description = ""; gainAmount[GAIN_HEALTH] = gainAmount[GAIN_MANA] = gainAmount[GAIN_SOUL] = 1; gain[GAIN_HEALTH] = gain[GAIN_MANA] = capGain = 5; gainTicks[GAIN_HEALTH] = gainTicks[GAIN_MANA] = 6; skillBase[SKILL_SHIELD] = 100; skillBase[SKILL_DIST] = 30; skillBase[SKILL_FISH] = 20; for(int32_t i = SKILL_FIST; i < SKILL_DIST; i++) skillBase[i] = 50; skillMultipliers[SKILL_FIST] = 1.5f; skillMultipliers[SKILL_FISH] = 1.1f; skillMultipliers[SKILL__LEVEL] = 1.0f; for(int32_t i = SKILL_CLUB; i < SKILL_FISH; i++) skillMultipliers[i] = 2.0f; formulaMultipliers[MULTIPLIER_MANA] = 4.0f; for(int32_t i = MULTIPLIER_FIRST; i < MULTIPLIER_LAST; i++) formulaMultipliers[i] = 1.0f; } int16_t Vocation::getReflect(CombatType_t combat) const { if(reflect[REFLECT_CHANCE][combat] < random_range(0, 100)) return reflect[REFLECT_PERCENT][combat]; return 0; } uint32_t Vocation::getReqSkillTries(int32_t skill, int32_t level) { if(skill < SKILL_FIRST || skill > SKILL_LAST) return 0; cacheMap& skillMap = cacheSkill[skill]; cacheMap::iterator it = skillMap.find(level); if(it != cacheSkill[skill].end()) return it->second; skillMap[level] = (uint32_t)(skillBase[skill] * std::pow(skillMultipliers[skill], (level - 11))); return skillMap[level]; } uint64_t Vocation::getReqMana(uint32_t magLevel) { cacheMap::iterator it = cacheMana.find(magLevel); if(it != cacheMana.end()) return it->second; cacheMana[magLevel] = (uint64_t)(1600 * std::pow(formulaMultipliers[MULTIPLIER_MANA], (float)(magLevel - 1))); return cacheMana[magLevel]; }