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 }