ManaPlus
itemdb.cpp
Go to the documentation of this file.
1 /*
2  * The ManaPlus Client
3  * Copyright (C) 2004-2009 The Mana World Development Team
4  * Copyright (C) 2009-2010 The Mana Developers
5  * Copyright (C) 2011-2018 The ManaPlus Developers
6  *
7  * This file is part of The ManaPlus Client.
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program. If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 #include "resources/db/itemdb.h"
24 
26 
27 #include "configuration.h"
28 
30 
31 #include "fs/virtfs/tools.h"
32 
33 #include "resources/iteminfo.h"
34 #include "resources/itemmenuitem.h"
36 
38 
40 
41 #ifdef TMWA_SUPPORT
42 #include "net/net.h"
43 #endif // TMWA_SUPPORT
44 
45 #include "utils/checkutils.h"
46 #include "utils/delete2.h"
47 #include "utils/dtor.h"
48 #include "utils/foreach.h"
49 #include "utils/itemxmlutils.h"
50 #include "utils/stdmove.h"
51 #include "utils/stringmap.h"
52 
53 #include "debug.h"
54 
55 namespace
56 {
59  ItemInfo *mUnknown = nullptr;
60  bool mLoaded = false;
61  bool mConstructed = false;
64  std::map<std::string, ItemSoundEvent::Type> mSoundNames;
66 } // namespace
67 
68 // Forward declarations
69 static void loadSpriteRef(ItemInfo *const itemInfo,
70  XmlNodeConstPtr node) A_NONNULL(1);
71 static void loadSoundRef(ItemInfo *const itemInfo,
72  XmlNodeConstPtr node) A_NONNULL(1);
73 static void loadFloorSprite(SpriteDisplay &display,
74  XmlNodeConstPtrConst node);
75 static void loadReplaceSprite(ItemInfo *const itemInfo,
76  XmlNodeConstPtr replaceNode) A_NONNULL(1);
77 static void loadOrderSprite(ItemInfo *const itemInfo,
78  XmlNodeConstPtr node,
79  const bool drawAfter) A_NONNULL(1);
80 static int parseSpriteName(const std::string &name);
81 static int parseDirectionName(const std::string &name);
82 
83 static ItemDbTypeT itemTypeFromString(const std::string &name)
84 {
85  const size_t sz = sizeof(itemTypeMap) / sizeof(itemTypeMap[0]);
86  for (size_t f = 0; f < sz; f ++)
87  {
88  const ItemTypeMap &type = itemTypeMap[f];
89  if (type.name == name)
90  return type.type;
91  }
92  logger->log("Unknown item type: " + name);
93  return ItemDbType::UNUSABLE;
94 }
95 
96 static std::string useButtonFromItemType(const ItemDbTypeT &type)
97 {
98  const size_t sz = sizeof(itemTypeMap) / sizeof(itemTypeMap[0]);
99  for (size_t f = 0; f < sz; f ++)
100  {
101  const ItemTypeMap &item = itemTypeMap[f];
102  if (item.type == type)
103  {
104  if (item.useButton.empty())
105  return std::string();
106  return gettext(item.useButton.c_str());
107  }
108  }
109  logger->log("Unknown item type");
110  return std::string();
111 }
112 
113 static std::string useButton2FromItemType(const ItemDbTypeT &type)
114 {
115  const size_t sz = sizeof(itemTypeMap) / sizeof(itemTypeMap[0]);
116  for (size_t f = 0; f < sz; f ++)
117  {
118  const ItemTypeMap &item = itemTypeMap[f];
119  if (item.type == type)
120  {
121  if (item.useButton2.empty())
122  return std::string();
123  return gettext(item.useButton2.c_str());
124  }
125  }
126  logger->log("Unknown item type");
127  return std::string();
128 }
129 
130 static void initStatic()
131 {
132  mConstructed = true;
134  mSoundNames["strike"] = ItemSoundEvent::MISS;
144 }
145 
147 {
148  if (mLoaded)
149  unload();
150 
151  logger->log1("Initializing item database...");
152 
153  if (!mConstructed)
154  initStatic();
155 
156  int tagNum = 0;
157 
158  mTags.clear();
159  mTagNames.clear();
160  mTagNames.push_back("All");
161  mTagNames.push_back("Usable");
162  mTagNames.push_back("Unusable");
163  mTagNames.push_back("Equipment");
164  mTags["All"] = tagNum ++;
165  mTags["Usable"] = tagNum ++;
166  mTags["Unusable"] = tagNum ++;
167  mTags["Equipment"] = tagNum ++;
168 
169  mUnknown = new ItemInfo;
170  // TRANSLATORS: item name
171  mUnknown->setName(_("Unknown item"));
173  std::string errFile = paths.getStringValue("spriteErrorFile");
174  mUnknown->setSprite(errFile, Gender::MALE, 0);
175  mUnknown->setSprite(errFile, Gender::FEMALE, 0);
176  mUnknown->setSprite(errFile, Gender::HIDDEN, 0);
177  mUnknown->addTag(mTags["All"]);
178  loadXmlFile(paths.getStringValue("itemsFile"),
179  tagNum,
181  loadXmlFile(paths.getStringValue("itemsPatchFile"),
182  tagNum,
184 
185  StringVect list;
186  VirtFs::getFilesInDir(paths.getStringValue("itemsPatchDir"),
187  list,
188  ".xml");
189  FOR_EACH (StringVectCIter, it, list)
190  loadXmlFile(*it, tagNum, SkipError_true);
191 
192  // Hairstyles are encoded as negative numbers. Count how far negative
193  // we can go.
194  int hairstyles = 1;
195  while (ItemDB::exists(-hairstyles) &&
196  ItemDB::get(-hairstyles).getSprite(Gender::MALE,
197  BeingTypeId_zero) != paths.getStringValue("spriteErrorFile"))
198  {
199  hairstyles ++;
200  }
201  mNumberOfHairstyles = hairstyles;
202 
203  int races = 100;
204  while (ItemDB::exists(-races) &&
206  paths.getStringValue("spriteErrorFile"))
207  {
208  races ++;
209  }
210 }
211 
212 static void loadMenu(XmlNodePtrConst parentNode,
213  STD_VECTOR<ItemMenuItem> &menu)
214 {
215  for_each_xml_child_node(node, parentNode)
216  {
217  if (xmlNameEqual(node, "menu"))
218  {
219  const std::string name1 = XML::langProperty(node,
220  "name1", "");
221  const std::string name2 = XML::langProperty(node,
222  "name2", "");
223  const std::string command1 = XML::getProperty(node,
224  "command1", "");
225  const std::string command2 = XML::getProperty(node,
226  "command2", command1);
227  menu.push_back(ItemMenuItem(name1,
228  name2,
229  command1,
230  command2));
231  }
232  }
233 }
234 
235 static bool getIsEquipment(const ItemDbTypeT type)
236 {
237  switch (type)
238  {
251  return true;
253  case ItemDbType::USABLE:
254  case ItemDbType::CARD:
257  default:
258  return false;
259  }
260 }
261 
262 void ItemDB::loadXmlFile(const std::string &fileName,
263  int &tagNum,
264  const SkipError skipError)
265 {
266  if (fileName.empty())
267  {
268  mLoaded = true;
269  return;
270  }
271 
272  XML::Document doc(fileName,
274  skipError);
275  XmlNodeConstPtrConst rootNode = doc.rootNode();
276 
277  if ((rootNode == nullptr) || !xmlNameEqual(rootNode, "items"))
278  {
279  logger->log("ItemDB: Error while loading %s!", fileName.c_str());
280  mLoaded = true;
281  return;
282  }
283 
284  const ItemFieldInfos &requiredFields =
286  const ItemFieldInfos &addFields =
288 
289  for_each_xml_child_node(node, rootNode)
290  {
291  if (xmlNameEqual(node, "include"))
292  {
293  const std::string name = XML::getProperty(node, "name", "");
294  if (!name.empty())
295  loadXmlFile(name, tagNum, skipError);
296  continue;
297  }
298  if (!xmlNameEqual(node, "item"))
299  continue;
300 
301  const int id = XML::getProperty(node, "id", 0);
302  ItemInfo *itemInfo = nullptr;
303 
304  if (id == 0)
305  {
306  reportAlways("ItemDB: Invalid or missing item ID in %s!",
307  fileName.c_str());
308  continue;
309  }
310  else if (mItemInfos.find(id) != mItemInfos.end())
311  {
312  logger->log("ItemDB: Redefinition of item ID %d", id);
313  itemInfo = mItemInfos[id];
314  }
315  if (itemInfo == nullptr)
316  itemInfo = new ItemInfo;
317 
318  const std::string typeStr = XML::getProperty(node, "type", "");
319  int weight = XML::getProperty(node, "weight", 0);
320  int view = XML::getProperty(node, "view", 0);
321  const int cardColor = XML::getProperty(node, "cardColor", -1);
322  const int inherit = XML::getProperty(node, "inherit", -1);
323 
324  std::string name = XML::langProperty(node, "name", "");
325  std::string nameEn = XML::getProperty(node, "name", "");
326  std::string image = XML::getProperty(node, "image", "");
327  std::string floor = XML::getProperty(node, "floor", "");
328  std::string description = XML::langProperty(node, "description", "");
329  std::string attackAction = XML::getProperty(node, "attack-action", "");
330  std::string skyAttackAction = XML::getProperty(
331  node, "skyattack-action", "");
332  std::string waterAttackAction = XML::getProperty(
333  node, "waterattack-action", "");
334  std::string rideAttackAction = XML::getProperty(
335  node, "rideattack-action", "");
336  std::string drawBefore = XML::getProperty(node, "drawBefore", "");
337  std::string drawAfter = XML::getProperty(node, "drawAfter", "");
338  const int maxFloorOffset = XML::getIntProperty(
339  node, "maxFloorOffset", mapTileSize, 0, mapTileSize);
340  const int maxFloorOffsetX = XML::getIntProperty(
341  node, "maxFloorOffsetX", maxFloorOffset, 0, mapTileSize);
342  const int maxFloorOffsetY = XML::getIntProperty(
343  node, "maxFloorOffsetY", maxFloorOffset, 0, mapTileSize);
344  std::string useButton = XML::langProperty(node, "useButton", "");
345  std::string useButton2 = XML::langProperty(node, "useButton2", "");
346  std::string colors = XML::getProperty(node, "colors", "");
347  std::string iconColors = XML::getProperty(node, "iconColors", "");
348  if (iconColors.empty())
349  iconColors = colors;
350 
351  // check for empty hair palete
352  if (id <= -1 && id > -100)
353  {
354  if (colors.empty())
355  colors = "hair";
356  if (iconColors.empty())
357  iconColors = "hair";
358  }
359 
360  std::string tags[3];
361  tags[0] = XML::getProperty(node, "tag",
362  XML::getProperty(node, "tag1", ""));
363  tags[1] = XML::getProperty(node, "tag2", "");
364  tags[2] = XML::getProperty(node, "tag3", "");
365 
366  const int drawPriority = XML::getProperty(node, "drawPriority", 0);
367 
368  int attackRange = XML::getProperty(node, "attack-range", 0);
369  std::string missileParticle = XML::getProperty(
370  node, "missile-particle", "");
371  float missileZ = XML::getFloatProperty(
372  node, "missile-z", 32.0f);
373  int missileLifeTime = XML::getProperty(
374  node, "missile-lifetime", 500);
375  float missileSpeed = XML::getFloatProperty(
376  node, "missile-speed", 7.0f);
377  float missileDieDistance = XML::getFloatProperty(
378  node, "missile-diedistance", 8.0f);
379  int hitEffectId = XML::getProperty(node, "hit-effect-id",
380  paths.getIntValue("hitEffectId"));
381  int criticalEffectId = XML::getProperty(
382  node, "critical-hit-effect-id",
383  paths.getIntValue("criticalHitEffectId"));
384  int missEffectId = XML::getProperty(node, "miss-effect-id",
385  paths.getIntValue("missEffectId"));
386 
387  SpriteDisplay display;
388  display.image = image;
389  if (!floor.empty())
390  display.floor = STD_MOVE(floor);
391  else
392  display.floor = image;
393 
394  const ItemInfo *inheritItemInfo = nullptr;
395 
396  if (inherit >= 0)
397  {
398  if (mItemInfos.find(inherit) != mItemInfos.end())
399  {
400  inheritItemInfo = mItemInfos[inherit];
401  }
402  else
403  {
404  reportAlways("Inherit item %d from not existing item %d",
405  id,
406  inherit);
407  }
408  }
409 
410  itemInfo->setId(id);
411  if (name.empty() && (inheritItemInfo != nullptr))
412  name = inheritItemInfo->getName();
413  // TRANSLATORS: item info name
414  itemInfo->setName(name.empty() ? _("unnamed") : name);
415  if (nameEn.empty())
416  {
417  // TRANSLATORS: item info name
418  itemInfo->setNameEn(name.empty() ? _("unnamed") : name);
419  }
420  else
421  {
422  itemInfo->setNameEn(nameEn);
423  }
424 
425  if (description.empty() && (inheritItemInfo != nullptr))
426  description = inheritItemInfo->getDescription();
427  itemInfo->setDescription(description);
428  if (typeStr.empty())
429  {
430  if (inheritItemInfo != nullptr)
431  itemInfo->setType(inheritItemInfo->getType());
432  else
433  itemInfo->setType(itemTypeFromString("other"));
434  }
435  else
436  {
437  itemInfo->setType(itemTypeFromString(typeStr));
438  }
439  itemInfo->setType(itemTypeFromString(typeStr));
440  if (useButton.empty() && (inheritItemInfo != nullptr))
441  useButton = inheritItemInfo->getUseButton();
442  if (useButton.empty())
443  useButton = useButtonFromItemType(itemInfo->getType());
444  itemInfo->setUseButton(useButton);
445  if (useButton2.empty() && (inheritItemInfo != nullptr))
446  useButton2 = inheritItemInfo->getUseButton();
447  if (useButton2.empty())
448  useButton2 = useButton2FromItemType(itemInfo->getType());
449  itemInfo->setUseButton2(useButton2);
450  itemInfo->addTag(mTags["All"]);
452  node, "sellProtected", false));
453  if (cardColor != -1)
454  itemInfo->setCardColor(fromInt(cardColor, ItemColor));
455  else if (inheritItemInfo != nullptr)
456  itemInfo->setCardColor(inheritItemInfo->getCardColor());
457 
458  switch (itemInfo->getType())
459  {
460  case ItemDbType::USABLE:
461  itemInfo->addTag(mTags["Usable"]);
462  break;
463  case ItemDbType::CARD:
465  itemInfo->addTag(mTags["Unusable"]);
466  break;
467  default:
482  itemInfo->addTag(mTags["Equipment"]);
483  break;
484  }
485  for (int f = 0; f < 3; f++)
486  {
487  if (!tags[f].empty())
488  {
489  if (mTags.find(tags[f]) == mTags.end())
490  {
491  mTagNames.push_back(tags[f]);
492  mTags[tags[f]] = tagNum ++;
493  }
494  itemInfo->addTag(mTags[tags[f]]);
495  }
496  }
497 
498  std::string effect;
499  readItemStatsString(effect, node, requiredFields);
500  readItemStatsString(effect, node, addFields);
501  std::string temp = XML::langProperty(node, "effect", "");
502  if (!effect.empty() && !temp.empty())
503  effect.append(" / ");
504  effect.append(temp);
505 
506  if (inheritItemInfo != nullptr)
507  {
508  if (view == 0)
509  view = inheritItemInfo->getView();
510  if (weight == 0)
511  weight = inheritItemInfo->getWeight();
512  if (attackAction.empty())
513  attackAction = inheritItemInfo->getAttackAction();
514  if (skyAttackAction.empty())
515  skyAttackAction = inheritItemInfo->getSkyAttackAction();
516  if (waterAttackAction.empty())
517  waterAttackAction = inheritItemInfo->getWaterAttackAction();
518  if (rideAttackAction.empty())
519  rideAttackAction = inheritItemInfo->getRideAttackAction();
520  if (attackRange == 0)
521  attackRange = inheritItemInfo->getAttackRange();
522  if (hitEffectId == 0)
523  hitEffectId = inheritItemInfo->getHitEffectId();
524  if (criticalEffectId == 0)
525  criticalEffectId = inheritItemInfo->getCriticalHitEffectId();
526  if (missEffectId == 0)
527  missEffectId = inheritItemInfo->getMissEffectId();
528  if (colors.empty())
529  colors = inheritItemInfo->getColorsListName();
530  if (iconColors.empty())
531  iconColors = inheritItemInfo->getIconColorsListName();
532  if (effect.empty())
533  effect = inheritItemInfo->getEffect();
534 
535  const MissileInfo &inheritMissile =
536  inheritItemInfo->getMissileConst();
537  if (missileParticle.empty())
538  missileParticle = inheritMissile.particle;
539  if (missileZ == 32.0F)
540  missileZ = inheritMissile.z;
541  if (missileLifeTime == 500)
542  missileLifeTime = inheritMissile.lifeTime;
543  if (missileSpeed == 7.0F)
544  missileSpeed = inheritMissile.speed;
545  if (missileDieDistance == 8.0F)
546  missileDieDistance = inheritMissile.dieDistance;
547  }
548 
549  itemInfo->setView(view);
550  itemInfo->setWeight(weight);
551  itemInfo->setAttackAction(attackAction);
552  itemInfo->setSkyAttackAction(skyAttackAction);
553  itemInfo->setWaterAttackAction(waterAttackAction);
554  itemInfo->setRideAttackAction(rideAttackAction);
555  itemInfo->setAttackRange(attackRange);
556  itemInfo->setHitEffectId(hitEffectId);
557  itemInfo->setCriticalHitEffectId(criticalEffectId);
558  itemInfo->setMissEffectId(missEffectId);
559  itemInfo->setDrawBefore(-1, parseSpriteName(drawBefore));
560  itemInfo->setDrawAfter(-1, parseSpriteName(drawAfter));
561  itemInfo->setDrawPriority(-1, drawPriority);
562  itemInfo->setColorsList(colors);
563  itemInfo->setIconColorsList(iconColors);
564  itemInfo->setMaxFloorOffsetX(maxFloorOffsetX);
565  itemInfo->setMaxFloorOffsetY(maxFloorOffsetY);
567  node, "pickupCursor", "pickup"));
568 
569  MissileInfo &missile = itemInfo->getMissile();
570  missile.particle = STD_MOVE(missileParticle);
571  missile.z = missileZ;
572  missile.lifeTime = missileLifeTime;
573  missile.speed = missileSpeed;
574  missile.dieDistance = missileDieDistance;
575 
576  for_each_xml_child_node(itemChild, node)
577  {
578  if (xmlNameEqual(itemChild, "sprite"))
579  {
580  loadSpriteRef(itemInfo, itemChild);
581  }
582  else if (xmlNameEqual(itemChild, "particlefx"))
583  {
584  if (XmlHaveChildContent(itemChild))
585  display.particles.push_back(XmlChildContent(itemChild));
586  }
587  else if (xmlNameEqual(itemChild, "sound"))
588  {
589  loadSoundRef(itemInfo, itemChild);
590  }
591  else if (xmlNameEqual(itemChild, "floor"))
592  {
593  loadFloorSprite(display, itemChild);
594  }
595  else if (xmlNameEqual(itemChild, "replace"))
596  {
597  loadReplaceSprite(itemInfo, itemChild);
598  }
599  else if (xmlNameEqual(itemChild, "drawAfter"))
600  {
601  loadOrderSprite(itemInfo, itemChild, true);
602  }
603  else if (xmlNameEqual(itemChild, "drawBefore"))
604  {
605  loadOrderSprite(itemInfo, itemChild, false);
606  }
607  else if (xmlNameEqual(itemChild, "inventory"))
608  {
609  loadMenu(itemChild, itemInfo->getInventoryMenu());
610  }
611  else if (xmlNameEqual(itemChild, "storage"))
612  {
613  loadMenu(itemChild, itemInfo->getStorageMenu());
614  }
615  else if (xmlNameEqual(itemChild, "cart"))
616  {
617  loadMenu(itemChild, itemInfo->getCartMenu());
618  }
619  else if (xmlNameEqual(itemChild, "addStats"))
620  {
621  readItemStatsString(effect, itemChild, addFields);
622  }
623  else if (xmlNameEqual(itemChild, "requireStats"))
624  {
625  readItemStatsString(effect, itemChild, requiredFields);
626  }
627  }
628  itemInfo->setEffect(effect);
629 
630 /*
631  logger->log("start dump item: %d", id);
632  if (itemInfo->isRemoveSprites())
633  {
634  for (int f = 0; f < 10; f ++)
635  {
636  logger->log("dir: %d", f);
637  SpriteToItemMap *const spriteToItems
638  = itemInfo->getSpriteToItemReplaceMap(f);
639  if (!spriteToItems)
640  {
641  logger->log("null");
642  continue;
643  }
644  for (SpriteToItemMapCIter itr = spriteToItems->begin(),
645  itr_end = spriteToItems->end(); itr != itr_end; ++ itr)
646  {
647  const int remSprite = itr->first;
648  const IntMap &itemReplacer = itr->second;
649  logger->log("sprite: %d", remSprite);
650 
651  for (IntMapCIter repIt = itemReplacer.begin(),
652  repIt_end = itemReplacer.end();
653  repIt != repIt_end; ++ repIt)
654  {
655  logger->log("from %d to %d", repIt->first,
656  repIt->second);
657  }
658  }
659  }
660  }
661 
662  logger->log("--------------------------------");
663  logger->log("end dump item");
664 */
665 
666  itemInfo->setDisplay(display);
667 
668  mItemInfos[id] = itemInfo;
669  if (!name.empty())
670  {
671  temp = normalize(name);
672  mNamedItemInfos[temp] = itemInfo;
673  }
674  if (!nameEn.empty())
675  {
676  temp = normalize(nameEn);
677  mNamedItemInfos[temp] = itemInfo;
678  }
679 
680  if (!attackAction.empty())
681  {
682  if (attackRange == 0)
683  {
684  reportAlways("ItemDB: Missing attack range from weapon %i!",
685  id);
686  }
687  }
688 
689  STD_VECTOR<ItemMenuItem> &inventoryMenu = itemInfo->getInventoryMenu();
690 
691  if (inventoryMenu.empty())
692  {
693  std::string name1 = itemInfo->getUseButton();
694  std::string name2 = itemInfo->getUseButton2();
695  const bool isEquipment = getIsEquipment(itemInfo->getType());
696 
697  if (isEquipment)
698  {
699  if (name1.empty())
700  {
701  // TRANSLATORS: popup menu item
702  name1 = _("Equip");
703  }
704  if (name2.empty())
705  {
706  // TRANSLATORS: popup menu item
707  name2 = _("Unequip");
708  }
709  }
710  else
711  {
712  if (name1.empty())
713  {
714  // TRANSLATORS: popup menu item
715  name1 = _("Use");
716  }
717  if (name2.empty())
718  {
719  // TRANSLATORS: popup menu item
720  name2 = _("Use");
721  }
722  }
723  inventoryMenu.push_back(ItemMenuItem(
724  name1,
725  name2,
726  "useinv 'INVINDEX'",
727  "useinv 'INVINDEX'"));
728  }
729 
730 #define CHECK_PARAM(param) \
731  if (param.empty()) \
732  { \
733  logger->log("ItemDB: Missing " #param " attribute for item %i!", \
734  id); \
735  }
736 
737  if (id >= 0 && typeStr != "other")
738  {
739  CHECK_PARAM(name)
740  CHECK_PARAM(description)
741  CHECK_PARAM(image)
742  }
743 #undef CHECK_PARAM
744  }
745 
746  mLoaded = true;
747 }
748 
750 {
751  return mTagNames;
752 }
753 
754 int ItemDB::getTagId(const std::string &tagName)
755 {
756  return mTags[tagName];
757 }
758 
760 {
761  logger->log1("Unloading item database...");
762 
763  delete2(mUnknown);
764 
766  mItemInfos.clear();
767  mNamedItemInfos.clear();
768  mTags.clear();
769  mTagNames.clear();
770  mLoaded = false;
771 }
772 
773 bool ItemDB::exists(const int id)
774 {
775  if (!mLoaded)
776  return false;
777 
778  const ItemInfos::const_iterator i = mItemInfos.find(id);
779  return i != mItemInfos.end();
780 }
781 
782 bool ItemDB::exists(const std::string &name)
783 {
784  if (!mLoaded)
785  return false;
786 
787  const NamedItemInfos::const_iterator i = mNamedItemInfos.find(
788  normalize(name));
789  return i != mNamedItemInfos.end();
790 }
791 
792 const ItemInfo &ItemDB::get(const int id)
793 {
794  if (!mLoaded)
795  load();
796 
797  const ItemInfos::const_iterator i = mItemInfos.find(id);
798 
799  if (i == mItemInfos.end())
800  {
801  reportAlways("ItemDB: Warning, unknown item ID# %d", id);
802  return *mUnknown;
803  }
804 
805  return *(i->second);
806 }
807 
808 const ItemInfo &ItemDB::get(const std::string &name)
809 {
810  if (!mLoaded)
811  load();
812 
813  const NamedItemInfos::const_iterator i = mNamedItemInfos.find(
814  normalize(name));
815 
816  if (i == mNamedItemInfos.end())
817  {
818  if (!name.empty())
819  {
820  reportAlways("ItemDB: Warning, unknown item name \"%s\"",
821  name.c_str());
822  }
823  return *mUnknown;
824  }
825 
826  return *(i->second);
827 }
828 
830 {
831  return mItemInfos;
832 }
833 
835 {
836  return *mUnknown;
837 }
838 
839 static int parseSpriteName(const std::string &name)
840 {
841  int id = -1;
842  if (name == "race" || name == "type")
843  {
844  id = 0;
845  }
846  else if (name == "shoes" || name == "boot" || name == "boots")
847  {
848  id = 1;
849  }
850  else if (name == "bottomclothes" || name == "bottom" || name == "pants")
851  {
852  id = 2;
853  }
854  else if (name == "topclothes" || name == "top"
855  || name == "torso" || name == "body")
856  {
857  id = 3;
858  }
859  else if (name == "misc1")
860  {
861  id = 4;
862  }
863  else if (name == "misc2" || name == "scarf" || name == "scarfs")
864  {
865  id = 5;
866  }
867  else if (name == "hair")
868  {
869  id = 6;
870  }
871  else if (name == "hat" || name == "hats")
872  {
873  id = 7;
874  }
875  else if (name == "wings")
876  {
877  id = 8;
878  }
879  else if (name == "glove" || name == "gloves")
880  {
881  id = 9;
882  }
883  else if (name == "weapon" || name == "weapons")
884  {
885  id = 10;
886  }
887  else if (name == "shield" || name == "shields")
888  {
889  id = 11;
890  }
891  else if (name == "amulet" || name == "amulets")
892  {
893  id = 12;
894  }
895  else if (name == "ring" || name == "rings")
896  {
897  id = 13;
898  }
899 
900  return id;
901 }
902 
903 static int parseDirectionName(const std::string &name)
904 {
905  int id = -1;
906  if (name == "down")
907  {
908 #ifdef TMWA_SUPPORT
910  id = -2;
911  else
912 #endif
914  }
915  else if (name == "downleft" || name == "leftdown")
916  {
918  }
919  else if (name == "left")
920  {
922  }
923  else if (name == "upleft" || name == "leftup")
924  {
926  }
927  else if (name == "up")
928  {
929 #ifdef TMWA_SUPPORT
931  id = -3;
932  else
933 #endif
934  id = SpriteDirection::UP;
935  }
936  else if (name == "upright" || name == "rightup")
937  {
939  }
940  else if (name == "right")
941  {
943  }
944  else if (name == "downright" || name == "rightdown")
945  {
947  }
948  else if (name == "downall")
949  {
950  id = -2;
951  }
952  else if (name == "upall")
953  {
954  id = -3;
955  }
956  // hack for died action.
957  else if (name == "died")
958  {
959  id = 9;
960  }
961 
962  return id;
963 }
964 
965 static void loadSpriteRef(ItemInfo *const itemInfo, XmlNodeConstPtr node)
966 {
967  const std::string gender = XML::getProperty(node, "gender", "unisex");
968  if ((node == nullptr) || !XmlHaveChildContent(node))
969  return;
970 
971  const std::string filename = XmlChildContent(node);
972 
973  const int race = XML::getProperty(node, "race", 0);
974  if (gender == "male" || gender == "unisex")
975  itemInfo->setSprite(filename, Gender::MALE, race);
976  if (gender == "female" || gender == "unisex")
977  itemInfo->setSprite(filename, Gender::FEMALE, race);
978  // "other" is legacy from 20180630
979  if (gender == "hidden" || gender == "other" || gender == "unisex")
980  itemInfo->setSprite(filename, Gender::HIDDEN, race);
981 }
982 
983 static void loadSoundRef(ItemInfo *const itemInfo, XmlNodeConstPtr node)
984 {
985  if (node == nullptr ||
986  !XmlHaveChildContent(node))
987  {
988  return;
989  }
990  const std::string event = XML::getProperty(node, "event", "");
991  const int delay = XML::getProperty(node, "delay", 0);
992 
993  const std::map<std::string, ItemSoundEvent::Type>::const_iterator
994  it = mSoundNames.find(event);
995  if (it != mSoundNames.end())
996  {
997  const std::string filename = XmlChildContent(node);
998  itemInfo->addSound((*it).second, filename, delay);
999  }
1000  else
1001  {
1002  reportAlways("ItemDB: Ignoring unknown sound event '%s'",
1003  event.c_str());
1004  }
1005 }
1006 
1007 static void loadFloorSprite(SpriteDisplay &display,
1008  XmlNodeConstPtrConst floorNode)
1009 {
1010  if (floorNode == nullptr)
1011  return;
1012  for_each_xml_child_node(spriteNode, floorNode)
1013  {
1014  if (!XmlHaveChildContent(spriteNode))
1015  continue;
1016  if (xmlNameEqual(spriteNode, "sprite"))
1017  {
1018  SpriteReference *const currentSprite = new SpriteReference;
1019  currentSprite->sprite = XmlChildContent(spriteNode);
1020  currentSprite->variant
1021  = XML::getProperty(spriteNode, "variant", 0);
1022  display.sprites.push_back(currentSprite);
1023  }
1024  else if (xmlNameEqual(spriteNode, "particlefx"))
1025  {
1026  display.particles.push_back(XmlChildContent(spriteNode));
1027  }
1028  }
1029 }
1030 
1032  XmlNodeConstPtr replaceNode)
1033 {
1034  if (replaceNode == nullptr)
1035  return;
1036  const std::string removeSprite = XML::getProperty(
1037  replaceNode, "sprite", "");
1038  const int direction = parseDirectionName(XML::getProperty(
1039  replaceNode, "direction", "all"));
1040 
1041  itemInfo->setRemoveSprites();
1042 
1043  switch (direction)
1044  {
1045  case -1:
1046  {
1047  if (removeSprite.empty())
1048  { // remove all sprites
1049  for (int f = 0; f < 10; f ++)
1050  {
1051  for (int sprite = 0; sprite < 13; sprite ++)
1052  itemInfo->addReplaceSprite(sprite, f);
1053  }
1054  }
1055  else
1056  { // replace only given sprites
1057  for (int f = 0; f < 10; f ++)
1058  {
1059  IntMap *const mapList = itemInfo->addReplaceSprite(
1060  parseSpriteName(removeSprite), f);
1061  if (mapList == nullptr)
1062  continue;
1063  for_each_xml_child_node(itemNode, replaceNode)
1064  {
1065  if (xmlNameEqual(itemNode, "item"))
1066  {
1067  const int from = XML::getProperty(
1068  itemNode, "from", 0);
1069  const int to = XML::getProperty(
1070  itemNode, "to", 1);
1071  (*mapList)[from] = to;
1072  }
1073  }
1074  }
1075  }
1076  break;
1077  }
1078  case -2:
1079  {
1081  removeSprite), SpriteDirection::DOWN);
1083  removeSprite), SpriteDirection::DOWNLEFT);
1085  removeSprite), SpriteDirection::DOWNRIGHT);
1086 
1087  for_each_xml_child_node(itemNode, replaceNode)
1088  {
1089  if (xmlNameEqual(itemNode, "item"))
1090  {
1091  const int from = XML::getProperty(itemNode, "from", 0);
1092  const int to = XML::getProperty(itemNode, "to", 1);
1093  IntMap *mapList = itemInfo->addReplaceSprite(
1094  parseSpriteName(removeSprite), SpriteDirection::DOWN);
1095  if (mapList != nullptr)
1096  (*mapList)[from] = to;
1097 
1098  mapList = itemInfo->addReplaceSprite(parseSpriteName(
1099  removeSprite), SpriteDirection::DOWNLEFT);
1100  if (mapList != nullptr)
1101  (*mapList)[from] = to;
1102 
1103  mapList = itemInfo->addReplaceSprite(parseSpriteName(
1104  removeSprite), SpriteDirection::DOWNRIGHT);
1105  if (mapList != nullptr)
1106  (*mapList)[from] = to;
1107  }
1108  }
1109  break;
1110  }
1111  case -3:
1112  {
1114  removeSprite), SpriteDirection::UP);
1116  removeSprite), SpriteDirection::UPLEFT);
1118  removeSprite), SpriteDirection::UPRIGHT);
1119 
1120  for_each_xml_child_node(itemNode, replaceNode)
1121  {
1122  if (xmlNameEqual(itemNode, "item"))
1123  {
1124  const int from = XML::getProperty(itemNode, "from", 0);
1125  const int to = XML::getProperty(itemNode, "to", 1);
1126  IntMap *mapList = itemInfo->addReplaceSprite(
1127  parseSpriteName(removeSprite), SpriteDirection::UP);
1128  if (mapList != nullptr)
1129  (*mapList)[from] = to;
1130 
1131  mapList = itemInfo->addReplaceSprite(parseSpriteName(
1132  removeSprite), SpriteDirection::UPLEFT);
1133  if (mapList != nullptr)
1134  (*mapList)[from] = to;
1135 
1136  mapList = itemInfo->addReplaceSprite(parseSpriteName(
1137  removeSprite), SpriteDirection::UPRIGHT);
1138  if (mapList != nullptr)
1139  (*mapList)[from] = to;
1140  }
1141  }
1142  break;
1143  }
1144  default:
1145  {
1146  IntMap *const mapList = itemInfo->addReplaceSprite(
1147  parseSpriteName(removeSprite), direction);
1148  if (mapList == nullptr)
1149  return;
1150  for_each_xml_child_node(itemNode, replaceNode)
1151  {
1152  if (xmlNameEqual(itemNode, "item"))
1153  {
1154  const int from = XML::getProperty(itemNode, "from", 0);
1155  const int to = XML::getProperty(itemNode, "to", 1);
1156  (*mapList)[from] = to;
1157  }
1158  }
1159  break;
1160  }
1161  }
1162 }
1163 
1164 static void loadOrderSprite(ItemInfo *const itemInfo,
1165  XmlNodeConstPtr node,
1166  const bool drawAfter)
1167 {
1168  const int sprite = parseSpriteName(XML::getProperty(node, "name", ""));
1169  const int priority = XML::getProperty(node, "priority", 0);
1170 
1171  const int direction = parseDirectionName(XML::getProperty(
1172  node, "direction", "all"));
1173  if (drawAfter)
1174  itemInfo->setDrawAfter(direction, sprite);
1175  else
1176  itemInfo->setDrawBefore(direction, sprite);
1177  itemInfo->setDrawPriority(direction, priority);
1178 }
1179 
1180 std::string ItemDB::getNamesStr(const STD_VECTOR<int> &parts)
1181 {
1182  std::string str;
1183  FOR_EACH (STD_VECTOR<int>::const_iterator, it, parts)
1184  {
1185  const int id = *it;
1186  if (exists(id))
1187  {
1188  if (!str.empty())
1189  str.append(",");
1190  str.append(get(id).getName());
1191  }
1192  }
1193  return str;
1194 }
1195 
1197 {
1198  return mNumberOfHairstyles;
1199 }
1200 
1201 #ifdef UNITTESTS
1202 ItemDB::NamedItemInfos &ItemDB::getNamedItemInfosTest()
1203 {
1204  return mNamedItemInfos;
1205 }
1206 
1207 ItemDB::ItemInfos &ItemDB::getItemInfosTest()
1208 {
1209  return mItemInfos;
1210 }
1211 #endif // UNITTESTS
void readItemStatsString(std::string &effect, const xmlNodePtr node, const ItemFieldInfos &fields)
const ItemInfo & getEmpty()
Definition: itemdb.cpp:834
#define FOR_EACH(type, iter, array)
Definition: foreach.h:24
void setAttackAction(const std::string &attackAction)
Definition: iteminfo.cpp:120
std::string getStringValue(const std::string &key) const
#define _(s)
Definition: gettext.h:34
const ItemInfo & get(const int id)
Definition: itemdb.cpp:792
const std::string & getEffect() const
Definition: iteminfo.h:108
const bool SkipError_false
Definition: skiperror.h:29
std::vector< ItemMenuItem > & getInventoryMenu()
Definition: iteminfo.h:318
void unload()
Definition: net.cpp:173
void unload()
Definition: itemdb.cpp:759
std::map< int, int > IntMap
Definition: intmap.h:26
ItemDbTypeT type
Definition: itemtypemap.h:35
void log1(const char *const log_text)
Definition: logger.cpp:233
std::map< std::string, int > StringIntMap
Definition: stringmap.h:27
ItemDB::ItemInfos mItemInfos
Definition: itemdb.cpp:57
void setMissEffectId(const int s)
Definition: iteminfo.h:179
std::vector< SpriteReference * > sprites
Definition: spritedisplay.h:46
StringVect::const_iterator StringVectCIter
Definition: stringvector.h:30
float getFloatProperty(const xmlNodePtr node, const char *const name, float def)
Definition: libxml.cpp:210
const std::string & getWaterAttackAction() const
Definition: iteminfo.h:191
void setView(const int view)
Definition: iteminfo.h:138
const StringVect & getTags()
Definition: itemdb.cpp:749
static void loadFloorSprite(SpriteDisplay &display, const xmlNode *const node)
Definition: itemdb.cpp:1007
const std::string & getAttackAction() const
Definition: iteminfo.h:185
void setDescription(const std::string &description)
Definition: iteminfo.h:96
static void loadOrderSprite(ItemInfo *const itemInfo, const xmlNodePtr node, const bool drawAfter)
Definition: itemdb.cpp:1164
const std::string & getSprite(const GenderT gender, const BeingTypeId race) const
Definition: iteminfo.cpp:99
#define fromInt(val, name)
Definition: intdefines.h:45
const ItemFieldInfos & getAddFields()
std::string fileName
Definition: testmain.cpp:38
void setNameEn(const std::string &name)
Definition: iteminfo.h:79
void setUseButton(const std::string &str)
Definition: iteminfo.h:114
std::string useButton2
Definition: itemtypemap.h:37
const ItemFieldInfos & getRequiredFields()
void setId(const int id)
Definition: iteminfo.h:64
const std::string & getDescription() const
Definition: iteminfo.h:99
const BeingTypeId BeingTypeId_zero
Definition: beingtypeid.h:29
bool exists(const int id)
Definition: itemdb.cpp:773
int getProperty(const xmlNodePtr node, const char *const name, int def)
Definition: libxml.cpp:173
#define CHECK_PARAM(param)
static void loadSoundRef(ItemInfo *const itemInfo, const xmlNodePtr node)
Definition: itemdb.cpp:983
if(!vert) return
std::string getNamesStr(const std::vector< int > &parts)
Definition: itemdb.cpp:1180
uint16_t ItemColor
Definition: itemcolor.h:29
int getIntValue(const std::string &key) const
void setMaxFloorOffsetX(const int i)
Definition: iteminfo.h:234
void setRemoveSprites()
Definition: iteminfo.h:228
static bool getIsEquipment(const ItemDbTypeT type)
Definition: itemdb.cpp:235
void load()
Definition: itemdb.cpp:146
std::vector< std::string > StringVect
Definition: stringvector.h:28
const std::string & getName() const
Definition: iteminfo.h:73
#define delete2(var)
Definition: delete2.h:24
const std::string & getSkyAttackAction() const
Definition: iteminfo.h:188
MissileInfo & getMissile()
Definition: iteminfo.h:161
#define A_NONNULL(...)
Definition: localconsts.h:159
static void loadReplaceSprite(ItemInfo *const itemInfo, const xmlNodePtr replaceNode)
Definition: itemdb.cpp:1031
const std::string & getRideAttackAction() const
Definition: iteminfo.h:194
std::vector< ItemMenuItem > & getCartMenu()
Definition: iteminfo.h:322
std::string sprite
const std::string & getUseButton() const
Definition: iteminfo.h:117
Logger * logger
Definition: logger.cpp:88
void setColorsList(const std::string &name)
Definition: iteminfo.cpp:198
int getIntProperty(const xmlNodePtr node, const char *const name, int def, const int min, const int max)
Definition: libxml.cpp:189
int getTagId(const std::string &tagName)
Definition: itemdb.cpp:754
void setHitEffectId(const int s)
Definition: iteminfo.h:167
void setCriticalHitEffectId(const int s)
Definition: iteminfo.h:173
const bool UseVirtFs_true
Definition: usevirtfs.h:29
void getFilesInDir(const std::string &dir, StringVect &list, const std::string &ext)
Definition: tools.cpp:80
const std::string & getName(const int id)
Definition: groupdb.cpp:343
const std::string & getUseButton2() const
Definition: iteminfo.h:123
const MissileInfo & getMissileConst() const
Definition: iteminfo.h:164
void delete_all(Container &c)
Definition: dtor.h:55
static void loadSpriteRef(ItemInfo *const itemInfo, const xmlNodePtr node)
Definition: itemdb.cpp:965
ItemDB::NamedItemInfos mNamedItemInfos
Definition: itemdb.cpp:58
void setAttackRange(const int r)
Definition: iteminfo.h:200
void setSkyAttackAction(const std::string &attackAction)
Definition: iteminfo.cpp:128
bool getBoolProperty(const xmlNodePtr node, const char *const name, const bool def)
Definition: libxml.cpp:268
int getAttackRange() const
Definition: iteminfo.h:197
StringVect particles
Definition: spritedisplay.h:47
std::map< std::string, ItemInfo * > NamedItemInfos
Definition: itemdb.h:63
int getHitEffectId() const
Definition: iteminfo.h:170
std::string getColorsListName() const
Definition: iteminfo.h:278
void setDrawBefore(const int direction, const int n)
Definition: iteminfo.cpp:369
void setMaxFloorOffsetY(const int i)
Definition: iteminfo.h:237
std::string normalize(const std::string &name)
static void initStatic()
Definition: itemdb.cpp:130
MenuTypeT menu
Definition: menu.cpp:27
void loadXmlFile(const std::string &fileName, int &tagNum, const SkipError skipError)
Definition: itemdb.cpp:262
void setDrawAfter(const int direction, const int n)
Definition: iteminfo.cpp:374
static int parseSpriteName(const std::string &name)
Definition: itemdb.cpp:839
static void loadXmlFile(const std::string &file, const std::string &name, BadgesInfos &arr, const SkipError skipError)
Definition: badgesdb.cpp:42
void addTag(const int tag)
Definition: iteminfo.h:225
std::string name
Definition: itemtypemap.h:34
int getMissEffectId() const
Definition: iteminfo.h:182
std::string particle
Definition: missileinfo.h:40
std::string empty
Definition: podict.cpp:25
xmlNodePtr rootNode()
Definition: libxml.cpp:168
#define STD_MOVE(var)
Definition: stdmove.h:27
std::map< std::string, ItemSoundEvent::Type > mSoundNames
Definition: itemdb.cpp:64
void setType(const ItemDbTypeT type)
Definition: iteminfo.h:111
ServerTypeT getNetworkType()
Definition: net.cpp:182
Configuration paths
static std::string useButton2FromItemType(const ItemDbTypeT &type)
Definition: itemdb.cpp:113
std::string langProperty(const xmlNodePtr node, const char *const name, const std::string &def)
Definition: libxml.cpp:257
int getWeight() const
Definition: iteminfo.h:132
IntMap * addReplaceSprite(const int sprite, const int direction)
Definition: iteminfo.cpp:173
int getView() const
Definition: iteminfo.h:135
float dieDistance
Definition: missileinfo.h:43
#define for_each_xml_child_node(var, parent)
Definition: libxml.h:160
std::map< int, ItemInfo * > ItemInfos
Definition: itemdb.h:62
void setEffect(const std::string &effect)
Definition: iteminfo.h:105
bool exists(std::string name)
Definition: fs.cpp:123
const EmoteSprite * getSprite(const int id, const bool allowNull)
Definition: emotedb.cpp:291
void addSound(const ItemSoundEvent::Type event, const std::string &filename, const int delay)
Definition: iteminfo.cpp:152
ItemDbTypeT getType() const
Definition: iteminfo.h:126
int getNumOfHairstyles()
Definition: itemdb.cpp:1196
ItemTypeMap itemTypeMap[]
void setUseButton2(const std::string &str)
Definition: iteminfo.h:120
static const int mapTileSize
Definition: map.h:26
int getCriticalHitEffectId() const
Definition: iteminfo.h:176
void setCardColor(const ItemColor color)
Definition: iteminfo.h:248
void setWaterAttackAction(const std::string &attackAction)
Definition: iteminfo.cpp:136
std::vector< ItemMenuItem > & getStorageMenu()
Definition: iteminfo.h:320
void setWeight(const int weight)
Definition: iteminfo.h:129
ItemColor getCardColor() const
Definition: iteminfo.h:251
void setName(const std::string &name)
Definition: iteminfo.h:70
static std::string useButtonFromItemType(const ItemDbTypeT &type)
Definition: itemdb.cpp:96
std::map< std::string, ItemFieldType * > ItemFieldInfos
void log(const char *const log_text,...)
Definition: logger.cpp:264
void setDrawPriority(const int direction, const int n)
Definition: iteminfo.cpp:379
const bool SkipError_true
Definition: skiperror.h:29
std::string useButton
Definition: itemtypemap.h:36
std::string getIconColorsListName() const
Definition: iteminfo.h:281
static void loadMenu(xmlNode *const parentNode, std::vector< ItemMenuItem > &menu)
Definition: itemdb.cpp:212
#define reportAlways(...)
Definition: checkutils.h:252
void setRideAttackAction(const std::string &attackAction)
Definition: iteminfo.cpp:144
static ItemDbTypeT itemTypeFromString(const std::string &name)
Definition: itemdb.cpp:83
static int parseDirectionName(const std::string &name)
Definition: itemdb.cpp:903
void setProtected(const bool b)
Definition: iteminfo.h:297
ItemDbType ::T ItemDbTypeT
Definition: itemdbtype.h:48
const ItemDB::ItemInfos & getItemInfos()
Definition: itemdb.cpp:829
bool itemInfo(InputEvent &event)
Definition: commands.cpp:104
std::string image
Definition: spritedisplay.h:44
void setPickupCursor(const std::string &cursor)
Definition: iteminfo.h:288
void setSprite(const std::string &animationFile, const GenderT gender, const int race)
Definition: iteminfo.cpp:405
float speed
Definition: missileinfo.h:42
void setDisplay(const SpriteDisplay &display)
Definition: iteminfo.h:90
void setIconColorsList(const std::string &name)
Definition: iteminfo.cpp:212