Mana
Loading...
Searching...
No Matches
attributes.cpp
Go to the documentation of this file.
1/*
2 * The Mana Client
3 * Copyright (C) 2010-2013 The Mana Developers
4 *
5 * This file is part of The Mana Client.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
22
23#include "log.h"
24#include "playerinfo.h"
25
26#include "gui/statuswindow.h"
27
28#include "resources/itemdb.h"
29
30#include "utils/gettext.h"
31#include "utils/stringutils.h"
32
33#include <list>
34#include <map>
35
36#define DEFAULT_ATTRIBUTESDB_FILE "attributes.xml"
37#define DEFAULT_POINTS 30
38#define DEFAULT_MIN_PTS 1
39#define DEFAULT_MAX_PTS 9
40
41namespace Attributes {
42
43 struct Attribute
44 {
45 unsigned int id;
46 std::string name;
47 std::string description;
51 std::string scope;
54 };
55
57 static std::map<unsigned int, Attribute> attributes;
58
60 static std::map<std::string, std::string> tags;
61
63 static std::vector<std::string> attributeLabels;
64
66 static unsigned int creationPoints = DEFAULT_POINTS;
67 static unsigned int attributeMinimum = DEFAULT_MIN_PTS;
68 static unsigned int attributeMaximum = DEFAULT_MAX_PTS;
69
70 unsigned int getCreationPoints()
71 {
72 return creationPoints;
73 }
74
75 unsigned int getAttributeMinimum()
76 {
77 return attributeMinimum;
78 }
79
80 unsigned int getAttributeMaximum()
81 {
82 return attributeMaximum;
83 }
84
85 std::vector<std::string>& getLabels()
86 {
87 return attributeLabels;
88 }
89
93 static void fillLabels()
94 {
95 // Fill up the modifiable attribute label list.
96 attributeLabels.clear();
97 for (const auto &[_, attribute] : attributes)
98 {
99 if (attribute.modifiable &&
100 (attribute.scope == "character" || attribute.scope == "being"))
101 attributeLabels.push_back(attribute.name + ":");
102 }
103 }
104
108 static int getPlayerInfoIdFromAttrType(std::string attrType)
109 {
110 toLower(attrType);
111 if (attrType == "level")
112 return ::LEVEL;
113 else if (attrType == "hp")
114 return ::HP;
115 else if (attrType == "max-hp")
116 return ::MAX_HP;
117 else if (attrType == "mp")
118 return ::MP;
119 else if (attrType == "max-mp")
120 return ::MAX_MP;
121 else if (attrType == "exp")
122 return ::EXP;
123 else if (attrType == "exp-needed")
124 return ::EXP_NEEDED;
125 else if (attrType == "money")
126 return ::MONEY;
127 else if (attrType == "total-weight")
128 return ::TOTAL_WEIGHT;
129 else if (attrType == "max-weight")
130 return ::MAX_WEIGHT;
131 else if (attrType == "skill-points")
132 return ::SKILL_POINTS;
133 else if (attrType == "char-points")
134 return ::CHAR_POINTS;
135 else if (attrType == "corr-points")
136 return ::CORR_POINTS;
137 else if (attrType == "none")
138 return -2; // Used to hide the attribute display.
139
140 return -1; // Not linked to a playerinfo stat.
141 }
142
144 {
145 auto it = attributes.find(attrId);
146 if (it != attributes.end())
147 {
148 return it->second.playerInfoId;
149 }
150
151 return -1;
152 }
153
154 static void loadBuiltins()
155 {
156 {
157 Attribute a;
158 a.id = 16;
159 a.name = _("Strength");
160 a.modifiable = true;
161 a.scope = "character";
162 a.playerInfoId = -1;
163
164 attributes[a.id] = a;
165 tags.insert(std::make_pair("str", _("Strength %+.1f")));
166 }
167
168 {
169 Attribute a;
170 a.id = 17;
171 a.name = _("Agility");
172 a.modifiable = true;
173 a.scope = "character";
174 a.playerInfoId = -1;
175
176 attributes[a.id] = a;
177 tags.insert(std::make_pair("agi", _("Agility %+.1f")));
178 }
179
180 {
181 Attribute a;
182 a.id = 18;
183 a.name = _("Dexterity");
184 a.modifiable = true;
185 a.scope = "character";
186 a.playerInfoId = -1;
187
188 attributes[a.id] = a;
189 tags.insert(std::make_pair("dex", _("Dexterity %+.1f")));
190 }
191
192 {
193 Attribute a;
194 a.id = 19;
195 a.name = _("Vitality");
196 a.modifiable = true;
197 a.scope = "character";
198 a.playerInfoId = -1;
199
200 attributes[a.id] = a;
201 tags.insert(std::make_pair("vit", _("Vitality %+.1f")));
202 }
203
204 {
205 Attribute a;
206 a.id = 20;
207 a.name = _("Intelligence");
208 a.modifiable = true;
209 a.scope = "character";
210 a.playerInfoId = -1;
211
212 attributes[a.id] = a;
213 tags.insert(std::make_pair("int", _("Intelligence %+.1f")));
214 }
215
216 {
217 Attribute a;
218 a.id = 21;
219 a.name = _("Willpower");
220 a.modifiable = true;
221 a.scope = "character";
222 a.playerInfoId = -1;
223
224 attributes[a.id] = a;
225 tags.insert(std::make_pair("wil", _("Willpower %+.1f")));
226 }
227 }
228
229 void init()
230 {
231 if (!attributes.empty())
232 unload();
233 }
234
238 void readAttributeNode(XML::Node node, const std::string &filename)
239 {
240 int id = node.getProperty("id", 0);
241 if (!id)
242 {
243 Log::info("Attributes: Invalid or missing stat ID in "
245 return;
246 }
247
248 if (attributes.find(id) != attributes.end())
249 {
250 Log::info("Attributes: Redefinition of stat ID %d", id);
251 }
252
253 std::string name = node.getProperty("name", "");
254
255 if (name.empty())
256 {
257 Log::info("Attributes: Invalid or missing stat name in "
259 return;
260 }
261
262 // Create the attribute.
263 Attribute &a = attributes[id];
264 a.id = id;
265 a.name = name;
266 a.description = node.getProperty("desc", std::string());
267 a.modifiable = node.getBoolProperty("modifiable", false);
268 a.scope = node.getProperty("scope", "none");
269 a.playerInfoId = getPlayerInfoIdFromAttrType(
270 node.getProperty("player-info", ""));
271
272 unsigned int count = 0;
273 for (auto effectNode : node.children())
274 {
275 if (effectNode.name() != "modifier")
276 continue;
277 ++count;
278 std::string tag = effectNode.getProperty("tag", "");
279 if (tag.empty())
280 {
281 if (name.empty())
282 {
283 Log::info("Attribute modifier in attribute %u:%s: "
284 "Empty name definition "
285 "on empty tag definition, skipping.",
286 a.id, a.name.c_str());
287 --count;
288 continue;
289 }
290 tag = name.substr(0, name.size() > 3 ? 3 : name.size());
291 tag = toLower(tag) + toString(count);
292 }
293
294 std::string effect = effectNode.getProperty("effect", "");
295 if (effect.empty())
296 {
297 if (name.empty())
298 {
299 Log::info("Attribute modifier in attribute %u:%s: "
300 "Empty name definition "
301 "on empty effect definition, skipping.",
302 a.id, a.name.c_str());
303 --count;
304 continue;
305 }
306 else
307 effect = name + " %+f";
308 }
309 tags.insert(std::make_pair(tag, effect));
310 }
311 Log::info("Found %d tags for attribute %d.", count, id);
312 }
313
317 void readPointsNode(XML::Node node, const std::string &filename)
318 {
319 creationPoints = node.getProperty("start",DEFAULT_POINTS);
320 attributeMinimum = node.getProperty("minimum",
322 attributeMaximum = node.getProperty("maximum",
324 Log::info("Loaded points: start: %i, min: %i, max: %i.",
325 creationPoints, attributeMinimum, attributeMaximum);
326 }
327
332 {
333 Log::info("Found %d tags for %d attributes.", int(tags.size()),
334 int(attributes.size()));
335
336 if (attributes.size() == 0)
337 {
338 loadBuiltins();
339 }
340
341 fillLabels();
342
343 // Sanity checks on starting points
344 auto modifiableAttributeCount = (float) attributeLabels.size();
345 float averageValue = ((float) creationPoints) / modifiableAttributeCount;
346 if (averageValue > attributeMaximum || averageValue < attributeMinimum
347 || creationPoints < 1)
348 {
349 Log::info("Attributes: Character's point values make "
350 "the character's creation impossible. "
351 "Switch back to defaults.");
352 creationPoints = DEFAULT_POINTS;
353 attributeMinimum = DEFAULT_MIN_PTS;
354 attributeMaximum = DEFAULT_MAX_PTS;
355 }
356 }
357
358 void unload()
359 {
360 attributes.clear();
361 }
362
364 {
365 std::list<ItemStat> dbStats;
366
367 for (const auto &[tag, format] : tags)
368 dbStats.emplace_back(tag, format);
369
370 setStatsList(std::move(dbStats));
371 }
372
374 {
375 for (const auto &[_, attribute] : attributes)
376 {
377 if (attribute.playerInfoId == -1 &&
378 (attribute.scope == "character" || attribute.scope == "being"))
379 {
380 statusWindow->addAttribute(attribute.id,
381 attribute.name,
382 attribute.modifiable,
383 attribute.description);
384 }
385 }
386 }
387
388} // namespace Attributes
#define DEFAULT_POINTS
#define DEFAULT_MAX_PTS
#define DEFAULT_MIN_PTS
#define DEFAULT_ATTRIBUTESDB_FILE
void addAttribute(int id, const std::string &name, bool modifiable, const std::string &description)
int getProperty(const char *name, int def) const
Definition xml.h:144
Children children() const
Definition xml.h:97
bool getBoolProperty(const char *name, bool def) const
Definition xml.h:168
StatusWindow * statusWindow
Definition game.cpp:94
#define _(s)
Definition gettext.h:38
void setStatsList(std::list< ItemStat > stats)
Definition itemdb.cpp:40
void informItemDB()
void readPointsNode(XML::Node node, const std::string &filename)
Read points node.
unsigned int getAttributeMaximum()
Give the maximum attribute point possible at character's creation.
void informStatusWindow()
std::vector< std::string > & getLabels()
Returns the list of base attribute labels.
void checkStatus()
Check if all the data loaded by readPointsNode and readAttributeNode is ok.
unsigned int getCreationPoints()
Give the attribute points given to a character at its creation.
int getPlayerInfoIdFromAttrId(int attrId)
Give back the corresponding playerinfo Id from the attribute id defined in the xml file.
void unload()
void readAttributeNode(XML::Node node, const std::string &filename)
Read attribute node.
unsigned int getAttributeMinimum()
Give the minimum attribute point possible at character's creation.
void info(const char *log_text,...) LOG_PRINTF_ATTR
Attribute
Standard attributes for players.
Definition playerinfo.h:29
std::string & toLower(std::string &str)
Converts the given string to lower case.
std::string toString(const T &arg)
Converts the given value to a string using std::stringstream.
Definition stringutils.h:68
std::string description
int playerInfoId
The playerInfo core Id the attribute is linked with or -1 if not.
bool modifiable
Whether the attribute value can be modified by the player.