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-2019 The ManaPlus Developers
6  * Copyright (C) 2019-2021 Andrei Karas
7  *
8  * This file is part of The ManaPlus Client.
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program. If not, see <http://www.gnu.org/licenses/>.
22  */
23 
24 #include "resources/db/itemdb.h"
25 
27 
28 #include "configuration.h"
29 
31 
32 #include "fs/virtfs/tools.h"
33 
34 #include "resources/iteminfo.h"
35 #include "resources/itemmenuitem.h"
37 
39 
41 
42 #ifdef TMWA_SUPPORT
43 #include "net/net.h"
44 #endif // TMWA_SUPPORT
45 
46 #include "utils/checkutils.h"
47 #include "utils/delete2.h"
48 #include "utils/dtor.h"
49 #include "utils/foreach.h"
50 #include "utils/itemxmlutils.h"
51 #include "utils/stdmove.h"
52 #include "utils/stringmap.h"
53 
54 #include "debug.h"
55 
56 namespace
57 {
60  ItemInfo *mUnknown = nullptr;
61  bool mLoaded = false;
62  bool mConstructed = false;
65  std::map<std::string, ItemSoundEvent::Type> mSoundNames;
67 } // namespace
68 
69 // Forward declarations
70 static void loadSpriteRef(ItemInfo *const itemInfo,
71  XmlNodeConstPtr node) A_NONNULL(1);
72 static void loadSoundRef(ItemInfo *const itemInfo,
73  XmlNodeConstPtr node) A_NONNULL(1);
74 static void loadFloorSprite(SpriteDisplay &display,
75  XmlNodeConstPtrConst node);
76 static void loadReplaceSprite(ItemInfo *const itemInfo,
77  XmlNodeConstPtr replaceNode) A_NONNULL(1);
78 static void loadOrderSprite(ItemInfo *const itemInfo,
79  XmlNodeConstPtr node,
80  const bool drawAfter) A_NONNULL(1);
81 static int parseSpriteName(const std::string &name);
82 static int parseDirectionName(const std::string &name);
83 
84 static ItemDbTypeT itemTypeFromString(const std::string &name)
85 {
86  const size_t sz = sizeof(itemTypeMap) / sizeof(itemTypeMap[0]);
87  for (size_t f = 0; f < sz; f ++)
88  {
89  const ItemTypeMap &type = itemTypeMap[f];
90  if (type.name == name)
91  return type.type;
92  }
93  logger->log("Unknown item type: " + name);
94  return ItemDbType::UNUSABLE;
95 }
96 
97 static std::string useButtonFromItemType(const ItemDbTypeT &type)
98 {
99  const size_t sz = sizeof(itemTypeMap) / sizeof(itemTypeMap[0]);
100  for (size_t f = 0; f < sz; f ++)
101  {
102  const ItemTypeMap &item = itemTypeMap[f];
103  if (item.type == type)
104  {
105  if (item.useButton.empty())
106  return std::string();
107  return gettext(item.useButton.c_str());
108  }
109  }
110  logger->log("Unknown item type");
111  return std::string();
112 }
113 
114 static std::string useButton2FromItemType(const ItemDbTypeT &type)
115 {
116  const size_t sz = sizeof(itemTypeMap) / sizeof(itemTypeMap[0]);
117  for (size_t f = 0; f < sz; f ++)
118  {
119  const ItemTypeMap &item = itemTypeMap[f];
120  if (item.type == type)
121  {
122  if (item.useButton2.empty())
123  return std::string();
124  return gettext(item.useButton2.c_str());
125  }
126  }
127  logger->log("Unknown item type");
128  return std::string();
129 }
130 
131 static void initStatic()
132 {
133  mConstructed = true;
135  mSoundNames["strike"] = ItemSoundEvent::MISS;
145 }
146 
148 {
149  if (mLoaded)
150  unload();
151 
152  logger->log1("Initializing item database...");
153 
154  if (!mConstructed)
155  initStatic();
156 
157  int tagNum = 0;
158 
159  mTags.clear();
160  mTagNames.clear();
161  mTagNames.push_back("All");
162  mTagNames.push_back("Usable");
163  mTagNames.push_back("Unusable");
164  mTagNames.push_back("Equipment");
165  mTags["All"] = tagNum ++;
166  mTags["Usable"] = tagNum ++;
167  mTags["Unusable"] = tagNum ++;
168  mTags["Equipment"] = tagNum ++;
169 
170  mUnknown = new ItemInfo;
171  // TRANSLATORS: item name
172  mUnknown->setName(_("Unknown item"));
174  std::string errFile = paths.getStringValue("spriteErrorFile");
175  mUnknown->setSprite(errFile, Gender::MALE, 0);
176  mUnknown->setSprite(errFile, Gender::FEMALE, 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 
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"]);
451  itemInfo->setProtected(XML::getBoolProperty(
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);
566  itemInfo->setPickupCursor(XML::getProperty(
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 
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 }
979 
980 static void loadSoundRef(ItemInfo *const itemInfo, XmlNodeConstPtr node)
981 {
982  if (node == nullptr ||
983  !XmlHaveChildContent(node))
984  {
985  return;
986  }
987  const std::string event = XML::getProperty(node, "event", "");
988  const std::map<std::string, ItemSoundEvent::Type>::const_iterator
989  it = mSoundNames.find(event);
990  if (it != mSoundNames.end())
991  {
992  const int delay = XML::getProperty(node, "delay", 0);
993  const std::string filename = XmlChildContent(node);
994  itemInfo->addSound((*it).second, filename, delay);
995  }
996  else
997  {
998  reportAlways("ItemDB: Ignoring unknown sound event '%s'",
999  event.c_str())
1000  }
1001 }
1002 
1003 static void loadFloorSprite(SpriteDisplay &display,
1004  XmlNodeConstPtrConst floorNode)
1005 {
1006  if (floorNode == nullptr)
1007  return;
1008  for_each_xml_child_node(spriteNode, floorNode)
1009  {
1010  if (!XmlHaveChildContent(spriteNode))
1011  continue;
1012  if (xmlNameEqual(spriteNode, "sprite"))
1013  {
1014  SpriteReference *const currentSprite = new SpriteReference;
1015  currentSprite->sprite = XmlChildContent(spriteNode);
1016  currentSprite->variant
1017  = XML::getProperty(spriteNode, "variant", 0);
1018  display.sprites.push_back(currentSprite);
1019  }
1020  else if (xmlNameEqual(spriteNode, "particlefx"))
1021  {
1022  display.particles.push_back(XmlChildContent(spriteNode));
1023  }
1024  }
1025 }
1026 
1028  XmlNodeConstPtr replaceNode)
1029 {
1030  if (replaceNode == nullptr)
1031  return;
1032  const std::string removeSprite = XML::getProperty(
1033  replaceNode, "sprite", "");
1034  const int direction = parseDirectionName(XML::getProperty(
1035  replaceNode, "direction", "all"));
1036 
1037  itemInfo->setRemoveSprites();
1038 
1039  switch (direction)
1040  {
1041  case -1:
1042  {
1043  if (removeSprite.empty())
1044  { // remove all sprites
1045  for (int f = 0; f < 10; f ++)
1046  {
1047  for (int sprite = 0; sprite < 13; sprite ++)
1048  itemInfo->addReplaceSprite(sprite, f);
1049  }
1050  }
1051  else
1052  { // replace only given sprites
1053  for (int f = 0; f < 10; f ++)
1054  {
1055  IntMap *const mapList = itemInfo->addReplaceSprite(
1056  parseSpriteName(removeSprite), f);
1057  if (mapList == nullptr)
1058  continue;
1059  for_each_xml_child_node(itemNode, replaceNode)
1060  {
1061  if (xmlNameEqual(itemNode, "item"))
1062  {
1063  const int from = XML::getProperty(
1064  itemNode, "from", 0);
1065  const int to = XML::getProperty(
1066  itemNode, "to", 1);
1067  (*mapList)[from] = to;
1068  }
1069  }
1070  }
1071  }
1072  break;
1073  }
1074  case -2:
1075  {
1076  itemInfo->addReplaceSprite(parseSpriteName(
1077  removeSprite), SpriteDirection::DOWN);
1078  itemInfo->addReplaceSprite(parseSpriteName(
1079  removeSprite), SpriteDirection::DOWNLEFT);
1080  itemInfo->addReplaceSprite(parseSpriteName(
1081  removeSprite), SpriteDirection::DOWNRIGHT);
1082 
1083  for_each_xml_child_node(itemNode, replaceNode)
1084  {
1085  if (xmlNameEqual(itemNode, "item"))
1086  {
1087  const int from = XML::getProperty(itemNode, "from", 0);
1088  const int to = XML::getProperty(itemNode, "to", 1);
1089  IntMap *mapList = itemInfo->addReplaceSprite(
1090  parseSpriteName(removeSprite), SpriteDirection::DOWN);
1091  if (mapList != nullptr)
1092  (*mapList)[from] = to;
1093 
1094  mapList = itemInfo->addReplaceSprite(parseSpriteName(
1095  removeSprite), SpriteDirection::DOWNLEFT);
1096  if (mapList != nullptr)
1097  (*mapList)[from] = to;
1098 
1099  mapList = itemInfo->addReplaceSprite(parseSpriteName(
1100  removeSprite), SpriteDirection::DOWNRIGHT);
1101  if (mapList != nullptr)
1102  (*mapList)[from] = to;
1103  }
1104  }
1105  break;
1106  }
1107  case -3:
1108  {
1109  itemInfo->addReplaceSprite(parseSpriteName(
1110  removeSprite), SpriteDirection::UP);
1111  itemInfo->addReplaceSprite(parseSpriteName(
1112  removeSprite), SpriteDirection::UPLEFT);
1113  itemInfo->addReplaceSprite(parseSpriteName(
1114  removeSprite), SpriteDirection::UPRIGHT);
1115 
1116  for_each_xml_child_node(itemNode, replaceNode)
1117  {
1118  if (xmlNameEqual(itemNode, "item"))
1119  {
1120  const int from = XML::getProperty(itemNode, "from", 0);
1121  const int to = XML::getProperty(itemNode, "to", 1);
1122  IntMap *mapList = itemInfo->addReplaceSprite(
1123  parseSpriteName(removeSprite), SpriteDirection::UP);
1124  if (mapList != nullptr)
1125  (*mapList)[from] = to;
1126 
1127  mapList = itemInfo->addReplaceSprite(parseSpriteName(
1128  removeSprite), SpriteDirection::UPLEFT);
1129  if (mapList != nullptr)
1130  (*mapList)[from] = to;
1131 
1132  mapList = itemInfo->addReplaceSprite(parseSpriteName(
1133  removeSprite), SpriteDirection::UPRIGHT);
1134  if (mapList != nullptr)
1135  (*mapList)[from] = to;
1136  }
1137  }
1138  break;
1139  }
1140  default:
1141  {
1142  IntMap *const mapList = itemInfo->addReplaceSprite(
1143  parseSpriteName(removeSprite), direction);
1144  if (mapList == nullptr)
1145  return;
1146  for_each_xml_child_node(itemNode, replaceNode)
1147  {
1148  if (xmlNameEqual(itemNode, "item"))
1149  {
1150  const int from = XML::getProperty(itemNode, "from", 0);
1151  const int to = XML::getProperty(itemNode, "to", 1);
1152  (*mapList)[from] = to;
1153  }
1154  }
1155  break;
1156  }
1157  }
1158 }
1159 
1160 static void loadOrderSprite(ItemInfo *const itemInfo,
1161  XmlNodeConstPtr node,
1162  const bool drawAfter)
1163 {
1164  const int sprite = parseSpriteName(XML::getProperty(node, "name", ""));
1165  const int priority = XML::getProperty(node, "priority", 0);
1166 
1167  const int direction = parseDirectionName(XML::getProperty(
1168  node, "direction", "all"));
1169  if (drawAfter)
1170  itemInfo->setDrawAfter(direction, sprite);
1171  else
1172  itemInfo->setDrawBefore(direction, sprite);
1173  itemInfo->setDrawPriority(direction, priority);
1174 }
1175 
1176 std::string ItemDB::getNamesStr(const STD_VECTOR<int> &parts)
1177 {
1178  std::string str;
1179  FOR_EACH (STD_VECTOR<int>::const_iterator, it, parts)
1180  {
1181  const int id = *it;
1182  if (exists(id))
1183  {
1184  if (!str.empty())
1185  str.append(",");
1186  str.append(get(id).getName());
1187  }
1188  }
1189  return str;
1190 }
1191 
1193 {
1194  return mNumberOfHairstyles;
1195 }
1196 
1197 #ifdef UNITTESTS
1198 ItemDB::NamedItemInfos &ItemDB::getNamedItemInfosTest()
1199 {
1200  return mNamedItemInfos;
1201 }
1202 
1203 ItemDB::ItemInfos &ItemDB::getItemInfosTest()
1204 {
1205  return mItemInfos;
1206 }
1207 #endif // UNITTESTS
static void loadXmlFile(const std::string &file, const std::string &name, BadgesInfos &arr, const SkipError skipError)
Definition: badgesdb.cpp:43
const BeingTypeId BeingTypeId_zero
Definition: beingtypeid.h:30
#define reportAlways(...)
Definition: checkutils.h:253
std::string getStringValue(const std::string &key) const
int getIntValue(const std::string &key) const
const std::string & getDescription() const
Definition: iteminfo.h:100
std::string getColorsListName() const
Definition: iteminfo.h:279
const std::string & getWaterAttackAction() const
Definition: iteminfo.h:192
const std::string & getName() const
Definition: iteminfo.h:74
const std::string & getUseButton() const
Definition: iteminfo.h:118
ItemColor getCardColor() const
Definition: iteminfo.h:252
int getWeight() const
Definition: iteminfo.h:133
int getCriticalHitEffectId() const
Definition: iteminfo.h:177
ItemDbTypeT getType() const
Definition: iteminfo.h:127
int getMissEffectId() const
Definition: iteminfo.h:183
const MissileInfo & getMissileConst() const
Definition: iteminfo.h:165
int getAttackRange() const
Definition: iteminfo.h:198
const std::string & getEffect() const
Definition: iteminfo.h:109
void setSprite(const std::string &animationFile, const GenderT gender, const int race)
Definition: iteminfo.cpp:406
std::string getIconColorsListName() const
Definition: iteminfo.h:282
const std::string & getRideAttackAction() const
Definition: iteminfo.h:195
int getHitEffectId() const
Definition: iteminfo.h:171
void addTag(const int tag)
Definition: iteminfo.h:226
void setDisplay(const SpriteDisplay &display)
Definition: iteminfo.h:91
const std::string & getAttackAction() const
Definition: iteminfo.h:186
const std::string & getSkyAttackAction() const
Definition: iteminfo.h:189
int getView() const
Definition: iteminfo.h:136
void setName(const std::string &name)
Definition: iteminfo.h:71
void log(const char *const log_text,...)
Definition: logger.cpp:269
void log1(const char *const log_text)
Definition: logger.cpp:238
xmlNodePtr rootNode()
Definition: libxml.cpp:169
Configuration paths
static const int mapTileSize
Definition: map.h:27
#define delete2(var)
Definition: delete2.h:25
void delete_all(Container &c)
Definition: dtor.h:56
#define FOR_EACH(type, iter, array)
Definition: foreach.h:25
#define _(s)
Definition: gettext.h:35
#define fromInt(val, name)
Definition: intdefines.h:46
std::map< int, int > IntMap
Definition: intmap.h:27
uint16_t ItemColor
Definition: itemcolor.h:30
static std::string useButtonFromItemType(const ItemDbTypeT &type)
Definition: itemdb.cpp:97
static int parseSpriteName(const std::string &name)
Definition: itemdb.cpp:839
static void loadSpriteRef(ItemInfo *const itemInfo, const xmlNodePtr node)
Definition: itemdb.cpp:965
#define CHECK_PARAM(param)
static bool getIsEquipment(const ItemDbTypeT type)
Definition: itemdb.cpp:235
static void loadReplaceSprite(ItemInfo *const itemInfo, const xmlNodePtr replaceNode)
Definition: itemdb.cpp:1027
static std::string useButton2FromItemType(const ItemDbTypeT &type)
Definition: itemdb.cpp:114
static void loadOrderSprite(ItemInfo *const itemInfo, const xmlNodePtr node, const bool drawAfter)
Definition: itemdb.cpp:1160
static void initStatic()
Definition: itemdb.cpp:131
static void loadSoundRef(ItemInfo *const itemInfo, const xmlNodePtr node)
Definition: itemdb.cpp:980
static void loadFloorSprite(SpriteDisplay &display, const xmlNode *const node)
Definition: itemdb.cpp:1003
static int parseDirectionName(const std::string &name)
Definition: itemdb.cpp:903
static void loadMenu(xmlNode *const parentNode, std::vector< ItemMenuItem > &menu)
Definition: itemdb.cpp:212
static ItemDbTypeT itemTypeFromString(const std::string &name)
Definition: itemdb.cpp:84
ItemDbType ::T ItemDbTypeT
Definition: itemdbtype.h:49
std::map< std::string, ItemFieldType * > ItemFieldInfos
ItemTypeMap itemTypeMap[]
void readItemStatsString(std::string &effect, const xmlNodePtr node, const ItemFieldInfos &fields)
#define for_each_xml_child_node(var, parent)
Definition: libxml.h:161
#define A_NONNULL(...)
Definition: localconsts.h:168
Logger * logger
Definition: logger.cpp:89
bool itemInfo(InputEvent &event)
Definition: commands.cpp:105
AttributesT get(const std::string &key)
void load()
Definition: avatardb.cpp:46
MenuTypeT menu
Definition: menu.cpp:28
const EmoteSprite * getSprite(const int id, const bool allowNull)
Definition: emotedb.cpp:292
@ MALE
Definition: gender.h:31
@ FEMALE
Definition: gender.h:32
const std::string & getName(const int id)
Definition: groupdb.cpp:344
bool exists(const int id)
Definition: itemdb.cpp:773
void load()
Definition: itemdb.cpp:147
void unload()
Definition: itemdb.cpp:759
int getNumOfHairstyles()
Definition: itemdb.cpp:1192
const StringVect & getTags()
Definition: itemdb.cpp:749
const ItemInfo & getEmpty()
Definition: itemdb.cpp:834
const ItemInfo & get(const int id)
Definition: itemdb.cpp:792
int getTagId(const std::string &tagName)
Definition: itemdb.cpp:754
const ItemDB::ItemInfos & getItemInfos()
Definition: itemdb.cpp:829
std::string getNamesStr(const std::vector< int > &parts)
Definition: itemdb.cpp:1176
std::map< std::string, ItemInfo * > NamedItemInfos
Definition: itemdb.h:64
void loadXmlFile(const std::string &fileName, int &tagNum, const SkipError skipError)
Definition: itemdb.cpp:262
std::map< int, ItemInfo * > ItemInfos
Definition: itemdb.h:63
@ EQUIPMENT_HEAD
Definition: itemdbtype.h:38
@ EQUIPMENT_RING
Definition: itemdbtype.h:41
@ EQUIPMENT_LEGS
Definition: itemdbtype.h:39
@ EQUIPMENT_TWO_HANDS_WEAPON
Definition: itemdbtype.h:34
@ EQUIPMENT_FEET
Definition: itemdbtype.h:43
@ EQUIPMENT_AMMO
Definition: itemdbtype.h:44
@ EQUIPMENT_SHIELD
Definition: itemdbtype.h:40
@ EQUIPMENT_ONE_HAND_WEAPON
Definition: itemdbtype.h:33
@ EQUIPMENT_CHARM
Definition: itemdbtype.h:45
@ EQUIPMENT_TORSO
Definition: itemdbtype.h:35
@ EQUIPMENT_ARMS
Definition: itemdbtype.h:36
@ EQUIPMENT_NECKLACE
Definition: itemdbtype.h:42
const ItemFieldInfos & getRequiredFields()
const ItemFieldInfos & getAddFields()
void unload()
Definition: net.cpp:180
ServerTypeT getNetworkType()
Definition: net.cpp:189
void getFilesInDir(const std::string &dir, StringVect &list, const std::string &ext)
Definition: tools.cpp:81
bool exists(std::string name)
Definition: fs.cpp:124
std::string langProperty(const xmlNodePtr node, const char *const name, const std::string &def)
Definition: libxml.cpp:258
float getFloatProperty(const xmlNodePtr node, const char *const name, float def)
Definition: libxml.cpp:211
bool getBoolProperty(const xmlNodePtr node, const char *const name, const bool def)
Definition: libxml.cpp:269
int getProperty(const xmlNodePtr node, const char *const name, int def)
Definition: libxml.cpp:174
int getIntProperty(const xmlNodePtr node, const char *const name, int def, const int min, const int max)
Definition: libxml.cpp:190
ItemDB::NamedItemInfos mNamedItemInfos
Definition: itemdb.cpp:59
std::map< std::string, ItemSoundEvent::Type > mSoundNames
Definition: itemdb.cpp:65
ItemDB::ItemInfos mItemInfos
Definition: itemdb.cpp:58
std::string empty
Definition: podict.cpp:26
const bool SkipError_false
Definition: skiperror.h:30
const bool SkipError_true
Definition: skiperror.h:30
bool SkipError
Definition: skiperror.h:30
#define STD_MOVE(var)
Definition: stdmove.h:28
std::map< std::string, int > StringIntMap
Definition: stringmap.h:28
std::string normalize(const std::string &name)
StringVect::const_iterator StringVectCIter
Definition: stringvector.h:31
std::vector< std::string > StringVect
Definition: stringvector.h:29
ItemDbTypeT type
Definition: itemtypemap.h:36
std::string useButton2
Definition: itemtypemap.h:38
std::string name
Definition: itemtypemap.h:35
std::string useButton
Definition: itemtypemap.h:37
std::string particle
Definition: missileinfo.h:41
float dieDistance
Definition: missileinfo.h:44
float speed
Definition: missileinfo.h:43
StringVect particles
Definition: spritedisplay.h:48
std::string image
Definition: spritedisplay.h:45
std::string floor
Definition: spritedisplay.h:46
std::vector< SpriteReference * > sprites
Definition: spritedisplay.h:47
std::string sprite
std::string fileName
Definition: testmain.cpp:39
const bool UseVirtFs_true
Definition: usevirtfs.h:30