Mana
Loading...
Searching...
No Matches
itemdb.cpp
Go to the documentation of this file.
1/*
2 * The Mana Client
3 * Copyright (C) 2004-2009 The Mana World Development Team
4 * Copyright (C) 2009-2012 The Mana Developers
5 *
6 * This file is part of The Mana Client.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22#include "resources/itemdb.h"
23
24#include "configuration.h"
25#include "log.h"
26
27#include "resources/hairdb.h"
28#include "resources/iteminfo.h"
29
30#include "utils/dtor.h"
31#include "utils/gettext.h"
32#include "utils/stringutils.h"
33
34#include "net/tmwa/protocol.h"
35
36#include <algorithm>
37#include <cassert>
38#include <string_view>
39
40void setStatsList(std::list<ItemStat> stats)
41{
42 extraStats = std::move(stats);
43}
44
45static ItemType itemTypeFromString(const std::string &name, int id = 0)
46{
47 if (name == "generic") return ITEM_UNUSABLE;
48 if (name == "usable") return ITEM_USABLE;
49 if (name == "equip-1hand") return ITEM_EQUIPMENT_ONE_HAND_WEAPON;
50 if (name == "equip-2hand") return ITEM_EQUIPMENT_TWO_HANDS_WEAPON;
51 if (name == "equip-torso") return ITEM_EQUIPMENT_TORSO;
52 if (name == "equip-arms") return ITEM_EQUIPMENT_ARMS;
53 if (name == "equip-head") return ITEM_EQUIPMENT_HEAD;
54 if (name == "equip-legs") return ITEM_EQUIPMENT_LEGS;
55 if (name == "equip-shield") return ITEM_EQUIPMENT_SHIELD;
56 if (name == "equip-ring") return ITEM_EQUIPMENT_RING;
57 if (name == "equip-charm") return ITEM_EQUIPMENT_CHARM;
58 if (name == "equip-necklace") return ITEM_EQUIPMENT_NECKLACE;
59 if (name == "equip-feet") return ITEM_EQUIPMENT_FEET;
60 if (name == "equip-ammo") return ITEM_EQUIPMENT_AMMO;
61 if (name == "racesprite") return ITEM_SPRITE_RACE;
62 if (name == "hairsprite") return ITEM_SPRITE_HAIR;
63 return ITEM_UNUSABLE;
64}
65
66static uint8_t spriteFromString(std::string_view name)
67{
68 if (name.empty())
69 return SPRITE_ALL;
70 if (name == "race" || name == "type")
72 if (name == "shoes" || name == "boot" || name == "boots")
74 if (name == "bottomclothes" || name == "bottom" || name == "pants")
76 if (name == "topclothes" || name == "top" || name == "torso" || name == "body")
78 if (name == "misc1")
80 if (name == "misc2" || name == "scarf" || name == "scarfs")
82 if (name == "hair")
84 if (name == "hat" || name == "hats")
86 if (name == "wings")
88 if (name == "glove" || name == "gloves")
90 if (name == "weapon" || name == "weapons")
92 if (name == "shield" || name == "shields")
94 if (name == "amulet" || name == "amulets")
95 return 12;
96 if (name == "ring" || name == "rings")
97 return 13;
98
99 return SPRITE_UNKNOWN;
100}
101
102static uint8_t directionFromString(std::string_view name)
103{
104 if (name.empty())
105 return DIRECTION_ALL;
106 if (name == "down" || name == "downall")
107 return DIRECTION_DOWN;
108 if (name == "left")
109 return DIRECTION_LEFT;
110 if (name == "up" || name == "upall")
111 return DIRECTION_UP;
112 if (name == "right")
113 return DIRECTION_RIGHT;
114
115 // hack for died action.
116 if (name == "died")
117 return DIRECTION_DEAD;
118
119 return DIRECTION_UNKNOWN;
120}
121
123{
124 mUnknown->name = _("Unknown item");
126 std::string errFile = paths.getStringValue("spriteErrorFile");
127 mUnknown->setSprite(errFile, Gender::Male, 0);
128 mUnknown->setSprite(errFile, Gender::Female, 0);
129 mUnknown->setSprite(errFile, Gender::Neutral, 0);
130 mUnknown->hitEffectId = paths.getIntValue("hitEffectId");
131 mUnknown->criticalHitEffectId = paths.getIntValue("criticalHitEffectId");
132}
133
134/*
135 * Common itemDB functions
136 */
137
138bool ItemDB::exists(int id) const
139{
140 assert(mLoaded);
141
142 return mItemInfos.find(id) != mItemInfos.end();
143}
144
145const ItemInfo &ItemDB::get(int id) const
146{
147 assert(mLoaded);
148
149 auto i = mItemInfos.find(id);
150 if (i == mItemInfos.end())
151 {
152 Log::info("ItemDB: Warning, unknown item ID# %d", id);
153 return *mUnknown;
154 }
155
156 return *(i->second);
157}
158
159const ItemInfo &ItemDB::get(const std::string &name) const
160{
161 assert(mLoaded);
162
163 auto i = mNamedItemInfos.find(normalize(name));
164 if (i == mNamedItemInfos.end())
165 {
166 if (!name.empty())
167 {
168 Log::info("ItemDB: Warning, unknown item name \"%s\"",
169 name.c_str());
170 }
171 return *mUnknown;
172 }
173
174 return *(i->second);
175}
176
178{
179 std::string gender = node.getProperty("gender", "unisex");
180 std::string filename { node.textContent() };
181
182 const int race = node.getProperty("race", 0);
183 if (gender == "male" || gender == "unisex")
184 itemInfo.setSprite(filename, Gender::Male, race);
185 if (gender == "female" || gender == "unisex")
186 itemInfo.setSprite(filename, Gender::Female, race);
187 if (gender == "hidden" || gender == "other" || gender == "unisex")
188 itemInfo.setSprite(filename, Gender::Neutral, race);
189}
190
192{
193 std::string event = node.getProperty("event", std::string());
194 std::string filename { node.textContent() };
195
196 if (event == "hit")
197 {
198 itemInfo.addSound(EquipmentSoundEvent::Hit, filename);
199 }
200 else if (event == "strike" || event == "miss")
201 {
202 itemInfo.addSound(EquipmentSoundEvent::Strike, filename);
203 }
204 else
205 {
206 Log::info("ItemDB: Ignoring unknown sound event '%s'",
207 event.c_str());
208 }
209}
210
212{
213 for (auto spriteNode : floorNode.children())
214 {
215 if (spriteNode.name() == "sprite")
216 {
217 SpriteReference &currentSprite = display.sprites.emplace_back();
218 currentSprite.sprite = spriteNode.textContent();
219 currentSprite.variant = spriteNode.getProperty("variant", 0);
220 }
221 else if (spriteNode.name() == "particlefx")
222 {
223 display.particles.emplace_back(spriteNode.textContent());
224 }
225 }
226}
227
229{
230 std::string_view spriteString;
231 std::string_view directionString;
232
233 replaceNode.attribute("sprite", spriteString);
234 replaceNode.attribute("direction", directionString);
235
236 const uint8_t sprite = spriteFromString(spriteString);
237 const uint8_t direction = directionFromString(directionString);
238
239 if (sprite == SPRITE_UNKNOWN)
240 {
241 Log::info("ItemDB: Invalid sprite name '%s' in replace tag",
242 spriteString.data());
243 return;
244 }
245
246 if (direction == DIRECTION_UNKNOWN)
247 {
248 Log::info("ItemDB: Invalid direction name '%s' in replace tag",
249 directionString.data());
250 return;
251 }
252
253 Replacement &replace = info.replacements.emplace_back();
254 replace.sprite = sprite;
255 replace.direction = direction;
256
257 for (auto child : replaceNode.children())
258 {
259 if (child.name() == "item")
260 {
261 Replacement::Item &item = replace.items.emplace_back();
262 child.attribute("from", item.from);
263 child.attribute("to", item.to);
264 }
265 }
266}
267
269{
270 Log::info("Unloading item database...");
271
272 delete mUnknown;
273 mUnknown = nullptr;
274
276 mItemInfos.clear();
277 mNamedItemInfos.clear();
278 mLoaded = false;
279}
280
281void ItemDB::loadCommonRef(ItemInfo &itemInfo, XML::Node node, const std::string &filename)
282{
283 itemInfo.id = node.getProperty("id", 0);
284
285 if (!itemInfo.id)
286 {
287 Log::info("ItemDB: Invalid or missing item Id in %s!", filename.c_str());
288 return;
289 }
290 else if (mItemInfos.find(itemInfo.id) != mItemInfos.end())
291 {
292 Log::info("ItemDB: Redefinition of item Id %d in %s", itemInfo.id, filename.c_str());
293 }
294
295 itemInfo.mView = node.getProperty("view", 0);
296 itemInfo.name = node.getProperty("name", std::string());
297 itemInfo.display.image = node.getProperty("image", std::string());
298 itemInfo.description = node.getProperty("description", std::string());
299 itemInfo.attackAction = node.getProperty("attack-action", SpriteAction::INVALID);
300 itemInfo.attackRange = node.getProperty("attack-range", 0);
301 itemInfo.missileParticleFile = node.getProperty("missile-particle", std::string());
302 itemInfo.hitEffectId = node.getProperty("hit-effect-id",
303 paths.getIntValue("hitEffectId"));
304 itemInfo.criticalHitEffectId = node.getProperty("critical-hit-effect-id",
305 paths.getIntValue("criticalHitEffectId"));
306
307 // Load Ta Item Type
308 std::string typeStr = node.getProperty("type", "other");
309 itemInfo.type = itemTypeFromString(typeStr);
310 itemInfo.weight = node.getProperty("weight", 0);
311
312 for (auto itemChild : node.children())
313 {
314 if (itemChild.name() == "sprite")
315 {
316 loadSpriteRef(itemInfo, itemChild);
317 }
318 else if (itemChild.name() == "particlefx")
319 {
320 itemInfo.display.particles.emplace_back(itemChild.textContent());
321 }
322 else if (itemChild.name() == "sound")
323 {
324 loadSoundRef(itemInfo, itemChild);
325 }
326 else if (itemChild.name() == "floor")
327 {
328 loadFloorSprite(itemInfo.display, itemChild);
329 }
330 else if (itemChild.name() == "replace")
331 {
332 loadReplacement(itemInfo, itemChild);
333 }
334 }
335}
336
338{
339 std::string itemName = itemInfo->name;
340 itemInfo->name = itemName.empty() ? _("unnamed") : itemName;
341 mItemInfos[itemInfo->id] = itemInfo;
342 if (!itemName.empty())
343 {
344 std::string temp = normalize(itemName);
345
346 auto itr = mNamedItemInfos.find(temp);
347 if (itr == mNamedItemInfos.end())
348 mNamedItemInfos[temp] = itemInfo;
349 else
350 Log::info("ItemDB: Duplicate name (%s) for item id %d found.",
351 temp.c_str(), itemInfo->id);
352 }
353}
354
355template <class T>
356static void checkParameter(int id, const T param, const T errorValue)
357{
358 if (param == errorValue)
359 {
360 std::stringstream errMsg;
361 errMsg << "ItemDB: Missing " << param << " attribute for item id "
362 << id << "!";
363 Log::info("%s", errMsg.str().c_str());
364 }
365}
366
368{
369 int id = itemInfo.id;
370 if (!itemInfo.attackAction.empty())
371 if (itemInfo.attackRange == 0)
372 Log::info("ItemDB: Missing attack range from weapon %i!", id);
373
374 if (id >= 0)
375 {
376 checkParameter(id, itemInfo.name, std::string());
377 checkParameter(id, itemInfo.description, std::string());
378 checkParameter(id, itemInfo.display.image, std::string());
379 checkParameter(id, itemInfo.weight, 0);
380 }
381}
382
383namespace TmwAthena {
384
385// Description fields used by TaItemDB *itemInfo->mEffect.
386
387static char const *const fields[][2] =
388{
389 { "attack", N_("Attack %+d") },
390 { "defense", N_("Defense %+d") },
391 { "hp", N_("HP %+d") },
392 { "mp", N_("MP %+d") }
393};
394
396{
397 if (mLoaded)
398 unload();
399}
400
401void TaItemDB::readItemNode(XML::Node node, const std::string &filename)
402{
403 auto *itemInfo = new ItemInfo;
404
405 loadCommonRef(*itemInfo, node, filename);
406
407 // Everything not unusable or usable is equippable by the Ta type system.
408 itemInfo->equippable = itemInfo->type != ITEM_UNUSABLE
409 && itemInfo->type != ITEM_USABLE;
410 itemInfo->activatable = itemInfo->type == ITEM_USABLE;
411
412 // Load nano description
413 std::vector<std::string> effect;
414 for (auto field : fields)
415 {
416 int value = node.getProperty(field[0], 0);
417 if (!value)
418 continue;
419 effect.push_back(strprintf(gettext(field[1]), value));
420 }
421 for (auto &extraStat : extraStats)
422 {
423 int value = node.getProperty(extraStat.mTag.c_str(), 0);
424 if (!value)
425 continue;
426 effect.push_back(strprintf(extraStat.mFormat.c_str(), value));
427 }
428 std::string temp = node.getProperty("effect", std::string());
429 if (!temp.empty())
430 effect.push_back(temp);
431
432 itemInfo->effect = effect;
433
434 checkItemInfo(*itemInfo);
435
436 addItem(itemInfo);
437
438 // Insert hairstyle id while letting the info as an item.
439 if (itemInfo->type == ITEM_SPRITE_HAIR)
440 hairDB.addHairStyle(itemInfo->id);
441}
442
444{
445 mUnknown = new ItemInfo;
447
449
450 mLoaded = true;
451}
452
454{
455 ItemDB::checkItemInfo(itemInfo);
456
457 // Check for unusable items?
458 //checkParameter(id, itemInfo->mType, 0);
459}
460
461}; // namespace TmwAthena
462
463namespace ManaServ {
464
466{
467 if (mLoaded)
468 unload();
469}
470
471void ManaServItemDB::readItemNode(XML::Node node, const std::string &filename)
472{
473 // Trigger table for effect descriptions
474 // FIXME: This should ideally be softcoded via XML or similar.
475 static const std::map<std::string, const char* > triggerTable = {
476 { "existence", " when it is in the inventory" },
477 { "activation", " upon activation" },
478 { "equip", " upon successful equip" },
479 { "leave-inventory", " when it leaves the inventory" },
480 { "unequip", " when it is unequipped" },
481 { "equip-change", " when it changes the way it is equipped" },
482 };
483
484 auto *itemInfo = new ItemInfo;
485
486 loadCommonRef(*itemInfo, node, filename);
487
488 // We default eqippable and activatable to false as their actual value will be set
489 // within the <equip> and <effect> sub-nodes..
490 itemInfo->activatable = false;
491 itemInfo->equippable = false;
492
493 // Load <equip>, and <effect> sub nodes.
494 std::vector<std::string> effect;
495 for (auto itemChild : node.children())
496 {
497 if (itemChild.name() == "equip")
498 {
499 // The fact that there is a way to equip is enough.
500 // Discard any details, but mark the item as equippable.
501 itemInfo->equippable = true;
502 }
503 else if (itemChild.name() == "effect")
504 {
505 std::string trigger = itemChild.getProperty("trigger", std::string());
506 if (trigger.empty())
507 {
508 Log::info("Found empty trigger effect label in %s, skipping.", filename.c_str());
509 continue;
510 }
511
512 if (trigger == "activation")
513 itemInfo->activatable = true;
514
515 auto triggerLabel = triggerTable.find(trigger);
516 if (triggerLabel == triggerTable.end())
517 {
518 Log::warn("Unknown trigger %s in item %d!",
519 trigger.c_str(), itemInfo->id);
520 continue;
521 }
522
523 for (auto effectChild : itemChild.children())
524 {
525 if (effectChild.name() == "modifier")
526 {
527 std::string attribute = effectChild.getProperty("attribute", std::string());
528 double value = effectChild.getFloatProperty("value", 0.0);
529 int duration = effectChild.getProperty("duration", 0);
530 if (attribute.empty() || !value)
531 {
532 Log::warn("Incomplete modifier definition in %s, skipping.", filename.c_str());
533 continue;
534 }
535 auto it = std::find(extraStats.cbegin(), extraStats.cend(), attribute);
536 if (it == extraStats.end())
537 {
538 Log::warn("Unknown modifier tag %s in %s, skipping.", attribute.c_str(), filename.c_str());
539 continue;
540 }
541 effect.push_back(
543 duration ?
544 strprintf("%%s%%s. This effect lasts %d ticks.", duration).c_str()
545 : "%s%s.", it->mFormat.c_str(), triggerLabel->second).c_str(), value));
546 }
547 else if (effectChild.name() == "modifier")
548 effect.push_back(strprintf("Provides an autoattack%s.",
549 triggerLabel->second));
550 else if (effectChild.name() == "consumes")
551 effect.push_back(strprintf("This will be consumed%s.",
552 triggerLabel->second));
553 else if (effectChild.name() == "label")
554 effect.emplace_back(effectChild.textContent());
555 }
556 }
557
558 // FIXME: Load hair styles through the races.xml file
559 if (itemInfo->type == ITEM_SPRITE_HAIR)
560 hairDB.addHairStyle(itemInfo->id);
561
562 // Set Item Type based on subnodes info
563 // TODO: Improve it once the itemTypes are loaded through xml
564 itemInfo->type = ITEM_UNUSABLE;
565 if (itemInfo->activatable)
566 itemInfo->type = ITEM_USABLE;
567 else if (itemInfo->equippable)
568 itemInfo->type = ITEM_EQUIPMENT_TORSO;
569 } // end for (auto itemChild : node.children())
570
571 itemInfo->effect = effect;
572
573 checkItemInfo(*itemInfo);
574
575 addItem(itemInfo);
576}
577
579{
580 mUnknown = new ItemInfo;
582
583 mLoaded = true;
584}
585
587{
588 ItemDB::checkItemInfo(itemInfo);
589
590 // Add specific Manaserv checks here
591}
592
593} // namespace ManaServ
std::string getStringValue(const std::string &key) const
int getIntValue(const std::string &key) const
returns a value corresponding to the given key.
void addHairStyle(int id)
Add a hair style to the database.
Definition hairdb.cpp:66
void addItem(ItemInfo *itemInfo)
Registers the item to mItemInfos and mNamedItemsInfos.
Definition itemdb.cpp:337
void loadSoundRef(ItemInfo &itemInfo, XML::Node node)
Loads the sound references contained in a <sound> tag.
Definition itemdb.cpp:191
virtual void unload()
Frees item data.
Definition itemdb.cpp:268
std::map< std::string, ItemInfo * > mNamedItemInfos
Definition itemdb.h:150
void loadReplacement(ItemInfo &info, XML::Node replaceNode)
Loads the <replace> tag.
Definition itemdb.cpp:228
void loadCommonRef(ItemInfo &itemInfo, XML::Node node, const std::string &filename)
Permits to load item definitions which are common for each protocols to avoid code duplication.
Definition itemdb.cpp:281
bool exists(int id) const
Definition itemdb.cpp:138
std::map< int, ItemInfo * > mItemInfos
Definition itemdb.h:149
void loadSpriteRef(ItemInfo &itemInfo, XML::Node node)
Loads the sprite references contained in a <sprite> tag.
Definition itemdb.cpp:177
const ItemInfo & get(int id) const
Definition itemdb.cpp:145
void loadFloorSprite(SpriteDisplay &display, XML::Node node)
Loads the floor item references contained in a <floor> tag.
Definition itemdb.cpp:211
ItemInfo * mUnknown
Definition itemdb.h:123
void loadEmptyItemDefinition()
Loads the empty item definition.
Definition itemdb.cpp:122
virtual void checkItemInfo(ItemInfo &itemInfo)
Checks the items parameters consistency.
Definition itemdb.cpp:367
bool mLoaded
Definition itemdb.h:125
Defines a class for storing generic item infos.
Definition iteminfo.h:100
int attackRange
Attack range, will be equal to ATTACK_RANGE_NOT_SET if no weapon.
Definition iteminfo.h:131
std::string description
Short description.
Definition iteminfo.h:115
std::string name
Definition iteminfo.h:113
int hitEffectId
Definition iteminfo.h:121
std::string attackAction
Attack type, in case of weapon.
Definition iteminfo.h:128
std::string missileParticleFile
Effects to be shown when weapon attacks - see also effects.xml.
Definition iteminfo.h:120
SpriteDisplay display
Display info (like icon)
Definition iteminfo.h:114
int mView
Item ID of how this item looks.
Definition iteminfo.h:147
void setSprite(const std::string &animationFile, Gender gender, int race)
Definition iteminfo.cpp:57
int weight
Weight in grams.
Definition iteminfo.h:117
ItemType type
Item type.
Definition iteminfo.h:136
int id
Item ID.
Definition iteminfo.h:112
void addSound(EquipmentSoundEvent event, const std::string &filename)
Definition iteminfo.cpp:45
int criticalHitEffectId
Definition iteminfo.h:122
void readItemNode(XML::Node node, const std::string &filename) override
Definition itemdb.cpp:471
void init() override
Definition itemdb.cpp:465
void checkStatus() override
Definition itemdb.cpp:578
void checkItemInfo(ItemInfo &itemInfo) override
Checks the items parameters consistency.
Definition itemdb.cpp:586
void init() override
Definition itemdb.cpp:395
void checkStatus() override
Definition itemdb.cpp:443
void readItemNode(XML::Node node, const std::string &filename) override
Definition itemdb.cpp:401
void checkHairWeaponsRacesSpecialIds()
Check items id specific hard limits and log errors found.
Definition itemdb.h:177
void checkItemInfo(ItemInfo &itemInfo) override
Checks the items parameters consistency.
Definition itemdb.cpp:453
int getProperty(const char *name, int def) const
Definition xml.h:144
Children children() const
Definition xml.h:97
std::string_view textContent() const
Definition xml.h:105
bool attribute(const char *name, T &value) const
Definition xml.h:129
HairDB hairDB
Hair styles and colors info database.
Definition client.cpp:107
Configuration paths
XML default paths information reader.
Definition client.cpp:99
void delete_all(Container &c)
Definition dtor.h:46
#define gettext(s)
Definition gettext.h:37
#define N_(s)
Definition gettext.h:39
#define _(s)
Definition gettext.h:38
void setStatsList(std::list< ItemStat > stats)
Definition itemdb.cpp:40
ItemType
Enumeration of available Item types.
Definition iteminfo.h:43
@ ITEM_EQUIPMENT_LEGS
Definition iteminfo.h:51
@ ITEM_EQUIPMENT_ARMS
Definition iteminfo.h:49
@ ITEM_EQUIPMENT_RING
Definition iteminfo.h:53
@ ITEM_EQUIPMENT_NECKLACE
Definition iteminfo.h:54
@ ITEM_SPRITE_HAIR
Definition iteminfo.h:59
@ ITEM_EQUIPMENT_ONE_HAND_WEAPON
Definition iteminfo.h:46
@ ITEM_EQUIPMENT_TORSO
Definition iteminfo.h:48
@ ITEM_EQUIPMENT_CHARM
Definition iteminfo.h:57
@ ITEM_EQUIPMENT_HEAD
Definition iteminfo.h:50
@ ITEM_SPRITE_RACE
Definition iteminfo.h:58
@ ITEM_EQUIPMENT_AMMO
Definition iteminfo.h:56
@ ITEM_UNUSABLE
Definition iteminfo.h:44
@ ITEM_EQUIPMENT_TWO_HANDS_WEAPON
Definition iteminfo.h:47
@ ITEM_EQUIPMENT_FEET
Definition iteminfo.h:55
@ ITEM_EQUIPMENT_SHIELD
Definition iteminfo.h:52
@ ITEM_USABLE
Definition iteminfo.h:45
@ SPRITE_UNKNOWN
Definition iteminfo.h:79
@ SPRITE_ALL
Definition iteminfo.h:80
@ DIRECTION_DEAD
Definition iteminfo.h:73
@ DIRECTION_UNKNOWN
Definition iteminfo.h:74
@ DIRECTION_ALL
Definition iteminfo.h:72
void warn(const char *log_text,...) LOG_PRINTF_ATTR
void info(const char *log_text,...) LOG_PRINTF_ATTR
Warning: buffers and other variables are shared, so there can be only one connection active at a time...
@ SPRITE_GLOVES
Definition protocol.h:60
@ SPRITE_BASE
Definition protocol.h:51
@ SPRITE_BOTTOMCLOTHES
Definition protocol.h:53
@ SPRITE_WEAPON
Definition protocol.h:61
@ SPRITE_MISC1
Definition protocol.h:55
@ SPRITE_SHOE
Definition protocol.h:52
@ SPRITE_HAT
Definition protocol.h:58
@ SPRITE_HAIR
Definition protocol.h:57
@ SPRITE_MISC2
Definition protocol.h:56
@ SPRITE_SHIELD
Definition protocol.h:62
@ SPRITE_CAPE
Definition protocol.h:59
@ SPRITE_TOPCLOTHES
Definition protocol.h:54
unsigned char uint8_t
Definition sha256.cpp:81
@ DIRECTION_DOWN
Definition spritedef.h:77
@ DIRECTION_LEFT
Definition spritedef.h:78
@ DIRECTION_UP
Definition spritedef.h:76
@ DIRECTION_RIGHT
Definition spritedef.h:79
std::string strprintf(char const *format,...)
A safe version of sprintf that returns a std::string of the result.
std::string normalize(const std::string &name)
Normalize a string, which means lowercase and trim it.
uint8_t direction
Definition iteminfo.h:92
uint8_t sprite
Definition iteminfo.h:91
std::vector< Item > items
Definition iteminfo.h:93
std::string image
Definition spritedef.h:43
std::vector< SpriteReference > sprites
Definition spritedef.h:44
std::vector< std::string > particles
Definition spritedef.h:45
std::string sprite
Definition spritedef.h:37