GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
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 |
|||
26 |
#include "const/resources/map/map.h" |
||
27 |
|||
28 |
#include "configuration.h" |
||
29 |
|||
30 |
#include "enums/resources/spritedirection.h" |
||
31 |
|||
32 |
#include "fs/virtfs/tools.h" |
||
33 |
|||
34 |
#include "resources/iteminfo.h" |
||
35 |
#include "resources/itemmenuitem.h" |
||
36 |
#include "resources/itemtypemapdata.h" |
||
37 |
|||
38 |
#include "resources/db/itemfielddb.h" |
||
39 |
|||
40 |
#include "resources/sprite/spritereference.h" |
||
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 |
{ |
||
58 |
1 |
ItemDB::ItemInfos mItemInfos; |
|
59 |
1 |
ItemDB::NamedItemInfos mNamedItemInfos; |
|
60 |
ItemInfo *mUnknown = nullptr; |
||
61 |
bool mLoaded = false; |
||
62 |
bool mConstructed = false; |
||
63 |
1 |
StringVect mTagNames; |
|
64 |
1 |
StringIntMap mTags; |
|
65 |
1 |
std::map<std::string, ItemSoundEvent::Type> mSoundNames; |
|
66 |
int mNumberOfHairstyles = 1; |
||
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 |
72 |
static ItemDbTypeT itemTypeFromString(const std::string &name) |
|
85 |
{ |
||
86 |
72 |
const size_t sz = sizeof(itemTypeMap) / sizeof(itemTypeMap[0]); |
|
87 |
✓✗ | 1296 |
for (size_t f = 0; f < sz; f ++) |
88 |
{ |
||
89 |
1296 |
const ItemTypeMap &type = itemTypeMap[f]; |
|
90 |
✓✓ | 1296 |
if (type.name == name) |
91 |
72 |
return type.type; |
|
92 |
} |
||
93 |
logger->log("Unknown item type: " + name); |
||
94 |
return ItemDbType::UNUSABLE; |
||
95 |
} |
||
96 |
|||
97 |
30 |
static std::string useButtonFromItemType(const ItemDbTypeT &type) |
|
98 |
{ |
||
99 |
30 |
const size_t sz = sizeof(itemTypeMap) / sizeof(itemTypeMap[0]); |
|
100 |
✓✗ | 546 |
for (size_t f = 0; f < sz; f ++) |
101 |
{ |
||
102 |
546 |
const ItemTypeMap &item = itemTypeMap[f]; |
|
103 |
✓✓ | 546 |
if (item.type == type) |
104 |
{ |
||
105 |
✓✗ | 60 |
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 |
36 |
static std::string useButton2FromItemType(const ItemDbTypeT &type) |
|
115 |
{ |
||
116 |
36 |
const size_t sz = sizeof(itemTypeMap) / sizeof(itemTypeMap[0]); |
|
117 |
✓✗ | 648 |
for (size_t f = 0; f < sz; f ++) |
118 |
{ |
||
119 |
648 |
const ItemTypeMap &item = itemTypeMap[f]; |
|
120 |
✓✓ | 648 |
if (item.type == type) |
121 |
{ |
||
122 |
✓✗ | 72 |
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 |
1 |
static void initStatic() |
|
132 |
{ |
||
133 |
1 |
mConstructed = true; |
|
134 |
✓✗ | 4 |
mSoundNames["hit"] = ItemSoundEvent::HIT; |
135 |
✓✗ | 4 |
mSoundNames["strike"] = ItemSoundEvent::MISS; |
136 |
✓✗ | 4 |
mSoundNames["miss"] = ItemSoundEvent::MISS; |
137 |
✓✗ | 4 |
mSoundNames["use"] = ItemSoundEvent::USE; |
138 |
✓✗ | 4 |
mSoundNames["equip"] = ItemSoundEvent::EQUIP; |
139 |
✓✗ | 3 |
mSoundNames["unequip"] = ItemSoundEvent::UNEQUIP; |
140 |
✓✗ | 4 |
mSoundNames["drop"] = ItemSoundEvent::DROP; |
141 |
✓✗ | 4 |
mSoundNames["pickup"] = ItemSoundEvent::PICKUP; |
142 |
✓✗ | 4 |
mSoundNames["take"] = ItemSoundEvent::TAKE; |
143 |
✓✗ | 4 |
mSoundNames["put"] = ItemSoundEvent::PUT; |
144 |
✓✗ | 4 |
mSoundNames["usecard"] = ItemSoundEvent::USECARD; |
145 |
1 |
} |
|
146 |
|||
147 |
3 |
void ItemDB::load() |
|
148 |
{ |
||
149 |
✗✓ | 3 |
if (mLoaded) |
150 |
unload(); |
||
151 |
|||
152 |
3 |
logger->log1("Initializing item database..."); |
|
153 |
|||
154 |
✓✓ | 3 |
if (!mConstructed) |
155 |
1 |
initStatic(); |
|
156 |
|||
157 |
3 |
int tagNum = 0; |
|
158 |
|||
159 |
3 |
mTags.clear(); |
|
160 |
3 |
mTagNames.clear(); |
|
161 |
15 |
mTagNames.push_back("All"); |
|
162 |
15 |
mTagNames.push_back("Usable"); |
|
163 |
15 |
mTagNames.push_back("Unusable"); |
|
164 |
15 |
mTagNames.push_back("Equipment"); |
|
165 |
✓✗ | 12 |
mTags["All"] = tagNum ++; |
166 |
✓✗ | 12 |
mTags["Usable"] = tagNum ++; |
167 |
✓✗ | 12 |
mTags["Unusable"] = tagNum ++; |
168 |
✓✗ | 12 |
mTags["Equipment"] = tagNum ++; |
169 |
|||
170 |
✓✗ | 3 |
mUnknown = new ItemInfo; |
171 |
// TRANSLATORS: item name |
||
172 |
15 |
mUnknown->setName(_("Unknown item")); |
|
173 |
6 |
mUnknown->setDisplay(SpriteDisplay()); |
|
174 |
✓✗ | 15 |
std::string errFile = paths.getStringValue("spriteErrorFile"); |
175 |
✓✗ | 3 |
mUnknown->setSprite(errFile, Gender::MALE, 0); |
176 |
✓✗ | 3 |
mUnknown->setSprite(errFile, Gender::FEMALE, 0); |
177 |
✓✗✓✗ |
15 |
mUnknown->addTag(mTags["All"]); |
178 |
✓✗✓✗ |
15 |
loadXmlFile(paths.getStringValue("itemsFile"), |
179 |
tagNum, |
||
180 |
✓✗ | 3 |
SkipError_false); |
181 |
✓✗✓✗ |
15 |
loadXmlFile(paths.getStringValue("itemsPatchFile"), |
182 |
tagNum, |
||
183 |
✓✗ | 3 |
SkipError_true); |
184 |
|||
185 |
6 |
StringVect list; |
|
186 |
✓✗✓✗ ✓✗ |
24 |
VirtFs::getFilesInDir(paths.getStringValue("itemsPatchDir"), |
187 |
list, |
||
188 |
✓✗ | 3 |
".xml"); |
189 |
✓✗ | 15 |
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 |
✓✓✗✓ ✓✓ |
39 |
while (ItemDB::exists(-hairstyles) && |
196 |
✓✗ | 6 |
ItemDB::get(-hairstyles).getSprite(Gender::MALE, |
197 |
✓✗✓✗ ✓✗✓✓ ✓✓✗✗ ✗✗ |
45 |
BeingTypeId_zero) != paths.getStringValue("spriteErrorFile")) |
198 |
{ |
||
199 |
6 |
hairstyles ++; |
|
200 |
} |
||
201 |
3 |
mNumberOfHairstyles = hairstyles; |
|
202 |
|||
203 |
3 |
int races = 100; |
|
204 |
✓✓✗✓ ✓✓ |
114 |
while (ItemDB::exists(-races) && |
205 |
✓✗✓✗ |
42 |
ItemDB::get(-races).getSprite(Gender::MALE, BeingTypeId_zero) != |
206 |
✓✗✓✗ ✓✓✓✓ ✗✗✗✗ |
108 |
paths.getStringValue("spriteErrorFile")) |
207 |
{ |
||
208 |
21 |
races ++; |
|
209 |
} |
||
210 |
3 |
} |
|
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 |
{ |
||
239 |
case ItemDbType::EQUIPMENT_ONE_HAND_WEAPON: |
||
240 |
case ItemDbType::EQUIPMENT_TWO_HANDS_WEAPON: |
||
241 |
case ItemDbType::EQUIPMENT_TORSO: |
||
242 |
case ItemDbType::EQUIPMENT_ARMS: |
||
243 |
case ItemDbType::EQUIPMENT_HEAD: |
||
244 |
case ItemDbType::EQUIPMENT_LEGS: |
||
245 |
case ItemDbType::EQUIPMENT_SHIELD: |
||
246 |
case ItemDbType::EQUIPMENT_RING: |
||
247 |
case ItemDbType::EQUIPMENT_NECKLACE: |
||
248 |
case ItemDbType::EQUIPMENT_FEET: |
||
249 |
case ItemDbType::EQUIPMENT_AMMO: |
||
250 |
case ItemDbType::EQUIPMENT_CHARM: |
||
251 |
return true; |
||
252 |
case ItemDbType::UNUSABLE: |
||
253 |
case ItemDbType::USABLE: |
||
254 |
case ItemDbType::CARD: |
||
255 |
case ItemDbType::SPRITE_RACE: |
||
256 |
case ItemDbType::SPRITE_HAIR: |
||
257 |
default: |
||
258 |
return false; |
||
259 |
} |
||
260 |
} |
||
261 |
|||
262 |
6 |
void ItemDB::loadXmlFile(const std::string &fileName, |
|
263 |
int &tagNum, |
||
264 |
const SkipError skipError) |
||
265 |
{ |
||
266 |
✗✓ | 6 |
if (fileName.empty()) |
267 |
{ |
||
268 |
mLoaded = true; |
||
269 |
3 |
return; |
|
270 |
} |
||
271 |
|||
272 |
XML::Document doc(fileName, |
||
273 |
UseVirtFs_true, |
||
274 |
9 |
skipError); |
|
275 |
✓✗ | 6 |
XmlNodeConstPtrConst rootNode = doc.rootNode(); |
276 |
|||
277 |
✓✓✓✗ ✗✓✓✓ |
6 |
if ((rootNode == nullptr) || !xmlNameEqual(rootNode, "items")) |
278 |
{ |
||
279 |
✓✗ | 3 |
logger->log("ItemDB: Error while loading %s!", fileName.c_str()); |
280 |
3 |
mLoaded = true; |
|
281 |
3 |
return; |
|
282 |
} |
||
283 |
|||
284 |
const ItemFieldInfos &requiredFields = |
||
285 |
✓✗ | 3 |
ItemFieldDb::getRequiredFields(); |
286 |
const ItemFieldInfos &addFields = |
||
287 |
✓✗ | 3 |
ItemFieldDb::getAddFields(); |
288 |
|||
289 |
✓✓ | 102 |
for_each_xml_child_node(node, rootNode) |
290 |
{ |
||
291 |
✓✗✗✓ |
99 |
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 |
✓✗✓✓ |
99 |
if (!xmlNameEqual(node, "item")) |
299 |
continue; |
||
300 |
|||
301 |
✓✗ | 36 |
const int id = XML::getProperty(node, "id", 0); |
302 |
36 |
ItemInfo *itemInfo = nullptr; |
|
303 |
|||
304 |
✗✓ | 36 |
if (id == 0) |
305 |
{ |
||
306 |
reportAlways("ItemDB: Invalid or missing item ID in %s!", |
||
307 |
fileName.c_str()) |
||
308 |
continue; |
||
309 |
} |
||
310 |
✗✓ | 72 |
else if (mItemInfos.find(id) != mItemInfos.end()) |
311 |
{ |
||
312 |
logger->log("ItemDB: Redefinition of item ID %d", id); |
||
313 |
itemInfo = mItemInfos[id]; |
||
314 |
} |
||
315 |
✓✗ | 36 |
if (itemInfo == nullptr) |
316 |
✓✗✓✗ |
36 |
itemInfo = new ItemInfo; |
317 |
|||
318 |
✓✗✓✗ |
180 |
const std::string typeStr = XML::getProperty(node, "type", ""); |
319 |
✓✗ | 36 |
int weight = XML::getProperty(node, "weight", 0); |
320 |
✓✗ | 36 |
int view = XML::getProperty(node, "view", 0); |
321 |
✓✗ | 36 |
const int cardColor = XML::getProperty(node, "cardColor", -1); |
322 |
✓✗ | 36 |
const int inherit = XML::getProperty(node, "inherit", -1); |
323 |
|||
324 |
✓✗✓✗ |
180 |
std::string name = XML::langProperty(node, "name", ""); |
325 |
✓✗✓✗ |
180 |
std::string nameEn = XML::getProperty(node, "name", ""); |
326 |
✓✗✓✗ |
180 |
std::string image = XML::getProperty(node, "image", ""); |
327 |
✓✗✓✗ |
180 |
std::string floor = XML::getProperty(node, "floor", ""); |
328 |
✓✗✓✗ |
180 |
std::string description = XML::langProperty(node, "description", ""); |
329 |
✓✗✓✗ |
180 |
std::string attackAction = XML::getProperty(node, "attack-action", ""); |
330 |
std::string skyAttackAction = XML::getProperty( |
||
331 |
✓✗✓✗ |
180 |
node, "skyattack-action", ""); |
332 |
std::string waterAttackAction = XML::getProperty( |
||
333 |
✓✗✓✗ |
180 |
node, "waterattack-action", ""); |
334 |
std::string rideAttackAction = XML::getProperty( |
||
335 |
✓✗✓✗ |
180 |
node, "rideattack-action", ""); |
336 |
✓✗✓✗ |
180 |
std::string drawBefore = XML::getProperty(node, "drawBefore", ""); |
337 |
✓✗✓✗ |
180 |
std::string drawAfter = XML::getProperty(node, "drawAfter", ""); |
338 |
const int maxFloorOffset = XML::getIntProperty( |
||
339 |
✓✗ | 36 |
node, "maxFloorOffset", mapTileSize, 0, mapTileSize); |
340 |
const int maxFloorOffsetX = XML::getIntProperty( |
||
341 |
✓✗ | 36 |
node, "maxFloorOffsetX", maxFloorOffset, 0, mapTileSize); |
342 |
const int maxFloorOffsetY = XML::getIntProperty( |
||
343 |
✓✗ | 36 |
node, "maxFloorOffsetY", maxFloorOffset, 0, mapTileSize); |
344 |
✓✗✓✗ |
180 |
std::string useButton = XML::langProperty(node, "useButton", ""); |
345 |
✓✗✓✗ |
180 |
std::string useButton2 = XML::langProperty(node, "useButton2", ""); |
346 |
✓✗✓✗ |
180 |
std::string colors = XML::getProperty(node, "colors", ""); |
347 |
✓✗✓✗ |
180 |
std::string iconColors = XML::getProperty(node, "iconColors", ""); |
348 |
✓✗ | 36 |
if (iconColors.empty()) |
349 |
iconColors = colors; |
||
350 |
|||
351 |
// check for empty hair palete |
||
352 |
✓✓ | 36 |
if (id <= -1 && id > -100) |
353 |
{ |
||
354 |
✓✓ | 6 |
if (colors.empty()) |
355 |
colors = "hair"; |
||
356 |
✓✓ | 6 |
if (iconColors.empty()) |
357 |
iconColors = "hair"; |
||
358 |
} |
||
359 |
|||
360 |
✓✓✓✓ ✗✗ |
396 |
std::string tags[3]; |
361 |
✓✗ | 72 |
tags[0] = XML::getProperty(node, "tag", |
362 |
✓✗✓✗ |
216 |
XML::getProperty(node, "tag1", "")); |
363 |
✓✗✓✗ |
180 |
tags[1] = XML::getProperty(node, "tag2", ""); |
364 |
✓✗✓✗ |
180 |
tags[2] = XML::getProperty(node, "tag3", ""); |
365 |
|||
366 |
✓✗ | 36 |
const int drawPriority = XML::getProperty(node, "drawPriority", 0); |
367 |
|||
368 |
✓✗ | 36 |
int attackRange = XML::getProperty(node, "attack-range", 0); |
369 |
std::string missileParticle = XML::getProperty( |
||
370 |
✓✗✓✗ |
180 |
node, "missile-particle", ""); |
371 |
float missileZ = XML::getFloatProperty( |
||
372 |
✓✗ | 36 |
node, "missile-z", 32.0F); |
373 |
int missileLifeTime = XML::getProperty( |
||
374 |
✓✗ | 36 |
node, "missile-lifetime", 500); |
375 |
float missileSpeed = XML::getFloatProperty( |
||
376 |
✓✗ | 36 |
node, "missile-speed", 7.0F); |
377 |
float missileDieDistance = XML::getFloatProperty( |
||
378 |
✓✗ | 36 |
node, "missile-diedistance", 8.0F); |
379 |
✓✗✓✗ |
144 |
int hitEffectId = XML::getProperty(node, "hit-effect-id", |
380 |
✓✗ | 36 |
paths.getIntValue("hitEffectId")); |
381 |
✓✗✓✗ |
144 |
int criticalEffectId = XML::getProperty( |
382 |
node, "critical-hit-effect-id", |
||
383 |
✓✗ | 36 |
paths.getIntValue("criticalHitEffectId")); |
384 |
✓✗✓✗ |
144 |
int missEffectId = XML::getProperty(node, "miss-effect-id", |
385 |
✓✗ | 36 |
paths.getIntValue("missEffectId")); |
386 |
|||
387 |
72 |
SpriteDisplay display; |
|
388 |
36 |
display.image = image; |
|
389 |
✗✓ | 36 |
if (!floor.empty()) |
390 |
display.floor = STD_MOVE(floor); |
||
391 |
else |
||
392 |
display.floor = image; |
||
393 |
|||
394 |
36 |
const ItemInfo *inheritItemInfo = nullptr; |
|
395 |
|||
396 |
✗✓ | 36 |
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 |
72 |
itemInfo->setId(id); |
|
411 |
✗✓✗✗ ✗✓ |
36 |
if (name.empty() && (inheritItemInfo != nullptr)) |
412 |
name = inheritItemInfo->getName(); |
||
413 |
// TRANSLATORS: item info name |
||
414 |
✗✓✗✗ |
108 |
itemInfo->setName(name.empty() ? _("unnamed") : name); |
415 |
✗✓ | 36 |
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 |
✓✓✓✗ ✗✓ |
36 |
if (description.empty() && (inheritItemInfo != nullptr)) |
426 |
description = inheritItemInfo->getDescription(); |
||
427 |
36 |
itemInfo->setDescription(description); |
|
428 |
✗✓ | 36 |
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 |
✓✗ | 36 |
itemInfo->setType(itemTypeFromString(typeStr)); |
438 |
} |
||
439 |
✓✗ | 72 |
itemInfo->setType(itemTypeFromString(typeStr)); |
440 |
✓✓✓✗ ✗✓ |
36 |
if (useButton.empty() && (inheritItemInfo != nullptr)) |
441 |
useButton = inheritItemInfo->getUseButton(); |
||
442 |
✓✓ | 36 |
if (useButton.empty()) |
443 |
✓✗ | 60 |
useButton = useButtonFromItemType(itemInfo->getType()); |
444 |
36 |
itemInfo->setUseButton(useButton); |
|
445 |
✓✗✓✗ ✗✓ |
36 |
if (useButton2.empty() && (inheritItemInfo != nullptr)) |
446 |
useButton2 = inheritItemInfo->getUseButton(); |
||
447 |
✓✗ | 36 |
if (useButton2.empty()) |
448 |
✓✗ | 72 |
useButton2 = useButton2FromItemType(itemInfo->getType()); |
449 |
36 |
itemInfo->setUseButton2(useButton2); |
|
450 |
✓✗✓✗ |
180 |
itemInfo->addTag(mTags["All"]); |
451 |
✓✗ | 36 |
itemInfo->setProtected(XML::getBoolProperty( |
452 |
36 |
node, "sellProtected", false)); |
|
453 |
✓✓ | 36 |
if (cardColor != -1) |
454 |
6 |
itemInfo->setCardColor(fromInt(cardColor, ItemColor)); |
|
455 |
✗✓ | 30 |
else if (inheritItemInfo != nullptr) |
456 |
itemInfo->setCardColor(inheritItemInfo->getCardColor()); |
||
457 |
|||
458 |
✗✓✓ | 36 |
switch (itemInfo->getType()) |
459 |
{ |
||
460 |
case ItemDbType::USABLE: |
||
461 |
itemInfo->addTag(mTags["Usable"]); |
||
462 |
break; |
||
463 |
case ItemDbType::CARD: |
||
464 |
case ItemDbType::UNUSABLE: |
||
465 |
✓✗✓✗ |
30 |
itemInfo->addTag(mTags["Unusable"]); |
466 |
6 |
break; |
|
467 |
default: |
||
468 |
case ItemDbType::EQUIPMENT_ONE_HAND_WEAPON: |
||
469 |
case ItemDbType::EQUIPMENT_TWO_HANDS_WEAPON: |
||
470 |
case ItemDbType::EQUIPMENT_TORSO: |
||
471 |
case ItemDbType::EQUIPMENT_ARMS: |
||
472 |
case ItemDbType::EQUIPMENT_HEAD: |
||
473 |
case ItemDbType::EQUIPMENT_LEGS: |
||
474 |
case ItemDbType::EQUIPMENT_SHIELD: |
||
475 |
case ItemDbType::EQUIPMENT_RING: |
||
476 |
case ItemDbType::EQUIPMENT_NECKLACE: |
||
477 |
case ItemDbType::EQUIPMENT_FEET: |
||
478 |
case ItemDbType::EQUIPMENT_AMMO: |
||
479 |
case ItemDbType::EQUIPMENT_CHARM: |
||
480 |
case ItemDbType::SPRITE_RACE: |
||
481 |
case ItemDbType::SPRITE_HAIR: |
||
482 |
✓✗✓✗ |
150 |
itemInfo->addTag(mTags["Equipment"]); |
483 |
30 |
break; |
|
484 |
} |
||
485 |
✓✓ | 252 |
for (int f = 0; f < 3; f++) |
486 |
{ |
||
487 |
✗✓ | 216 |
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 |
72 |
std::string effect; |
|
499 |
✓✗ | 36 |
readItemStatsString(effect, node, requiredFields); |
500 |
✓✗ | 36 |
readItemStatsString(effect, node, addFields); |
501 |
✓✗✓✗ |
180 |
std::string temp = XML::langProperty(node, "effect", ""); |
502 |
✗✓✗✗ ✗✓ |
36 |
if (!effect.empty() && !temp.empty()) |
503 |
effect.append(" / "); |
||
504 |
36 |
effect.append(temp); |
|
505 |
|||
506 |
✗✓ | 36 |
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 |
72 |
itemInfo->setView(view); |
|
550 |
72 |
itemInfo->setWeight(weight); |
|
551 |
✓✗ | 36 |
itemInfo->setAttackAction(attackAction); |
552 |
✓✗ | 36 |
itemInfo->setSkyAttackAction(skyAttackAction); |
553 |
✓✗ | 36 |
itemInfo->setWaterAttackAction(waterAttackAction); |
554 |
✓✗ | 36 |
itemInfo->setRideAttackAction(rideAttackAction); |
555 |
72 |
itemInfo->setAttackRange(attackRange); |
|
556 |
72 |
itemInfo->setHitEffectId(hitEffectId); |
|
557 |
72 |
itemInfo->setCriticalHitEffectId(criticalEffectId); |
|
558 |
72 |
itemInfo->setMissEffectId(missEffectId); |
|
559 |
✓✗✓✗ |
36 |
itemInfo->setDrawBefore(-1, parseSpriteName(drawBefore)); |
560 |
✓✗✓✗ |
36 |
itemInfo->setDrawAfter(-1, parseSpriteName(drawAfter)); |
561 |
✓✗ | 36 |
itemInfo->setDrawPriority(-1, drawPriority); |
562 |
✓✗ | 36 |
itemInfo->setColorsList(colors); |
563 |
✓✗ | 36 |
itemInfo->setIconColorsList(iconColors); |
564 |
72 |
itemInfo->setMaxFloorOffsetX(maxFloorOffsetX); |
|
565 |
72 |
itemInfo->setMaxFloorOffsetY(maxFloorOffsetY); |
|
566 |
✓✗✓✗ |
180 |
itemInfo->setPickupCursor(XML::getProperty( |
567 |
36 |
node, "pickupCursor", "pickup")); |
|
568 |
|||
569 |
36 |
MissileInfo &missile = itemInfo->getMissile(); |
|
570 |
36 |
missile.particle = STD_MOVE(missileParticle); |
|
571 |
36 |
missile.z = missileZ; |
|
572 |
36 |
missile.lifeTime = missileLifeTime; |
|
573 |
36 |
missile.speed = missileSpeed; |
|
574 |
36 |
missile.dieDistance = missileDieDistance; |
|
575 |
|||
576 |
✓✓ | 264 |
for_each_xml_child_node(itemChild, node) |
577 |
{ |
||
578 |
✓✗✓✓ |
228 |
if (xmlNameEqual(itemChild, "sprite")) |
579 |
{ |
||
580 |
✓✗ | 51 |
loadSpriteRef(itemInfo, itemChild); |
581 |
} |
||
582 |
✓✗✗✓ |
177 |
else if (xmlNameEqual(itemChild, "particlefx")) |
583 |
{ |
||
584 |
if (XmlHaveChildContent(itemChild)) |
||
585 |
display.particles.push_back(XmlChildContent(itemChild)); |
||
586 |
} |
||
587 |
✓✗✓✓ |
177 |
else if (xmlNameEqual(itemChild, "sound")) |
588 |
{ |
||
589 |
✓✗ | 48 |
loadSoundRef(itemInfo, itemChild); |
590 |
} |
||
591 |
✓✗✗✓ |
129 |
else if (xmlNameEqual(itemChild, "floor")) |
592 |
{ |
||
593 |
loadFloorSprite(display, itemChild); |
||
594 |
} |
||
595 |
✓✗✗✓ |
129 |
else if (xmlNameEqual(itemChild, "replace")) |
596 |
{ |
||
597 |
loadReplaceSprite(itemInfo, itemChild); |
||
598 |
} |
||
599 |
✓✗✗✓ |
129 |
else if (xmlNameEqual(itemChild, "drawAfter")) |
600 |
{ |
||
601 |
loadOrderSprite(itemInfo, itemChild, true); |
||
602 |
} |
||
603 |
✓✗✗✓ |
129 |
else if (xmlNameEqual(itemChild, "drawBefore")) |
604 |
{ |
||
605 |
loadOrderSprite(itemInfo, itemChild, false); |
||
606 |
} |
||
607 |
✓✗✗✓ |
129 |
else if (xmlNameEqual(itemChild, "inventory")) |
608 |
{ |
||
609 |
loadMenu(itemChild, itemInfo->getInventoryMenu()); |
||
610 |
} |
||
611 |
✓✗✗✓ |
129 |
else if (xmlNameEqual(itemChild, "storage")) |
612 |
{ |
||
613 |
loadMenu(itemChild, itemInfo->getStorageMenu()); |
||
614 |
} |
||
615 |
✓✗✗✓ |
129 |
else if (xmlNameEqual(itemChild, "cart")) |
616 |
{ |
||
617 |
loadMenu(itemChild, itemInfo->getCartMenu()); |
||
618 |
} |
||
619 |
✓✗✗✓ |
129 |
else if (xmlNameEqual(itemChild, "addStats")) |
620 |
{ |
||
621 |
readItemStatsString(effect, itemChild, addFields); |
||
622 |
} |
||
623 |
✓✗✗✓ |
129 |
else if (xmlNameEqual(itemChild, "requireStats")) |
624 |
{ |
||
625 |
readItemStatsString(effect, itemChild, requiredFields); |
||
626 |
} |
||
627 |
} |
||
628 |
36 |
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 |
36 |
itemInfo->setDisplay(display); |
|
667 |
|||
668 |
✓✗ | 36 |
mItemInfos[id] = itemInfo; |
669 |
✓✗ | 36 |
if (!name.empty()) |
670 |
{ |
||
671 |
✓✗ | 72 |
temp = normalize(name); |
672 |
✓✗ | 36 |
mNamedItemInfos[temp] = itemInfo; |
673 |
} |
||
674 |
✓✗ | 36 |
if (!nameEn.empty()) |
675 |
{ |
||
676 |
✓✗ | 72 |
temp = normalize(nameEn); |
677 |
✓✗ | 36 |
mNamedItemInfos[temp] = itemInfo; |
678 |
} |
||
679 |
|||
680 |
✗✓ | 36 |
if (!attackAction.empty()) |
681 |
{ |
||
682 |
if (attackRange == 0) |
||
683 |
{ |
||
684 |
reportAlways("ItemDB: Missing attack range from weapon %i!", |
||
685 |
id) |
||
686 |
} |
||
687 |
} |
||
688 |
|||
689 |
36 |
STD_VECTOR<ItemMenuItem> &inventoryMenu = itemInfo->getInventoryMenu(); |
|
690 |
|||
691 |
✓✗ | 36 |
if (inventoryMenu.empty()) |
692 |
{ |
||
693 |
108 |
std::string name1 = itemInfo->getUseButton(); |
|
694 |
108 |
std::string name2 = itemInfo->getUseButton2(); |
|
695 |
✓✓✓✗ |
36 |
const bool isEquipment = getIsEquipment(itemInfo->getType()); |
696 |
|||
697 |
✗✓ | 36 |
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 |
✓✓ | 36 |
if (name1.empty()) |
713 |
{ |
||
714 |
// TRANSLATORS: popup menu item |
||
715 |
30 |
name1 = _("Use"); |
|
716 |
} |
||
717 |
✓✗ | 36 |
if (name2.empty()) |
718 |
{ |
||
719 |
// TRANSLATORS: popup menu item |
||
720 |
36 |
name2 = _("Use"); |
|
721 |
} |
||
722 |
} |
||
723 |
✓✗✓✗ ✓✗ |
288 |
inventoryMenu.push_back(ItemMenuItem( |
724 |
name1, |
||
725 |
name2, |
||
726 |
"useinv 'INVINDEX'", |
||
727 |
36 |
"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 |
✓✓✗✓ ✓✓ |
42 |
if (id >= 0 && typeStr != "other") |
738 |
{ |
||
739 |
✗✓✗✗ |
6 |
CHECK_PARAM(name) |
740 |
✗✓✗✗ |
6 |
CHECK_PARAM(description) |
741 |
✗✓✗✗ |
6 |
CHECK_PARAM(image) |
742 |
} |
||
743 |
#undef CHECK_PARAM |
||
744 |
} |
||
745 |
|||
746 |
3 |
mLoaded = true; |
|
747 |
} |
||
748 |
|||
749 |
1 |
const StringVect &ItemDB::getTags() |
|
750 |
{ |
||
751 |
1 |
return mTagNames; |
|
752 |
} |
||
753 |
|||
754 |
int ItemDB::getTagId(const std::string &tagName) |
||
755 |
{ |
||
756 |
return mTags[tagName]; |
||
757 |
} |
||
758 |
|||
759 |
204 |
void ItemDB::unload() |
|
760 |
{ |
||
761 |
204 |
logger->log1("Unloading item database..."); |
|
762 |
|||
763 |
✓✓ | 204 |
delete2(mUnknown) |
764 |
|||
765 |
204 |
delete_all(mItemInfos); |
|
766 |
204 |
mItemInfos.clear(); |
|
767 |
204 |
mNamedItemInfos.clear(); |
|
768 |
204 |
mTags.clear(); |
|
769 |
204 |
mTagNames.clear(); |
|
770 |
204 |
mLoaded = false; |
|
771 |
204 |
} |
|
772 |
|||
773 |
bool ItemDB::exists(const int id) |
||
774 |
{ |
||
775 |
✗✗✗✗ ✓✗✓✗ |
33 |
if (!mLoaded) |
776 |
return false; |
||
777 |
|||
778 |
66 |
const ItemInfos::const_iterator i = mItemInfos.find(id); |
|
779 |
66 |
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 |
32 |
const ItemInfo &ItemDB::get(const int id) |
|
793 |
{ |
||
794 |
✓✓ | 32 |
if (!mLoaded) |
795 |
2 |
load(); |
|
796 |
|||
797 |
64 |
const ItemInfos::const_iterator i = mItemInfos.find(id); |
|
798 |
|||
799 |
✗✓ | 32 |
if (i == mItemInfos.end()) |
800 |
{ |
||
801 |
reportAlways("ItemDB: Warning, unknown item ID# %d", id) |
||
802 |
return *mUnknown; |
||
803 |
} |
||
804 |
|||
805 |
32 |
return *(i->second); |
|
806 |
} |
||
807 |
|||
808 |
19 |
const ItemInfo &ItemDB::get(const std::string &name) |
|
809 |
{ |
||
810 |
✓✓ | 19 |
if (!mLoaded) |
811 |
1 |
load(); |
|
812 |
|||
813 |
38 |
const NamedItemInfos::const_iterator i = mNamedItemInfos.find( |
|
814 |
57 |
normalize(name)); |
|
815 |
|||
816 |
✗✓ | 19 |
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 |
19 |
return *(i->second); |
|
827 |
} |
||
828 |
|||
829 |
1 |
const ItemDB::ItemInfos &ItemDB::getItemInfos() |
|
830 |
{ |
||
831 |
1 |
return mItemInfos; |
|
832 |
} |
||
833 |
|||
834 |
const ItemInfo &ItemDB::getEmpty() |
||
835 |
{ |
||
836 |
return *mUnknown; |
||
837 |
} |
||
838 |
|||
839 |
72 |
static int parseSpriteName(const std::string &name) |
|
840 |
{ |
||
841 |
72 |
int id = -1; |
|
842 |
✓✗✗✓ ✓✗ |
144 |
if (name == "race" || name == "type") |
843 |
{ |
||
844 |
id = 0; |
||
845 |
} |
||
846 |
✓✗✓✗ ✗✓✓✗ |
216 |
else if (name == "shoes" || name == "boot" || name == "boots") |
847 |
{ |
||
848 |
id = 1; |
||
849 |
} |
||
850 |
✓✗✓✗ ✗✓✓✗ |
216 |
else if (name == "bottomclothes" || name == "bottom" || name == "pants") |
851 |
{ |
||
852 |
id = 2; |
||
853 |
} |
||
854 |
✓✗✓✗ |
216 |
else if (name == "topclothes" || name == "top" |
855 |
✓✗✓✗ ✗✓ |
216 |
|| name == "torso" || name == "body") |
856 |
{ |
||
857 |
id = 3; |
||
858 |
} |
||
859 |
✓✗ | 72 |
else if (name == "misc1") |
860 |
{ |
||
861 |
id = 4; |
||
862 |
} |
||
863 |
✓✗✓✗ ✗✓✓✗ |
216 |
else if (name == "misc2" || name == "scarf" || name == "scarfs") |
864 |
{ |
||
865 |
id = 5; |
||
866 |
} |
||
867 |
✓✗ | 72 |
else if (name == "hair") |
868 |
{ |
||
869 |
id = 6; |
||
870 |
} |
||
871 |
✓✗✗✓ ✓✗ |
144 |
else if (name == "hat" || name == "hats") |
872 |
{ |
||
873 |
id = 7; |
||
874 |
} |
||
875 |
✓✗ | 72 |
else if (name == "wings") |
876 |
{ |
||
877 |
id = 8; |
||
878 |
} |
||
879 |
✓✗✗✓ ✓✗ |
144 |
else if (name == "glove" || name == "gloves") |
880 |
{ |
||
881 |
id = 9; |
||
882 |
} |
||
883 |
✓✗✗✓ ✓✗ |
144 |
else if (name == "weapon" || name == "weapons") |
884 |
{ |
||
885 |
id = 10; |
||
886 |
} |
||
887 |
✓✗✗✓ ✓✗ |
144 |
else if (name == "shield" || name == "shields") |
888 |
{ |
||
889 |
id = 11; |
||
890 |
} |
||
891 |
✓✗✗✓ ✓✗ |
144 |
else if (name == "amulet" || name == "amulets") |
892 |
{ |
||
893 |
id = 12; |
||
894 |
} |
||
895 |
✓✗✗✓ ✗✓ |
144 |
else if (name == "ring" || name == "rings") |
896 |
{ |
||
897 |
id = 13; |
||
898 |
} |
||
899 |
|||
900 |
72 |
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 |
||
909 |
if (Net::getNetworkType() == ServerType::TMWATHENA) |
||
910 |
id = -2; |
||
911 |
else |
||
912 |
#endif |
||
913 |
id = SpriteDirection::DOWN; |
||
914 |
} |
||
915 |
else if (name == "downleft" || name == "leftdown") |
||
916 |
{ |
||
917 |
id = SpriteDirection::DOWNLEFT; |
||
918 |
} |
||
919 |
else if (name == "left") |
||
920 |
{ |
||
921 |
id = SpriteDirection::LEFT; |
||
922 |
} |
||
923 |
else if (name == "upleft" || name == "leftup") |
||
924 |
{ |
||
925 |
id = SpriteDirection::UPLEFT; |
||
926 |
} |
||
927 |
else if (name == "up") |
||
928 |
{ |
||
929 |
#ifdef TMWA_SUPPORT |
||
930 |
if (Net::getNetworkType() == ServerType::TMWATHENA) |
||
931 |
id = -3; |
||
932 |
else |
||
933 |
#endif |
||
934 |
id = SpriteDirection::UP; |
||
935 |
} |
||
936 |
else if (name == "upright" || name == "rightup") |
||
937 |
{ |
||
938 |
id = SpriteDirection::UPRIGHT; |
||
939 |
} |
||
940 |
else if (name == "right") |
||
941 |
{ |
||
942 |
id = SpriteDirection::RIGHT; |
||
943 |
} |
||
944 |
else if (name == "downright" || name == "rightdown") |
||
945 |
{ |
||
946 |
id = SpriteDirection::DOWNRIGHT; |
||
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 |
51 |
static void loadSpriteRef(ItemInfo *const itemInfo, XmlNodeConstPtr node) |
|
966 |
{ |
||
967 |
✓✗ | 255 |
const std::string gender = XML::getProperty(node, "gender", "unisex"); |
968 |
✓✗✓✗ ✓✗ |
51 |
if ((node == nullptr) || !XmlHaveChildContent(node)) |
969 |
return; |
||
970 |
|||
971 |
✓✗ | 204 |
const std::string filename = XmlChildContent(node); |
972 |
|||
973 |
✓✗ | 51 |
const int race = XML::getProperty(node, "race", 0); |
974 |
✓✗✓✓ ✓✓ |
102 |
if (gender == "male" || gender == "unisex") |
975 |
✓✗ | 30 |
itemInfo->setSprite(filename, Gender::MALE, race); |
976 |
✓✓✓✗ ✓✗ |
81 |
if (gender == "female" || gender == "unisex") |
977 |
✓✗ | 51 |
itemInfo->setSprite(filename, Gender::FEMALE, race); |
978 |
} |
||
979 |
|||
980 |
48 |
static void loadSoundRef(ItemInfo *const itemInfo, XmlNodeConstPtr node) |
|
981 |
{ |
||
982 |
✓✗✓✗ |
96 |
if (node == nullptr || |
983 |
✓✗ | 96 |
!XmlHaveChildContent(node)) |
984 |
{ |
||
985 |
return; |
||
986 |
} |
||
987 |
✓✗ | 240 |
const std::string event = XML::getProperty(node, "event", ""); |
988 |
const std::map<std::string, ItemSoundEvent::Type>::const_iterator |
||
989 |
96 |
it = mSoundNames.find(event); |
|
990 |
✓✗ | 48 |
if (it != mSoundNames.end()) |
991 |
{ |
||
992 |
✓✗ | 48 |
const int delay = XML::getProperty(node, "delay", 0); |
993 |
✓✗ | 192 |
const std::string filename = XmlChildContent(node); |
994 |
✓✗ | 48 |
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 |
|||
1027 |
static void loadReplaceSprite(ItemInfo *const itemInfo, |
||
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 |
|||
1192 |
int ItemDB::getNumOfHairstyles() |
||
1193 |
{ |
||
1194 |
return mNumberOfHairstyles; |
||
1195 |
} |
||
1196 |
|||
1197 |
#ifdef UNITTESTS |
||
1198 |
5 |
ItemDB::NamedItemInfos &ItemDB::getNamedItemInfosTest() |
|
1199 |
{ |
||
1200 |
5 |
return mNamedItemInfos; |
|
1201 |
} |
||
1202 |
|||
1203 |
5 |
ItemDB::ItemInfos &ItemDB::getItemInfosTest() |
|
1204 |
{ |
||
1205 |
5 |
return mItemInfos; |
|
1206 |
✓✗✓✗ |
3 |
} |
1207 |
#endif // UNITTESTS |
Generated by: GCOVR (Version 3.3) |