vocation.cpp

main 211 lines · 6.4 KB Raw
Alex Alex Commit Server upload 18/10/2026 23:38
1/**
2 * The Forgotten Server - a free and open-source MMORPG server emulator
3 * Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#include "otpch.h"
21
22#include "vocation.h"
23
24#include "pugicast.h"
25#include "tools.h"
26
27bool Vocations::loadFromXml()
28{
29 pugi::xml_document doc;
30 pugi::xml_parse_result result = doc.load_file("data/XML/vocations.xml");
31 if (!result) {
32 printXMLError("Error - Vocations::loadFromXml", "data/XML/vocations.xml", result);
33 return false;
34 }
35
36 for (auto vocationNode : doc.child("vocations").children()) {
37 pugi::xml_attribute attr;
38 if (!(attr = vocationNode.attribute("id"))) {
39 std::cout << "[Warning - Vocations::loadFromXml] Missing vocation id" << std::endl;
40 continue;
41 }
42
43 uint16_t id = pugi::cast<uint16_t>(attr.value());
44
45 auto res = vocationsMap.emplace(std::piecewise_construct,
46 std::forward_as_tuple(id), std::forward_as_tuple(id));
47 Vocation& voc = res.first->second;
48
49 if ((attr = vocationNode.attribute("name"))) {
50 voc.name = attr.as_string();
51 }
52
53 if ((attr = vocationNode.attribute("clientid"))) {
54 voc.clientId = pugi::cast<uint16_t>(attr.value());
55 }
56
57 if ((attr = vocationNode.attribute("description"))) {
58 voc.description = attr.as_string();
59 }
60
61 if ((attr = vocationNode.attribute("gaincap"))) {
62 voc.gainCap = pugi::cast<uint32_t>(attr.value()) * 100;
63 }
64
65 if ((attr = vocationNode.attribute("gainhp"))) {
66 voc.gainHP = pugi::cast<uint32_t>(attr.value());
67 }
68
69 if ((attr = vocationNode.attribute("gainmana"))) {
70 voc.gainMana = pugi::cast<uint32_t>(attr.value());
71 }
72
73 if ((attr = vocationNode.attribute("gainhpticks"))) {
74 voc.gainHealthTicks = pugi::cast<uint32_t>(attr.value());
75 }
76
77 if ((attr = vocationNode.attribute("gainhpamount"))) {
78 voc.gainHealthAmount = pugi::cast<uint32_t>(attr.value());
79 }
80
81 if ((attr = vocationNode.attribute("gainmanaticks"))) {
82 voc.gainManaTicks = pugi::cast<uint32_t>(attr.value());
83 }
84
85 if ((attr = vocationNode.attribute("gainmanaamount"))) {
86 voc.gainManaAmount = pugi::cast<uint32_t>(attr.value());
87 }
88
89 if ((attr = vocationNode.attribute("manamultiplier"))) {
90 voc.manaMultiplier = pugi::cast<float>(attr.value());
91 }
92
93 if ((attr = vocationNode.attribute("attackspeed"))) {
94 voc.attackSpeed = pugi::cast<uint32_t>(attr.value());
95 }
96
97 if ((attr = vocationNode.attribute("basespeed"))) {
98 voc.baseSpeed = pugi::cast<uint32_t>(attr.value());
99 }
100
101 if ((attr = vocationNode.attribute("soulmax"))) {
102 voc.soulMax = pugi::cast<uint16_t>(attr.value());
103 }
104
105 if ((attr = vocationNode.attribute("gainsoulticks"))) {
106 voc.gainSoulTicks = pugi::cast<uint16_t>(attr.value());
107 }
108
109 if ((attr = vocationNode.attribute("fromvoc"))) {
110 voc.fromVocation = pugi::cast<uint32_t>(attr.value());
111 }
112
113 for (auto childNode : vocationNode.children()) {
114 if (strcasecmp(childNode.name(), "skill") == 0) {
115 pugi::xml_attribute skillIdAttribute = childNode.attribute("id");
116 if (skillIdAttribute) {
117 uint16_t skill_id = pugi::cast<uint16_t>(skillIdAttribute.value());
118 if (skill_id <= SKILL_LAST) {
119 voc.skillMultipliers[skill_id] = pugi::cast<float>(childNode.attribute("multiplier").value());
120 } else {
121 std::cout << "[Notice - Vocations::loadFromXml] No valid skill id: " << skill_id << " for vocation: " << voc.id << std::endl;
122 }
123 } else {
124 std::cout << "[Notice - Vocations::loadFromXml] Missing skill id for vocation: " << voc.id << std::endl;
125 }
126 } else if (strcasecmp(childNode.name(), "formula") == 0) {
127 pugi::xml_attribute meleeDamageAttribute = childNode.attribute("meleeDamage");
128 if (meleeDamageAttribute) {
129 voc.meleeDamageMultiplier = pugi::cast<float>(meleeDamageAttribute.value());
130 }
131
132 pugi::xml_attribute distDamageAttribute = childNode.attribute("distDamage");
133 if (distDamageAttribute) {
134 voc.distDamageMultiplier = pugi::cast<float>(distDamageAttribute.value());
135 }
136
137 pugi::xml_attribute defenseAttribute = childNode.attribute("defense");
138 if (defenseAttribute) {
139 voc.defenseMultiplier = pugi::cast<float>(defenseAttribute.value());
140 }
141
142 pugi::xml_attribute armorAttribute = childNode.attribute("armor");
143 if (armorAttribute) {
144 voc.armorMultiplier = pugi::cast<float>(armorAttribute.value());
145 }
146 }
147 }
148 }
149 return true;
150}
151
152Vocation* Vocations::getVocation(uint16_t id)
153{
154 auto it = vocationsMap.find(id);
155 if (it == vocationsMap.end()) {
156 std::cout << "[Warning - Vocations::getVocation] Vocation " << id << " not found." << std::endl;
157 return nullptr;
158 }
159 return &it->second;
160}
161
162int32_t Vocations::getVocationId(const std::string& name) const
163{
164 for (const auto& it : vocationsMap) {
165 if (strcasecmp(it.second.name.c_str(), name.c_str()) == 0) {
166 return it.first;
167 }
168 }
169 return -1;
170}
171
172uint16_t Vocations::getPromotedVocation(uint16_t vocationId) const
173{
174 for (const auto& it : vocationsMap) {
175 if (it.second.fromVocation == vocationId && it.first != vocationId) {
176 return it.first;
177 }
178 }
179 return VOCATION_NONE;
180}
181
182uint32_t Vocation::skillBase[SKILL_LAST + 1] = {50, 50, 50, 50, 30, 100, 20};
183
184uint64_t Vocation::getReqSkillTries(uint8_t skill, uint16_t level)
185{
186 if (skill > SKILL_LAST) {
187 return 0;
188 }
189
190 auto it = cacheSkill[skill].find(level);
191 if (it != cacheSkill[skill].end()) {
192 return it->second;
193 }
194
195 uint64_t tries = static_cast<uint64_t>(skillBase[skill] * std::pow(static_cast<double>(skillMultipliers[skill]), level - 11));
196 cacheSkill[skill][level] = tries;
197 return tries;
198}
199
200uint64_t Vocation::getReqMana(uint32_t magLevel)
201{
202 auto it = cacheMana.find(magLevel);
203 if (it != cacheMana.end()) {
204 return it->second;
205 }
206
207 uint64_t reqMana = std::floor<uint64_t>(1600 * std::pow<double>(manaMultiplier, static_cast<int32_t>(magLevel) - 1));
208 cacheMana[magLevel] = reqMana;
209 return reqMana;
210}
211
Top