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