1 /*-------------------------------------------------------------------------------
2
3 BARONY
4 File: items.cpp
5 Desc: contains helper functions for item stuff
6
7 Copyright 2013-2016 (c) Turning Wheel LLC, all rights reserved.
8 See LICENSE for details.
9
10 -------------------------------------------------------------------------------*/
11
12 #include "main.hpp"
13 #include "game.hpp"
14 #include "stat.hpp"
15 #include "items.hpp"
16 #include "messages.hpp"
17 #include "interface/interface.hpp"
18 #include "magic/magic.hpp"
19 #include "sound.hpp"
20 #include "book.hpp"
21 #include "scrolls.hpp"
22 #include "shops.hpp"
23 #include "prng.hpp"
24 #include "scores.hpp"
25 #include "net.hpp"
26 #include "player.hpp"
27
28 Uint32 itemuids = 1;
29 ItemGeneric items[NUMITEMS];
30 int INVENTORY_SIZEY = 3;
31 const real_t potionDamageSkillMultipliers[6] = { 1.f, 1.1, 1.25, 1.5, 2.5, 4.f };
32 const real_t thrownDamageSkillMultipliers[6] = { 1.f, 1.1, 1.25, 1.5, 2.f, 3.f };
33 std::mt19937 enchantedFeatherScrollSeed(0);
34 std::vector<int> enchantedFeatherScrollsShuffled;
35 bool overrideTinkeringLimit = false;
36 int decoyBoxRange = 15;
37
38 /*-------------------------------------------------------------------------------
39
40 newItem
41
42 Creates a new item and places it in an inventory
43
44 -------------------------------------------------------------------------------*/
45
newItem(const ItemType type,const Status status,const Sint16 beatitude,const Sint16 count,const Uint32 appearance,const bool identified,list_t * const inventory)46 Item* newItem(const ItemType type, const Status status, const Sint16 beatitude, const Sint16 count, const Uint32 appearance, const bool identified, list_t* const inventory)
47 {
48 Item* item;
49
50 // allocate memory for the item
51 if ( (item = static_cast<Item*>(malloc(sizeof(Item)))) == nullptr )
52 {
53 printlog( "failed to allocate memory for new item!\n" );
54 exit(1);
55 }
56
57 //item->captured_monster = nullptr;
58
59 // add the item to the inventory
60 if ( inventory != nullptr )
61 {
62 item->node = list_AddNodeLast(inventory);
63 item->node->element = item;
64 item->node->deconstructor = &defaultDeconstructor;
65 item->node->size = sizeof(Item);
66 }
67 else
68 {
69 item->node = nullptr;
70 }
71
72 // now set all of my data elements
73 item->type = type;
74 item->status = status;
75 item->beatitude = beatitude;
76 item->count = count;
77 item->appearance = appearance;
78 item->identified = identified;
79 item->uid = itemuids;
80 item->ownerUid = 0;
81 item->isDroppable = true;
82 if ( inventory )
83 {
84 int y;
85 bool notfree = false, foundaspot = false;
86
87 bool is_spell = false;
88 if (itemCategory(item) == SPELL_CAT)
89 {
90 is_spell = true;
91 }
92
93 int x = 0;
94 int inventory_y = INVENTORY_SIZEY;
95 if ( is_spell )
96 {
97 if ( list_Size(&spellList) >= INVENTORY_SIZEX * 3 )
98 {
99 inventory_y = INVENTORY_SIZEY = 4 + ((list_Size(&spellList) - (INVENTORY_SIZEX * 3)) / INVENTORY_SIZEX);
100 }
101 else
102 {
103 inventory_y = 3;
104 }
105 }
106 else if ( multiplayer != CLIENT )
107 {
108 for ( int i = 0; i < MAXPLAYERS; ++i )
109 {
110 if ( stats[i] && &stats[i]->inventory == inventory )
111 {
112 if ( stats[i]->cloak && stats[i]->cloak->type == CLOAK_BACKPACK
113 && (shouldInvertEquipmentBeatitude(stats[i]) ? abs(stats[i]->cloak->beatitude) >= 0 : stats[i]->cloak->beatitude >= 0) )
114 {
115 inventory_y = 4;
116 break;
117 }
118 break;
119 }
120 }
121 }
122 else if ( multiplayer == CLIENT )
123 {
124 if ( stats[clientnum] && &stats[clientnum]->inventory == inventory )
125 {
126 if ( stats[clientnum]->cloak && stats[clientnum]->cloak->type == CLOAK_BACKPACK
127 && (shouldInvertEquipmentBeatitude(stats[clientnum]) ? abs(stats[clientnum]->cloak->beatitude) >= 0 : stats[clientnum]->cloak->beatitude >= 0) )
128 {
129 inventory_y = 4;
130 }
131 }
132 }
133 const int sort_y = std::min(std::max(inventory_y, 2), 3); // only sort y values of 2-3, if extra row don't auto sort into it.
134
135 while ( true )
136 {
137 for ( y = 0; y < sort_y; y++ )
138 {
139 for ( node_t* node = inventory->first; node != nullptr; node = node->next )
140 {
141 Item* tempItem = static_cast<Item*>(node->element);
142 if ( tempItem == item )
143 {
144 continue;
145 }
146 if ( tempItem )
147 {
148 if ( tempItem->x == x && tempItem->y == y )
149 {
150 if (is_spell && itemCategory(tempItem) == SPELL_CAT)
151 {
152 notfree = true; //Both spells. Can't fit in the same slot.
153 }
154 else if (!is_spell && itemCategory(tempItem) != SPELL_CAT)
155 {
156 notfree = true; //Both not spells. Can't fit in the same slot.
157 }
158 }
159 }
160 }
161 if ( notfree )
162 {
163 notfree = false;
164 continue;
165 }
166 item->x = x;
167 item->y = y;
168 foundaspot = true;
169 break;
170 }
171 if ( foundaspot )
172 {
173 break;
174 }
175 x++;
176 }
177
178
179 // backpack sorting, sort into here as last priority.
180 if ( x > INVENTORY_SIZEX - 1 && inventory_y > 3 )
181 {
182 x = 0;
183 foundaspot = false;
184 notfree = false;
185 while ( true )
186 {
187 for ( y = 3; y < inventory_y; y++ )
188 {
189 for ( node_t* node = inventory->first; node != nullptr; node = node->next )
190 {
191 Item* tempItem = static_cast<Item*>(node->element);
192 if ( tempItem == item )
193 {
194 continue;
195 }
196 if ( tempItem )
197 {
198 if ( tempItem->x == x && tempItem->y == y )
199 {
200 if ( is_spell && itemCategory(tempItem) == SPELL_CAT )
201 {
202 notfree = true; //Both spells. Can't fit in the same slot.
203 }
204 else if ( !is_spell && itemCategory(tempItem) != SPELL_CAT )
205 {
206 notfree = true; //Both not spells. Can't fit in the same slot.
207 }
208 }
209 }
210 }
211 if ( notfree )
212 {
213 notfree = false;
214 continue;
215 }
216 item->x = x;
217 item->y = y;
218 foundaspot = true;
219 break;
220 }
221 if ( foundaspot )
222 {
223 break;
224 }
225 x++;
226 }
227 }
228
229 // add the item to the hotbar automatically
230 if ( !intro && auto_hotbar_new_items)
231 {
232 if ( inventory == &stats[clientnum]->inventory )
233 {
234 for ( int c = 0; c < NUM_HOTBAR_SLOTS; c++ )
235 {
236 if ( !uidToItem(hotbar[c].item) )
237 {
238 if ( autoAddHotbarFilter(*item) )
239 {
240 if ( players[clientnum] && players[clientnum]->entity && players[clientnum]->entity->effectShapeshift != NOTHING )
241 {
242 if ( item->usableWhileShapeshifted(stats[clientnum]) )
243 {
244 hotbar[c].item = item->uid;
245 }
246 }
247 else
248 {
249 hotbar[c].item = item->uid;
250 }
251 break;
252 }
253 }
254 }
255 }
256 }
257 }
258 else
259 {
260 item->x = 0;
261 item->y = 0;
262 }
263
264 itemuids++;
265 return item;
266 }
267
addItemToMonsterInventory(Item & item,list_t & inventory)268 void addItemToMonsterInventory(Item &item, list_t& inventory)
269 {
270 // add the item to the inventory
271
272 item.node = list_AddNodeLast(&inventory);
273 item.node->element = &item;
274 item.node->deconstructor = &defaultDeconstructor;
275 item.node->size = sizeof(Item);
276
277 bool notfree = false, foundaspot = false;
278
279 bool is_spell = false;
280 if (itemCategory(&item) == SPELL_CAT)
281 {
282 is_spell = true;
283 }
284
285 int x = 0;
286 while ( true )
287 {
288 for ( int y = 0; y < INVENTORY_SIZEY; ++y )
289 {
290 for ( node_t* node = inventory.first; node != nullptr; node = node->next )
291 {
292 Item* tempItem = static_cast<Item*>(node->element);
293 if ( tempItem == &item )
294 {
295 continue;
296 }
297 if ( tempItem )
298 {
299 if ( tempItem->x == x && tempItem->y == y )
300 {
301 if (is_spell && itemCategory(tempItem) == SPELL_CAT)
302 {
303 notfree = true; //Both spells. Can't fit in the same slot.
304 }
305 else if (!is_spell && itemCategory(tempItem) != SPELL_CAT)
306 {
307 notfree = true; //Both not spells. Can't fit in the same slot.
308 }
309 }
310 }
311 }
312 if ( notfree )
313 {
314 notfree = false;
315 continue;
316 }
317 item.x = x;
318 item.y = y;
319 foundaspot = true;
320 break;
321 }
322 if ( foundaspot )
323 {
324 break;
325 }
326 ++x;
327 }
328
329 // add the item to the hotbar automatically
330 if ( !intro && auto_hotbar_new_items )
331 {
332 if ( &inventory == &stats[clientnum]->inventory )
333 {
334 for ( int c = 0; c < NUM_HOTBAR_SLOTS; c++ )
335 {
336 if ( !uidToItem(hotbar[c].item) )
337 {
338 hotbar[c].item = item.uid;
339 break;
340 }
341 }
342 }
343 }
344 }
345
346 /*-------------------------------------------------------------------------------
347
348 uidToItem
349
350 returns an item from the player's inventory from the given uid
351
352 -------------------------------------------------------------------------------*/
353
uidToItem(const Uint32 uid)354 Item* uidToItem(const Uint32 uid)
355 {
356 if ( uid == 0 )
357 {
358 return nullptr;
359 }
360 for ( node_t* node = stats[clientnum]->inventory.first; node != nullptr; node = node->next )
361 {
362 Item* item = static_cast<Item*>(node->element);
363 if ( item->uid == uid )
364 {
365 return item;
366 }
367 }
368 return nullptr;
369 }
370
371 /*-------------------------------------------------------------------------------
372
373 itemCurve
374
375 Selects an item type from the given category of items by factoring in
376 dungeon level, value of the item, etc.
377
378 -------------------------------------------------------------------------------*/
379
itemCurve(const Category cat)380 ItemType itemCurve(const Category cat)
381 {
382 const int numitems = NUMITEMS - ( NUMITEMS - static_cast<int>(ARTIFACT_SWORD) );
383 bool chances[NUMITEMS];
384 int c;
385
386 if ( cat < 0 || cat >= NUMCATEGORIES )
387 {
388 printlog("warning: itemCurve() called with bad category value!\n");
389 return GEM_ROCK;
390 }
391
392 // find highest value of items in category
393 Uint32 highestvalue = 0;
394 Uint32 lowestvalue = 0;
395 Uint32 numoftype = 0;
396 for ( c = 0; c < numitems; c++ )
397 {
398 if ( items[c].category == cat )
399 {
400 highestvalue = std::max<Uint32>(highestvalue, items[c].value); //TODO: Why are Uint32 and int being compared?
401 lowestvalue = std::min<Uint32>(lowestvalue, items[c].value); //TODO: Why are Uint32 and int being compared?
402 numoftype++;
403 }
404 }
405 if ( numoftype == 0 )
406 {
407 printlog("warning: category passed to itemCurve has no items!\n");
408 return GEM_ROCK;
409 }
410
411 if ( cat == SCROLL || cat == POTION || cat == BOOK )
412 {
413 // these item categories will spawn anything of their type
414 for ( c = 0; c < numitems; c++ )
415 {
416 chances[c] = false;
417 if ( items[c].category == cat )
418 {
419 chances[c] = true;
420 }
421 }
422 }
423 else if ( cat == TOOL )
424 {
425 // this category will spawn specific items more frequently regardless of level
426 for ( c = 0; c < numitems; c++ )
427 {
428 chances[c] = false;
429 if ( items[c].category == cat )
430 {
431 switch ( static_cast<ItemType>(c) )
432 {
433 case TOOL_TINOPENER:
434 if ( prng_get_uint() % 2 ) // 50% chance
435 {
436 chances[c] = true;
437 }
438 break;
439 case TOOL_LANTERN:
440 if ( prng_get_uint() % 4 ) // 75% chance
441 {
442 chances[c] = true;
443 }
444 break;
445 case TOOL_SKELETONKEY:
446 chances[c] = false; // 0% chance
447 break;
448 default:
449 chances[c] = true;
450 break;
451 }
452 }
453 }
454 }
455 else
456 {
457 // other categories get a special chance algorithm based on item value and dungeon level
458 const int acceptablehigh = std::max<Uint32>(highestvalue * fmin(1.0, (currentlevel + 10) / 25.0), lowestvalue); //TODO: Why are double and Uint32 being compared?
459 for ( c = 0; c < numitems; c++ )
460 {
461 chances[c] = false;
462 if ( items[c].category == cat && items[c].value <= acceptablehigh )
463 {
464 chances[c] = true;
465 }
466 }
467 }
468
469 // calculate number of items left
470 Uint32 numleft = 0;
471 for ( c = 0; c < numitems; c++ )
472 {
473 if ( chances[c] == true )
474 {
475 numleft++;
476 }
477 }
478 if ( numleft == 0 )
479 {
480 return GEM_ROCK;
481 }
482
483 // most gems are worthless pieces of glass
484 if ( cat == GEM )
485 {
486 if ( prng_get_uint() % 10 )
487 {
488 return GEM_GLASS;
489 }
490 }
491
492 // pick the item
493 Uint32 pick = prng_get_uint() % numleft;
494 for ( c = 0; c < numitems; c++ )
495 {
496 if ( items[c].category == cat )
497 {
498 if ( chances[c] == true )
499 {
500 if ( pick == 0 )
501 {
502 return static_cast<ItemType>(c);
503 }
504 else
505 {
506 pick--;
507 }
508 }
509 }
510 }
511
512 return GEM_ROCK;
513 }
514
515 /*-------------------------------------------------------------------------------
516
517 itemLevelCurve
518
519 Selects an item type from the given category of items by factoring in
520 dungeon level and defined level of the item
521
522 -------------------------------------------------------------------------------*/
523
itemLevelCurve(const Category cat,const int minLevel,const int maxLevel)524 ItemType itemLevelCurve(const Category cat, const int minLevel, const int maxLevel)
525 {
526 const int numitems = NUMITEMS;
527 bool chances[NUMITEMS];
528 int c;
529
530 if ( cat < 0 || cat >= NUMCATEGORIES )
531 {
532 printlog("warning: itemLevelCurve() called with bad category value!\n");
533 return GEM_ROCK;
534 }
535
536 Uint32 numoftype = 0;
537 for ( c = 0; c < numitems; ++c )
538 {
539 chances[c] = false;
540 if ( items[c].category == cat )
541 {
542 if ( items[c].level != -1 && (items[c].level >= minLevel && items[c].level <= maxLevel) )
543 {
544 chances[c] = true;
545 numoftype++;
546 if ( cat == TOOL )
547 {
548 switch ( static_cast<ItemType>(c) )
549 {
550 case TOOL_TINOPENER:
551 if ( prng_get_uint() % 2 ) // 50% chance
552 {
553 chances[c] = false;
554 }
555 break;
556 case TOOL_LANTERN:
557 if ( prng_get_uint() % 4 == 0 ) // 25% chance
558 {
559 chances[c] = false;
560 }
561 break;
562 default:
563 break;
564 }
565 }
566 else if ( cat == ARMOR )
567 {
568 switch ( static_cast<ItemType>(c) )
569 {
570 case CLOAK_BACKPACK:
571 if ( prng_get_uint() % 4 ) // 25% chance
572 {
573 chances[c] = false;
574 }
575 break;
576 default:
577 break;
578 }
579 }
580 }
581 }
582 }
583 if ( numoftype == 0 )
584 {
585 printlog("warning: category passed to itemLevelCurve has no items!\n");
586 return GEM_ROCK;
587 }
588
589 // calculate number of items left
590 Uint32 numleft = 0;
591 for ( c = 0; c < numitems; c++ )
592 {
593 if ( chances[c] == true )
594 {
595 numleft++;
596 }
597 }
598 if ( numleft == 0 )
599 {
600 return GEM_ROCK;
601 }
602
603 // most gems are worthless pieces of glass
604 if ( cat == GEM )
605 {
606 if ( prng_get_uint() % 10 )
607 {
608 return GEM_GLASS;
609 }
610 }
611
612 // pick the item
613 Uint32 pick = prng_get_uint() % numleft;
614 for ( c = 0; c < numitems; c++ )
615 {
616 if ( items[c].category == cat )
617 {
618 if ( chances[c] == true )
619 {
620 if ( pick == 0 )
621 {
622 //messagePlayer(0, "Chose item: %s of %d items.", items[c].name_identified ,numleft);
623 return static_cast<ItemType>(c);
624 }
625 else
626 {
627 pick--;
628 }
629 }
630 }
631 }
632
633 return GEM_ROCK;
634 }
635
636 /*-------------------------------------------------------------------------------
637
638 Item::description
639
640 Returns a string that describes the given item's properties
641
642 -------------------------------------------------------------------------------*/
643
description() const644 char* Item::description() const
645 {
646 int c = 0;
647
648 if ( identified == true )
649 {
650 if ( count < 2 )
651 {
652 if ( itemCategory(this) == WEAPON || itemCategory(this) == ARMOR || itemCategory(this) == MAGICSTAFF || itemCategory(this) == TOOL || itemCategory(this) == THROWN )
653 {
654 if ( this->type == TOOL_GYROBOT || this->type == TOOL_DUMMYBOT || this->type == TOOL_SENTRYBOT || this->type == TOOL_SPELLBOT )
655 {
656 snprintf(tempstr, 1024, language[3653 + status]);
657 }
658 else if ( itemTypeIsQuiver(this->type) )
659 {
660 snprintf(tempstr, 1024, language[3738], beatitude);
661 }
662 else
663 {
664 snprintf(tempstr, 1024, language[982 + status], beatitude);
665 }
666 }
667 else if ( itemCategory(this) == AMULET || itemCategory(this) == RING || itemCategory(this) == GEM )
668 {
669 snprintf(tempstr, 1024, language[987 + status], beatitude);
670 }
671 else if ( itemCategory(this) == POTION )
672 {
673 if ( type == POTION_EMPTY )
674 {
675 //No fancy descriptives for empty potions.
676 snprintf(tempstr, 1024, language[982 + status], beatitude);
677 }
678 else
679 {
680 snprintf(tempstr, 1024, language[992 + status], language[974 + items[type].index + appearance % items[type].variations - 50], beatitude);
681 }
682 }
683 else if ( itemCategory(this) == SCROLL || itemCategory(this) == SPELLBOOK || itemCategory(this) == BOOK )
684 {
685 snprintf(tempstr, 1024, language[997 + status], beatitude);
686 }
687 else if ( itemCategory(this) == FOOD )
688 {
689 snprintf(tempstr, 1024, language[1002 + status], beatitude);
690 }
691
692 for ( c = 0; c < 1024; ++c )
693 {
694 if ( tempstr[c] == 0 )
695 {
696 break;
697 }
698 }
699
700 if ( type >= 0 && type < NUMITEMS )
701 {
702 if ( itemCategory(this) == BOOK )
703 {
704 snprintf(&tempstr[c], 1024 - c, language[1007], books[appearance % numbooks]->name);
705 }
706 else
707 {
708 snprintf(&tempstr[c], 1024 - c, "%s", items[type].name_identified);
709 }
710 }
711 else
712 {
713 snprintf(&tempstr[c], 1024 - c, "ITEM%03d", type);
714 }
715 }
716 else
717 {
718 if ( itemCategory(this) == WEAPON || itemCategory(this) == ARMOR || itemCategory(this) == MAGICSTAFF || itemCategory(this) == TOOL || itemCategory(this) == THROWN )
719 {
720 if ( this->type == TOOL_GYROBOT || this->type == TOOL_DUMMYBOT || this->type == TOOL_SENTRYBOT || this->type == TOOL_SPELLBOT )
721 {
722 snprintf(tempstr, 1024, language[3658 + status], count);
723 }
724 else if ( itemTypeIsQuiver(this->type) )
725 {
726 snprintf(tempstr, 1024, language[3738], beatitude);
727 }
728 else
729 {
730 snprintf(tempstr, 1024, language[1008 + status], count, beatitude);
731 }
732 }
733 else if ( itemCategory(this) == AMULET || itemCategory(this) == RING || itemCategory(this) == GEM )
734 {
735 snprintf(tempstr, 1024, language[1013 + status], count, beatitude);
736 }
737 else if ( itemCategory(this) == POTION )
738 {
739 if ( type == POTION_EMPTY )
740 {
741 //No fancy descriptives for empty potions.
742 snprintf(tempstr, 1024, language[1008 + status], count, beatitude);
743 }
744 else
745 {
746 snprintf(tempstr, 1024, language[1018 + status], count, language[974 + items[type].index + appearance % items[type].variations - 50], beatitude);
747 }
748 }
749 else if ( itemCategory(this) == SCROLL || itemCategory(this) == SPELLBOOK || itemCategory(this) == BOOK )
750 {
751 snprintf(tempstr, 1024, language[1023 + status], count, beatitude);
752 }
753 else if ( itemCategory(this) == FOOD )
754 {
755 snprintf(tempstr, 1024, language[1028 + status], count, beatitude);
756 }
757
758 for ( c = 0; c < 1024; ++c )
759 {
760 if ( tempstr[c] == 0 )
761 {
762 break;
763 }
764 }
765
766 if ( type >= 0 && type < NUMITEMS )
767 {
768 if ( itemCategory(this) == BOOK )
769 {
770 snprintf(&tempstr[c], 1024 - c, language[1033], count, books[appearance % numbooks]->name);
771 }
772 else
773 {
774 snprintf(&tempstr[c], 1024 - c, "%s", items[type].name_identified);
775 }
776 }
777 else
778 {
779 snprintf(&tempstr[c], 1024 - c, "ITEM%03d", type);
780 }
781 }
782 }
783 else
784 {
785 if ( count < 2 )
786 {
787 if ( itemCategory(this) == WEAPON || itemCategory(this) == ARMOR || itemCategory(this) == MAGICSTAFF || itemCategory(this) == TOOL || itemCategory(this) == THROWN )
788 {
789 if ( this->type == TOOL_GYROBOT || this->type == TOOL_DUMMYBOT || this->type == TOOL_SENTRYBOT || this->type == TOOL_SPELLBOT )
790 {
791 strncpy(tempstr, language[3653 + status], 1024);
792 }
793 else if ( itemTypeIsQuiver(this->type) )
794 {
795 snprintf(tempstr, 1024, language[3763]);
796 }
797 else
798 {
799 strncpy(tempstr, language[1034 + status], 1024);
800 }
801 }
802 else if ( itemCategory(this) == AMULET || itemCategory(this) == RING || itemCategory(this) == GEM )
803 {
804 strncpy(tempstr, language[1039 + status], 1024);
805 }
806 else if ( itemCategory(this) == POTION )
807 {
808 if ( type == POTION_EMPTY )
809 {
810 //No fancy descriptives for empty potions.
811 snprintf(tempstr, 1024, language[1034 + status], beatitude);
812 }
813 else
814 {
815 snprintf(tempstr, 1024, language[1044 + status], language[974 + items[type].index + appearance % items[type].variations - 50]);
816 }
817 }
818 else if ( itemCategory(this) == SCROLL || itemCategory(this) == SPELLBOOK || itemCategory(this) == BOOK )
819 {
820 strncpy(tempstr, language[1049 + status], 1024);
821 }
822 else if ( itemCategory(this) == FOOD )
823 {
824 strncpy(tempstr, language[1054 + status], 1024);
825 }
826
827 for ( c = 0; c < 1024; ++c )
828 {
829 if ( tempstr[c] == 0 )
830 {
831 break;
832 }
833 }
834
835 if ( type >= 0 && type < NUMITEMS )
836 {
837 if ( itemCategory(this) == SCROLL )
838 {
839 snprintf(&tempstr[c], 1024 - c, language[1059], items[type].name_unidentified, this->getScrollLabel());
840 }
841 else
842 {
843 if ( itemCategory(this) == BOOK )
844 {
845 snprintf(&tempstr[c], 1024 - c, language[1007], books[appearance % numbooks]->name);
846 }
847 else
848 {
849 snprintf(&tempstr[c], 1024 - c, "%s", items[type].name_unidentified);
850 }
851 }
852 }
853 else
854 {
855 snprintf(&tempstr[c], 1024 - c, "ITEM%03d", type);
856 }
857 }
858 else
859 {
860 if ( itemCategory(this) == WEAPON || itemCategory(this) == ARMOR || itemCategory(this) == MAGICSTAFF || itemCategory(this) == TOOL || itemCategory(this) == THROWN )
861 {
862 if ( this->type == TOOL_GYROBOT || this->type == TOOL_DUMMYBOT || this->type == TOOL_SENTRYBOT || this->type == TOOL_SPELLBOT )
863 {
864 snprintf(tempstr, 1024, language[3658 + status], count);
865 }
866 else if ( itemTypeIsQuiver(this->type) )
867 {
868 snprintf(tempstr, 1024, language[3763]);
869 }
870 else
871 {
872 snprintf(tempstr, 1024, language[1060 + status], count);
873 }
874 }
875 else if ( itemCategory(this) == AMULET || itemCategory(this) == RING || itemCategory(this) == GEM )
876 {
877 snprintf(tempstr, 1024, language[1065 + status], count);
878 }
879 else if ( itemCategory(this) == POTION )
880 {
881 if ( type == POTION_EMPTY )
882 {
883 //No fancy descriptives for empty potions.
884 snprintf(tempstr, 1024, language[1060 + status], count);
885 }
886 else
887 {
888 snprintf(tempstr, 1024, language[1070 + status], count, language[974 + items[type].index + appearance % items[type].variations - 50]);
889 }
890 }
891 else if ( itemCategory(this) == SCROLL || itemCategory(this) == SPELLBOOK || itemCategory(this) == BOOK )
892 {
893 snprintf(tempstr, 1024, language[1075 + status], count);
894 }
895 else if ( itemCategory(this) == FOOD )
896 {
897 snprintf(tempstr, 1024, language[1080 + status], count);
898 }
899
900 for ( c = 0; c < 1024; ++c )
901 {
902 if ( tempstr[c] == 0 )
903 {
904 break;
905 }
906 }
907
908 if ( type >= 0 && type < NUMITEMS )
909 {
910 if ( itemCategory(this) == SCROLL )
911 {
912 snprintf(&tempstr[c], 1024 - c, language[1085], count, items[type].name_unidentified, this->getScrollLabel());
913 }
914 else
915 {
916 if ( itemCategory(this) == BOOK )
917 {
918 snprintf(&tempstr[c], 1024 - c, language[1086], count, books[appearance % numbooks]->name);
919 }
920 else
921 {
922 snprintf(&tempstr[c], 1024 - c, "%s", items[type].name_unidentified);
923 }
924 }
925 }
926 else
927 {
928 snprintf(&tempstr[c], 1024 - c, "ITEM%03d", type);
929 }
930 }
931 }
932 return tempstr;
933 }
934
935 /*-------------------------------------------------------------------------------
936
937 itemCategory
938
939 Returns the category that a specified item belongs to
940
941 -------------------------------------------------------------------------------*/
942
itemCategory(const Item * const item)943 Category itemCategory(const Item* const item)
944 {
945 if ( !item )
946 {
947 return GEM;
948 }
949 return items[item->type].category;
950 }
951
952 /*-------------------------------------------------------------------------------
953
954 Item::getName
955
956 Returns the name of an item type as a character string
957
958 -------------------------------------------------------------------------------*/
959
getName() const960 char* Item::getName() const
961 {
962 if ( type >= 0 && type < NUMITEMS )
963 {
964 if ( identified )
965 {
966 if ( itemCategory(this) == BOOK )
967 {
968 snprintf(tempstr, sizeof(tempstr), language[1007], books[appearance % numbooks]->name);
969 }
970 else
971 {
972 strcpy(tempstr, items[type].name_identified);
973 }
974 }
975 else
976 {
977 if ( itemCategory(this) == SCROLL )
978 {
979 snprintf(tempstr, sizeof(tempstr), language[1059], items[type].name_unidentified, this->getScrollLabel());
980 }
981 else if ( itemCategory(this) == BOOK )
982 {
983 snprintf(tempstr, sizeof(tempstr), language[1007], books[appearance % numbooks]->name);
984 }
985 else
986 {
987 strcpy(tempstr, items[type].name_unidentified);
988 }
989 }
990 }
991 else
992 {
993 snprintf(tempstr, sizeof(tempstr), "ITEM%03d", type);
994 }
995 return tempstr;
996 }
997
998 /*-------------------------------------------------------------------------------
999
1000 itemModel
1001
1002 returns a model index number based on the properties of the given item
1003
1004 -------------------------------------------------------------------------------*/
1005
itemModel(const Item * const item)1006 Sint32 itemModel(const Item* const item)
1007 {
1008 if ( !item )
1009 {
1010 return 0;
1011 }
1012 return items[item->type].index + item->appearance % items[item->type].variations;
1013 }
1014
1015 /*-------------------------------------------------------------------------------
1016
1017 itemModelFirstperson
1018
1019 returns the first person model of the given item
1020
1021 -------------------------------------------------------------------------------*/
1022
itemModelFirstperson(const Item * const item)1023 Sint32 itemModelFirstperson(const Item* const item)
1024 {
1025 if ( !item )
1026 {
1027 return 0;
1028 }
1029 return items[item->type].fpindex + item->appearance % items[item->type].variations;
1030 }
1031
1032 /*-------------------------------------------------------------------------------
1033
1034 itemSprite
1035
1036 returns a pointer to the SDL_Surface used to represent the item
1037
1038 -------------------------------------------------------------------------------*/
1039
itemSprite(Item * const item)1040 SDL_Surface* itemSprite(Item* const item)
1041 {
1042 if ( !item )
1043 {
1044 return nullptr;
1045 }
1046 if (itemCategory(item) == SPELL_CAT)
1047 {
1048 spell_t* spell = getSpellFromItem(item);
1049 if (spell)
1050 {
1051 node_t* node = list_Node(&items[item->type].surfaces, spell->ID);
1052 if ( !node )
1053 {
1054 return nullptr;
1055 }
1056 SDL_Surface** surface = static_cast<SDL_Surface**>(node->element);
1057 return *surface;
1058 }
1059 }
1060 else
1061 {
1062 node_t* node = list_Node(&items[item->type].surfaces, item->appearance % items[item->type].variations);
1063 if ( !node )
1064 {
1065 return nullptr;
1066 }
1067 SDL_Surface** surface = static_cast<SDL_Surface**>(node->element);
1068 return *surface;
1069 }
1070 return nullptr;
1071 }
1072
1073 /*-------------------------------------------------------------------------------
1074
1075 itemCompare
1076
1077 Compares two items and returns 0 if they are identical or 1 if they are
1078 not identical. Item count is excluded during comparison testing.
1079
1080 -------------------------------------------------------------------------------*/
1081
itemCompare(const Item * const item1,const Item * const item2,bool checkAppearance)1082 int itemCompare(const Item* const item1, const Item* const item2, bool checkAppearance)
1083 {
1084 Sint32 model1 = 0;
1085 Sint32 model2 = 0;
1086
1087 // null cases
1088 if ( item1 == nullptr )
1089 {
1090 if ( item2 == nullptr )
1091 {
1092 return 0;
1093 }
1094 else
1095 {
1096 return 1;
1097 }
1098 }
1099 else
1100 {
1101 if ( item2 == nullptr )
1102 {
1103 return 1;
1104 }
1105 }
1106
1107 // check attributes
1108 if (item1->type != item2->type)
1109 {
1110 return 1;
1111 }
1112 if ( itemCategory(item1) != THROWN && !itemTypeIsQuiver(item1->type) )
1113 {
1114 if (item1->status != item2->status)
1115 {
1116 return 1;
1117 }
1118 }
1119 if (item1->beatitude != item2->beatitude)
1120 {
1121 return 1;
1122 }
1123 model1 = items[item1->type].index + item1->appearance % items[item1->type].variations;
1124 model2 = items[item2->type].index + item2->appearance % items[item2->type].variations;
1125 //messagePlayer(0, "item1- %d, item2 - %d", model1, model2);
1126 if ( model1 != model2 )
1127 {
1128 return 1;
1129 }
1130 else if ( item1->type == SCROLL_MAIL || item1->type == READABLE_BOOK || items[item1->type].category == SPELL_CAT )
1131 {
1132 return 1; // these items do not stack
1133 }
1134
1135 if (item1->identified != item2->identified)
1136 {
1137 return 1;
1138 }
1139
1140 if ( !item1->identified && itemCategory(item1) == SCROLL && itemCategory(item2) == SCROLL )
1141 {
1142 if ( item1->getScrollLabel() != item2->getScrollLabel() )
1143 {
1144 return 1;
1145 }
1146 }
1147
1148 if ( item1->type == TOOL_GYROBOT || item1->type == TOOL_SENTRYBOT || item1->type == TOOL_SPELLBOT || item1->type == TOOL_DUMMYBOT )
1149 {
1150 checkAppearance = true; // these items store their HP inside appearance.
1151 }
1152
1153 if ( checkAppearance && (item1->appearance != item2->appearance) )
1154 {
1155 return 1;
1156 }
1157
1158 // items are identical
1159 return 0;
1160 }
1161
1162 /*-------------------------------------------------------------------------------
1163
1164 dropItem
1165
1166 Handles the client impulse to drop an item, returns true on free'd item.
1167
1168 -------------------------------------------------------------------------------*/
1169
dropItem(Item * const item,const int player,const bool notifyMessage)1170 bool dropItem(Item* const item, const int player, const bool notifyMessage)
1171 {
1172 if (!item)
1173 {
1174 return false;
1175 }
1176
1177 Sint16 oldcount;
1178
1179 if (item == nullptr || players[player] == nullptr || players[player]->entity == nullptr || itemCategory(item) == SPELL_CAT)
1180 {
1181 return false;
1182 }
1183
1184 if ( itemIsEquipped(item, player) )
1185 {
1186 if (!item->canUnequip(stats[player]))
1187 {
1188 if ( shouldInvertEquipmentBeatitude(stats[player]) && item->beatitude > 0 )
1189 {
1190 messagePlayer(player, language[3218]);
1191 }
1192 else
1193 {
1194 messagePlayer(player, language[1087]);
1195 }
1196 return false;
1197 }
1198 }
1199
1200 if ( multiplayer == CLIENT )
1201 {
1202 strcpy((char*)net_packet->data, "DROP");
1203 SDLNet_Write32(static_cast<Uint32>(item->type), &net_packet->data[4]);
1204 SDLNet_Write32(static_cast<Uint32>(item->status), &net_packet->data[8]);
1205 SDLNet_Write32(static_cast<Uint32>(item->beatitude), &net_packet->data[12]);
1206 SDLNet_Write32(static_cast<Uint32>(item->count), &net_packet->data[16]);
1207 SDLNet_Write32(static_cast<Uint32>(item->appearance), &net_packet->data[20]);
1208 net_packet->data[24] = item->identified;
1209 net_packet->data[25] = clientnum;
1210 net_packet->address.host = net_server.host;
1211 net_packet->address.port = net_server.port;
1212 net_packet->len = 26;
1213 sendPacketSafe(net_sock, -1, net_packet, 0);
1214 if ( item == open_book_item )
1215 {
1216 closeBookGUI();
1217 }
1218
1219 oldcount = item->count;
1220 if ( item->count >= 10 && (item->type == TOOL_METAL_SCRAP || item->type == TOOL_MAGIC_SCRAP) )
1221 {
1222 item->count = 10;
1223 messagePlayer(player, language[1088], item->description());
1224 item->count = oldcount - 10;
1225 }
1226 else if ( itemTypeIsQuiver(item->type) )
1227 {
1228 item->count = 1;
1229 if ( notifyMessage )
1230 {
1231 messagePlayer(player, language[1088], item->description());
1232 }
1233 item->count = 0;
1234 /*if ( oldcount >= 10 )
1235 {
1236 item->count = oldcount - 10;
1237 }
1238 else
1239 {
1240 item->count = 0;
1241 }*/
1242 }
1243 else
1244 {
1245 item->count = 1;
1246 if ( notifyMessage )
1247 {
1248 messagePlayer(player, language[1088], item->description());
1249 }
1250 item->count = oldcount - 1;
1251 }
1252
1253 // unequip the item
1254 /*if ( item->count <= 1 )
1255 {
1256 }*/
1257 Item** slot = itemSlot(stats[player], item);
1258 if ( slot != nullptr )
1259 {
1260 *slot = nullptr;
1261 }
1262
1263 if ( item->count <= 0 )
1264 {
1265 list_RemoveNode(item->node);
1266 return true;
1267 }
1268 return false;
1269 }
1270 else
1271 {
1272 if (item == open_book_item)
1273 {
1274 closeBookGUI();
1275 }
1276 int qtyToDrop = 1;
1277 if ( item->count >= 10 && (item->type == TOOL_METAL_SCRAP || item->type == TOOL_MAGIC_SCRAP) )
1278 {
1279 qtyToDrop = 10;
1280 }
1281 else if ( itemTypeIsQuiver(item->type) )
1282 {
1283 qtyToDrop = item->count;
1284 /*if ( item->count >= 10 )
1285 {
1286 qtyToDrop = 10;
1287 }
1288 else
1289 {
1290 qtyToDrop = item->count;
1291 }*/
1292 }
1293
1294 Entity* entity = newEntity(-1, 1, map.entities, nullptr); //Item entity.
1295 entity->flags[INVISIBLE] = true;
1296 entity->flags[UPDATENEEDED] = true;
1297 entity->x = players[player]->entity->x;
1298 entity->y = players[player]->entity->y;
1299 entity->sizex = 4;
1300 entity->sizey = 4;
1301 entity->yaw = players[player]->entity->yaw;
1302 entity->vel_x = (1.5 + .025 * (rand() % 11)) * cos(players[player]->entity->yaw);
1303 entity->vel_y = (1.5 + .025 * (rand() % 11)) * sin(players[player]->entity->yaw);
1304 entity->vel_z = (-10 - rand() % 20) * .01;
1305 entity->flags[PASSABLE] = true;
1306 entity->behavior = &actItem;
1307 entity->skill[10] = item->type;
1308 entity->skill[11] = item->status;
1309 entity->skill[12] = item->beatitude;
1310 entity->skill[13] = qtyToDrop;
1311 entity->skill[14] = item->appearance;
1312 entity->skill[15] = item->identified;
1313 entity->parent = players[player]->entity->getUID();
1314 entity->itemOriginalOwner = entity->parent;
1315
1316 // play sound
1317 playSoundEntity( players[player]->entity, 47 + rand() % 3, 64 );
1318
1319 // unequip the item
1320 Item** slot = itemSlot(stats[player], item);
1321 if ( slot != nullptr )
1322 {
1323 *slot = nullptr;
1324 }
1325
1326 if ( item->node != nullptr )
1327 {
1328 if ( item->node->list == &stats[0]->inventory )
1329 {
1330 oldcount = item->count;
1331 item->count = qtyToDrop;
1332 if ( notifyMessage )
1333 {
1334 messagePlayer(player, language[1088], item->description());
1335 }
1336 item->count = oldcount - qtyToDrop;
1337 if ( item->count <= 0 )
1338 {
1339 list_RemoveNode(item->node);
1340 return true;
1341 }
1342 }
1343 }
1344 else
1345 {
1346 item->count = item->count - qtyToDrop;
1347 if ( item->count <= 0 )
1348 {
1349 free(item);
1350 return true;
1351 }
1352 }
1353
1354 return false;
1355 }
1356 }
1357
dropItemMonster(Item * const item,Entity * const monster,Stat * const monsterStats,Sint16 count)1358 Entity* dropItemMonster(Item* const item, Entity* const monster, Stat* const monsterStats, Sint16 count)
1359 {
1360 // WARNING - dropItemMonster is used on playerDeaths, modifying this here neet to edit in actPlayer.cpp and net.cpp
1361 Entity* entity = nullptr;
1362 bool itemDroppable = true;
1363
1364 if ( !item || !monster )
1365 {
1366 return nullptr;
1367 }
1368
1369 if ( monster->behavior == &actPlayer && monster->skill[2] == clientnum )
1370 {
1371 if ( item == selectedItem )
1372 {
1373 selectedItem = nullptr;
1374 }
1375 }
1376
1377 /*if ( monsterStats->type == SHADOW && itemCategory(item) == SPELLBOOK )
1378 {
1379 //Shadows don't drop spellbooks.
1380 itemDroppable = false;
1381 }*/
1382 if ( monsterStats )
1383 {
1384 if ( monsterStats->monsterNoDropItems == 1 )
1385 {
1386 itemDroppable = false;
1387 }
1388 if ( monsterStats->type == SKELETON && monster->behavior == &actMonster && monster->monsterAllySummonRank != 0 )
1389 {
1390 itemDroppable = false;
1391 }
1392 if ( monsterStats->type == INCUBUS )
1393 {
1394 if ( !strncmp(monsterStats->name, "inner demon", strlen("inner demon")) )
1395 {
1396 itemDroppable = false;
1397 }
1398 }
1399 if ( !item->isDroppable )
1400 {
1401 itemDroppable = false;
1402 }
1403
1404 if ( item->appearance == MONSTER_ITEM_UNDROPPABLE_APPEARANCE )
1405 {
1406 if ( monster->behavior == &actMonster
1407 && (item->type < ARTIFACT_ORB_BLUE || item->type > ARTIFACT_ORB_GREEN) )
1408 {
1409 // default no monster drops these if appearance is set
1410 itemDroppable = false;
1411 }
1412 else
1413 {
1414 if ( monsterStats->type == SHADOW || monsterStats->type == AUTOMATON )
1415 {
1416 itemDroppable = false;
1417 }
1418 if ( monster->monsterIsTinkeringCreation() )
1419 {
1420 itemDroppable = false;
1421 }
1422
1423 if ( (monsterStats->type == KOBOLD
1424 || monsterStats->type == COCKATRICE
1425 || monsterStats->type == INSECTOID
1426 || monsterStats->type == INCUBUS
1427 || monsterStats->type == VAMPIRE
1428 || monsterStats->type == SUCCUBUS)
1429 && (itemCategory(item) == SPELLBOOK || itemCategory(item) == MAGICSTAFF) )
1430 {
1431 // monsters with special spell attacks won't drop their book.
1432 itemDroppable = false;
1433 }
1434 if ( monsterStats->type == INSECTOID && itemCategory(item) == THROWN )
1435 {
1436 // insectoids won't drop their un-thrown daggers.
1437 itemDroppable = false;
1438 }
1439 if ( monsterStats->type == INCUBUS && itemCategory(item) == POTION )
1440 {
1441 // incubus won't drop excess potions.
1442 itemDroppable = false;
1443 }
1444 if ( monsterStats->type == GOATMAN && (itemCategory(item) == POTION || itemCategory(item) == SPELLBOOK) )
1445 {
1446 // goatman sometimes won't drop excess potions.
1447 itemDroppable = false;
1448 }
1449 }
1450 }
1451 else if ( monsterStats->HP <= 0 )
1452 {
1453 // we're dropping the item on death.
1454 switch ( itemCategory(item) )
1455 {
1456 case WEAPON:
1457 case ARMOR:
1458 case THROWN:
1459 if ( item->status == BROKEN )
1460 {
1461 itemDroppable = false;
1462 }
1463 break;
1464 default:
1465 break;
1466 }
1467 if ( monster->behavior == &actPlayer )
1468 {
1469 if ( item->type >= ARTIFACT_SWORD && item->type <= ARTIFACT_GLOVES )
1470 {
1471 for ( int c = 1; c < MAXPLAYERS; ++c )
1472 {
1473 if ( players[c] && players[c]->entity && players[c]->entity == monster )
1474 {
1475 if ( itemIsEquipped(item, c) )
1476 {
1477 steamAchievementClient(c, "BARONY_ACH_CHOSEN_ONE");
1478 }
1479 break;
1480 }
1481 }
1482 }
1483 }
1484 }
1485 }
1486
1487 count = std::min(count, item->count);
1488 if ( itemTypeIsQuiver(item->type) )
1489 {
1490 count = item->count;
1491 }
1492
1493 if ( itemDroppable )
1494 {
1495 //TODO: Spawn multiple entities for count...
1496 entity = newEntity(-1, 1, map.entities, nullptr); //Item entity.
1497 entity->flags[INVISIBLE] = true;
1498 entity->flags[UPDATENEEDED] = true;
1499 entity->x = monster->x;
1500 entity->y = monster->y;
1501 entity->sizex = 4;
1502 entity->sizey = 4;
1503 entity->yaw = monster->yaw;
1504 entity->vel_x = (rand() % 20 - 10) / 10.0;
1505 entity->vel_y = (rand() % 20 - 10) / 10.0;
1506 entity->vel_z = -.5;
1507 entity->flags[PASSABLE] = true;
1508 entity->flags[USERFLAG1] = true; // speeds up game when many items are dropped
1509 entity->behavior = &actItem;
1510 entity->skill[10] = item->type;
1511 entity->skill[11] = item->status;
1512 entity->skill[12] = item->beatitude;
1513 entity->skill[13] = count;
1514 entity->skill[14] = item->appearance;
1515 entity->skill[15] = item->identified;
1516 entity->itemOriginalOwner = item->ownerUid;
1517 entity->parent = monster->getUID();
1518
1519 if ( monsterStats )
1520 {
1521 if (monsterStats->type == INCUBUS || monsterStats->type == SUCCUBUS )
1522 {
1523 // check if item was stolen.
1524 for ( int c = 0; c < MAXPLAYERS; ++c )
1525 {
1526 if ( players[c] && players[c]->entity )
1527 {
1528 if ( entity->itemOriginalOwner == players[c]->entity->getUID() )
1529 {
1530 entity->itemStolen = 1;
1531 break;
1532 }
1533 }
1534 }
1535 }
1536 else if ( monsterStats->type == DUMMYBOT )
1537 {
1538 entity->z = 4;
1539 }
1540 else if ( monsterStats->type == SENTRYBOT || monsterStats->type == SPELLBOT )
1541 {
1542 entity->vel_x *= 0.1;
1543 entity->vel_y *= 0.1;
1544 entity->vel_z = -.5;
1545 }
1546 }
1547 }
1548
1549 item->count -= count;
1550 if ( item->count <= 0 )
1551 {
1552 Item** slot;
1553 if ( (slot = itemSlot(monsterStats, item)) != nullptr )
1554 {
1555 *slot = nullptr; // clear the item slot
1556 }
1557
1558 if ( item->node )
1559 {
1560 list_RemoveNode(item->node);
1561 }
1562 else
1563 {
1564 free(item);
1565 }
1566 }
1567
1568 return entity;
1569 }
1570
1571 /*-------------------------------------------------------------------------------
1572
1573 consumeItem
1574
1575 consumes an item
1576
1577 -------------------------------------------------------------------------------*/
1578
consumeItem(Item * & item,const int player)1579 void consumeItem(Item*& item, const int player)
1580 {
1581 if ( item == nullptr )
1582 {
1583 return;
1584 }
1585 if ( appraisal_item == item->uid && item->count == 1 )
1586 {
1587 appraisal_item = 0;
1588 appraisal_timer = 0;
1589 }
1590
1591 if ( player > 0 && multiplayer == SERVER )
1592 {
1593 Item** slot = nullptr;
1594 if ( (slot = itemSlot(stats[player], item)) != nullptr )
1595 {
1596 (*slot)->count--; // if client had consumed item equipped, this'll update the count.
1597 }
1598 }
1599
1600 item->count--;
1601 if ( item->count <= 0 )
1602 {
1603 if ( item->node != nullptr )
1604 {
1605 for ( int i = 0; i < MAXPLAYERS; i++ )
1606 {
1607 if ( item->node->list == &stats[i]->inventory )
1608 {
1609 Item** slot;
1610 if ( (slot = itemSlot(stats[i], item)) != nullptr )
1611 {
1612 *slot = nullptr;
1613 }
1614 }
1615 }
1616 list_RemoveNode(item->node);
1617 }
1618 else
1619 {
1620 free(item);
1621 }
1622 item = nullptr;
1623 }
1624 }
1625
1626 /*-------------------------------------------------------------------------------
1627
1628 equipItem
1629
1630 Handles the client impulse to equip an item
1631
1632 -------------------------------------------------------------------------------*/
1633
equipItem(Item * const item,Item ** const slot,const int player)1634 EquipItemResult equipItem(Item* const item, Item** const slot, const int player)
1635 {
1636 int oldcount;
1637
1638 if ( player == clientnum && pickaxeGimpTimer > 0 && !intro )
1639 {
1640 return EQUIP_ITEM_FAIL_CANT_UNEQUIP;
1641 }
1642
1643 if (!item) // needs "|| !slot " ?
1644 {
1645 return EQUIP_ITEM_FAIL_CANT_UNEQUIP;
1646 }
1647
1648 if ( player == clientnum && multiplayer != SINGLE
1649 && item->unableToEquipDueToSwapWeaponTimer() )
1650 {
1651 return EQUIP_ITEM_FAIL_CANT_UNEQUIP;
1652 }
1653
1654 if ( itemCompare(*slot, item, true) )
1655 {
1656 // if items are different... (excluding the quantity of both item nodes)
1657 if ( *slot != nullptr )
1658 {
1659 if (!(*slot)->canUnequip(stats[player]))
1660 {
1661 if ( item->type == ARTIFACT_ORB_PURPLE && !strncmp(map.name, "Boss", 4) )
1662 {
1663 // can unequip anything when trying to equip the dang orb.
1664 }
1665 else
1666 {
1667 if ( player == clientnum )
1668 {
1669 if ( shouldInvertEquipmentBeatitude(stats[player]) && (*slot)->beatitude > 0 )
1670 {
1671 messagePlayer(player, language[3217], (*slot)->getName());
1672 }
1673 else
1674 {
1675 messagePlayer(player, language[1089], (*slot)->getName());
1676 }
1677 }
1678 (*slot)->identified = true;
1679 return EQUIP_ITEM_FAIL_CANT_UNEQUIP;
1680 }
1681 }
1682 }
1683 if ( multiplayer != CLIENT && !intro && !fadeout )
1684 {
1685 if ( players[player] != nullptr && players[player]->entity != nullptr)
1686 {
1687 if (players[player]->entity->ticks > 60)
1688 {
1689 if ( itemCategory(item) == AMULET || itemCategory(item) == RING )
1690 {
1691 playSoundEntity(players[player]->entity, 33 + rand() % 2, 64);
1692 }
1693 else if ( item->type == BOOMERANG )
1694 {
1695 }
1696 else if ( itemCategory(item) == WEAPON || itemCategory(item) == THROWN )
1697 {
1698 playSoundEntity(players[player]->entity, 40 + rand() % 4, 64);
1699 }
1700 else if ( itemCategory(item) == ARMOR
1701 || item->type == TOOL_TINKERING_KIT
1702 || itemTypeIsQuiver(item->type) )
1703 {
1704 playSoundEntity(players[player]->entity, 44 + rand() % 3, 64);
1705 }
1706 else if ( item->type == TOOL_TORCH || item->type == TOOL_LANTERN || item->type == TOOL_CRYSTALSHARD )
1707 {
1708 playSoundEntity(players[player]->entity, 134, 64);
1709 }
1710 }
1711 }
1712 }
1713 if ( multiplayer == SERVER && player > 0 )
1714 {
1715 if ( *slot != nullptr )
1716 {
1717 if ( (*slot)->node )
1718 {
1719 list_RemoveNode((*slot)->node);
1720 }
1721 else
1722 {
1723 free(*slot);
1724 }
1725 }
1726 }
1727 else
1728 {
1729 oldcount = item->count;
1730 item->count = 1;
1731 if ( intro == false )
1732 {
1733 messagePlayer(player, language[1090], item->description());
1734 }
1735 item->count = oldcount;
1736 }
1737 *slot = item;
1738 if ( player == clientnum )
1739 {
1740 if ( slot == &stats[player]->weapon )
1741 {
1742 weaponSwitch = true;
1743 }
1744 else if ( slot == &stats[player]->shield )
1745 {
1746 shieldSwitch = true;
1747 }
1748 }
1749 return EQUIP_ITEM_SUCCESS_NEWITEM;
1750 }
1751 else
1752 {
1753 // if items are the same... (excluding the quantity of both item nodes)
1754 if ( *slot != nullptr )
1755 {
1756 if ( (*slot)->count == item->count ) // if quantity is the same then it's the same item, can unequip
1757 {
1758 if (!(*slot)->canUnequip(stats[player]))
1759 {
1760 if ( player == clientnum )
1761 {
1762 if ( shouldInvertEquipmentBeatitude(stats[player]) && (*slot)->beatitude > 0 )
1763 {
1764 messagePlayer(player, language[3217], (*slot)->getName());
1765 }
1766 else
1767 {
1768 messagePlayer(player, language[1089], (*slot)->getName());
1769 }
1770 }
1771 (*slot)->identified = true;
1772 return EQUIP_ITEM_FAIL_CANT_UNEQUIP;
1773 }
1774 }
1775 else
1776 {
1777 // This lets the server know when a client "equipped" a new item in their slot but actually just updated the count.
1778 // Otherwise if this count check were not here, server would think that equipping 2 rocks after only holding 1 rock is
1779 // the same as unequipping the slot since they are the same item, barring the quantity. So the client would appear to
1780 // the server as empty handed, while the client holds 2 rocks, and when thrown on client end, the server never sees the item
1781 // and the client "throws" nothing, but actually loses their thrown items into nothingness. This fixes that issue.
1782 (*slot)->count = item->count; // update quantity.
1783 return EQUIP_ITEM_SUCCESS_UPDATE_QTY;
1784 }
1785 }
1786 if (multiplayer != CLIENT && !intro && !fadeout)
1787 {
1788 if (players[player] != nullptr && players[player]->entity != nullptr)
1789 {
1790 if (players[player]->entity->ticks > 60)
1791 {
1792 if (itemCategory(item) == ARMOR )
1793 {
1794 playSoundEntity(players[player]->entity, 44 + rand() % 3, 64);
1795 }
1796 }
1797 }
1798 }
1799 if ( player != 0 && multiplayer == SERVER )
1800 {
1801 if ( item->node )
1802 {
1803 list_RemoveNode(item->node);
1804 }
1805 else
1806 {
1807 free(item);
1808 }
1809 if ( *slot != nullptr )
1810 {
1811 if ( (*slot)->node )
1812 {
1813 list_RemoveNode((*slot)->node);
1814 }
1815 else
1816 {
1817 free(*slot);
1818 }
1819 }
1820 }
1821 else
1822 {
1823 oldcount = item->count;
1824 item->count = 1;
1825 if ( intro == false && !fadeout )
1826 {
1827 messagePlayer(player, language[1091], item->description());
1828 }
1829 item->count = oldcount;
1830 }
1831 *slot = nullptr;
1832 return EQUIP_ITEM_SUCCESS_UNEQUIP;
1833 }
1834 }
1835 /*-------------------------------------------------------------------------------
1836
1837 useItem
1838
1839 Handles the client impulse to use an item
1840
1841 -------------------------------------------------------------------------------*/
1842
useItem(Item * item,const int player,Entity * usedBy)1843 void useItem(Item* item, const int player, Entity* usedBy)
1844 {
1845 if ( item == nullptr )
1846 {
1847 return;
1848 }
1849
1850 if ( !usedBy && player >= 0 && player < MAXPLAYERS && players[player] && players[player]->entity )
1851 {
1852 // assume used by the player unless otherwise (a fountain potion effect e.g)
1853 usedBy = players[player]->entity;
1854 }
1855
1856 if ( openedChest[player] && itemCategory(item) != SPELL_CAT ) //TODO: What if fountain called this function for its potion effect?
1857 {
1858 //If a chest is open, put the item in the chest.
1859 openedChest[player]->addItemToChestFromInventory(player, item, false);
1860 return;
1861 }
1862 else if ( gui_mode == GUI_MODE_SHOP && player == clientnum && itemCategory(item) != SPELL_CAT) //TODO: What if fountain called this function for its potion effect?
1863 {
1864 bool deal = true;
1865 switch ( shopkeepertype )
1866 {
1867 case 0: // arms & armor
1868 if ( itemCategory(item) != WEAPON && itemCategory(item) != ARMOR && itemCategory(item) != THROWN )
1869 {
1870 deal = false;
1871 }
1872 break;
1873 case 1: // hats
1874 if ( itemCategory(item) != ARMOR )
1875 {
1876 deal = false;
1877 }
1878 break;
1879 case 2: // jewelry
1880 if ( itemCategory(item) != RING && itemCategory(item) != AMULET && itemCategory(item) != GEM )
1881 {
1882 deal = false;
1883 }
1884 break;
1885 case 3: // bookstore
1886 if ( itemCategory(item) != SPELLBOOK && itemCategory(item) != SCROLL && itemCategory(item) != BOOK )
1887 {
1888 deal = false;
1889 }
1890 break;
1891 case 4: // potion shop
1892 if ( itemCategory(item) != POTION )
1893 {
1894 deal = false;
1895 }
1896 break;
1897 case 5: // magicstaffs
1898 if ( itemCategory(item) != MAGICSTAFF )
1899 {
1900 deal = false;
1901 }
1902 break;
1903 case 6: // food
1904 if ( itemCategory(item) != FOOD )
1905 {
1906 deal = false;
1907 }
1908 break;
1909 case 7: // tools
1910 case 8: // lights
1911 if ( itemCategory(item) != TOOL )
1912 {
1913 deal = false;
1914 }
1915 break;
1916 default:
1917 break;
1918 }
1919 if ( deal )
1920 {
1921 sellitem = item;
1922 shopspeech = language[215];
1923 shoptimer = ticks - 1;
1924 }
1925 else
1926 {
1927 shopspeech = language[212 + rand() % 3];
1928 shoptimer = ticks - 1;
1929 }
1930 return;
1931 }
1932
1933 if ( item->status == BROKEN && player == clientnum )
1934 {
1935 messagePlayer(player, language[1092], item->getName());
1936 return;
1937 }
1938 if ( item->type == FOOD_CREAMPIE && player == clientnum && itemIsEquipped(item, player) )
1939 {
1940 messagePlayer(player, language[3874]); // can't eat while equipped.
1941 return;
1942 }
1943
1944 // tins need a tin opener to open...
1945 if ( player == clientnum && !(stats[player]->type == GOATMAN || stats[player]->type == AUTOMATON) )
1946 {
1947 if ( item->type == FOOD_TIN )
1948 {
1949 bool havetinopener = false;
1950 for ( node_t* node = stats[clientnum]->inventory.first; node != nullptr; node = node->next )
1951 {
1952 Item* tempitem = static_cast<Item*>(node->element);
1953 if ( tempitem->type == TOOL_TINOPENER )
1954 {
1955 if ( tempitem->status != BROKEN )
1956 {
1957 havetinopener = true;
1958 break;
1959 }
1960 }
1961 }
1962 if ( !havetinopener )
1963 {
1964 messagePlayer(clientnum, language[1093]);
1965 return;
1966 }
1967 }
1968 }
1969
1970 if ( multiplayer == CLIENT && !intro )
1971 {
1972 if ( item->unableToEquipDueToSwapWeaponTimer() && itemCategory(item) != POTION )
1973 {
1974 // don't send to host as we're not allowed to "use" or equip these items.
1975 // will return false in equipItem.
1976 // potions allowed here because we're drinking em.
1977 }
1978 else
1979 {
1980 strcpy((char*)net_packet->data, "USEI");
1981 SDLNet_Write32(static_cast<Uint32>(item->type), &net_packet->data[4]);
1982 SDLNet_Write32(static_cast<Uint32>(item->status), &net_packet->data[8]);
1983 SDLNet_Write32(static_cast<Uint32>(item->beatitude), &net_packet->data[12]);
1984 SDLNet_Write32(static_cast<Uint32>(item->count), &net_packet->data[16]);
1985 SDLNet_Write32(static_cast<Uint32>(item->appearance), &net_packet->data[20]);
1986 net_packet->data[24] = item->identified;
1987 net_packet->data[25] = clientnum;
1988 net_packet->address.host = net_server.host;
1989 net_packet->address.port = net_server.port;
1990 net_packet->len = 26;
1991 sendPacketSafe(net_sock, -1, net_packet, 0);
1992 }
1993 }
1994
1995 bool drankPotion = false;
1996 bool tryLearnPotionRecipe = false;
1997 const bool tryEmptyBottle = (item->status >= SERVICABLE);
1998 const ItemType potionType = item->type;
1999 if ( player == clientnum )
2000 {
2001 if ( itemCategory(item) == POTION && item->type != POTION_EMPTY && usedBy
2002 && (players[clientnum] && players[clientnum]->entity)
2003 && players[clientnum]->entity == usedBy )
2004 {
2005 if ( item->identified )
2006 {
2007 tryLearnPotionRecipe = true;
2008 }
2009 }
2010 }
2011
2012 switch ( item->type )
2013 {
2014 case WOODEN_SHIELD:
2015 equipItem(item, &stats[player]->shield, player);
2016 break;
2017 case QUARTERSTAFF:
2018 case BRONZE_SWORD:
2019 case BRONZE_MACE:
2020 case BRONZE_AXE:
2021 equipItem(item, &stats[player]->weapon, player);
2022 break;
2023 case BRONZE_SHIELD:
2024 equipItem(item, &stats[player]->shield, player);
2025 break;
2026 case SLING:
2027 case IRON_SPEAR:
2028 case IRON_SWORD:
2029 case IRON_MACE:
2030 case IRON_AXE:
2031 equipItem(item, &stats[player]->weapon, player);
2032 break;
2033 case IRON_SHIELD:
2034 equipItem(item, &stats[player]->shield, player);
2035 break;
2036 case SHORTBOW:
2037 case STEEL_HALBERD:
2038 case STEEL_SWORD:
2039 case STEEL_MACE:
2040 case STEEL_AXE:
2041 case CRYSTAL_SWORD:
2042 case CRYSTAL_SPEAR:
2043 case CRYSTAL_BATTLEAXE:
2044 case CRYSTAL_MACE:
2045 case BRONZE_TOMAHAWK:
2046 case IRON_DAGGER:
2047 case STEEL_CHAKRAM:
2048 case CRYSTAL_SHURIKEN:
2049 case BOOMERANG:
2050 equipItem(item, &stats[player]->weapon, player);
2051 break;
2052 case STEEL_SHIELD:
2053 case STEEL_SHIELD_RESISTANCE:
2054 case MIRROR_SHIELD:
2055 case CRYSTAL_SHIELD:
2056 equipItem(item, &stats[player]->shield, player);
2057 break;
2058 case CROSSBOW:
2059 case LONGBOW:
2060 case COMPOUND_BOW:
2061 case HEAVY_CROSSBOW:
2062 equipItem(item, &stats[player]->weapon, player);
2063 break;
2064 case GLOVES:
2065 case GLOVES_DEXTERITY:
2066 case BRACERS:
2067 case BRACERS_CONSTITUTION:
2068 case GAUNTLETS:
2069 case GAUNTLETS_STRENGTH:
2070 case ARTIFACT_GLOVES:
2071 case CRYSTAL_GLOVES:
2072 case BRASS_KNUCKLES:
2073 case IRON_KNUCKLES:
2074 case SPIKED_GAUNTLETS:
2075 case SUEDE_GLOVES:
2076 equipItem(item, &stats[player]->gloves, player);
2077 break;
2078 case CLOAK:
2079 case CLOAK_BLACK:
2080 case CLOAK_MAGICREFLECTION:
2081 case CLOAK_INVISIBILITY:
2082 case CLOAK_PROTECTION:
2083 case ARTIFACT_CLOAK:
2084 case CLOAK_BACKPACK:
2085 case CLOAK_SILVER:
2086 equipItem(item, &stats[player]->cloak, player);
2087 break;
2088 case LEATHER_BOOTS:
2089 case LEATHER_BOOTS_SPEED:
2090 case IRON_BOOTS:
2091 case IRON_BOOTS_WATERWALKING:
2092 case STEEL_BOOTS:
2093 case STEEL_BOOTS_LEVITATION:
2094 case STEEL_BOOTS_FEATHER:
2095 case ARTIFACT_BOOTS:
2096 case CRYSTAL_BOOTS:
2097 case SUEDE_BOOTS:
2098 equipItem(item, &stats[player]->shoes, player);
2099 break;
2100 case LEATHER_BREASTPIECE:
2101 case IRON_BREASTPIECE:
2102 case STEEL_BREASTPIECE:
2103 case CRYSTAL_BREASTPIECE:
2104 case VAMPIRE_DOUBLET:
2105 case WIZARD_DOUBLET:
2106 case HEALER_DOUBLET:
2107 case SILVER_DOUBLET:
2108 case ARTIFACT_BREASTPIECE:
2109 case TUNIC:
2110 case MACHINIST_APRON:
2111 equipItem(item, &stats[player]->breastplate, player);
2112 break;
2113 case HAT_PHRYGIAN:
2114 case HAT_HOOD:
2115 case HAT_WIZARD:
2116 case HAT_JESTER:
2117 case LEATHER_HELM:
2118 case IRON_HELM:
2119 case STEEL_HELM:
2120 case CRYSTAL_HELM:
2121 case ARTIFACT_HELM:
2122 case HAT_FEZ:
2123 case HAT_HOOD_RED:
2124 case HAT_HOOD_SILVER:
2125 case PUNISHER_HOOD:
2126 equipItem(item, &stats[player]->helmet, player);
2127 break;
2128 case AMULET_SEXCHANGE:
2129 messagePlayer(player, language[1094]);
2130 item_AmuletSexChange(item, player);
2131 consumeItem(item, player);
2132 break;
2133 case AMULET_LIFESAVING:
2134 case AMULET_WATERBREATHING:
2135 case AMULET_MAGICREFLECTION:
2136 equipItem(item, &stats[player]->amulet, player);
2137 break;
2138 case AMULET_STRANGULATION:
2139 equipItem(item, &stats[player]->amulet, player);
2140 if ( stats[player]->amulet )
2141 {
2142 messagePlayer(player, language[1095]);
2143 }
2144 if ( item->beatitude >= 0 )
2145 {
2146 item->beatitude = -1;
2147 }
2148 break;
2149 case AMULET_POISONRESISTANCE:
2150 equipItem(item, &stats[player]->amulet, player);
2151 break;
2152 case POTION_WATER:
2153 drankPotion = item_PotionWater(item, players[player]->entity, usedBy);
2154 break;
2155 case POTION_BOOZE:
2156 drankPotion = item_PotionBooze(item, players[player]->entity, usedBy);
2157 break;
2158 case POTION_JUICE:
2159 drankPotion = item_PotionJuice(item, players[player]->entity, usedBy);
2160 break;
2161 case POTION_SICKNESS:
2162 drankPotion = item_PotionSickness(item, players[player]->entity, usedBy);
2163 break;
2164 case POTION_CONFUSION:
2165 drankPotion = item_PotionConfusion(item, players[player]->entity, usedBy);
2166 break;
2167 case POTION_EXTRAHEALING:
2168 drankPotion = item_PotionExtraHealing(item, players[player]->entity, usedBy);
2169 break;
2170 case POTION_HEALING:
2171 drankPotion = item_PotionHealing(item, players[player]->entity, usedBy);
2172 break;
2173 case POTION_CUREAILMENT:
2174 drankPotion = item_PotionCureAilment(item, players[player]->entity, usedBy);
2175 break;
2176 case POTION_BLINDNESS:
2177 drankPotion = item_PotionBlindness(item, players[player]->entity, usedBy);
2178 break;
2179 case POTION_RESTOREMAGIC:
2180 drankPotion = item_PotionRestoreMagic(item, players[player]->entity, usedBy);
2181 break;
2182 case POTION_INVISIBILITY:
2183 drankPotion = item_PotionInvisibility(item, players[player]->entity, usedBy);
2184 break;
2185 case POTION_LEVITATION:
2186 drankPotion = item_PotionLevitation(item, players[player]->entity, usedBy);
2187 break;
2188 case POTION_SPEED:
2189 drankPotion = item_PotionSpeed(item, players[player]->entity, usedBy);
2190 break;
2191 case POTION_ACID:
2192 drankPotion = item_PotionAcid(item, players[player]->entity, usedBy);
2193 break;
2194 case POTION_PARALYSIS:
2195 drankPotion = item_PotionParalysis(item, players[player]->entity, usedBy);
2196 break;
2197 case POTION_EMPTY:
2198 messagePlayer(player, language[2359]);
2199 break;
2200 case POTION_POLYMORPH:
2201 {
2202 const int oldcount = item->count;
2203 item_PotionPolymorph(item, players[player]->entity, nullptr);
2204 if ( !item || (item && item->count < oldcount) )
2205 {
2206 drankPotion = true;
2207 }
2208 break;
2209 }
2210 case POTION_FIRESTORM:
2211 case POTION_ICESTORM:
2212 case POTION_THUNDERSTORM:
2213 drankPotion = item_PotionUnstableStorm(item, players[player]->entity, usedBy, nullptr);
2214 break;
2215 case POTION_STRENGTH:
2216 drankPotion = item_PotionStrength(item, players[player]->entity, usedBy);
2217 break;
2218 case SCROLL_MAIL:
2219 item_ScrollMail(item, player);
2220 break;
2221 case SCROLL_IDENTIFY:
2222 item_ScrollIdentify(item, player);
2223 if ( !players[player]->entity->isBlind() )
2224 {
2225 consumeItem(item, player);
2226 }
2227 break;
2228 case SCROLL_LIGHT:
2229 item_ScrollLight(item, player);
2230 if ( !players[player]->entity->isBlind() )
2231 {
2232 consumeItem(item, player);
2233 }
2234 break;
2235 case SCROLL_BLANK:
2236 item_ScrollBlank(item, player);
2237 break;
2238 case SCROLL_ENCHANTWEAPON:
2239 item_ScrollEnchantWeapon(item, player);
2240 if ( !players[player]->entity->isBlind() )
2241 {
2242 //consumeItem(item, player);
2243 }
2244 break;
2245 case SCROLL_ENCHANTARMOR:
2246 item_ScrollEnchantArmor(item, player);
2247 if ( !players[player]->entity->isBlind() )
2248 {
2249 //consumeItem(item, player);
2250 }
2251 break;
2252 case SCROLL_REMOVECURSE:
2253 item_ScrollRemoveCurse(item, player);
2254 if ( !players[player]->entity->isBlind() )
2255 {
2256 //consumeItem(item, player);
2257 }
2258 break;
2259 case SCROLL_FIRE:
2260 {
2261 const bool exploded = item_ScrollFire(item, player);
2262 if ( exploded && stats[player] && stats[player]->type == AUTOMATON )
2263 {
2264 if ( multiplayer != CLIENT )
2265 {
2266 stats[player]->HUNGER = std::min(stats[player]->HUNGER + 1500, 1500);
2267 players[player]->entity->modMP(stats[player]->MAXMP);
2268 // results of eating
2269 const Uint32 color = SDL_MapRGB(mainsurface->format, 255, 128, 0);
2270 messagePlayerColor(player, color, language[3699]); // superheats
2271 serverUpdateHunger(player);
2272 if ( stats[player]->playerRace == RACE_AUTOMATON && stats[player]->appearance == 0 )
2273 {
2274 steamStatisticUpdateClient(player, STEAM_STAT_SPICY, STEAM_STAT_INT, 1);
2275 steamStatisticUpdateClient(player, STEAM_STAT_FASCIST, STEAM_STAT_INT, 1);
2276 }
2277 }
2278 }
2279 if ( !players[player]->entity->isBlind() )
2280 {
2281 consumeItem(item, player);
2282 }
2283 break;
2284 }
2285 case SCROLL_FOOD:
2286 item_ScrollFood(item, player);
2287 if ( !players[player]->entity->isBlind() )
2288 {
2289 consumeItem(item, player);
2290 }
2291 break;
2292 case SCROLL_CONJUREARROW:
2293 item_ScrollConjureArrow(item, player);
2294 if ( !players[player]->entity->isBlind() )
2295 {
2296 consumeItem(item, player);
2297 }
2298 break;
2299 case SCROLL_MAGICMAPPING:
2300 item_ScrollMagicMapping(item, player);
2301 if ( !players[player]->entity->isBlind() )
2302 {
2303 consumeItem(item, player);
2304 }
2305 break;
2306 case SCROLL_REPAIR:
2307 case SCROLL_CHARGING:
2308 item_ScrollRepair(item, player);
2309 break;
2310 case SCROLL_DESTROYARMOR:
2311 item_ScrollDestroyArmor(item, player);
2312 if ( !players[player]->entity->isBlind() )
2313 {
2314 //consumeItem(item, player);
2315 }
2316 break;
2317 case SCROLL_TELEPORTATION:
2318 item_ScrollTeleportation(item, player);
2319 if ( !players[player]->entity->isBlind() )
2320 {
2321 consumeItem(item, player);
2322 }
2323 break;
2324 case SCROLL_SUMMON:
2325 item_ScrollSummon(item, player);
2326 if ( !players[player]->entity->isBlind() )
2327 {
2328 consumeItem(item, player);
2329 }
2330 break;
2331 case MAGICSTAFF_LIGHT:
2332 case MAGICSTAFF_DIGGING:
2333 case MAGICSTAFF_LOCKING:
2334 case MAGICSTAFF_MAGICMISSILE:
2335 case MAGICSTAFF_OPENING:
2336 case MAGICSTAFF_SLOW:
2337 case MAGICSTAFF_COLD:
2338 case MAGICSTAFF_FIRE:
2339 case MAGICSTAFF_LIGHTNING:
2340 case MAGICSTAFF_SLEEP:
2341 case MAGICSTAFF_STONEBLOOD:
2342 case MAGICSTAFF_BLEED:
2343 case MAGICSTAFF_SUMMON:
2344 case MAGICSTAFF_CHARM:
2345 case MAGICSTAFF_POISON:
2346 equipItem(item, &stats[player]->weapon, player);
2347 break;
2348 case RING_ADORNMENT:
2349 case RING_SLOWDIGESTION:
2350 case RING_PROTECTION:
2351 case RING_WARNING:
2352 case RING_STRENGTH:
2353 case RING_CONSTITUTION:
2354 case RING_INVISIBILITY:
2355 case RING_MAGICRESISTANCE:
2356 case RING_CONFLICT:
2357 case RING_LEVITATION:
2358 case RING_REGENERATION:
2359 case RING_TELEPORTATION:
2360 equipItem(item, &stats[player]->ring, player);
2361 break;
2362 case SPELLBOOK_FORCEBOLT:
2363 case SPELLBOOK_MAGICMISSILE:
2364 case SPELLBOOK_COLD:
2365 case SPELLBOOK_FIREBALL:
2366 case SPELLBOOK_LIGHTNING:
2367 case SPELLBOOK_REMOVECURSE:
2368 case SPELLBOOK_LIGHT:
2369 case SPELLBOOK_IDENTIFY:
2370 case SPELLBOOK_MAGICMAPPING:
2371 case SPELLBOOK_SLEEP:
2372 case SPELLBOOK_CONFUSE:
2373 case SPELLBOOK_SLOW:
2374 case SPELLBOOK_OPENING:
2375 case SPELLBOOK_LOCKING:
2376 case SPELLBOOK_LEVITATION:
2377 case SPELLBOOK_INVISIBILITY:
2378 case SPELLBOOK_TELEPORTATION:
2379 case SPELLBOOK_HEALING:
2380 case SPELLBOOK_EXTRAHEALING:
2381 case SPELLBOOK_CUREAILMENT:
2382 case SPELLBOOK_DIG:
2383 case SPELLBOOK_SUMMON:
2384 case SPELLBOOK_STONEBLOOD:
2385 case SPELLBOOK_BLEED:
2386 case SPELLBOOK_REFLECT_MAGIC:
2387 case SPELLBOOK_ACID_SPRAY:
2388 case SPELLBOOK_STEAL_WEAPON:
2389 case SPELLBOOK_DRAIN_SOUL:
2390 case SPELLBOOK_VAMPIRIC_AURA:
2391 case SPELLBOOK_CHARM_MONSTER:
2392 case SPELLBOOK_REVERT_FORM:
2393 case SPELLBOOK_RAT_FORM:
2394 case SPELLBOOK_SPIDER_FORM:
2395 case SPELLBOOK_TROLL_FORM:
2396 case SPELLBOOK_IMP_FORM:
2397 case SPELLBOOK_SPRAY_WEB:
2398 case SPELLBOOK_POISON:
2399 case SPELLBOOK_SPEED:
2400 case SPELLBOOK_FEAR:
2401 case SPELLBOOK_STRIKE:
2402 case SPELLBOOK_DETECT_FOOD:
2403 case SPELLBOOK_WEAKNESS:
2404 case SPELLBOOK_AMPLIFY_MAGIC:
2405 case SPELLBOOK_SHADOW_TAG:
2406 case SPELLBOOK_TELEPULL:
2407 case SPELLBOOK_DEMON_ILLU:
2408 case SPELLBOOK_TROLLS_BLOOD:
2409 case SPELLBOOK_SALVAGE:
2410 case SPELLBOOK_FLUTTER:
2411 case SPELLBOOK_DASH:
2412 case SPELLBOOK_SELF_POLYMORPH:
2413 item_Spellbook(item, player);
2414 break;
2415 case GEM_ROCK:
2416 case GEM_LUCK:
2417 case GEM_GARNET:
2418 case GEM_RUBY:
2419 case GEM_JACINTH:
2420 case GEM_AMBER:
2421 case GEM_CITRINE:
2422 case GEM_JADE:
2423 case GEM_EMERALD:
2424 case GEM_SAPPHIRE:
2425 case GEM_AQUAMARINE:
2426 case GEM_AMETHYST:
2427 case GEM_FLUORITE:
2428 case GEM_OPAL:
2429 case GEM_DIAMOND:
2430 case GEM_JETSTONE:
2431 case GEM_OBSIDIAN:
2432 case GEM_GLASS:
2433 equipItem(item, &stats[player]->weapon, player);
2434 break;
2435 case TOOL_PICKAXE:
2436 case TOOL_WHIP:
2437 equipItem(item, &stats[player]->weapon, player);
2438 break;
2439 case TOOL_TINOPENER:
2440 item_ToolTinOpener(item, player);
2441 break;
2442 case TOOL_MIRROR:
2443 item_ToolMirror(item, player);
2444 break;
2445 case TOOL_LOCKPICK:
2446 case TOOL_SKELETONKEY:
2447 case TOOL_BOMB:
2448 case TOOL_SLEEP_BOMB:
2449 case TOOL_FREEZE_BOMB:
2450 case TOOL_TELEPORT_BOMB:
2451 case TOOL_DECOY:
2452 case TOOL_DUMMYBOT:
2453 case TOOL_GYROBOT:
2454 case TOOL_SENTRYBOT:
2455 case TOOL_SPELLBOT:
2456 equipItem(item, &stats[player]->weapon, player);
2457 break;
2458 case TOOL_TORCH:
2459 case TOOL_LANTERN:
2460 case TOOL_CRYSTALSHARD:
2461 case TOOL_TINKERING_KIT:
2462 case QUIVER_SILVER:
2463 case QUIVER_PIERCE:
2464 case QUIVER_LIGHTWEIGHT:
2465 case QUIVER_FIRE:
2466 case QUIVER_KNOCKBACK:
2467 case QUIVER_CRYSTAL:
2468 case QUIVER_HUNTING:
2469 equipItem(item, &stats[player]->shield, player);
2470 break;
2471 case TOOL_BLINDFOLD:
2472 case TOOL_BLINDFOLD_FOCUS:
2473 case TOOL_BLINDFOLD_TELEPATHY:
2474 equipItem(item, &stats[player]->mask, player);
2475 break;
2476 case TOOL_TOWEL:
2477 item_ToolTowel(item, player);
2478 if ( multiplayer == CLIENT )
2479 if ( stats[player]->EFFECTS[EFF_BLEEDING] )
2480 {
2481 consumeItem(item, player);
2482 }
2483 break;
2484 case TOOL_GLASSES:
2485 case MASK_SHAMAN:
2486 equipItem(item, &stats[player]->mask, player);
2487 break;
2488 case TOOL_BEARTRAP:
2489 item_ToolBeartrap(item, player);
2490 break;
2491 case TOOL_ALEMBIC:
2492 if ( player != clientnum )
2493 {
2494 consumeItem(item, player);
2495 }
2496 else
2497 {
2498 GenericGUI.openGUI(GUI_TYPE_ALCHEMY, false, item);
2499 }
2500 break;
2501 case ENCHANTED_FEATHER:
2502 if ( player != clientnum )
2503 {
2504 consumeItem(item, player);
2505 }
2506 else
2507 {
2508 GenericGUI.openGUI(GUI_TYPE_SCRIBING, item);
2509 }
2510 break;
2511 case FOOD_BREAD:
2512 case FOOD_CREAMPIE:
2513 case FOOD_CHEESE:
2514 case FOOD_APPLE:
2515 case FOOD_MEAT:
2516 case FOOD_FISH:
2517 case FOOD_TOMALLEY:
2518 case FOOD_BLOOD:
2519 item_Food(item, player);
2520 break;
2521 case FOOD_TIN:
2522 item_FoodTin(item, player);
2523 break;
2524 case TOOL_MAGIC_SCRAP:
2525 case TOOL_METAL_SCRAP:
2526 if ( player == clientnum )
2527 {
2528 if ( item->type == TOOL_METAL_SCRAP )
2529 {
2530 messagePlayer(player, language[3705]);
2531 }
2532 else
2533 {
2534 messagePlayer(player, language[3706]);
2535 }
2536 }
2537 break;
2538 case READABLE_BOOK:
2539 if (numbooks && player == clientnum)
2540 {
2541 if (players[player] && players[player]->entity)
2542 {
2543 if (!players[player]->entity->isBlind())
2544 {
2545 openBook(books[item->appearance % numbooks], item);
2546 conductIlliterate = false;
2547 }
2548 else
2549 {
2550 messagePlayer(player, language[970]);
2551 }
2552 }
2553 }
2554 break;
2555 case SPELL_ITEM:
2556 {
2557 spell_t* spell = getSpellFromItem(item);
2558 if (spell)
2559 {
2560 equipSpell(spell, player, item);
2561 }
2562 break;
2563 }
2564 case ARTIFACT_SWORD:
2565 equipItem(item, &stats[player]->weapon, player);
2566 break;
2567 case ARTIFACT_MACE:
2568 if ( player == clientnum )
2569 {
2570 messagePlayer(clientnum, language[1096]);
2571 }
2572 equipItem(item, &stats[player]->weapon, player);
2573 break;
2574 case ARTIFACT_SPEAR:
2575 case ARTIFACT_AXE:
2576 case ARTIFACT_BOW:
2577 equipItem(item, &stats[player]->weapon, player);
2578 break;
2579 case ARTIFACT_ORB_BLUE:
2580 case ARTIFACT_ORB_RED:
2581 case ARTIFACT_ORB_PURPLE:
2582 case ARTIFACT_ORB_GREEN:
2583 equipItem(item, &stats[player]->weapon, player);
2584 break;
2585 default:
2586 printlog("error: item %d used, but it has no use case!\n", static_cast<int>(item->type));
2587 break;
2588 }
2589
2590 if ( player == clientnum )
2591 {
2592 if ( drankPotion && usedBy
2593 && (players[clientnum] && players[clientnum]->entity)
2594 && players[clientnum]->entity == usedBy )
2595 {
2596 if ( tryLearnPotionRecipe )
2597 {
2598 GenericGUI.alchemyLearnRecipe(potionType, true);
2599 }
2600 const int skillLVL = stats[clientnum]->PROFICIENCIES[PRO_ALCHEMY] / 20;
2601 if ( tryEmptyBottle && rand() % 100 < std::min(80, (60 + skillLVL * 10)) ) // 60 - 80% chance
2602 {
2603 Item* emptyBottle = newItem(POTION_EMPTY, SERVICABLE, 0, 1, 0, true, nullptr);
2604 itemPickup(clientnum, emptyBottle);
2605 messagePlayer(clientnum, language[3351], items[POTION_EMPTY].name_identified);
2606 free(emptyBottle);
2607 }
2608 }
2609 }
2610
2611 if ( !item )
2612 {
2613 return;
2614 }
2615
2616 // on-equip messages.
2617 if ( multiplayer != CLIENT && itemIsEquipped(item, player) )
2618 {
2619 switch ( item->type )
2620 {
2621 case ARTIFACT_BREASTPIECE:
2622 messagePlayer(player, language[2972]);
2623 break;
2624 case ARTIFACT_HELM:
2625 messagePlayer(player, language[2973]);
2626 break;
2627 case ARTIFACT_BOOTS:
2628 messagePlayer(player, language[2974]);
2629 break;
2630 case ARTIFACT_CLOAK:
2631 messagePlayer(player, language[2975]);
2632 break;
2633 case ARTIFACT_GLOVES:
2634 messagePlayer(player, language[2976]);
2635 break;
2636 case AMULET_LIFESAVING:
2637 messagePlayer(player, language[2478]);
2638 break;
2639 case AMULET_WATERBREATHING:
2640 messagePlayer(player, language[2479]);
2641 break;
2642 case AMULET_MAGICREFLECTION:
2643 messagePlayer(player, language[2480]);
2644 break;
2645 case HAT_WIZARD:
2646 messagePlayer(player, language[2481]);
2647 break;
2648 case SPIKED_GAUNTLETS:
2649 case BRASS_KNUCKLES:
2650 case IRON_KNUCKLES:
2651 messagePlayer(player, language[2482]);
2652 break;
2653 case HAT_JESTER:
2654 messagePlayer(player, language[2483]);
2655 break;
2656 case IRON_BOOTS_WATERWALKING:
2657 messagePlayer(player, language[2484]);
2658 break;
2659 case LEATHER_BOOTS_SPEED:
2660 messagePlayer(player, language[2485]);
2661 break;
2662 case CLOAK_INVISIBILITY:
2663 messagePlayer(player, language[2486]);
2664 break;
2665 case CLOAK_PROTECTION:
2666 messagePlayer(player, language[2487]);
2667 break;
2668 case CLOAK_MAGICREFLECTION:
2669 messagePlayer(player, language[2488]);
2670 break;
2671 case GLOVES_DEXTERITY:
2672 messagePlayer(player, language[2489]);
2673 break;
2674 case BRACERS_CONSTITUTION:
2675 messagePlayer(player, language[2490]);
2676 break;
2677 case GAUNTLETS_STRENGTH:
2678 messagePlayer(player, language[2491]);
2679 break;
2680 case AMULET_POISONRESISTANCE:
2681 messagePlayer(player, language[2492]);
2682 break;
2683 case RING_ADORNMENT:
2684 messagePlayer(player, language[2384]);
2685 break;
2686 case RING_SLOWDIGESTION:
2687 messagePlayer(player, language[2385]);
2688 break;
2689 case RING_PROTECTION:
2690 messagePlayer(player, language[2386]);
2691 break;
2692 case RING_WARNING:
2693 messagePlayer(player, language[2387]);
2694 break;
2695 case RING_STRENGTH:
2696 messagePlayer(player, language[2388]);
2697 break;
2698 case RING_CONSTITUTION:
2699 messagePlayer(player, language[2389]);
2700 break;
2701 case RING_INVISIBILITY:
2702 messagePlayer(player, language[2412]);
2703 break;
2704 case RING_MAGICRESISTANCE:
2705 messagePlayer(player, language[2413]);
2706 break;
2707 case RING_CONFLICT:
2708 messagePlayer(player, language[2414]);
2709 break;
2710 case RING_LEVITATION:
2711 if ( !MFLAG_DISABLELEVITATION )
2712 {
2713 // can levitate
2714 messagePlayer(player, language[2415]);
2715 }
2716 else
2717 {
2718 messagePlayer(player, language[2381]);
2719 }
2720 break;
2721 case RING_REGENERATION:
2722 messagePlayer(player, language[2416]);
2723 break;
2724 case RING_TELEPORTATION:
2725 messagePlayer(player, language[2417]);
2726 break;
2727 case STEEL_BOOTS_FEATHER:
2728 messagePlayer(player, language[2418]);
2729 break;
2730 case STEEL_BOOTS_LEVITATION:
2731 if ( !MFLAG_DISABLELEVITATION )
2732 {
2733 // can levitate
2734 messagePlayer(player, language[2419]);
2735 }
2736 else
2737 {
2738 messagePlayer(player, language[2381]);
2739 }
2740 break;
2741 case VAMPIRE_DOUBLET:
2742 messagePlayer(player, language[2597]);
2743 break;
2744 case TOOL_BLINDFOLD:
2745 break;
2746 case TOOL_BLINDFOLD_FOCUS:
2747 messagePlayer(player, language[2907]);
2748 break;
2749 case TOOL_BLINDFOLD_TELEPATHY:
2750 messagePlayer(player, language[2908]);
2751 break;
2752 default:
2753 break;
2754 }
2755 }
2756 }
2757
2758 /*-------------------------------------------------------------------------------
2759
2760 itemPickup
2761
2762 gives the supplied item to the specified player. Returns the item
2763
2764 -------------------------------------------------------------------------------*/
2765
itemPickup(const int player,Item * const item)2766 Item* itemPickup(const int player, Item* const item)
2767 {
2768 if (!item)
2769 {
2770 return nullptr;
2771 }
2772 Item* item2;
2773
2774 if ( stats[player]->PROFICIENCIES[PRO_APPRAISAL] >= CAPSTONE_UNLOCK_LEVEL[PRO_APPRAISAL] )
2775 {
2776 item->identified = true;
2777 if ( item->type == GEM_GLASS )
2778 {
2779 steamStatisticUpdate(STEAM_STAT_RHINESTONE_COWBOY, STEAM_STAT_INT, 1);
2780 }
2781 }
2782
2783 if ( multiplayer != CLIENT && players[player] && players[player]->entity )
2784 {
2785 bool assignNewOwner = true;
2786 if ( item->ownerUid != 0 && !achievementObserver.playerAchievements[player].ironicPunishmentTargets.empty() )
2787 {
2788 const auto it = achievementObserver.playerAchievements[player].ironicPunishmentTargets.find(item->ownerUid);
2789 if ( it != achievementObserver.playerAchievements[player].ironicPunishmentTargets.end() )
2790 {
2791 assignNewOwner = false;
2792 }
2793 }
2794
2795 if ( assignNewOwner )
2796 {
2797 item->ownerUid = players[player]->entity->getUID();
2798 }
2799 }
2800
2801 //messagePlayer(0, "id: %d", item->ownerUid);
2802
2803 if ( player != 0 && multiplayer == SERVER )
2804 {
2805 // send the client info on the item it just picked up
2806 strcpy((char*)net_packet->data, "ITEM");
2807 SDLNet_Write32(static_cast<Uint32>(item->type), &net_packet->data[4]);
2808 SDLNet_Write32(static_cast<Uint32>(item->status), &net_packet->data[8]);
2809 SDLNet_Write32(static_cast<Uint32>(item->beatitude), &net_packet->data[12]);
2810 SDLNet_Write32(static_cast<Uint32>(item->count), &net_packet->data[16]);
2811 SDLNet_Write32(static_cast<Uint32>(item->appearance), &net_packet->data[20]);
2812 SDLNet_Write32(static_cast<Uint32>(item->ownerUid), &net_packet->data[24]);
2813 net_packet->data[28] = item->identified;
2814 net_packet->address.host = net_clients[player - 1].host;
2815 net_packet->address.port = net_clients[player - 1].port;
2816 net_packet->len = 29;
2817 sendPacketSafe(net_sock, -1, net_packet, player - 1);
2818 }
2819 else
2820 {
2821 std::unordered_set<Uint32> appearancesOfSimilarItems;
2822 for ( node_t* node = stats[player]->inventory.first; node != nullptr; node = node->next )
2823 {
2824 item2 = static_cast<Item*>(node->element);
2825 if (!itemCompare(item, item2, false))
2826 {
2827 if ( (itemTypeIsQuiver(item2->type) && (item->count + item2->count) >= QUIVER_MAX_AMMO_QTY)
2828 || ((item2->type == TOOL_MAGIC_SCRAP || item2->type == TOOL_METAL_SCRAP)
2829 && (item->count + item2->count) >= SCRAP_MAX_STACK_QTY) )
2830 {
2831 int maxStack = QUIVER_MAX_AMMO_QTY;
2832 if ( item2->type == TOOL_MAGIC_SCRAP || item2->type == TOOL_METAL_SCRAP )
2833 {
2834 maxStack = SCRAP_MAX_STACK_QTY;
2835 }
2836
2837 if ( item2->count == maxStack - 1 )
2838 {
2839 // can't add anymore to this stack, let's skip over this.
2840
2841 if ( item->appearance == item2->appearance )
2842 {
2843 // items are the same (incl. appearance!)
2844 // if they shouldn't stack, we need to change appearance of the new item.
2845 appearancesOfSimilarItems.insert(item2->appearance);
2846 }
2847 continue;
2848 }
2849
2850 // too many arrows, split off into a new stack with reduced qty.
2851 const int total = item->count + item2->count;
2852 item2->count = maxStack - 1;
2853 item->count = total - item2->count;
2854
2855 if ( multiplayer == CLIENT && player == clientnum && itemIsEquipped(item2, clientnum) )
2856 {
2857 // if incrementing qty and holding item, then send "equip" for server to update their count of your held item.
2858 strcpy((char*)net_packet->data, "EQUS");
2859 SDLNet_Write32(static_cast<Uint32>(item2->type), &net_packet->data[4]);
2860 SDLNet_Write32(static_cast<Uint32>(item2->status), &net_packet->data[8]);
2861 SDLNet_Write32(static_cast<Uint32>(item2->beatitude), &net_packet->data[12]);
2862 SDLNet_Write32(static_cast<Uint32>(item2->count), &net_packet->data[16]);
2863 SDLNet_Write32(static_cast<Uint32>(item2->appearance), &net_packet->data[20]);
2864 net_packet->data[24] = item2->identified;
2865 net_packet->data[25] = clientnum;
2866 net_packet->address.host = net_server.host;
2867 net_packet->address.port = net_server.port;
2868 net_packet->len = 27;
2869 sendPacketSafe(net_sock, -1, net_packet, 0);
2870 }
2871 item2->ownerUid = item->ownerUid;
2872 if ( item->count <= 0 )
2873 {
2874 return item2;
2875 }
2876 else
2877 {
2878 // we have to search other items to stack with, otherwise this search ends after 1 full stack.
2879 if ( item->appearance == item2->appearance )
2880 {
2881 // items are the same (incl. appearance!)
2882 // if they shouldn't stack, we need to change appearance of the new item.
2883 appearancesOfSimilarItems.insert(item2->appearance);
2884 }
2885 continue;
2886 }
2887 }
2888 // if items are the same, check to see if they should stack
2889 else if ( item2->shouldItemStack(player) )
2890 {
2891 item2->count += item->count;
2892 if ( multiplayer == CLIENT && player == clientnum && itemIsEquipped(item2, clientnum) )
2893 {
2894 // if incrementing qty and holding item, then send "equip" for server to update their count of your held item.
2895 Item** slot = itemSlot(stats[clientnum], item2);
2896 if ( slot )
2897 {
2898 if ( slot == &stats[clientnum]->weapon )
2899 {
2900 clientSendEquipUpdateToServer(EQUIP_ITEM_SLOT_WEAPON, EQUIP_ITEM_SUCCESS_UPDATE_QTY, clientnum,
2901 item2->type, item2->status, item2->beatitude, item2->count, item2->appearance, item2->identified);
2902 }
2903 else if ( slot == &stats[clientnum]->shield )
2904 {
2905 clientSendEquipUpdateToServer(EQUIP_ITEM_SLOT_SHIELD, EQUIP_ITEM_SUCCESS_UPDATE_QTY, clientnum,
2906 item2->type, item2->status, item2->beatitude, item2->count, item2->appearance, item2->identified);
2907 }
2908 }
2909 }
2910 item2->ownerUid = item->ownerUid;
2911 return item2;
2912 }
2913 else if ( !itemCompare(item, item2, true) )
2914 {
2915 // items are the same (incl. appearance!)
2916 // if they shouldn't stack, we need to change appearance of the new item.
2917 appearancesOfSimilarItems.insert(item2->appearance);
2918 }
2919 }
2920 }
2921 if ( !appearancesOfSimilarItems.empty() )
2922 {
2923 int tries = 100;
2924 bool robot = false;
2925 // we need to find a unique appearance within the list.
2926 if ( item->type == TOOL_SENTRYBOT || item->type == TOOL_SPELLBOT || item->type == TOOL_GYROBOT
2927 || item->type == TOOL_DUMMYBOT )
2928 {
2929 robot = true;
2930 item->appearance += (rand() % 100000) * 10;
2931 }
2932 else
2933 {
2934 item->appearance = rand();
2935 }
2936 auto it = appearancesOfSimilarItems.find(item->appearance);
2937 while ( it != appearancesOfSimilarItems.end() && tries > 0 )
2938 {
2939 if ( robot )
2940 {
2941 item->appearance += (rand() % 100000) * 10;
2942 }
2943 else
2944 {
2945 item->appearance = rand();
2946 }
2947 it = appearancesOfSimilarItems.find(item->appearance);
2948 --tries;
2949 }
2950 }
2951
2952 item2 = newItem(item->type, item->status, item->beatitude, item->count, item->appearance, item->identified, &stats[player]->inventory);
2953 item2->ownerUid = item->ownerUid;
2954 return item2;
2955 }
2956
2957 return item;
2958 }
2959
2960 /*-------------------------------------------------------------------------------
2961
2962 newItemFromEntity
2963
2964 returns a pointer to an item struct from the given entity if it's an
2965 "item" entity, and returns NULL if the entity is anything else
2966
2967 -------------------------------------------------------------------------------*/
2968
newItemFromEntity(const Entity * const entity)2969 Item* newItemFromEntity(const Entity* const entity)
2970 {
2971 if ( entity == nullptr )
2972 {
2973 return nullptr;
2974 }
2975 Item* item = newItem(static_cast<ItemType>(entity->skill[10]), static_cast<Status>(entity->skill[11]), entity->skill[12], entity->skill[13], entity->skill[14], entity->skill[15], nullptr);
2976 item->ownerUid = static_cast<Uint32>(entity->itemOriginalOwner);
2977 item->interactNPCUid = static_cast<Uint32>(entity->interactedByMonster);
2978 return item;
2979 }
2980
2981 /*-------------------------------------------------------------------------------
2982
2983 itemSlot
2984
2985 returns a pointer to the equipment slot in which the item is residing,
2986 or NULL if the item isn't stored in an equipment slot
2987
2988 -------------------------------------------------------------------------------*/
2989
itemSlot(Stat * const myStats,Item * const item)2990 Item** itemSlot(Stat* const myStats, Item* const item)
2991 {
2992 if ( !myStats || !item )
2993 {
2994 return nullptr;
2995 }
2996 if (!itemCompare(item, myStats->helmet, true))
2997 {
2998 return &myStats->helmet;
2999 }
3000 if (!itemCompare(item, myStats->breastplate, true))
3001 {
3002 return &myStats->breastplate;
3003 }
3004 if (!itemCompare(item, myStats->gloves, true))
3005 {
3006 return &myStats->gloves;
3007 }
3008 if (!itemCompare(item, myStats->shoes, true))
3009 {
3010 return &myStats->shoes;
3011 }
3012 if (!itemCompare(item, myStats->shield, true))
3013 {
3014 return &myStats->shield;
3015 }
3016 if (!itemCompare(item, myStats->weapon, true))
3017 {
3018 return &myStats->weapon;
3019 }
3020 if (!itemCompare(item, myStats->cloak, true))
3021 {
3022 return &myStats->cloak;
3023 }
3024 if (!itemCompare(item, myStats->amulet, true))
3025 {
3026 return &myStats->amulet;
3027 }
3028 if (!itemCompare(item, myStats->ring, true))
3029 {
3030 return &myStats->ring;
3031 }
3032 if (!itemCompare(item, myStats->mask, true))
3033 {
3034 return &myStats->mask;
3035 }
3036 return nullptr;
3037 }
3038
3039 /*-------------------------------------------------------------------------------
3040
3041 itemIsEquipped
3042
3043 returns 1 if the passed item is equipped on the passed player number, otherwise returns 0
3044
3045 -------------------------------------------------------------------------------*/
3046
itemIsEquipped(const Item * const item,const int player)3047 bool itemIsEquipped(const Item* const item, const int player)
3048 {
3049 if ( !itemCompare(item, stats[player]->helmet, true) )
3050 {
3051 return true;
3052 }
3053 if ( !itemCompare(item, stats[player]->breastplate, true) )
3054 {
3055 return true;
3056 }
3057 if ( !itemCompare(item, stats[player]->gloves, true) )
3058 {
3059 return true;
3060 }
3061 if ( !itemCompare(item, stats[player]->shoes, true) )
3062 {
3063 return true;
3064 }
3065 if ( !itemCompare(item, stats[player]->shield, true) )
3066 {
3067 return true;
3068 }
3069 if ( !itemCompare(item, stats[player]->weapon, true) )
3070 {
3071 return true;
3072 }
3073 if ( !itemCompare(item, stats[player]->cloak, true) )
3074 {
3075 return true;
3076 }
3077 if ( !itemCompare(item, stats[player]->amulet, true) )
3078 {
3079 return true;
3080 }
3081 if ( !itemCompare(item, stats[player]->ring, true) )
3082 {
3083 return true;
3084 }
3085 if ( !itemCompare(item, stats[player]->mask, true) )
3086 {
3087 return true;
3088 }
3089
3090 return false;
3091 }
3092
3093 /*-------------------------------------------------------------------------------
3094
3095 Item::weaponGetAttack
3096
3097 returns the attack power of the given item
3098
3099 -------------------------------------------------------------------------------*/
3100
weaponGetAttack(const Stat * const wielder) const3101 Sint32 Item::weaponGetAttack(const Stat* const wielder) const
3102 {
3103 Sint32 attack = beatitude;
3104 if ( wielder )
3105 {
3106 if ( wielder->type == TROLL || wielder->type == RAT || wielder->type == SPIDER || wielder->type == CREATURE_IMP )
3107 {
3108 for ( int i = 0; i < MAXPLAYERS; ++i )
3109 {
3110 if ( wielder == stats[i] ) // is a player stat pointer.
3111 {
3112 return 0; // players that are these monsters do not benefit from weapons
3113 }
3114 }
3115 }
3116 if ( wielder->type == INCUBUS && wielder->playerRace == 0 && !strncmp(wielder->name, "inner demon", strlen("inner demon")) )
3117 {
3118 return -9999;
3119 }
3120 if ( shouldInvertEquipmentBeatitude(wielder) )
3121 {
3122 attack = abs(beatitude);
3123 }
3124 }
3125 if ( itemCategory(this) == MAGICSTAFF )
3126 {
3127 attack += 6;
3128 }
3129 else if ( itemCategory(this) == GEM )
3130 {
3131 attack += 4;
3132 }
3133 else if ( type == SLING )
3134 {
3135 attack += 4;
3136 }
3137 else if ( type == QUARTERSTAFF )
3138 {
3139 attack += 4;
3140 }
3141 else if ( type == BRONZE_SWORD )
3142 {
3143 attack += 4;
3144 }
3145 else if ( type == BRONZE_MACE )
3146 {
3147 attack += 4;
3148 }
3149 else if ( type == BRONZE_AXE )
3150 {
3151 attack += 4;
3152 }
3153 else if ( type == IRON_SPEAR )
3154 {
3155 attack += 5;
3156 }
3157 else if ( type == IRON_SWORD )
3158 {
3159 attack += 5;
3160 }
3161 else if ( type == IRON_MACE )
3162 {
3163 attack += 5;
3164 }
3165 else if ( type == IRON_AXE )
3166 {
3167 attack += 5;
3168 }
3169 else if ( type == STEEL_HALBERD )
3170 {
3171 attack += 6;
3172 }
3173 else if ( type == STEEL_SWORD )
3174 {
3175 attack += 6;
3176 }
3177 else if ( type == STEEL_MACE )
3178 {
3179 attack += 6;
3180 }
3181 else if ( type == STEEL_AXE )
3182 {
3183 attack += 6;
3184 }
3185 else if ( type == ARTIFACT_SWORD )
3186 {
3187 return (attack + 2 + status * 2);
3188 }
3189 else if ( type == ARTIFACT_MACE )
3190 {
3191 return (attack + 2 + status * 2);
3192 }
3193 else if ( type == ARTIFACT_SPEAR )
3194 {
3195 return (attack + 2 + status * 2);
3196 }
3197 else if ( type == ARTIFACT_AXE )
3198 {
3199 return (attack + 2 + status * 2);
3200 }
3201 else if ( type == ARTIFACT_BOW )
3202 {
3203 return (attack + 2 + status * 2);
3204 }
3205 else if ( type == SHORTBOW )
3206 {
3207 attack += 6;
3208 }
3209 else if ( type == CROSSBOW )
3210 {
3211 attack += 7;
3212 }
3213 else if ( type == LONGBOW )
3214 {
3215 attack += 10;
3216 }
3217 else if ( type == HEAVY_CROSSBOW )
3218 {
3219 attack += 16;
3220 }
3221 else if ( type == COMPOUND_BOW )
3222 {
3223 attack += 9;
3224 }
3225 else if ( type == CRYSTAL_SWORD )
3226 {
3227 attack += 10;
3228 }
3229 else if ( type == CRYSTAL_SPEAR )
3230 {
3231 attack += 10;
3232 }
3233 else if ( type == CRYSTAL_BATTLEAXE )
3234 {
3235 attack += 10;
3236 }
3237 else if ( type == CRYSTAL_MACE )
3238 {
3239 attack += 10;
3240 }
3241 else if ( type == BRONZE_TOMAHAWK )
3242 {
3243 attack += 6;
3244 }
3245 else if ( type == IRON_DAGGER )
3246 {
3247 attack += 8;
3248 }
3249 else if ( type == BOOMERANG )
3250 {
3251 return (attack + std::max(0, (status - 1) * 2));
3252 }
3253 else if ( type == STEEL_CHAKRAM )
3254 {
3255 attack += 10;
3256 }
3257 else if ( type == CRYSTAL_SHURIKEN )
3258 {
3259 attack += 12;
3260 }
3261 else if ( type == TOOL_WHIP )
3262 {
3263 attack += 2;
3264 }
3265 else if ( type == QUIVER_SILVER )
3266 {
3267 return attack + 2;
3268 }
3269 else if ( type == QUIVER_PIERCE )
3270 {
3271 return attack + 4;
3272 }
3273 else if ( type == QUIVER_LIGHTWEIGHT )
3274 {
3275 return attack - 2;
3276 }
3277 else if ( type == QUIVER_FIRE )
3278 {
3279 return attack + 2;
3280 }
3281 else if ( type == QUIVER_KNOCKBACK )
3282 {
3283 return attack + 4;
3284 }
3285 else if ( type == QUIVER_CRYSTAL )
3286 {
3287 return attack + 6;
3288 }
3289 else if ( type == QUIVER_HUNTING )
3290 {
3291 return attack + 4;
3292 }
3293 // old formula
3294 //attack *= (double)(status / 5.0);
3295 //
3296 if ( itemCategory(this) != TOOL && itemCategory(this) != THROWN && itemCategory(this) != GEM && itemCategory(this) != POTION )
3297 {
3298 // new formula
3299 attack += status - 3;
3300 }
3301
3302 return attack;
3303 }
3304
3305 /*-------------------------------------------------------------------------------
3306
3307 Item::armorGetAC
3308
3309 returns the armor value of the given item
3310
3311 -------------------------------------------------------------------------------*/
3312
armorGetAC(const Stat * const wielder) const3313 Sint32 Item::armorGetAC(const Stat* const wielder) const
3314 {
3315 Sint32 armor = beatitude;
3316 if ( wielder )
3317 {
3318 if ( shouldInvertEquipmentBeatitude(wielder) )
3319 {
3320 armor = abs(beatitude);
3321 }
3322 }
3323
3324 if ( itemTypeIsQuiver(type) )
3325 {
3326 armor = 0;
3327 }
3328
3329 if ( type == LEATHER_HELM )
3330 {
3331 armor += 1;
3332 }
3333 else if ( type == IRON_HELM )
3334 {
3335 armor += 2;
3336 }
3337 else if ( type == STEEL_HELM )
3338 {
3339 armor += 3;
3340 }
3341 else if ( type == LEATHER_BREASTPIECE )
3342 {
3343 armor += 2;
3344 }
3345 else if ( type == IRON_BREASTPIECE )
3346 {
3347 armor += 3;
3348 }
3349 else if ( type == STEEL_BREASTPIECE )
3350 {
3351 armor += 4;
3352 }
3353 else if ( type == MACHINIST_APRON )
3354 {
3355 armor += 1;
3356 }
3357 else if ( type == WIZARD_DOUBLET || type == HEALER_DOUBLET )
3358 {
3359 armor += 0;
3360 }
3361 else if ( type == VAMPIRE_DOUBLET )
3362 {
3363 armor += 1;
3364 }
3365 else if ( type == GLOVES || type == GLOVES_DEXTERITY )
3366 {
3367 armor += 1;
3368 }
3369 else if ( type == BRACERS || type == BRACERS_CONSTITUTION )
3370 {
3371 armor += 2;
3372 }
3373 else if ( type == GAUNTLETS || type == GAUNTLETS_STRENGTH )
3374 {
3375 armor += 3;
3376 }
3377 else if ( type == LEATHER_BOOTS || type == LEATHER_BOOTS_SPEED )
3378 {
3379 armor += 1;
3380 }
3381 else if ( type == IRON_BOOTS || type == IRON_BOOTS_WATERWALKING )
3382 {
3383 armor += 2;
3384 }
3385 else if ( type == STEEL_BOOTS || type == STEEL_BOOTS_LEVITATION || type == STEEL_BOOTS_FEATHER )
3386 {
3387 armor += 3;
3388 }
3389 else if ( type == WOODEN_SHIELD )
3390 {
3391 armor += 1;
3392 }
3393 else if ( type == BRONZE_SHIELD )
3394 {
3395 armor += 2;
3396 }
3397 else if ( type == IRON_SHIELD )
3398 {
3399 armor += 3;
3400 }
3401 else if ( type == STEEL_SHIELD || type == STEEL_SHIELD_RESISTANCE )
3402 {
3403 armor += 4;
3404 }
3405 else if ( type == CLOAK_PROTECTION )
3406 {
3407 armor += 1;
3408 }
3409 else if ( type == RING_PROTECTION )
3410 {
3411 armor += 1;
3412 }
3413 else if ( type == CRYSTAL_BREASTPIECE )
3414 {
3415 armor += 5;
3416 }
3417 else if ( type == CRYSTAL_HELM )
3418 {
3419 armor += 4;
3420 }
3421 else if ( type == CRYSTAL_BOOTS )
3422 {
3423 armor += 4;
3424 }
3425 else if ( type == CRYSTAL_SHIELD )
3426 {
3427 armor += 5;
3428 }
3429 else if ( type == CRYSTAL_GLOVES )
3430 {
3431 armor += 4;
3432 }
3433 else if ( type == ARTIFACT_BREASTPIECE )
3434 {
3435 armor += std::max(3, 3 + (status - 1)); // 2-6
3436 }
3437 else if ( type == ARTIFACT_HELM)
3438 {
3439 armor += std::max(1, 1 + (status - 1)); // 1-4
3440 }
3441 else if ( type == ARTIFACT_BOOTS )
3442 {
3443 armor += std::max(1, 1 + (status - 1)); // 1-4
3444 }
3445 else if ( type == ARTIFACT_GLOVES )
3446 {
3447 armor += std::max(1, 1 + (status - 1)); // 1-4
3448 }
3449 else if ( type == ARTIFACT_CLOAK )
3450 {
3451 armor += 0;
3452 }
3453 else if ( type == MIRROR_SHIELD )
3454 {
3455 armor += 0;
3456 }
3457 else if ( type == BRASS_KNUCKLES )
3458 {
3459 armor += 1;
3460 }
3461 else if ( type == IRON_KNUCKLES )
3462 {
3463 armor += 2;
3464 }
3465 else if ( type == SPIKED_GAUNTLETS )
3466 {
3467 armor += 3;
3468 }
3469 //armor *= (double)(item->status/5.0);
3470
3471 if ( wielder )
3472 {
3473 if ( wielder->type == TROLL || wielder->type == RAT || wielder->type == SPIDER || wielder->type == CREATURE_IMP )
3474 {
3475 for ( int i = 0; i < MAXPLAYERS; ++i )
3476 {
3477 if ( wielder == stats[i] ) // is a player stat pointer.
3478 {
3479 if ( itemCategory(this) == RING || itemCategory(this) == AMULET )
3480 {
3481 return armor;
3482 }
3483 return 0; // players that are these monsters do not benefit from non rings/amulets
3484 }
3485 }
3486 }
3487 }
3488
3489 return armor;
3490 }
3491
3492 /*-------------------------------------------------------------------------------
3493
3494 Item::canUnequip
3495
3496 returns true if the item may be unequipped (ie it isn't cursed)
3497
3498 -------------------------------------------------------------------------------*/
3499
canUnequip(const Stat * const wielder)3500 bool Item::canUnequip(const Stat* const wielder)
3501 {
3502 /*
3503 //Spellbooks are no longer equipable.
3504 if (type >= 100 && type <= 121) { //Spellbooks always unequipable regardless of cursed.
3505 return true;
3506 }*/
3507
3508 if ( wielder )
3509 {
3510 if ( wielder->type == AUTOMATON )
3511 {
3512 return true;
3513 }
3514 else if ( shouldInvertEquipmentBeatitude(wielder) )
3515 {
3516 if ( beatitude > 0 )
3517 {
3518 identified = true;
3519 return false;
3520 }
3521 else
3522 {
3523 return true;
3524 }
3525 }
3526 }
3527
3528 if (beatitude < 0)
3529 {
3530 identified = true;
3531 return false;
3532 }
3533
3534 return true;
3535 }
3536
3537 /*-------------------------------------------------------------------------------
3538
3539 Item::buyValue
3540
3541 returns value of an item to be bought by the given player
3542
3543 -------------------------------------------------------------------------------*/
3544
buyValue(const int player) const3545 int Item::buyValue(const int player) const
3546 {
3547 int value = items[type].value; // base value
3548
3549 // identified bonus
3550 if ( identified )
3551 {
3552 value *= .8;
3553 }
3554 else
3555 {
3556 if ( type == GEM_GLASS )
3557 {
3558 value = 1400;
3559 }
3560 else
3561 {
3562 value *= 1.25;
3563 }
3564 }
3565
3566 // cursed and status bonuses
3567 if ( beatitude > 0 )
3568 {
3569 value *= 1.f * beatitude * 3; // 3x multiplier for blessed gear.
3570 }
3571 else
3572 {
3573 value *= 1.f + beatitude / 2.f;
3574 }
3575 value *= (static_cast<int>(status) + 5) / 10.f;
3576
3577 // trading bonus
3578 value /= (50 + stats[player]->PROFICIENCIES[PRO_TRADING]) / 150.f;
3579
3580 // charisma bonus
3581 value /= 1.f + stats[player]->CHR / 20.f;
3582
3583 // result
3584 value = std::max(1, value);
3585
3586 if ( shopIsMysteriousShopkeeper(uidToEntity(shopkeeper)) )
3587 {
3588 value *= 2;
3589 }
3590
3591 if ( itemTypeIsQuiver(type) )
3592 {
3593 return std::max(value, items[type].value) * count;
3594 }
3595
3596 return std::max(value, items[type].value);
3597 }
3598
3599 /*-------------------------------------------------------------------------------
3600
3601 Item::sellValue
3602
3603 returns value of an item to be sold by the given player
3604
3605 -------------------------------------------------------------------------------*/
3606
sellValue(const int player) const3607 int Item::sellValue(const int player) const
3608 {
3609 int value = items[type].value; // base value
3610
3611 // identified bonus
3612 if ( identified )
3613 {
3614 value *= 1.20;
3615 }
3616 else
3617 {
3618 if ( itemCategory(this) == GEM )
3619 {
3620 value = items[GEM_GLASS].value;
3621 }
3622 else
3623 {
3624 value *= .75;
3625 }
3626 }
3627
3628 // cursed and status bonuses
3629 value *= 1.f + beatitude / 20.f;
3630 value *= (static_cast<int>(status) + 5) / 10.f;
3631
3632 // trading bonus
3633 value *= (50 + stats[player]->PROFICIENCIES[PRO_TRADING]) / 150.f;
3634
3635 // charisma bonus
3636 value *= 1.f + stats[player]->CHR / 20.f;
3637
3638 // result
3639 value = std::max(1, value);
3640
3641 if ( itemTypeIsQuiver(type) )
3642 {
3643 return std::min(value, items[type].value) * count;
3644 }
3645
3646 return std::min(value, items[type].value);
3647 }
3648
3649 /*-------------------------------------------------------------------------------
3650
3651 Item::apply
3652
3653 Applies the given item from the given player to the given entity
3654 (ie for key unlocking door)
3655
3656 -------------------------------------------------------------------------------*/
3657
apply(const int player,Entity * const entity)3658 void Item::apply(const int player, Entity* const entity)
3659 {
3660 if ( !entity )
3661 {
3662 return;
3663 }
3664
3665
3666 // for clients:
3667 if ( multiplayer == CLIENT )
3668 {
3669 strcpy((char*)net_packet->data, "APIT");
3670 SDLNet_Write32(static_cast<Uint32>(type), &net_packet->data[4]);
3671 SDLNet_Write32(static_cast<Uint32>(status), &net_packet->data[8]);
3672 SDLNet_Write32(static_cast<Uint32>(beatitude), &net_packet->data[12]);
3673 SDLNet_Write32(static_cast<Uint32>(count), &net_packet->data[16]);
3674 SDLNet_Write32(static_cast<Uint32>(appearance), &net_packet->data[20]);
3675 net_packet->data[24] = identified;
3676 net_packet->data[25] = player;
3677 SDLNet_Write32(static_cast<Uint32>(entity->getUID()), &net_packet->data[26]);
3678 net_packet->address.host = net_server.host;
3679 net_packet->address.port = net_server.port;
3680 net_packet->len = 30;
3681 sendPacketSafe(net_sock, -1, net_packet, 0);
3682 if ( type >= ARTIFACT_ORB_BLUE && type <= ARTIFACT_ORB_GREEN )
3683 {
3684 applyOrb(player, type, *entity);
3685 }
3686 else if ( type == POTION_EMPTY )
3687 {
3688 applyEmptyPotion(player, *entity);
3689 }
3690 return;
3691 }
3692
3693 if ( type >= ARTIFACT_ORB_BLUE && type <= ARTIFACT_ORB_GREEN )
3694 {
3695 applyOrb(player, type, *entity);
3696 }
3697 // effects
3698 if ( type == TOOL_SKELETONKEY )
3699 {
3700 applySkeletonKey(player, *entity);
3701 }
3702 else if ( type == TOOL_LOCKPICK )
3703 {
3704 applyLockpick(player, *entity);
3705 }
3706 else if ( type == POTION_EMPTY )
3707 {
3708 applyEmptyPotion(player, *entity);
3709 }
3710 }
3711
applyLockpickToWall(const int player,const int x,const int y) const3712 void Item::applyLockpickToWall(const int player, const int x, const int y) const
3713 {
3714 // for clients:
3715 if ( multiplayer == CLIENT )
3716 {
3717 strcpy((char*)net_packet->data, "APIW");
3718 SDLNet_Write32(static_cast<Uint32>(type), &net_packet->data[4]);
3719 SDLNet_Write32(static_cast<Uint32>(status), &net_packet->data[8]);
3720 SDLNet_Write32(static_cast<Uint32>(beatitude), &net_packet->data[12]);
3721 SDLNet_Write32(static_cast<Uint32>(count), &net_packet->data[16]);
3722 SDLNet_Write32(static_cast<Uint32>(appearance), &net_packet->data[20]);
3723 net_packet->data[24] = identified;
3724 net_packet->data[25] = player;
3725 SDLNet_Write16(x, &net_packet->data[26]);
3726 SDLNet_Write16(y, &net_packet->data[28]);
3727 net_packet->address.host = net_server.host;
3728 net_packet->address.port = net_server.port;
3729 net_packet->len = 30;
3730 sendPacketSafe(net_sock, -1, net_packet, 0);
3731 return;
3732 }
3733
3734 for ( node_t* node = map.entities->first; node != nullptr; node = node->next )
3735 {
3736 Entity* entity = static_cast<Entity*>(node->element);
3737 if ( entity && entity->behavior == &actArrowTrap
3738 && static_cast<int>(entity->x / 16) == x
3739 && static_cast<int>(entity->y / 16) == y )
3740 {
3741 // found a trap.
3742 if ( entity->skill[4] == 0 )
3743 {
3744 if ( players[player] && players[player]->entity
3745 && stats[player] && stats[player]->weapon
3746 && (stats[player]->weapon->type == TOOL_LOCKPICK || stats[player]->weapon->type == TOOL_SKELETONKEY) )
3747 {
3748 const int skill = std::max(1, stats[player]->PROFICIENCIES[PRO_LOCKPICKING] / 10);
3749 bool failed = false;
3750 if ( skill < 2 || rand() % skill == 0 ) // 20 skill requirement.
3751 {
3752 // failed.
3753 const Uint32 color = SDL_MapRGB(mainsurface->format, 255, 0, 0);
3754 messagePlayerColor(player, color, language[3871]); // trap fires.
3755 if ( skill < 2 )
3756 {
3757 messagePlayer(player, language[3887]); // not skilled enough.
3758 }
3759 failed = true;
3760 }
3761
3762 if ( failed )
3763 {
3764 entity->skill[4] = -1; // make the trap shoot.
3765 playSoundEntity(entity, 92, 64);
3766 }
3767 else
3768 {
3769 const Uint32 color = SDL_MapRGB(mainsurface->format, 0, 255, 0);
3770 messagePlayerColor(player, color, language[3872]);
3771 playSoundEntity(entity, 176, 128);
3772 entity->skill[4] = player + 1; // disabled flag and spit out items.
3773 serverUpdateEntitySkill(entity, 4); // update clients.
3774 }
3775
3776 // degrade lockpick.
3777 if ( !(stats[player]->weapon->type == TOOL_SKELETONKEY) && (rand() % 10 == 0 || (failed && rand() % 4 == 0)) )
3778 {
3779 if ( player == clientnum )
3780 {
3781 if ( count > 1 )
3782 {
3783 newItem(type, status, beatitude, count - 1, appearance, identified, &stats[player]->inventory);
3784 }
3785 }
3786 stats[player]->weapon->count = 1;
3787 stats[player]->weapon->status = static_cast<Status>(stats[player]->weapon->status - 1);
3788 if ( stats[player]->weapon->status == BROKEN )
3789 {
3790 messagePlayer(player, language[1104]);
3791 playSoundEntity(players[player]->entity, 76, 64);
3792 }
3793 else
3794 {
3795 messagePlayer(player, language[1103]);
3796 }
3797 if ( player > 0 && multiplayer == SERVER )
3798 {
3799 strcpy((char*)net_packet->data, "ARMR");
3800 net_packet->data[4] = 5;
3801 net_packet->data[5] = stats[player]->weapon->status;
3802 net_packet->address.host = net_clients[player - 1].host;
3803 net_packet->address.port = net_clients[player - 1].port;
3804 net_packet->len = 6;
3805 sendPacketSafe(net_sock, -1, net_packet, player - 1);
3806 }
3807 }
3808 if ( !failed && rand() % 5 == 0 )
3809 {
3810 players[player]->entity->increaseSkill(PRO_LOCKPICKING);
3811 }
3812 return;
3813 }
3814 else if ( entity->skill[4] != 0 )
3815 {
3816 messagePlayer(player, language[3870]);
3817 return;
3818 }
3819 }
3820 break;
3821 }
3822 }
3823
3824 if ( map.tiles[OBSTACLELAYER + y * MAPLAYERS + x * MAPLAYERS * map.height] == 53 )
3825 {
3826 messagePlayer(player, language[3873]);
3827 }
3828 }
3829
3830 SummonProperties::SummonProperties() = default;
3831
3832 SummonProperties::~SummonProperties() noexcept = default;
3833
isPotionBad(const Item & potion)3834 bool isPotionBad(const Item& potion)
3835 {
3836 if (itemCategory(&potion) != POTION)
3837 {
3838 return false;
3839 }
3840
3841 if (potion.type == POTION_SICKNESS
3842 || potion.type == POTION_CONFUSION
3843 || potion.type == POTION_BLINDNESS
3844 || potion.type == POTION_ACID
3845 || potion.type == POTION_PARALYSIS
3846 || potion.type == POTION_FIRESTORM
3847 || potion.type == POTION_ICESTORM
3848 || potion.type == POTION_THUNDERSTORM )
3849 {
3850 return true;
3851 }
3852
3853 return false;
3854 }
3855
createCustomInventory(Stat * const stats,const int itemLimit)3856 void createCustomInventory(Stat* const stats, const int itemLimit)
3857 {
3858 int itemSlots[6] = { ITEM_SLOT_INV_1, ITEM_SLOT_INV_2, ITEM_SLOT_INV_3, ITEM_SLOT_INV_4, ITEM_SLOT_INV_5, ITEM_SLOT_INV_6 };
3859 int i = 0;
3860 ItemType itemId { static_cast<ItemType>(-1) };
3861 int itemAppearance = rand();
3862 int category = 0;
3863 bool itemIdentified;
3864 int itemsGenerated = 0;
3865 int chance = 1;
3866
3867 if ( stats != nullptr )
3868 {
3869 for ( i = 0; i < 6 && itemsGenerated <= itemLimit; ++i )
3870 {
3871 category = stats->EDITOR_ITEMS[itemSlots[i] + ITEM_SLOT_CATEGORY];
3872 if ( category > 0 && stats->EDITOR_ITEMS[itemSlots[i]] == 1 )
3873 {
3874 if ( category > 0 && category <= 13 )
3875 {
3876 itemId = itemLevelCurve(static_cast<Category>(category - 1), 0, currentlevel);
3877 }
3878 else
3879 {
3880 int randType = 0;
3881 if ( category == 14 )
3882 {
3883 // equipment
3884 randType = rand() % 2;
3885 if ( randType == 0 )
3886 {
3887 itemId = itemLevelCurve(static_cast<Category>(WEAPON), 0, currentlevel);
3888 }
3889 else if ( randType == 1 )
3890 {
3891 itemId = itemLevelCurve(static_cast<Category>(ARMOR), 0, currentlevel);
3892 }
3893 }
3894 else if ( category == 15 )
3895 {
3896 // jewelry
3897 randType = rand() % 2;
3898 if ( randType == 0 )
3899 {
3900 itemId = itemLevelCurve(static_cast<Category>(AMULET), 0, currentlevel);
3901 }
3902 else
3903 {
3904 itemId = itemLevelCurve(static_cast<Category>(RING), 0, currentlevel);
3905 }
3906 }
3907 else if ( category == 16 )
3908 {
3909 // magical
3910 randType = rand() % 3;
3911 if ( randType == 0 )
3912 {
3913 itemId = itemLevelCurve(static_cast<Category>(SCROLL), 0, currentlevel);
3914 }
3915 else if ( randType == 1 )
3916 {
3917 itemId = itemLevelCurve(static_cast<Category>(MAGICSTAFF), 0, currentlevel);
3918 }
3919 else
3920 {
3921 itemId = itemLevelCurve(static_cast<Category>(SPELLBOOK), 0, currentlevel);
3922 }
3923 }
3924 }
3925 }
3926 else
3927 {
3928 itemId = static_cast<ItemType>(stats->EDITOR_ITEMS[itemSlots[i]] - 2);
3929 }
3930
3931 if ( itemId >= 0 )
3932 {
3933 Status itemStatus = static_cast<Status>(stats->EDITOR_ITEMS[itemSlots[i] + 1]);
3934 if ( itemStatus == 0 )
3935 {
3936 itemStatus = static_cast<Status>(DECREPIT + rand() % 4);
3937 }
3938 else if ( itemStatus > BROKEN )
3939 {
3940 itemStatus = static_cast<Status>(itemStatus - 1); // reserved '0' for random, so '1' is decrepit... etc to '5' being excellent.
3941 }
3942 int itemBless = stats->EDITOR_ITEMS[itemSlots[i] + 2];
3943 if ( itemBless == 10 )
3944 {
3945 itemBless = -1 + rand() % 3;
3946 }
3947 const int itemCount = stats->EDITOR_ITEMS[itemSlots[i] + 3];
3948 if ( stats->EDITOR_ITEMS[itemSlots[i] + 4] == 1 )
3949 {
3950 itemIdentified = false;
3951 }
3952 else if ( stats->EDITOR_ITEMS[itemSlots[i] + 4] == 2 )
3953 {
3954 itemIdentified = true;
3955 }
3956 else
3957 {
3958 itemIdentified = rand() % 2;
3959 }
3960 itemAppearance = rand();
3961 chance = stats->EDITOR_ITEMS[itemSlots[i] + 5];
3962 if ( rand() % 100 < chance )
3963 {
3964 newItem(itemId, itemStatus, itemBless, itemCount, itemAppearance, itemIdentified, &stats->inventory);
3965 }
3966 itemsGenerated++;
3967 }
3968 }
3969 }
3970 }
3971
itemNodeInInventory(const Stat * const myStats,const Sint32 itemToFind,const Category cat)3972 node_t* itemNodeInInventory(const Stat* const myStats, const Sint32 itemToFind, const Category cat)
3973 {
3974 if ( myStats == nullptr )
3975 {
3976 return nullptr;
3977 }
3978
3979 node_t* node = nullptr;
3980 node_t* nextnode = nullptr;
3981
3982 for ( node = myStats->inventory.first; node != nullptr; node = nextnode )
3983 {
3984 nextnode = node->next;
3985 Item* item = static_cast<Item*>(node->element);
3986 if ( item != nullptr )
3987 {
3988 if ( cat >= WEAPON && itemCategory(item) == cat )
3989 {
3990 return node;
3991 }
3992 else if ( itemToFind != -1 && item->type == itemToFind )
3993 {
3994 return node;
3995 }
3996 }
3997 }
3998
3999 return nullptr;
4000 }
4001
spellbookNodeInInventory(const Stat * const myStats,const int spellIDToFind)4002 node_t* spellbookNodeInInventory(const Stat* const myStats, const int spellIDToFind)
4003 {
4004 if ( spellIDToFind == SPELL_NONE )
4005 {
4006 return nullptr;
4007 }
4008
4009 if ( myStats == nullptr )
4010 {
4011 return nullptr;
4012 }
4013 //messagePlayer(clientnum, "Got into spellbookNodeInInventory().");
4014
4015 for ( node_t* node = myStats->inventory.first; node != nullptr; node = node->next )
4016 {
4017 Item* item = static_cast<Item*>(node->element);
4018 if ( item != nullptr && itemCategory(item) == SPELLBOOK && getSpellIDFromSpellbook(item->type) == spellIDToFind )
4019 {
4020 return node;
4021 }
4022 else
4023 {
4024 if ( itemCategory(item) == SPELLBOOK )
4025 {
4026 //messagePlayer(clientnum, "Well...I found a spellbook? Type: %d. Looking for: %d.", getSpellIDFromSpellbook(item->type), spellIDToFind);
4027 }
4028 }
4029 }
4030
4031 //messagePlayer(clientnum, "Spellbook %d not found.", spellIDToFind);
4032
4033 return nullptr;
4034 }
4035
getRangedWeaponItemNodeInInventory(const Stat * const myStats,const bool includeMagicstaff)4036 node_t* getRangedWeaponItemNodeInInventory(const Stat* const myStats, const bool includeMagicstaff)
4037 {
4038 if ( myStats == nullptr )
4039 {
4040 return nullptr;
4041 }
4042
4043 for ( node_t* node = myStats->inventory.first; node != nullptr; node = node->next )
4044 {
4045 Item* item = static_cast<Item*>(node->element);
4046 if ( item != nullptr )
4047 {
4048 if ( isRangedWeapon(*item) )
4049 {
4050 return node;
4051 }
4052 if ( includeMagicstaff && itemCategory(item) == MAGICSTAFF )
4053 {
4054 return node;
4055 }
4056 }
4057 }
4058
4059 return nullptr;
4060 }
4061
getMeleeWeaponItemNodeInInventory(const Stat * const myStats)4062 node_t* getMeleeWeaponItemNodeInInventory(const Stat* const myStats)
4063 {
4064 if ( myStats == nullptr )
4065 {
4066 return nullptr;
4067 }
4068
4069 for ( node_t* node = myStats->inventory.first; node != nullptr; node = node->next )
4070 {
4071 Item* item = static_cast<Item*>(node->element);
4072 if ( item != nullptr )
4073 {
4074 if ( isMeleeWeapon(*item) )
4075 {
4076 return node;
4077 }
4078 }
4079 }
4080
4081 return nullptr;
4082 }
4083
isRangedWeapon(const Item & item)4084 bool isRangedWeapon(const Item& item)
4085 {
4086 switch ( item.type )
4087 {
4088 case SLING:
4089 case SHORTBOW:
4090 case CROSSBOW:
4091 case ARTIFACT_BOW:
4092 case LONGBOW:
4093 case COMPOUND_BOW:
4094 case HEAVY_CROSSBOW:
4095 return true;
4096 default:
4097 return false;
4098 }
4099 }
4100
isMeleeWeapon(const Item & item)4101 bool isMeleeWeapon(const Item& item)
4102 {
4103 if ( itemCategory(&item) != WEAPON )
4104 {
4105 return false;
4106 }
4107
4108 return ( !isRangedWeapon(item) );
4109 }
4110
isShield() const4111 bool Item::isShield() const
4112 {
4113 if ( itemCategory(this) != ARMOR || checkEquipType(this) != TYPE_SHIELD )
4114 {
4115 return false;
4116 }
4117
4118 return true;
4119 }
4120
swapMonsterWeaponWithInventoryItem(Entity * const my,Stat * const myStats,node_t * const inventoryNode,const bool moveStack,const bool overrideCursed)4121 bool swapMonsterWeaponWithInventoryItem(Entity* const my, Stat* const myStats, node_t* const inventoryNode, const bool moveStack, const bool overrideCursed)
4122 {
4123 //TODO: Does this work with multiplayer?
4124 Item* item = nullptr;
4125 Item* tmpItem = nullptr;
4126
4127 if ( myStats == nullptr || inventoryNode == nullptr )
4128 {
4129 return false;
4130 }
4131
4132 if ( (myStats->weapon && myStats->weapon->beatitude < 0) && !overrideCursed )
4133 {
4134 return false; //Can't unequip cursed items!
4135 }
4136
4137 item = static_cast<Item*>(inventoryNode->element);
4138
4139 if ( item->count == 1 || moveStack )
4140 {
4141 // TODO: handle stacks.
4142 tmpItem = newItem(GEM_ROCK, EXCELLENT, 0, 1, 0, false, nullptr);
4143 if ( !tmpItem )
4144 {
4145 return false;
4146 }
4147 copyItem(tmpItem, item);
4148 if ( myStats->weapon != nullptr )
4149 {
4150 copyItem(item, myStats->weapon);
4151 copyItem(myStats->weapon, tmpItem);
4152 if ( multiplayer != CLIENT && (itemCategory(myStats->weapon) == WEAPON || itemCategory(myStats->weapon) == THROWN) )
4153 {
4154 playSoundEntity(my, 40 + rand() % 4, 64);
4155 }
4156 free(tmpItem);
4157 }
4158 else
4159 {
4160 myStats->weapon = tmpItem;
4161 // remove the new item we created.
4162 list_RemoveNode(inventoryNode);
4163 }
4164 return true;
4165 }
4166 else
4167 {
4168 //Move exactly 1 item into hand.
4169 if ( my == nullptr )
4170 {
4171 return false;
4172 }
4173
4174 tmpItem = newItem(GEM_ROCK, EXCELLENT, 0, 1, 0, false, nullptr);
4175 if ( !tmpItem )
4176 {
4177 return false;
4178 }
4179
4180 copyItem(tmpItem, item);
4181 tmpItem->count = 1;
4182 item->count--;
4183
4184 if ( myStats->weapon != nullptr )
4185 {
4186 my->addItemToMonsterInventory(myStats->weapon);
4187 myStats->weapon = tmpItem;
4188 if ( multiplayer != CLIENT && (itemCategory(myStats->weapon) == WEAPON || itemCategory(myStats->weapon) == THROWN) )
4189 {
4190 playSoundEntity(my, 40 + rand() % 4, 64);
4191 }
4192 }
4193 else
4194 {
4195 myStats->weapon = tmpItem;
4196 }
4197
4198 return true;
4199 }
4200 }
4201
monsterUnequipSlot(Stat * const myStats,Item ** const slot,Item * const itemToUnequip)4202 bool monsterUnequipSlot(Stat* const myStats, Item** const slot, Item* const itemToUnequip)
4203 {
4204 Item* tmpItem = nullptr;
4205
4206 if ( myStats == nullptr || *slot == nullptr )
4207 {
4208 return false;
4209 }
4210
4211 if ( itemCompare(*slot, itemToUnequip, false) )
4212 {
4213 tmpItem = newItem(GEM_ROCK, EXCELLENT, 0, 1, 0, false, &myStats->inventory);
4214 copyItem(tmpItem, itemToUnequip);
4215
4216 if ( (*slot)->node )
4217 {
4218 list_RemoveNode((*slot)->node);
4219 }
4220 else
4221 {
4222 free(*slot);
4223 }
4224
4225 *slot = nullptr;
4226 }
4227
4228 return true;
4229 }
4230
monsterUnequipSlotFromCategory(Stat * const myStats,Item ** const slot,const Category cat)4231 bool monsterUnequipSlotFromCategory(Stat* const myStats, Item** const slot, const Category cat)
4232 {
4233 Item* tmpItem = nullptr;
4234
4235 if ( myStats == nullptr || *slot == nullptr )
4236 {
4237 return false;
4238 }
4239
4240 if ( itemCategory(*slot) == cat)
4241 {
4242 tmpItem = newItem(GEM_ROCK, EXCELLENT, 0, 1, 0, false, &myStats->inventory);
4243 copyItem(tmpItem, *slot);
4244
4245 if ( (*slot)->node )
4246 {
4247 list_RemoveNode((*slot)->node);
4248 }
4249 else
4250 {
4251 free(*slot);
4252 }
4253
4254 *slot = nullptr;
4255 //messagePlayer(0, "un-equip!");
4256 return true;
4257 }
4258
4259 return false;
4260 }
4261
copyItem(Item * const itemToSet,const Item * const itemToCopy)4262 void copyItem(Item* const itemToSet, const Item* const itemToCopy) //This should probably use references instead...
4263 {
4264 if ( !itemToSet || !itemToCopy )
4265 {
4266 return;
4267 }
4268
4269 itemToSet->type = itemToCopy->type;
4270 itemToSet->status = itemToCopy->status;
4271 itemToSet->beatitude = itemToCopy->beatitude;
4272 itemToSet->count = itemToCopy->count;
4273 itemToSet->appearance = itemToCopy->appearance;
4274 itemToSet->identified = itemToCopy->identified;
4275 itemToSet->uid = itemToCopy->uid;
4276 itemToSet->ownerUid = itemToCopy->ownerUid;
4277 itemToSet->isDroppable = itemToCopy->isDroppable;
4278 }
4279
itemTypeWithinGoldValue(const int cat,const int minValue,const int maxValue)4280 ItemType itemTypeWithinGoldValue(const int cat, const int minValue, const int maxValue)
4281 {
4282 const int numitems = NUMITEMS;
4283 int numoftype = 0;
4284 bool chances[NUMITEMS] = { false };
4285 bool pickAnyCategory = false;
4286 int c;
4287
4288 if ( cat < -1 || cat >= NUMCATEGORIES )
4289 {
4290 printlog("warning: pickItemWithinGoldValue() called with bad category value!\n");
4291 return GEM_ROCK;
4292 }
4293
4294 if ( cat == -1 )
4295 {
4296 pickAnyCategory = true;
4297 }
4298
4299 // find highest value of items in category
4300 for ( c = 0; c < NUMITEMS; ++c )
4301 {
4302 if ( items[c].category == cat || (pickAnyCategory && items[c].category != SPELL_CAT) )
4303 {
4304 if ( items[c].value >= minValue && items[c].value <= maxValue && items[c].level != -1 )
4305 {
4306 // chance true for an item if it's not forbidden from the global item list.
4307 chances[c] = true;
4308 numoftype++;
4309 }
4310 }
4311 }
4312
4313 if ( numoftype == 0 )
4314 {
4315 printlog("warning: category passed has no items within gold values!\n");
4316 return GEM_ROCK;
4317 }
4318
4319 // pick the item
4320 int pick = rand() % numoftype;// prng_get_uint() % numoftype;
4321 for ( c = 0; c < numitems; c++ )
4322 {
4323 if ( chances[c] == true )
4324 {
4325 if ( pick == 0 )
4326 {
4327 return static_cast<ItemType>(c);
4328 }
4329 else
4330 {
4331 pick--;
4332 }
4333 }
4334 }
4335
4336 return GEM_ROCK;
4337 }
4338
isThisABetterWeapon(const Item & newWeapon,const Item * const weaponAlreadyHave)4339 bool Item::isThisABetterWeapon(const Item& newWeapon, const Item* const weaponAlreadyHave)
4340 {
4341 if ( !weaponAlreadyHave )
4342 {
4343 //Any thing is better than no thing!
4344 return true;
4345 }
4346
4347 if ( newWeapon.weaponGetAttack() > weaponAlreadyHave->weaponGetAttack() )
4348 {
4349 return true; //If the new weapon does more damage than the current weapon, it's better. Even if it's cursed, eh?
4350 }
4351
4352 return false;
4353 }
4354
isThisABetterArmor(const Item & newArmor,const Item * const armorAlreadyHave)4355 bool Item::isThisABetterArmor(const Item& newArmor, const Item* const armorAlreadyHave)
4356 {
4357 if ( !armorAlreadyHave )
4358 {
4359 //Some thing is better than no thing!
4360 return true;
4361 }
4362
4363 if ( FollowerMenu.entityToInteractWith )
4364 {
4365 if ( newArmor.interactNPCUid == FollowerMenu.entityToInteractWith->interactedByMonster )
4366 {
4367 return true;
4368 }
4369 }
4370
4371 if ( armorAlreadyHave->forcedPickupByPlayer == true )
4372 {
4373 return false;
4374 }
4375
4376 if ( itemTypeIsQuiver(armorAlreadyHave->type) )
4377 {
4378 return false;
4379 }
4380
4381 //If the new weapon defends better than the current armor, it's better. Even if it's cursed, eh?
4382 //TODO: Special effects/abilities, like magic resistance or reflection...
4383 if ( newArmor.armorGetAC() > armorAlreadyHave->armorGetAC() )
4384 {
4385 return true;
4386 }
4387
4388 return false;
4389 }
4390
shouldItemStack(const int player) const4391 bool Item::shouldItemStack(const int player) const
4392 {
4393 if ( player >= 0 )
4394 {
4395 if ( (!itemIsEquipped(this, player)
4396 && itemCategory(this) != ARMOR
4397 && itemCategory(this) != WEAPON
4398 && itemCategory(this) != MAGICSTAFF
4399 && itemCategory(this) != RING
4400 && itemCategory(this) != AMULET
4401 && itemCategory(this) != SPELLBOOK
4402 && this->type != TOOL_PICKAXE
4403 && this->type != TOOL_ALEMBIC
4404 && this->type != TOOL_TINKERING_KIT
4405 && this->type != ENCHANTED_FEATHER )
4406 || itemCategory(this) == THROWN
4407 || itemCategory(this) == GEM
4408 || itemCategory(this) == POTION
4409 || (itemCategory(this) == TOOL
4410 && this->type != TOOL_PICKAXE
4411 && this->type != TOOL_ALEMBIC
4412 && this->type != TOOL_TINKERING_KIT
4413 && this->type != ENCHANTED_FEATHER)
4414 )
4415 {
4416 // THROWN, GEM, TOOLS, POTIONS should stack when equipped.
4417 // otherwise most equippables should not stack.
4418 if ( itemCategory(this) == THROWN || itemCategory(this) == GEM )
4419 {
4420 if ( count >= 9 )
4421 {
4422 return false;
4423 }
4424 }
4425 else if ( itemTypeIsQuiver(this->type) )
4426 {
4427 if ( count >= QUIVER_MAX_AMMO_QTY - 1 )
4428 {
4429 return false;
4430 }
4431 return true;
4432 }
4433 else if ( type == TOOL_METAL_SCRAP || type == TOOL_MAGIC_SCRAP )
4434 {
4435 if ( count >= SCRAP_MAX_STACK_QTY - 1 )
4436 {
4437 return false;
4438 }
4439 return true;
4440 }
4441 else if ( type == TOOL_SPELLBOT || type == TOOL_DUMMYBOT || type == TOOL_SENTRYBOT || type == TOOL_GYROBOT )
4442 {
4443 return false;
4444 }
4445 return true;
4446 }
4447 }
4448 return false;
4449 }
4450
4451
shouldInvertEquipmentBeatitude(const Stat * const wielder)4452 bool shouldInvertEquipmentBeatitude(const Stat* const wielder)
4453 {
4454 if ( wielder->type == SUCCUBUS || wielder->type == INCUBUS )
4455 {
4456 return true;
4457 }
4458 return false;
4459 }
4460
isItemEquippableInShieldSlot(const Item * const item)4461 bool isItemEquippableInShieldSlot(const Item* const item)
4462 {
4463 if ( !item )
4464 {
4465 return false;
4466 }
4467
4468 if ( itemTypeIsQuiver(item->type) )
4469 {
4470 return true;
4471 }
4472
4473 switch ( item->type )
4474 {
4475 case WOODEN_SHIELD:
4476 case BRONZE_SHIELD:
4477 case IRON_SHIELD:
4478 case STEEL_SHIELD:
4479 case STEEL_SHIELD_RESISTANCE:
4480 case MIRROR_SHIELD:
4481 case CRYSTAL_SHIELD:
4482 case TOOL_TORCH:
4483 case TOOL_LANTERN:
4484 case TOOL_CRYSTALSHARD:
4485 return true;
4486 default:
4487 break;
4488 }
4489 return false;
4490 }
4491
usableWhileShapeshifted(const Stat * const wielder) const4492 bool Item::usableWhileShapeshifted(const Stat* const wielder) const
4493 {
4494 if ( !wielder )
4495 {
4496 return true;
4497 }
4498 switch ( itemCategory(this) )
4499 {
4500 case WEAPON:
4501 case ARMOR:
4502 case GEM:
4503 case THROWN:
4504 case TOOL:
4505 case BOOK:
4506 case SCROLL:
4507 return false;
4508 case MAGICSTAFF:
4509 case SPELLBOOK:
4510 {
4511 if (wielder->type == CREATURE_IMP)
4512 {
4513 return true;
4514 }
4515
4516 return false;
4517 }
4518 case POTION:
4519 {
4520 if (type == POTION_EMPTY)
4521 {
4522 return false;
4523 }
4524
4525 return true;
4526 }
4527 case AMULET:
4528 case RING:
4529 case FOOD:
4530 case SPELL_CAT:
4531 return true;
4532 default:
4533 break;
4534 }
4535 return false;
4536 }
4537
getScrollLabel() const4538 char* Item::getScrollLabel() const
4539 {
4540 if ( enchantedFeatherScrollsShuffled.empty() )
4541 {
4542 strcpy(tempstr, "");
4543 return tempstr;
4544 }
4545 std::vector<int> indices;
4546 for ( int i = 0; i < NUMLABELS && i < enchantedFeatherScrollsShuffled.size(); ++i )
4547 {
4548 if ( enchantedFeatherScrollsShuffled.at(i) == this->type )
4549 {
4550 indices.push_back(i);
4551 }
4552 }
4553
4554 if ( indices.empty() )
4555 {
4556 strcpy(tempstr, "");
4557 return tempstr;
4558 }
4559 int chosenLabel = 0;
4560 if ( this->appearance >= indices.size() )
4561 {
4562 chosenLabel = indices[this->appearance % indices.size()];
4563 }
4564 else
4565 {
4566 chosenLabel = indices[this->appearance];
4567 }
4568 return scroll_label[chosenLabel];
4569 }
4570
itemSpriteIsQuiverThirdPersonModel(const int sprite)4571 bool itemSpriteIsQuiverThirdPersonModel(const int sprite)
4572 {
4573 for ( int i = QUIVER_SILVER; i <= QUIVER_HUNTING; ++i )
4574 {
4575 if ( sprite == items[i].index
4576 || sprite == items[i].index + 1
4577 || sprite == items[i].index + 2
4578 || sprite == items[i].index + 3 )
4579 {
4580 return true;
4581 }
4582 }
4583 return false;
4584 }
4585
itemSpriteIsQuiverBaseThirdPersonModel(const int sprite)4586 bool itemSpriteIsQuiverBaseThirdPersonModel(const int sprite)
4587 {
4588 for ( int i = QUIVER_SILVER; i <= QUIVER_HUNTING; ++i )
4589 {
4590 if ( sprite == items[i].index + 1 )
4591 {
4592 return true;
4593 }
4594 }
4595 return false;
4596 }
4597
itemTypeIsQuiver(const ItemType type)4598 bool itemTypeIsQuiver(const ItemType type)
4599 {
4600 return (type >= QUIVER_SILVER && type <= QUIVER_HUNTING);
4601 }
4602
rangedAttackGetSpeedModifier(const Stat * const myStats)4603 real_t rangedAttackGetSpeedModifier(const Stat* const myStats)
4604 {
4605 if ( !myStats || !myStats->weapon )
4606 {
4607 return 1.0;
4608 }
4609
4610 real_t bowModifier = 1.00;
4611 real_t arrowModifier = 0.0;
4612 if ( myStats->shield )
4613 {
4614 if ( myStats->shield->type == QUIVER_LIGHTWEIGHT )
4615 {
4616 arrowModifier = -.5;
4617 }
4618 }
4619
4620 if ( myStats->weapon->type == LONGBOW )
4621 {
4622 bowModifier = 1.25;
4623 }
4624 else if ( myStats->weapon->type == COMPOUND_BOW )
4625 {
4626 bowModifier = 0.75;
4627 arrowModifier /= 2;
4628 }
4629 else if ( myStats->weapon->type == SLING )
4630 {
4631 bowModifier = 0.75;
4632 arrowModifier = 0.0; // no impact on slings.
4633 }
4634 else if ( myStats->weapon->type == HEAVY_CROSSBOW )
4635 {
4636 bowModifier = 0.4;
4637 return std::max(0.1, bowModifier + arrowModifier);
4638 }
4639 else
4640 {
4641 bowModifier = 1.00;
4642 }
4643
4644 return std::max(0.25, bowModifier + arrowModifier);
4645 }
4646
rangedWeaponUseQuiverOnAttack(const Stat * const myStats)4647 bool rangedWeaponUseQuiverOnAttack(const Stat* const myStats)
4648 {
4649 if ( !myStats || !myStats->weapon || !myStats->shield )
4650 {
4651 return false;
4652 }
4653 if ( !isRangedWeapon(*myStats->weapon) )
4654 {
4655 return false;
4656 }
4657
4658 if ( myStats->shield && itemTypeIsQuiver(myStats->shield->type) && !(myStats->weapon && myStats->weapon->type == SLING) )
4659 {
4660 return true;
4661 }
4662 return false;
4663 }
4664
itemSpriteIsBreastpiece(const int sprite)4665 bool itemSpriteIsBreastpiece(const int sprite)
4666 {
4667 if ( sprite < 0 || sprite > NUMITEMS )
4668 {
4669 return false;
4670 }
4671 if ( sprite == items[LEATHER_BREASTPIECE].index
4672 || sprite == items[IRON_BREASTPIECE].index
4673 || sprite == items[STEEL_BREASTPIECE].index
4674 || sprite == items[CRYSTAL_BREASTPIECE].index
4675 || sprite == items[VAMPIRE_DOUBLET].index
4676 || sprite == items[WIZARD_DOUBLET].index
4677 || sprite == items[HEALER_DOUBLET].index
4678 || sprite == items[SILVER_DOUBLET].index
4679 || sprite == items[ARTIFACT_BREASTPIECE].index
4680 || sprite == items[TUNIC].index
4681 || sprite == items[MACHINIST_APRON].index )
4682 {
4683 return true;
4684 }
4685 return false;
4686 }
4687
getArtifactWeaponEffectChance(const ItemType type,Stat & wielder,real_t * const effectAmount)4688 real_t getArtifactWeaponEffectChance(const ItemType type, Stat& wielder, real_t* const effectAmount)
4689 {
4690 if ( type == ARTIFACT_AXE )
4691 {
4692 const real_t percent = 25 * (wielder.PROFICIENCIES[PRO_AXE]) / 100.f; //0-25%
4693 if ( effectAmount )
4694 {
4695 *effectAmount = 1.5; //1.5x damage.
4696 }
4697
4698 return percent;
4699 }
4700 else if ( type == ARTIFACT_SWORD )
4701 {
4702 const real_t percent = (wielder.PROFICIENCIES[PRO_SWORD]); //0-100%
4703 if ( effectAmount )
4704 {
4705 *effectAmount = (wielder.PROFICIENCIES[PRO_SWORD]) / 200.f + 0.5; //0.5x-1.0x add to weapon multiplier
4706 }
4707
4708 return percent;
4709 }
4710 else if ( type == ARTIFACT_SPEAR )
4711 {
4712 const real_t percent = 25 * (wielder.PROFICIENCIES[PRO_POLEARM]) / 100.f; //0-25%
4713 if ( effectAmount )
4714 {
4715 *effectAmount = .5; // bypasses 50% enemies' armor.
4716 }
4717
4718 return percent;
4719 }
4720 else if ( type == ARTIFACT_MACE )
4721 {
4722 const real_t percent = 1.f; //100%
4723 if ( effectAmount )
4724 {
4725 *effectAmount = wielder.PROFICIENCIES[PRO_MACE]; // 0-2 second bonus mana regen
4726 }
4727
4728 return percent;
4729 }
4730 else if ( type == ARTIFACT_BOW )
4731 {
4732 const real_t percent = wielder.PROFICIENCIES[PRO_RANGED] / 2.f; //0-50%
4733 if ( effectAmount )
4734 {
4735 *effectAmount = 0.f; // no use here.
4736 }
4737
4738 return percent;
4739 }
4740
4741 return 0.0;
4742 }
4743
unableToEquipDueToSwapWeaponTimer() const4744 bool Item::unableToEquipDueToSwapWeaponTimer() const
4745 {
4746 if ( pickaxeGimpTimer > 0 && !intro )
4747 {
4748 return true;
4749 }
4750 if ( swapWeaponGimpTimer > 0 && !intro )
4751 {
4752 return true;
4753 }
4754 return false;
4755
4756 // not needed, block all items?
4757 /*if ( itemCategory(this) == POTION || itemCategory(this) == GEM || itemCategory(this) == THROWN
4758 || itemTypeIsQuiver(this->type) || this->type == FOOD_CREAMPIE )
4759 {
4760 return true;
4761 }
4762 return false;*/
4763 }
4764
tinkeringBotIsMaxHealth() const4765 bool Item::tinkeringBotIsMaxHealth() const
4766 {
4767 if ( type == TOOL_GYROBOT || type == TOOL_DUMMYBOT || type == TOOL_SENTRYBOT || type == TOOL_SPELLBOT )
4768 {
4769 if ( appearance == ITEM_TINKERING_APPEARANCE || (appearance > 0 && appearance % 10 == 0) )
4770 {
4771 return true;
4772 }
4773 }
4774 return false;
4775 }
4776
isTinkeringItemWithThrownLimit() const4777 bool Item::isTinkeringItemWithThrownLimit() const
4778 {
4779 if ( type == TOOL_SENTRYBOT || type == TOOL_SPELLBOT || type == TOOL_DUMMYBOT || type == TOOL_GYROBOT )
4780 {
4781 return true;
4782 }
4783 return false;
4784 }
4785
maximumTinkeringBotsCanBeDeployed(const Stat * const myStats)4786 int maximumTinkeringBotsCanBeDeployed(const Stat* const myStats)
4787 {
4788 if ( !myStats )
4789 {
4790 return 0;
4791 }
4792 int maxFollowers = 2;
4793 const int skillLVL = myStats->PROFICIENCIES[PRO_LOCKPICKING] / 20; // 0-5.
4794 switch ( skillLVL )
4795 {
4796 case 0:
4797 case 1:
4798 maxFollowers = 2;
4799 break;
4800 case 2:
4801 maxFollowers = 4;
4802 break;
4803 case 3:
4804 maxFollowers = 6;
4805 break;
4806 case 4:
4807 maxFollowers = 8;
4808 break;
4809 case 5:
4810 maxFollowers = 10;
4811 break;
4812 default:
4813 break;
4814 }
4815 return maxFollowers;
4816 }
4817
playerCanSpawnMoreTinkeringBots(const Stat * const myStats)4818 bool playerCanSpawnMoreTinkeringBots(const Stat* const myStats)
4819 {
4820 if ( !myStats )
4821 {
4822 return false;
4823 }
4824 if ( overrideTinkeringLimit )
4825 {
4826 return true;
4827 }
4828 int numBots = 0;
4829 for ( node_t* node = myStats->FOLLOWERS.first; node != nullptr; node = node->next )
4830 {
4831 Entity* follower = nullptr;
4832 if ( static_cast<Uint32*>(node->element) )
4833 {
4834 follower = uidToEntity(*static_cast<Uint32*>(node->element));
4835 }
4836 if ( follower )
4837 {
4838 Stat* followerStats = follower->getStats();
4839 if ( followerStats )
4840 {
4841 if ( followerStats->type == SENTRYBOT || followerStats->type == GYROBOT
4842 || followerStats->type == SPELLBOT || followerStats->type == DUMMYBOT )
4843 {
4844 ++numBots;
4845 }
4846 }
4847 }
4848 }
4849 if ( numBots < maximumTinkeringBotsCanBeDeployed(myStats) )
4850 {
4851 return true;
4852 }
4853 return false;
4854 }
4855
playerTryEquipItemAndUpdateServer(Item * const item)4856 void playerTryEquipItemAndUpdateServer(Item* const item)
4857 {
4858 if ( !item )
4859 {
4860 return;
4861 }
4862 if ( multiplayer == CLIENT )
4863 {
4864 // store these to send to server.
4865 const ItemType type = item->type;
4866 const Status status = item->status;
4867 const Sint16 beatitude = item->beatitude;
4868 const int count = item->count;
4869 const Uint32 appearance = item->appearance;
4870 const bool identified = item->identified;
4871
4872 const Category cat = itemCategory(item);
4873
4874 EquipItemResult equipResult = EQUIP_ITEM_FAIL_CANT_UNEQUIP;
4875 if ( cat == SPELLBOOK )
4876 {
4877 if ( !cast_animation.active_spellbook )
4878 {
4879 equipResult = equipItem(item, &stats[clientnum]->shield, clientnum);
4880 }
4881 }
4882 else
4883 {
4884 equipResult = equipItem(item, &stats[clientnum]->weapon, clientnum);
4885 }
4886 if ( equipResult != EQUIP_ITEM_FAIL_CANT_UNEQUIP )
4887 {
4888 if ( cat == SPELLBOOK )
4889 {
4890 if ( !cast_animation.active_spellbook )
4891 {
4892 clientSendEquipUpdateToServer(EQUIP_ITEM_SLOT_SHIELD, equipResult, clientnum,
4893 type, status, beatitude, count, appearance, identified);
4894 }
4895 }
4896 else
4897 {
4898 clientSendEquipUpdateToServer(EQUIP_ITEM_SLOT_WEAPON, equipResult, clientnum,
4899 type, status, beatitude, count, appearance, identified);
4900 }
4901 }
4902 }
4903 else
4904 {
4905 // server/singleplayer
4906 EquipItemResult equipResult = EQUIP_ITEM_FAIL_CANT_UNEQUIP;
4907 if ( itemCategory(item) == SPELLBOOK )
4908 {
4909 if ( !cast_animation.active_spellbook )
4910 {
4911 equipResult = equipItem(item, &stats[clientnum]->shield, clientnum);
4912 }
4913 }
4914 else
4915 {
4916 equipResult = equipItem(item, &stats[clientnum]->weapon, clientnum);
4917 }
4918 }
4919 }
4920
clientSendEquipUpdateToServer(const EquipItemSendToServerSlot slot,const EquipItemResult equipType,const int player,const ItemType type,const Status status,const Sint16 beatitude,const int count,const Uint32 appearance,const bool identified)4921 void clientSendEquipUpdateToServer(const EquipItemSendToServerSlot slot, const EquipItemResult equipType, const int player,
4922 const ItemType type, const Status status, const Sint16 beatitude, const int count, const Uint32 appearance, const bool identified)
4923 {
4924 if ( slot == EQUIP_ITEM_SLOT_SHIELD )
4925 {
4926 strcpy((char*)net_packet->data, "EQUS");
4927 }
4928 else if ( slot == EQUIP_ITEM_SLOT_WEAPON )
4929 {
4930 strcpy((char*)net_packet->data, "EQUI");
4931 }
4932 else
4933 {
4934 strcpy((char*)net_packet->data, "EQUM");
4935 }
4936 SDLNet_Write32(static_cast<Uint32>(type), &net_packet->data[4]);
4937 SDLNet_Write32(static_cast<Uint32>(status), &net_packet->data[8]);
4938 SDLNet_Write32(static_cast<Uint32>(beatitude), &net_packet->data[12]);
4939 SDLNet_Write32(static_cast<Uint32>(count), &net_packet->data[16]);
4940 SDLNet_Write32(static_cast<Uint32>(appearance), &net_packet->data[20]);
4941 net_packet->data[24] = identified;
4942 net_packet->data[25] = player;
4943 net_packet->data[26] = equipType;
4944 net_packet->data[27] = slot;
4945 net_packet->address.host = net_server.host;
4946 net_packet->address.port = net_server.port;
4947 net_packet->len = 28;
4948 sendPacketSafe(net_sock, -1, net_packet, 0);
4949 }
4950
clientUnequipSlotAndUpdateServer(const EquipItemSendToServerSlot slot,Item * const item)4951 void clientUnequipSlotAndUpdateServer(const EquipItemSendToServerSlot slot, Item* const item)
4952 {
4953 if ( !item )
4954 {
4955 return;
4956 }
4957 EquipItemResult equipType = EQUIP_ITEM_FAIL_CANT_UNEQUIP;
4958
4959 if ( slot == EQUIP_ITEM_SLOT_HELM )
4960 {
4961 equipType = equipItem(item, &stats[clientnum]->helmet, clientnum);
4962 }
4963 else if ( slot == EQUIP_ITEM_SLOT_BREASTPLATE )
4964 {
4965 equipType = equipItem(item, &stats[clientnum]->breastplate, clientnum);
4966 }
4967 else if ( slot == EQUIP_ITEM_SLOT_GLOVES )
4968 {
4969 equipType = equipItem(item, &stats[clientnum]->gloves, clientnum);
4970 }
4971 else if ( slot == EQUIP_ITEM_SLOT_BOOTS )
4972 {
4973 equipType = equipItem(item, &stats[clientnum]->shoes, clientnum);
4974 }
4975 else if ( slot == EQUIP_ITEM_SLOT_SHIELD )
4976 {
4977 equipType = equipItem(item, &stats[clientnum]->shield, clientnum);
4978 }
4979 else if ( slot == EQUIP_ITEM_SLOT_CLOAK )
4980 {
4981 equipType = equipItem(item, &stats[clientnum]->cloak, clientnum);
4982 }
4983 else if ( slot == EQUIP_ITEM_SLOT_AMULET )
4984 {
4985 equipType = equipItem(item, &stats[clientnum]->amulet, clientnum);
4986 }
4987 else if ( slot == EQUIP_ITEM_SLOT_RING )
4988 {
4989 equipType = equipItem(item, &stats[clientnum]->ring, clientnum);
4990 }
4991 else if ( slot == EQUIP_ITEM_SLOT_MASK )
4992 {
4993 equipType = equipItem(item, &stats[clientnum]->mask, clientnum);
4994 }
4995
4996 clientSendEquipUpdateToServer(slot, equipType, clientnum,
4997 item->type, item->status, item->beatitude, item->count, item->appearance, item->identified);
4998 }