1 // _________ __ __
2 // / _____// |_____________ _/ |______ ____ __ __ ______
3 // \_____ \\ __\_ __ \__ \\ __\__ \ / ___\| | \/ ___/
4 // / \| | | | \// __ \| | / __ \_/ /_/ > | /\___ |
5 // /_______ /|__| |__| (____ /__| (____ /\___ /|____//____ >
6 // \/ \/ \//_____/ \/
7 // ______________________ ______________________
8 // T H E W A R B E G I N S
9 // Stratagus - A free fantasy real time strategy game engine
10 //
11 /**@name item.cpp - The items. */
12 //
13 // (c) Copyright 2015-2019 by Andrettin
14 //
15 // This program is free software; you can redistribute it and/or modify
16 // it under the terms of the GNU General Public License as published by
17 // the Free Software Foundation; only version 2 of the License.
18 //
19 // This program is distributed in the hope that it will be useful,
20 // but WITHOUT ANY WARRANTY; without even the implied warranty of
21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 // GNU General Public License for more details.
23 //
24 // You should have received a copy of the GNU General Public License
25 // along with this program; if not, write to the Free Software
26 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
27 // 02111-1307, USA.
28 //
29
30 //@{
31
32 /*----------------------------------------------------------------------------
33 -- Includes
34 ----------------------------------------------------------------------------*/
35
36 #include "stratagus.h"
37
38 #include "item.h"
39
40 #include <ctype.h>
41
42 #include <string>
43 #include <map>
44
45 #include "character.h"
46 #include "config.h"
47 #include "game.h"
48 #include "network.h"
49 #include "parameters.h"
50 #include "player.h"
51 #include "spells.h"
52 #include "unit/unit.h"
53 #include "unit/unit_manager.h"
54 #include "unit/unittype.h"
55 #include "upgrade/upgrade.h"
56 #include "upgrade/upgrade_modifier.h"
57
58 /*----------------------------------------------------------------------------
59 -- Variables
60 ----------------------------------------------------------------------------*/
61
62 std::vector<CUniqueItem *> UniqueItems;
63
64 /*----------------------------------------------------------------------------
65 -- Functions
66 ----------------------------------------------------------------------------*/
67
GetItemSlotIdByName(const std::string & item_slot)68 int GetItemSlotIdByName(const std::string &item_slot)
69 {
70 if (item_slot == "weapon") {
71 return WeaponItemSlot;
72 } else if (item_slot == "shield") {
73 return ShieldItemSlot;
74 } else if (item_slot == "helmet") {
75 return HelmetItemSlot;
76 } else if (item_slot == "armor") {
77 return ArmorItemSlot;
78 } else if (item_slot == "gloves") {
79 return GlovesItemSlot;
80 } else if (item_slot == "boots") {
81 return BootsItemSlot;
82 } else if (item_slot == "belt") {
83 return BeltItemSlot;
84 } else if (item_slot == "amulet") {
85 return AmuletItemSlot;
86 } else if (item_slot == "ring") {
87 return RingItemSlot;
88 } else if (item_slot == "arrows") {
89 return ArrowsItemSlot;
90 }
91
92 return -1;
93 }
94
GetItemSlotNameById(int item_slot)95 std::string GetItemSlotNameById(int item_slot)
96 {
97 if (item_slot == WeaponItemSlot) {
98 return "weapon";
99 } else if (item_slot == ShieldItemSlot) {
100 return "shield";
101 } else if (item_slot == HelmetItemSlot) {
102 return "helmet";
103 } else if (item_slot == ArmorItemSlot) {
104 return "armor";
105 } else if (item_slot == GlovesItemSlot) {
106 return "gloves";
107 } else if (item_slot == BootsItemSlot) {
108 return "boots";
109 } else if (item_slot == BeltItemSlot) {
110 return "belt";
111 } else if (item_slot == AmuletItemSlot) {
112 return "amulet";
113 } else if (item_slot == RingItemSlot) {
114 return "ring";
115 } else if (item_slot == ArrowsItemSlot) {
116 return "arrows";
117 }
118
119 return "";
120 }
121
GetItemClassIdByName(const std::string & item_class)122 int GetItemClassIdByName(const std::string &item_class)
123 {
124 if (item_class == "dagger") {
125 return DaggerItemClass;
126 } else if (item_class == "sword") {
127 return SwordItemClass;
128 } else if (item_class == "thrusting-sword") {
129 return ThrustingSwordItemClass;
130 } else if (item_class == "axe") {
131 return AxeItemClass;
132 } else if (item_class == "mace") {
133 return MaceItemClass;
134 } else if (item_class == "spear") {
135 return SpearItemClass;
136 } else if (item_class == "bow") {
137 return BowItemClass;
138 } else if (item_class == "throwing-axe") {
139 return ThrowingAxeItemClass;
140 } else if (item_class == "javelin") {
141 return JavelinItemClass;
142 } else if (item_class == "gun") {
143 return GunItemClass;
144 } else if (item_class == "shield") {
145 return ShieldItemClass;
146 } else if (item_class == "horn") {
147 return HornItemClass;
148 } else if (item_class == "helmet") {
149 return HelmetItemClass;
150 } else if (item_class == "armor") {
151 return ArmorItemClass;
152 } else if (item_class == "cloak") {
153 return CloakItemClass;
154 } else if (item_class == "gloves") {
155 return GlovesItemClass;
156 } else if (item_class == "belt") {
157 return BeltItemClass;
158 } else if (item_class == "boots") {
159 return BootsItemClass;
160 } else if (item_class == "amulet") {
161 return AmuletItemClass;
162 } else if (item_class == "ring") {
163 return RingItemClass;
164 } else if (item_class == "arrows") {
165 return ArrowsItemClass;
166 } else if (item_class == "food") {
167 return FoodItemClass;
168 } else if (item_class == "potion") {
169 return PotionItemClass;
170 } else if (item_class == "scroll") {
171 return ScrollItemClass;
172 } else if (item_class == "book") {
173 return BookItemClass;
174 }
175
176 return -1;
177 }
178
GetItemClassNameById(int item_class)179 std::string GetItemClassNameById(int item_class)
180 {
181 if (item_class == DaggerItemClass) {
182 return "dagger";
183 } else if (item_class == SwordItemClass) {
184 return "sword";
185 } else if (item_class == ThrustingSwordItemClass) {
186 return "thrusting-sword";
187 } else if (item_class == AxeItemClass) {
188 return "axe";
189 } else if (item_class == MaceItemClass) {
190 return "mace";
191 } else if (item_class == SpearItemClass) {
192 return "spear";
193 } else if (item_class == BowItemClass) {
194 return "bow";
195 } else if (item_class == ThrowingAxeItemClass) {
196 return "throwing-axe";
197 } else if (item_class == JavelinItemClass) {
198 return "javelin";
199 } else if (item_class == GunItemClass) {
200 return "gun";
201 } else if (item_class == ShieldItemClass) {
202 return "shield";
203 } else if (item_class == HornItemClass) {
204 return "horn";
205 } else if (item_class == HelmetItemClass) {
206 return "helmet";
207 } else if (item_class == ArmorItemClass) {
208 return "armor";
209 } else if (item_class == CloakItemClass) {
210 return "cloak";
211 } else if (item_class == GlovesItemClass) {
212 return "gloves";
213 } else if (item_class == BeltItemClass) {
214 return "belt";
215 } else if (item_class == BootsItemClass) {
216 return "boots";
217 } else if (item_class == AmuletItemClass) {
218 return "amulet";
219 } else if (item_class == RingItemClass) {
220 return "ring";
221 } else if (item_class == ArrowsItemClass) {
222 return "arrows";
223 } else if (item_class == FoodItemClass) {
224 return "food";
225 } else if (item_class == PotionItemClass) {
226 return "potion";
227 } else if (item_class == ScrollItemClass) {
228 return "scroll";
229 } else if (item_class == BookItemClass) {
230 return "book";
231 }
232
233 return "";
234 }
235
GetItemClassSlot(int item_class)236 int GetItemClassSlot(int item_class)
237 {
238 if (
239 item_class == DaggerItemClass
240 || item_class == SwordItemClass
241 || item_class == ThrustingSwordItemClass
242 || item_class == AxeItemClass
243 || item_class == MaceItemClass
244 || item_class == SpearItemClass
245 || item_class == BowItemClass
246 || item_class == ThrowingAxeItemClass
247 || item_class == JavelinItemClass
248 || item_class == GunItemClass
249 ) {
250 return WeaponItemSlot;
251 } else if (item_class == ShieldItemClass || item_class == HornItemClass) {
252 return ShieldItemSlot;
253 } else if (item_class == HelmetItemClass) {
254 return HelmetItemSlot;
255 } else if (item_class == ArmorItemClass || item_class == CloakItemClass) {
256 return ArmorItemSlot;
257 } else if (item_class == GlovesItemClass) {
258 return GlovesItemSlot;
259 } else if (item_class == BootsItemClass) {
260 return BootsItemSlot;
261 } else if (item_class == BeltItemClass) {
262 return BeltItemSlot;
263 } else if (item_class == AmuletItemClass) {
264 return AmuletItemSlot;
265 } else if (item_class == RingItemClass) {
266 return RingItemSlot;
267 } else if (item_class == ArrowsItemClass) {
268 return ArrowsItemSlot;
269 }
270
271 return -1;
272 }
273
IsItemClassConsumable(int item_class)274 bool IsItemClassConsumable(int item_class)
275 {
276 if (item_class == FoodItemClass || item_class == PotionItemClass || item_class == ScrollItemClass || item_class == BookItemClass) {
277 return true;
278 }
279
280 return false;
281 }
282
CanDrop() const283 bool CUniqueItem::CanDrop() const
284 {
285 // unique items cannot drop if a persistent hero owns them already, or if there's already one of them in the current scenario; unless it's a character-specific bound item, in which case it can still drop
286 if (!IsNetworkGame()) {
287 for (CCharacter *character : CCharacter::Characters) {
288 for (CPersistentItem *item : character->Items) {
289 if (item->Unique == this && !item->Bound) {
290 return false;
291 }
292 }
293 }
294
295 for (std::map<std::string, CCharacter *>::iterator iterator = CustomHeroes.begin(); iterator != CustomHeroes.end(); ++iterator) {
296 for (CPersistentItem *item : iterator->second->Items) {
297 if (item->Unique == this && !item->Bound) {
298 return false;
299 }
300 }
301 }
302 }
303
304 if (GameRunning) {
305 for (CUnitManager::Iterator it = UnitManager.begin(); it != UnitManager.end(); ++it) {
306 CUnit &unit = **it;
307 if (unit.Unique == this && !unit.Bound) {
308 return false;
309 }
310 }
311 }
312
313 return true;
314 }
315
GetIcon() const316 IconConfig CUniqueItem::GetIcon() const
317 {
318 if (this->Icon.Icon) {
319 return this->Icon;
320 } else {
321 return this->Type->Icon;
322 }
323 }
324
GetMagicLevel() const325 int CUniqueItem::GetMagicLevel() const
326 {
327 int magic_level = 0;
328
329 if (this->Prefix) {
330 magic_level += this->Prefix->MagicLevel;
331 }
332
333 if (this->Suffix) {
334 magic_level += this->Suffix->MagicLevel;
335 }
336
337 if (this->Set) {
338 magic_level += this->Set->MagicLevel;
339 }
340
341 if (this->Work) {
342 magic_level += this->Work->MagicLevel;
343 }
344
345 if (this->Elixir) {
346 magic_level += this->Elixir->MagicLevel;
347 }
348
349 return magic_level;
350 }
351
CleanUniqueItems()352 void CleanUniqueItems()
353 {
354 for (size_t i = 0; i < UniqueItems.size(); ++i) {
355 delete UniqueItems[i];
356 }
357 UniqueItems.clear();
358 }
359
GetUniqueItem(const std::string & item_ident)360 CUniqueItem *GetUniqueItem(const std::string &item_ident)
361 {
362 for (size_t i = 0; i < UniqueItems.size(); ++i) {
363 if (item_ident == UniqueItems[i]->Ident) {
364 return UniqueItems[i];
365 }
366 }
367 for (size_t i = 0; i < UniqueItems.size(); ++i) { // for backwards compatibility, search the name of the unique too
368 if (NameToIdent(item_ident) == UniqueItems[i]->Ident) {
369 return UniqueItems[i];
370 }
371 }
372 return nullptr;
373 }
374
375 /**
376 ** @brief Process data provided by a configuration file
377 **
378 ** @param config_data The configuration data
379 */
ProcessConfigData(const CConfigData * config_data)380 void CPersistentItem::ProcessConfigData(const CConfigData *config_data)
381 {
382 bool is_equipped = false;
383
384 for (size_t i = 0; i < config_data->Properties.size(); ++i) {
385 std::string key = config_data->Properties[i].first;
386 std::string value = config_data->Properties[i].second;
387
388 if (key == "name") {
389 this->Name = value;
390 } else if (key == "type") {
391 value = FindAndReplaceString(value, "_", "-");
392 CUnitType *unit_type = UnitTypeByIdent(value);
393 if (unit_type) {
394 this->Type = unit_type;
395 } else {
396 fprintf(stderr, "Unit type \"%s\" doesn't exist.\n", value.c_str());
397 }
398 } else if (key == "prefix") {
399 value = FindAndReplaceString(value, "_", "-");
400 CUpgrade *upgrade = CUpgrade::Get(value);
401 if (upgrade) {
402 this->Prefix = upgrade;
403 } else {
404 fprintf(stderr, "Upgrade \"%s\" doesn't exist.\n", value.c_str());
405 }
406 } else if (key == "suffix") {
407 value = FindAndReplaceString(value, "_", "-");
408 CUpgrade *upgrade = CUpgrade::Get(value);
409 if (upgrade) {
410 this->Suffix = upgrade;
411 } else {
412 fprintf(stderr, "Upgrade \"%s\" doesn't exist.\n", value.c_str());
413 }
414 } else if (key == "spell") {
415 value = FindAndReplaceString(value, "_", "-");
416 CSpell *spell = CSpell::GetSpell(value);
417 if (spell) {
418 this->Spell = spell;
419 } else {
420 fprintf(stderr, "Spell \"%s\" doesn't exist.\n", value.c_str());
421 }
422 } else if (key == "work") {
423 value = FindAndReplaceString(value, "_", "-");
424 CUpgrade *upgrade = CUpgrade::Get(value);
425 if (upgrade) {
426 this->Work = upgrade;
427 } else {
428 fprintf(stderr, "Upgrade \"%s\" doesn't exist.\n", value.c_str());
429 }
430 } else if (key == "elixir") {
431 value = FindAndReplaceString(value, "_", "-");
432 CUpgrade *upgrade = CUpgrade::Get(value);
433 if (upgrade) {
434 this->Elixir = upgrade;
435 } else {
436 fprintf(stderr, "Upgrade \"%s\" doesn't exist.\n", value.c_str());
437 }
438 } else if (key == "unique") {
439 value = FindAndReplaceString(value, "_", "-");
440 CUniqueItem *unique_item = GetUniqueItem(value);
441 if (unique_item) {
442 this->Unique = unique_item;
443 this->Name = unique_item->Name;
444 if (unique_item->Type != nullptr) {
445 this->Type = unique_item->Type;
446 } else {
447 fprintf(stderr, "Unique item \"%s\" has no type.\n", unique_item->Ident.c_str());
448 }
449 this->Prefix = unique_item->Prefix;
450 this->Suffix = unique_item->Suffix;
451 this->Spell = unique_item->Spell;
452 this->Work = unique_item->Work;
453 this->Elixir = unique_item->Elixir;
454 } else {
455 fprintf(stderr, "Unique item \"%s\" doesn't exist.\n", value.c_str());
456 }
457 } else if (key == "bound") {
458 this->Bound = StringToBool(value);
459 } else if (key == "identified") {
460 this->Identified = StringToBool(value);
461 } else if (key == "equipped") {
462 is_equipped = StringToBool(value);
463 } else {
464 fprintf(stderr, "Invalid item property: \"%s\".\n", key.c_str());
465 }
466 }
467
468 if (is_equipped && this->Owner && GetItemClassSlot(this->Type->ItemClass) != -1) {
469 this->Owner->EquippedItems[GetItemClassSlot(this->Type->ItemClass)].push_back(this);
470 }
471 }
472
GetItemEffectsString(const std::string & item_ident)473 std::string GetItemEffectsString(const std::string &item_ident)
474 {
475 const CUnitType *item = UnitTypeByIdent(item_ident);
476
477 if (item) {
478 std::string item_effects_string;
479
480 bool first_var = true;
481 for (size_t var = 0; var < UnitTypeVar.GetNumberVariable(); ++var) {
482 if (
483 !(var == BASICDAMAGE_INDEX || var == PIERCINGDAMAGE_INDEX || var == THORNSDAMAGE_INDEX
484 || var == FIREDAMAGE_INDEX || var == COLDDAMAGE_INDEX || var == ARCANEDAMAGE_INDEX || var == LIGHTNINGDAMAGE_INDEX
485 || var == AIRDAMAGE_INDEX || var == EARTHDAMAGE_INDEX || var == WATERDAMAGE_INDEX || var == ACIDDAMAGE_INDEX
486 || var == ARMOR_INDEX || var == FIRERESISTANCE_INDEX || var == COLDRESISTANCE_INDEX || var == ARCANERESISTANCE_INDEX || var == LIGHTNINGRESISTANCE_INDEX
487 || var == AIRRESISTANCE_INDEX || var == EARTHRESISTANCE_INDEX || var == WATERRESISTANCE_INDEX || var == ACIDRESISTANCE_INDEX
488 || var == HACKRESISTANCE_INDEX || var == PIERCERESISTANCE_INDEX || var == BLUNTRESISTANCE_INDEX
489 || var == ACCURACY_INDEX || var == EVASION_INDEX || var == SPEED_INDEX || var == CHARGEBONUS_INDEX || var == BACKSTAB_INDEX
490 || var == HITPOINTHEALING_INDEX || var == HITPOINTBONUS_INDEX || var == SIGHTRANGE_INDEX || var == DAYSIGHTRANGEBONUS_INDEX || var == NIGHTSIGHTRANGEBONUS_INDEX || var == HP_INDEX || var == MANA_INDEX
491 || var == ATTACKRANGE_INDEX)
492 ) {
493 continue;
494 }
495
496 if (var != HP_INDEX) { //only for elixirs, equippable items use the hit point bonus variable instead
497 if (item->DefaultStat.Variables[var].Enable) {
498 if (!first_var) {
499 item_effects_string += ", ";
500 } else {
501 first_var = false;
502 }
503
504 if (IsBooleanVariable(var) && item->DefaultStat.Variables[var].Value < 0) {
505 item_effects_string += "Lose ";
506 }
507
508 if (!IsBooleanVariable(var)) {
509 if (item->DefaultStat.Variables[var].Value >= 0 && var != HITPOINTHEALING_INDEX) {
510 item_effects_string += "+";
511 }
512 item_effects_string += std::to_string((long long) item->DefaultStat.Variables[var].Value);
513 if (IsPercentageVariable(var)) {
514 item_effects_string += "%";
515 }
516 item_effects_string += " ";
517 }
518
519 item_effects_string += GetVariableDisplayName(var);
520 }
521
522 if (item->DefaultStat.Variables[var].Increase != 0) {
523 if (!first_var) {
524 item_effects_string += ", ";
525 } else {
526 first_var = false;
527 }
528
529 if (item->DefaultStat.Variables[var].Increase > 0) {
530 item_effects_string += "+";
531 }
532 item_effects_string += std::to_string((long long) item->DefaultStat.Variables[var].Increase);
533 item_effects_string += " ";
534
535 item_effects_string += GetVariableDisplayName(var, true);
536 }
537 }
538
539 if (item->Elixir) {
540 for (size_t z = 0; z < item->Elixir->UpgradeModifiers.size(); ++z) {
541 if (item->Elixir->UpgradeModifiers[z]->Modifier.Variables[var].Value != 0) {
542 if (!first_var) {
543 item_effects_string += ", ";
544 } else {
545 first_var = false;
546 }
547
548 if (IsBooleanVariable(var) && item->Elixir->UpgradeModifiers[z]->Modifier.Variables[var].Value < 0) {
549 item_effects_string += "Lose ";
550 }
551
552 if (!IsBooleanVariable(var)) {
553 if (item->Elixir->UpgradeModifiers[z]->Modifier.Variables[var].Value >= 0 && var != HITPOINTHEALING_INDEX) {
554 item_effects_string += "+";
555 }
556 item_effects_string += std::to_string((long long) item->Elixir->UpgradeModifiers[z]->Modifier.Variables[var].Value);
557 if (IsPercentageVariable(var)) {
558 item_effects_string += "%";
559 }
560 item_effects_string += " ";
561 }
562
563 item_effects_string += GetVariableDisplayName(var);
564 }
565
566 if (item->Elixir->UpgradeModifiers[z]->Modifier.Variables[var].Increase != 0) {
567 if (!first_var) {
568 item_effects_string += ", ";
569 } else {
570 first_var = false;
571 }
572
573 if (item->Elixir->UpgradeModifiers[z]->Modifier.Variables[var].Increase > 0) {
574 item_effects_string += "+";
575 }
576 item_effects_string += std::to_string((long long) item->Elixir->UpgradeModifiers[z]->Modifier.Variables[var].Increase);
577 item_effects_string += " ";
578
579 item_effects_string += GetVariableDisplayName(var, true);
580 }
581 }
582 }
583 }
584
585 return item_effects_string;
586 }
587
588 return "";
589 }
590
GetUniqueItemEffectsString(const std::string & item_ident)591 std::string GetUniqueItemEffectsString(const std::string &item_ident)
592 {
593 const CUniqueItem *item = GetUniqueItem(item_ident);
594
595 if (item) {
596 std::string item_effects_string;
597
598 bool first_var = true;
599
600 for (size_t var = 0; var < UnitTypeVar.GetNumberVariable(); ++var) {
601 if (
602 !(var == BASICDAMAGE_INDEX || var == PIERCINGDAMAGE_INDEX || var == THORNSDAMAGE_INDEX
603 || var == FIREDAMAGE_INDEX || var == COLDDAMAGE_INDEX || var == ARCANEDAMAGE_INDEX || var == LIGHTNINGDAMAGE_INDEX
604 || var == AIRDAMAGE_INDEX || var == EARTHDAMAGE_INDEX || var == WATERDAMAGE_INDEX || var == ACIDDAMAGE_INDEX
605 || var == ARMOR_INDEX || var == FIRERESISTANCE_INDEX || var == COLDRESISTANCE_INDEX || var == ARCANERESISTANCE_INDEX || var == LIGHTNINGRESISTANCE_INDEX
606 || var == AIRRESISTANCE_INDEX || var == EARTHRESISTANCE_INDEX || var == WATERRESISTANCE_INDEX || var == ACIDRESISTANCE_INDEX
607 || var == HACKRESISTANCE_INDEX || var == PIERCERESISTANCE_INDEX || var == BLUNTRESISTANCE_INDEX
608 || var == ACCURACY_INDEX || var == EVASION_INDEX || var == SPEED_INDEX || var == CHARGEBONUS_INDEX || var == BACKSTAB_INDEX
609 || var == HITPOINTHEALING_INDEX || var == HITPOINTBONUS_INDEX
610 || var == SIGHTRANGE_INDEX || var == DAYSIGHTRANGEBONUS_INDEX || var == NIGHTSIGHTRANGEBONUS_INDEX
611 || var == GIVERESOURCE_INDEX || var == TIMEEFFICIENCYBONUS_INDEX || var == RESEARCHSPEEDBONUS_INDEX || var == GARRISONEDRANGEBONUS_INDEX
612 || var == KNOWLEDGEMAGIC_INDEX || var == KNOWLEDGEWARFARE_INDEX || var == KNOWLEDGEMINING_INDEX
613 || var == BONUSAGAINSTMOUNTED_INDEX|| var == BONUSAGAINSTBUILDINGS_INDEX || var == BONUSAGAINSTAIR_INDEX || var == BONUSAGAINSTGIANTS_INDEX || var == BONUSAGAINSTDRAGONS_INDEX
614 || var == SUPPLY_INDEX || var == ETHEREALVISION_INDEX
615 || var == ATTACKRANGE_INDEX)
616 ) {
617 continue;
618 }
619
620 int variable_value = 0;
621 int variable_increase = 0;
622 if (item->Type->BoolFlag[ITEM_INDEX].value && item->Work == nullptr && item->Elixir == nullptr) {
623 variable_value = item->Type->DefaultStat.Variables[var].Value;
624 variable_increase = item->Type->DefaultStat.Variables[var].Increase;
625 }
626
627 if (var == GIVERESOURCE_INDEX && item->ResourcesHeld != 0) {
628 variable_value = item->ResourcesHeld;
629 }
630
631 for (const CUpgradeModifier *modifier : CUpgradeModifier::UpgradeModifiers) {
632 if (
633 (item->Prefix != nullptr && modifier->UpgradeId == item->Prefix->ID)
634 || (item->Suffix != nullptr && modifier->UpgradeId == item->Suffix->ID)
635 || (item->Work != nullptr && modifier->UpgradeId == item->Work->ID)
636 || (item->Elixir != nullptr && modifier->UpgradeId == item->Elixir->ID)
637 ) {
638 variable_value += modifier->Modifier.Variables[var].Value;
639 variable_increase += modifier->Modifier.Variables[var].Increase;
640 }
641 }
642
643 if ((item->Type->BoolFlag[ITEM_INDEX].value && item->Type->DefaultStat.Variables[var].Enable && item->Work == nullptr && item->Elixir == nullptr) || variable_value != 0) {
644 if (!first_var) {
645 item_effects_string += ", ";
646 } else {
647 first_var = false;
648 }
649
650 if (IsBooleanVariable(var) && variable_value < 0) {
651 item_effects_string += "Lose ";
652 }
653
654 if (!IsBooleanVariable(var)) {
655 if (variable_value >= 0 && var != HITPOINTHEALING_INDEX && var != GIVERESOURCE_INDEX) {
656 item_effects_string += "+";
657 }
658 item_effects_string += std::to_string((long long) variable_value);
659 if (IsPercentageVariable(var)) {
660 item_effects_string += "%";
661 }
662 item_effects_string += " ";
663 }
664
665 item_effects_string += GetVariableDisplayName(var);
666 }
667
668 if (variable_increase != 0) {
669 if (!first_var) {
670 item_effects_string += ", ";
671 } else {
672 first_var = false;
673 }
674
675 if (variable_increase > 0) {
676 item_effects_string += "+";
677 }
678 item_effects_string += std::to_string((long long) variable_increase);
679 item_effects_string += " ";
680
681 item_effects_string += GetVariableDisplayName(var, true);
682 }
683 }
684
685 if (item->Set) {
686 for (size_t var = 0; var < UnitTypeVar.GetNumberVariable(); ++var) {
687 if (
688 !(var == BASICDAMAGE_INDEX || var == PIERCINGDAMAGE_INDEX || var == THORNSDAMAGE_INDEX
689 || var == FIREDAMAGE_INDEX || var == COLDDAMAGE_INDEX || var == ARCANEDAMAGE_INDEX || var == LIGHTNINGDAMAGE_INDEX
690 || var == AIRDAMAGE_INDEX || var == EARTHDAMAGE_INDEX || var == WATERDAMAGE_INDEX || var == ACIDDAMAGE_INDEX
691 || var == ARMOR_INDEX || var == FIRERESISTANCE_INDEX || var == COLDRESISTANCE_INDEX || var == ARCANERESISTANCE_INDEX || var == LIGHTNINGRESISTANCE_INDEX
692 || var == AIRRESISTANCE_INDEX || var == EARTHRESISTANCE_INDEX || var == WATERRESISTANCE_INDEX || var == ACIDRESISTANCE_INDEX
693 || var == HACKRESISTANCE_INDEX || var == PIERCERESISTANCE_INDEX || var == BLUNTRESISTANCE_INDEX
694 || var == ACCURACY_INDEX || var == EVASION_INDEX || var == SPEED_INDEX || var == CHARGEBONUS_INDEX || var == BACKSTAB_INDEX
695 || var == HITPOINTHEALING_INDEX || var == HITPOINTBONUS_INDEX
696 || var == SIGHTRANGE_INDEX || var == DAYSIGHTRANGEBONUS_INDEX || var == NIGHTSIGHTRANGEBONUS_INDEX
697 || var == GIVERESOURCE_INDEX || var == TIMEEFFICIENCYBONUS_INDEX || var == RESEARCHSPEEDBONUS_INDEX || var == GARRISONEDRANGEBONUS_INDEX
698 || var == KNOWLEDGEMAGIC_INDEX || var == KNOWLEDGEWARFARE_INDEX || var == KNOWLEDGEMINING_INDEX
699 || var == BONUSAGAINSTMOUNTED_INDEX|| var == BONUSAGAINSTBUILDINGS_INDEX || var == BONUSAGAINSTAIR_INDEX || var == BONUSAGAINSTGIANTS_INDEX || var == BONUSAGAINSTDRAGONS_INDEX
700 || var == SUPPLY_INDEX
701 || var == ATTACKRANGE_INDEX)
702 ) {
703 continue;
704 }
705
706 int variable_value = 0;
707 int variable_increase = 0;
708
709 for (size_t z = 0; z < item->Set->UpgradeModifiers.size(); ++z) {
710 variable_value += item->Set->UpgradeModifiers[z]->Modifier.Variables[var].Value;
711 variable_increase += item->Set->UpgradeModifiers[z]->Modifier.Variables[var].Increase;
712 }
713
714 if (variable_value != 0) {
715 if (!first_var) {
716 item_effects_string += ", ";
717 } else {
718 first_var = false;
719 }
720
721 if (IsBooleanVariable(var) && variable_value < 0) {
722 item_effects_string += "Lose ";
723 }
724
725 if (!IsBooleanVariable(var)) {
726 if (variable_value >= 0 && var != HITPOINTHEALING_INDEX && var != GIVERESOURCE_INDEX) {
727 item_effects_string += "+";
728 }
729 item_effects_string += std::to_string((long long) variable_value);
730 if (IsPercentageVariable(var)) {
731 item_effects_string += "%";
732 }
733 item_effects_string += " ";
734 }
735
736 item_effects_string += GetVariableDisplayName(var);
737 item_effects_string += " (Set Bonus)";
738 }
739
740 if (variable_increase != 0) {
741 if (!first_var) {
742 item_effects_string += ", ";
743 } else {
744 first_var = false;
745 }
746
747 if (variable_increase > 0) {
748 item_effects_string += "+";
749 }
750 item_effects_string += std::to_string((long long) variable_increase);
751 item_effects_string += " ";
752
753 item_effects_string += GetVariableDisplayName(var, true);
754 item_effects_string += " (Set Bonus)";
755 }
756 }
757 }
758
759 return item_effects_string;
760 }
761
762 return "";
763 }
764
765 //@}
766