1 /*-------------------------------------------------------------------------------
2 
3 	BARONY
4 	File: actchest.cpp
5 	Desc: implements all chest related code
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 "entity.hpp"
16 #include "interface/interface.hpp"
17 #include "items.hpp"
18 #include "sound.hpp"
19 #include "net.hpp"
20 #include "player.hpp"
21 
22 /*
23  * Chest theme ideas:
24 "random"
25 "empty / tiny amount of worthless garbage"
26 "food"
27 "treasures, jewelry, gems"
28 "weapons / armor"
29 "tools"
30 "spellbooks / scrolls"
31 "magicstaffs"
32 "potions"
33  */
34 
35 //chest->children->first is the chest's inventory.
36 
actChest(Entity * my)37 void actChest(Entity* my)
38 {
39 	if ( !my )
40 	{
41 		return;
42 	}
43 
44 	my->actChest();
45 }
46 
actChest()47 void Entity::actChest()
48 {
49 	chestAmbience--;
50 	if ( chestAmbience <= 0 )
51 	{
52 		chestAmbience = TICKS_PER_SECOND * 30;
53 		playSoundEntityLocal(this, 149, 32);
54 	}
55 
56 	if ( multiplayer == CLIENT )
57 	{
58 		return;
59 	}
60 
61 	int i;
62 
63 	if (!chestInit)
64 	{
65 		chestInit = 1;
66 		chestHealth = 90 + rand() % 20;
67 		chestMaxHealth = chestHealth;
68 		chestPreventLockpickCapstoneExploit = 1;
69 		chestLockpickHealth = 40;
70 		int roll = 0;
71 
72 		if ( chestLocked == -1 )
73 		{
74 			roll = rand() % 10;
75 			if ( roll == 0 )   // 10% chance //TODO: This should be weighted, depending on chest type.
76 			{
77 				chestLocked = 1;
78 				chestPreventLockpickCapstoneExploit = 0;
79 			}
80 			else
81 			{
82 				chestLocked = 0;
83 			}
84 			//messagePlayer(0, "Chest rolled: %d, locked: %d", roll, chestLocked); //debug print
85 		}
86 		else  if ( chestLocked >= 0 )
87 		{
88 			roll = rand() % 100;
89 			if ( roll < chestLocked )
90 			{
91 				chestLocked = 1;
92 				chestPreventLockpickCapstoneExploit = 0;
93 			}
94 			else
95 			{
96 				chestLocked = 0;
97 			}
98 
99 			//messagePlayer(0, "Chest rolled: %d, locked: %d", roll, chestLocked); //debug print
100 		}
101 
102 		node_t* node = NULL;
103 		node = list_AddNodeFirst(&children);
104 		node->element = malloc(sizeof(list_t)); //Allocate memory for the inventory list.
105 		node->deconstructor = &listDeconstructor;
106 		list_t* inventory = (list_t*) node->element;
107 		inventory->first = NULL;
108 		inventory->last = NULL;
109 
110 		int itemcount = 0;
111 
112 		int chesttype = 0;
113 
114 		if (chestType > 0) //If chest spawned by editor sprite, manually set the chest content category. Otherwise this value should be 0 (random).
115 		{
116 			chesttype = chestType; //Value between 0 and 7.
117 		}
118 		else
119 		{
120 			if (strcmp(map.name, "The Mystic Library"))
121 			{
122 				chesttype = rand() % 8;
123 				if ( chesttype == 1 )
124 				{
125 					if ( currentlevel > 10 )
126 					{
127 						// re-roll the garbage chest.
128 						while ( chesttype == 1 )
129 						{
130 							chesttype = rand() % 8;
131 						}
132 					}
133 					else
134 					{
135 						// re-roll the garbage chest 50% chance
136 						if ( rand() % 2 == 0 )
137 						{
138 							chesttype = rand() % 8;
139 						}
140 					}
141 				}
142 			}
143 			else
144 			{
145 				chesttype = 6; // magic chest
146 			}
147 		}
148 
149 		int minimumQuality = 0;
150 		if ( currentlevel >= 32 )
151 		{
152 			minimumQuality = 10;
153 		}
154 		else if ( currentlevel >= 18 )
155 		{
156 			minimumQuality = 5;
157 		}
158 
159 		if ( chestHasVampireBook )
160 		{
161 			newItem(SPELLBOOK_VAMPIRIC_AURA, EXCELLENT, 0, 1, rand(), true, inventory);
162 		}
163 
164 		switch (chesttype)   //Note that all of this needs to be properly balanced over time.
165 		{
166 			//TODO: Make all applicable item additions work on a category based search?
167 			case 0:
168 				//Completely random.
169 				itemcount = (rand() % 5) + 1;
170 				for (i = 0; i < itemcount; ++i)
171 				{
172 					//And add the current entity to it.
173 					//int itemnum = rand() % NUMITEMS;
174 					//while (itemnum == SPELL_ITEM || (items[itemnum].level == -1) || items[itemnum].level > currentlevel + 5 )
175 					//{
176 					//	//messagePlayer(0, "Skipping item %d, level %d", itemnum, items[itemnum].level);
177 					//	itemnum = rand() % NUMITEMS;    //Keep trying until you don't get a spell or invalid item.
178 					//}
179 					//newItem(static_cast<ItemType>(itemnum), static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), false, inventory);
180 					int cat = rand() % (NUMCATEGORIES - 1); // exclude spell_cat
181 					Item* currentItem = newItem(itemLevelCurve(static_cast<Category>(cat), 0, currentlevel + 5), static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), false, inventory);
182 					if ( currentItem )
183 					{
184 						if ( currentItem->type >= BRONZE_TOMAHAWK && currentItem->type <= CRYSTAL_SHURIKEN )
185 						{
186 							// thrown weapons always fixed status. (tomahawk = decrepit, shuriken = excellent)
187 							currentItem->status = std::min(static_cast<Status>(DECREPIT + (currentItem->type - BRONZE_TOMAHAWK)), EXCELLENT);
188 						}
189 					}
190 				}
191 				break;
192 			case 1:
193 				//Garbage chest
194 				if (rand() % 2)
195 				{
196 					//Empty.
197 				}
198 				else
199 				{
200 					//Some worthless garbage. Like a rock. //TODO: Sometimes spawn item 139, worthless piece of glass. Maybe go a step further and have a random amount of items, say 1 - 5, and they can be either rock or the worthless piece of glass or any other garbage.
201 					itemcount = (rand() % 3) + 1;
202 					int itemStatus = WORN + rand() % 3;
203 					for ( i = 0; i < itemcount; ++i )
204 					{
205 						newItem(GEM_ROCK, static_cast<Status>(itemStatus), 0, 1, rand(), false, inventory);
206 					}
207 				}
208 				break;
209 			case 2:
210 				//Food.
211 				//Items 152 - 158 are all food.
212 				itemcount = (rand() % 5) + 1;
213 				for (i = 0; i < itemcount; ++i)
214 				{
215 					//newItem(static_cast<ItemType>(FOOD_BREAD + (rand() % 7)), static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), false, inventory);
216 					newItem(itemLevelCurve(FOOD, 0, currentlevel + 5), static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), false, inventory);
217 				}
218 				break;
219 			case 3:
220 				//Treasures, jewelry, gems 'n stuff.
221 				itemcount = (rand() % 5) + 1;
222 				for (i = 0; i < itemcount; ++i)
223 				{
224 					if ( rand() % 4 )
225 					{
226 						newItem(static_cast<ItemType>(GEM_GARNET + rand() % 15), static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), false, inventory);
227 					}
228 					else
229 					{
230 						newItem(GEM_GLASS, static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), false, inventory);
231 					}
232 				}
233 				//Random chance to spawn a ring or an amulet or some other jewelry.
234 				if (rand() % 2)
235 				{
236 					if (rand() % 2)
237 					{
238 						//Spawn a ring.
239 						//newItem(static_cast<ItemType>(RING_ADORNMENT + rand() % 12), static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), false, inventory);
240 						newItem(itemLevelCurve(RING, 0, currentlevel + 5), static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), false, inventory);
241 					}
242 					else
243 					{
244 						//Spawn an amulet.
245 						//newItem(static_cast<ItemType>(AMULET_SEXCHANGE + rand() % 6), static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), false, inventory);
246 						newItem(itemLevelCurve(AMULET, 0, currentlevel + 5), static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), false, inventory);
247 					}
248 				}
249 				break;
250 			case 4:
251 				//Weapons, armor, stuff.
252 				//Further break this down into either spawning only weapon(s), only armor(s), or a combo, like a set.
253 
254 				switch (rand() % 3)   //TODO: Note, switch to rand()%4 if/when case 3 is implemented.
255 				{
256 					case 0:
257 						//Only a weapon. Items 0 - 16.
258 					{
259 						//int item = rand() % 18;
260 						////Since the weapons are not a continuous set, check to see if the weapon is part of the continuous set. If it is not, move on to the next block. In this case, there's only one weapon that is not part of the continous set: the crossbow.
261 						//if (item < 16)
262 						//	//Almost every weapon.
263 						//{
264 						//	newItem(static_cast<ItemType>(rand() % 17), static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), false, inventory);
265 						//}
266 						//else
267 						//	//Crossbow.
268 						//{
269 						//	newItem(CROSSBOW, static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), false, inventory);
270 						//}
271 						newItem(itemLevelCurve(WEAPON, minimumQuality, currentlevel + 5), static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), false, inventory);
272 					}
273 					break;
274 					case 1:
275 						//Only a piece of armor.
276 					{
277 						/*
278 						 * 0 - 1 are the steel shields, items 17 and 18.
279 						 * 2 - 5 are the gauntlets, items 20 - 23.
280 						 * 6 - 15 are the boots & shirts (as in, breastplates and all variants), items 28 - 37.
281 						 * 16 - 19 are the hats & helmets, items 40 - 43
282 						 */
283 						//int item = rand() % 15;
284 						//if (item <= 1)
285 						//	//Steel shields. Items 17 & 18.
286 						//{
287 						//	newItem(static_cast<ItemType>(17 + rand() % 2), static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), false, inventory);
288 						//}
289 						//else if (item <= 5)
290 						//	//Gauntlets. Items 20 - 23.
291 						//{
292 						//	if ( rand() % 3 > 0 )
293 						//	{
294 						//		newItem(static_cast<ItemType>(20 + rand() % 4), static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), false, inventory);
295 						//	}
296 						//	else
297 						//	{
298 						//		// new gauntlets
299 						//		newItem(static_cast<ItemType>(BRASS_KNUCKLES + rand() % 3), static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), false, inventory);
300 						//	}
301 						//}
302 						//else if (item <= 10)
303 						//	//Hats & helmets. Items 40 - 43.
304 						//{
305 						//	newItem(static_cast<ItemType>(40 + rand() % 4), static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), false, inventory);
306 						//}
307 						//else if (item <= 15)
308 						//	//Boots & shirts. Items 28 - 37.
309 						//{
310 						//	newItem(static_cast<ItemType>(28 + rand() % 10), static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), false, inventory);
311 						//}
312 						newItem(itemLevelCurve(ARMOR, minimumQuality, currentlevel + 5), static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), false, inventory);
313 					}
314 					break;
315 					case 2:
316 						//A weapon and an armor, chance of thrown.
317 					{
318 						//int item = rand() % 18;
319 						////Since the weapons are not a continuous set, check to see if the weapon is part of the continuous set. If it is not, move on to the next block. In this case, there's only one weapon that is not part of the continous set: the crossbow.
320 						//if (item < 16)
321 						//	//Almost every weapon.
322 						//{
323 						//	newItem(static_cast<ItemType>(rand() % 17), static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), false, inventory);
324 						//}
325 						//else
326 						//	//Crossbow.
327 						//{
328 						//	newItem(static_cast<ItemType>(19), static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), false, inventory);
329 						//}
330 
331 						///*
332 						// * 0 - 1 are the steel shields, items 17 and 18.
333 						// * 2 - 5 are the gauntlets, items 20 - 23.
334 						// * 6 - 15 are the boots & shirts (as in, breastplates and all variants), items 28 - 37.
335 						// * 16 - 19 are the hats & helmets, items 40 - 43
336 						// */
337 						//item = rand() % 20;
338 						//if (item <= 1)
339 						//	//Steel shields. Items 17 & 18.
340 						//{
341 						//	newItem(static_cast<ItemType>(17 + rand() % 2), static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), false, inventory);
342 						//}
343 						//else if (item <= 5)
344 						//	//Gauntlets. Items 20 - 23.
345 						//{
346 						//	if ( rand() % 3 > 0 )
347 						//	{
348 						//		newItem(static_cast<ItemType>(20 + rand() % 4), static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), false, inventory);
349 						//	}
350 						//	else
351 						//	{
352 						//		// new gauntlets
353 						//		newItem(static_cast<ItemType>(BRASS_KNUCKLES + rand() % 3), static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), false, inventory);
354 						//	}
355 						//}
356 						//else if (item <= 10)
357 						//	//Hats & helmets. Items 40 - 43.
358 						//{
359 						//	newItem(static_cast<ItemType>(40 + rand() % 4), static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), false, inventory);
360 						//}
361 						//else if (item <= 15)
362 						//	//Boots & shirts. Items 28 - 37.
363 						//{
364 						//	newItem(static_cast<ItemType>(28 + rand() % 10), static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), false, inventory);
365 						//}
366 
367 						newItem(itemLevelCurve(WEAPON, minimumQuality, currentlevel + 5), static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), false, inventory);
368 						newItem(itemLevelCurve(ARMOR, minimumQuality, currentlevel + 5), static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), false, inventory);
369 
370 						// try for thrown items.
371 						itemcount = 0 + rand() % 2;
372 						for ( i = 0; i < itemcount; ++i )
373 						{
374 							Item* thrown = newItem(itemLevelCurve(THROWN, minimumQuality, currentlevel), WORN, 0, 3 + rand() % 3, rand(), false, inventory);
375 							if ( thrown )
376 							{
377 								if ( thrown->type >= BRONZE_TOMAHAWK && thrown->type <= CRYSTAL_SHURIKEN )
378 								{
379 									// thrown weapons always fixed status. (tomahawk = decrepit, shuriken = excellent)
380 									thrown->status = std::min(static_cast<Status>(DECREPIT + (thrown->type - BRONZE_TOMAHAWK)), EXCELLENT);
381 								}
382 							}
383 						}
384 					}
385 					break;
386 					case 3:
387 						//TODO: Rarer. Getting a full set of armor + a weapon.
388 						break;
389 				}
390 				break;
391 			case 5:
392 			{
393 				//Tools.
394 				Status durability = static_cast<Status>(WORN + rand() % 3);
395 				switch ( rand() % 3 )
396 				{
397 					case 0:
398 						itemcount = rand() % 3;
399 						for ( i = 0; i < itemcount; ++i )
400 						{
401 							newItem(TOOL_BEARTRAP, durability, 0, 1 + rand() % 3, rand(), false, inventory);
402 						}
403 						// fall through
404 					case 1:
405 						itemcount = 1 + rand() % 2;
406 						for (i = 0; i < itemcount; ++i)
407 						{
408 							newItem(static_cast<ItemType>(TOOL_PICKAXE + rand() % 12), static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), false, inventory);
409 						}
410 						if ( rand() % 20 == 0 )
411 						{
412 							newItem(CLOAK_BACKPACK, durability, 0, 1, rand(), false, inventory);
413 						}
414 						if ( rand() % 20 == 0 )
415 						{
416 							newItem(TOOL_TINKERING_KIT, DECREPIT, 0, 1, rand(), false, inventory);
417 							newItem(TOOL_METAL_SCRAP, DECREPIT, 0, 10 + rand() % 11, 0, true, inventory);
418 							newItem(TOOL_MAGIC_SCRAP, DECREPIT, 0, 10 + rand() % 11, 0, true, inventory);
419 						}
420 						break;
421 					case 2:
422 						itemcount = 1 + rand() % 2;
423 						for ( i = 0; i < itemcount; ++i )
424 						{
425 							Item* thrown = newItem(itemLevelCurve(THROWN, minimumQuality, currentlevel), WORN, 0, 3 + rand() % 3, rand(), false, inventory);
426 							if ( thrown )
427 							{
428 								if ( thrown->type >= BRONZE_TOMAHAWK && thrown->type <= CRYSTAL_SHURIKEN )
429 								{
430 									// thrown weapons always fixed status. (tomahawk = decrepit, shuriken = excellent)
431 									thrown->status = std::min(static_cast<Status>(DECREPIT + (thrown->type - BRONZE_TOMAHAWK)), EXCELLENT);
432 								}
433 							}
434 						}
435 						break;
436 					default:
437 						break;
438 				}
439 				break;
440 			}
441 			case 6:
442 				//Magic chest.
443 				//So first choose what kind of magic chest it is.
444 			{
445 				/*
446 				 * Types:
447 				 * * Scroll chest. Has some scrolls in it ( 3 - 5).
448 				 * * Book chest. Basically a small library. 1-3 books.
449 				 * * Staff chest. Staff or 2.
450 				 * * Wizard's chest, which will contain 1-2 scrolls, a magic book, a staff, and either a wizard/magician/whatever implement of some sort or a piece of armor.
451 				 */
452 				int magic_type = rand() % 4;
453 
454 				switch (magic_type)
455 				{
456 					case 0:
457 						//Have 3-5 scrolls.
458 						itemcount = 3 + (rand() % 3);
459 						for (i = 0; i < itemcount; ++i)
460 						{
461 							//newItem(static_cast<ItemType>(SCROLL_IDENTIFY + rand() % 12), static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), false, inventory);
462 							newItem(itemLevelCurve(SCROLL, 0, currentlevel + 5), static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), false, inventory);
463 						}
464 						if ( rand() % 10 == 0 )
465 						{
466 							if ( rand() % 5 == 0 )
467 							{
468 								newItem(ENCHANTED_FEATHER, EXCELLENT, 0, 1, ENCHANTED_FEATHER_MAX_DURABILITY - 1, false, inventory);
469 							}
470 							else
471 							{
472 								newItem(ENCHANTED_FEATHER, SERVICABLE, 0, 1, (3 * (ENCHANTED_FEATHER_MAX_DURABILITY - 1)) / 4, false, inventory);
473 							}
474 							if ( rand() % 2 == 0 )
475 							{
476 								newItem(SCROLL_BLANK, static_cast<Status>(WORN + rand() % 3), 0, 1 + rand() % 3, rand(), false, inventory);
477 							}
478 						}
479 						break;
480 					case 1:
481 						//Have 1-3 books.
482 						itemcount = 1 + (rand() % 3);
483 						for (i = 0; i < itemcount; ++i)
484 						{
485 							//newItem(static_cast<ItemType>(SPELLBOOK_FORCEBOLT + rand() % 22), static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), false, inventory);
486 							newItem(itemLevelCurve(SPELLBOOK, 0, currentlevel + 6), static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), false, inventory);
487 						}
488 						break;
489 					case 2:
490 						//A staff.
491 						//newItem(static_cast<ItemType>(MAGICSTAFF_LIGHT + rand() % 10), static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), false, inventory);
492 						newItem(itemLevelCurve(MAGICSTAFF, 0, currentlevel + 5), static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), false, inventory);
493 						break;
494 					case 3:
495 						//So spawn several items at once. A wizard's chest!
496 
497 						//First the scrolls (1 - 2).
498 						itemcount = 1 + rand() % 2;
499 						for (i = 0; i < itemcount; ++i)
500 						{
501 							//newItem(static_cast<ItemType>(SCROLL_IDENTIFY + rand() % 12), static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), false, inventory);
502 							newItem(itemLevelCurve(SCROLL, 0, currentlevel + 5), static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), false, inventory);
503 						}
504 
505 						//newItem(static_cast<ItemType>(SPELLBOOK_FORCEBOLT + rand() % 22), static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), false, inventory);
506 						newItem(itemLevelCurve(SPELLBOOK, 0, currentlevel + 6), static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), false, inventory);
507 						//newItem(static_cast<ItemType>(MAGICSTAFF_LIGHT + rand() % 10), static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), false, inventory);
508 						newItem(itemLevelCurve(MAGICSTAFF, 0, currentlevel + 5), static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), false, inventory);
509 						switch (rand() % 7)
510 						{
511 							case 0:
512 								//A cloak. Item 24.
513 								newItem(CLOAK, static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), false, inventory);
514 								break;
515 							case 1:
516 								//A cloak of magic resistance. Item 25.
517 								newItem(CLOAK_MAGICREFLECTION, static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), false, inventory);
518 								break;
519 							case 2:
520 								//A cloak of invisibility. Item 26.
521 								newItem(CLOAK_INVISIBILITY, static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), false, inventory);
522 								break;
523 							case 3:
524 								//A cloak of protection. Item 27.
525 								newItem(CLOAK_PROTECTION, static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), false, inventory);
526 								break;
527 							case 4:
528 								//A phyregian's hat/fez hat. Item 38.
529 								if ( rand() % 5 == 0 )
530 								{
531 									newItem(HAT_FEZ, static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), false, inventory);
532 								}
533 								else
534 								{
535 									newItem(HAT_PHRYGIAN, static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), false, inventory);
536 								}
537 								break;
538 							case 5:
539 								//A wizard's hat. Item 39.
540 								newItem(HAT_WIZARD, static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), false, inventory);
541 								break;
542 							case 6:
543 								newItem(ENCHANTED_FEATHER, EXCELLENT, 0, 1, ENCHANTED_FEATHER_MAX_DURABILITY - 1, false, inventory);
544 								if ( rand() % 2 == 0 )
545 								{
546 									newItem(SCROLL_BLANK, static_cast<Status>(WORN + rand() % 3), 0, 1 + rand() % 3, rand(), false, inventory);
547 								}
548 								break;
549 						}
550 						break;
551 				}
552 			}
553 			break;
554 			case 7:
555 				//Potions.
556 				//Items 50 - 64 are potions.
557 				itemcount = (rand() % 3) + 1;
558 				for (i = 0; i < itemcount; ++i)
559 				{
560 					//newItem(static_cast<ItemType>(POTION_WATER + (rand() % 15)), static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), false, inventory);
561 					newItem(itemLevelCurve(POTION, 0, currentlevel + 7), static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), false, inventory);
562 				}
563 				if ( rand() % 2 == 0 )
564 				{
565 					newItem(TOOL_ALEMBIC, static_cast<Status>(WORN + rand() % 3), -1 + rand() % 3, 1, rand(), false, inventory);
566 					newItem(POTION_EMPTY, SERVICABLE, 0, 2 + rand() % 3, 0, true, inventory);
567 				}
568 				if ( rand() % 4 == 0 )
569 				{
570 					newItem(TOOL_ALEMBIC, static_cast<Status>(WORN + rand() % 3), -1 + rand() % 3, 1, rand(), false, inventory);
571 					newItem(POTION_EMPTY, SERVICABLE, 0, 0 + rand() % 3, 0, true, inventory);
572 				}
573 				break;
574 			case 8:
575 				break;
576 			default:
577 				//Default case. Should never be reached.
578 				newItem(static_cast<ItemType>(0), BROKEN, 0, 1, rand(), false, inventory);
579 				printlog("warning: default cause in chest init theme type reached. This should never happen.");
580 				break;
581 		}
582 	}
583 
584 	list_t* inventory = static_cast<list_t* >(children.first->element);
585 	node_t* node = NULL;
586 	Item* item = NULL;
587 
588 	if ( chestHealth <= 0 )
589 	{
590 		// the chest busts open, drops some items randomly, then destroys itself.
591 		node_t* nextnode;
592 		for ( node = inventory->first; node != NULL; node = nextnode )
593 		{
594 			nextnode = node->next;
595 			item = (Item*)node->element;
596 			if ( rand() % 2 == 0 )
597 			{
598 				dropItemMonster(item, this, NULL);
599 			}
600 		}
601 
602 		// wood chunk particles
603 		int c;
604 		for ( c = 0; c < 10; c++ )
605 		{
606 			Entity* entity = spawnGib(this);
607 			entity->flags[INVISIBLE] = false;
608 			entity->sprite = 187; // Splinter.vox
609 			entity->x = floor(x / 16) * 16 + 8;
610 			entity->y = floor(y / 16) * 16 + 8;
611 			entity->z = -7 + rand() % 14;
612 			entity->yaw = (rand() % 360) * PI / 180.0;
613 			entity->pitch = (rand() % 360) * PI / 180.0;
614 			entity->roll = (rand() % 360) * PI / 180.0;
615 			entity->vel_x = cos(entity->yaw) * (0.5 + (rand() % 100) / 100.f);
616 			entity->vel_y = sin(entity->yaw) * (0.5 + (rand() % 100) / 100.f);
617 			entity->vel_z = -.25;
618 			entity->fskill[3] = 0.04;
619 			serverSpawnGibForClient(entity);
620 		}
621 		playSoundEntity(this, 177, 64);
622 
623 		if ( chestStatus == 1 )
624 		{
625 			messagePlayer(chestOpener, language[671]); // "The chest is smashed into pieces!" only notify if chest is currently open.
626 		}
627 
628 		this->closeChest();
629 
630 		// remove chest entities
631 		Entity* parentEntity = uidToEntity(parent);
632 		if ( parentEntity )
633 		{
634 			list_RemoveNode(parentEntity->mynode);    // remove lid
635 		}
636 		list_RemoveNode(mynode); // remove me
637 		return;
638 	}
639 	else
640 	{
641 		if ( multiplayer != CLIENT && chestHasVampireBook )
642 		{
643 			node = inventory->first;
644 			if ( node )
645 			{
646 				item = (Item*)node->element;
647 				if ( item )
648 				{
649 					if ( item->type == SPELLBOOK_VAMPIRIC_AURA )
650 					{
651 						spawnAmbientParticles(40, 600, 20 + rand() % 30, 0.5, true);
652 					}
653 					else
654 					{
655 						chestHasVampireBook = 0;
656 						serverUpdateEntitySkill(this, 11);
657 					}
658 				}
659 			}
660 		}
661 		if ( chestHasVampireBook )
662 		{
663 			spawnAmbientParticles(40, 600, 20 + rand() % 30, 0.5, true);
664 		}
665 	}
666 
667 	if ( chestStatus == 1 )
668 	{
669 		if ( players[chestOpener] && players[chestOpener]->entity )
670 		{
671 			unsigned int distance = sqrt(pow(x - players[chestOpener]->entity->x, 2) + pow(y - players[chestOpener]->entity->y, 2));
672 			if (distance > TOUCHRANGE)
673 			{
674 				closeChest();
675 			}
676 		}
677 		else
678 		{
679 			closeChest();
680 		}
681 	}
682 
683 	//Using the chest (TODO: Monsters using it?).
684 	int chestclicked = -1;
685 	for (i = 0; i < MAXPLAYERS; ++i)
686 	{
687 		if ( (i == 0 && selectedEntity == this) || (client_selected[i] == this) )
688 		{
689 			if (inrange[i])
690 			{
691 				chestclicked = i;
692 			}
693 		}
694 	}
695 	if ( chestLidClicked )
696 	{
697 		chestclicked = chestLidClicked - 1;
698 		chestLidClicked = 0;
699 	}
700 	if ( chestclicked >= 0 )
701 	{
702 		if ( !chestLocked && !openedChest[chestclicked] )
703 		{
704 			if ( !chestStatus )
705 			{
706 				messagePlayer(chestclicked, language[459]);
707 				openedChest[chestclicked] = this;
708 				chestOpener = chestclicked;
709 				if ( chestclicked == clientnum ) // i.e host opened the chest, close GUIs
710 				{
711 					closeAllGUIs(DONT_CHANGE_SHOOTMODE, CLOSEGUI_DONT_CLOSE_CHEST);
712 				}
713 				if (chestclicked != 0 && multiplayer == SERVER)
714 				{
715 					//Send all of the items to the client.
716 					strcpy((char*)net_packet->data, "CHST");  //Chest.
717 					SDLNet_Write32((Uint32)getUID(), &net_packet->data[4]); //Give the client the UID.
718 					net_packet->address.host = net_clients[chestclicked - 1].host;
719 					net_packet->address.port = net_clients[chestclicked - 1].port;
720 					net_packet->len = 8;
721 					sendPacketSafe(net_sock, -1, net_packet, chestclicked - 1);
722 					for (node = inventory->first; node != NULL; node = node->next)
723 					{
724 						item = (Item*) node->element;
725 						strcpy((char*)net_packet->data, "CITM");  //Chest item.
726 						SDLNet_Write32((Uint32)item->type, &net_packet->data[4]);
727 						SDLNet_Write32((Uint32)item->status, &net_packet->data[8]);
728 						SDLNet_Write32((Uint32)item->beatitude, &net_packet->data[12]);
729 						SDLNet_Write32((Uint32)item->count, &net_packet->data[16]);
730 						SDLNet_Write32((Uint32)item->appearance, &net_packet->data[20]);
731 						net_packet->data[24] = item->identified;
732 						net_packet->address.host = net_clients[chestclicked - 1].host;
733 						net_packet->address.port = net_clients[chestclicked - 1].port;
734 						net_packet->len = 25;
735 						sendPacketSafe(net_sock, -1, net_packet, chestclicked - 1);
736 					}
737 				}
738 				else
739 				{
740 					openStatusScreen(GUI_MODE_INVENTORY, INVENTORY_MODE_ITEM); // Reset the GUI to the inventory.
741 					if ( numItemsInChest() > 0 )   //Warp mouse to first item in chest only if there are any items!
742 					{
743 						selectedChestSlot = 0;
744 						warpMouseToSelectedChestSlot();
745 					}
746 					else
747 					{
748 						selectedChestSlot = -1;
749 						warpMouseToSelectedInventorySlot(); //Because setting shootmode to false tends to start the mouse in the middle of the screen. Which is not nice.
750 					}
751 				}
752 				chestStatus = 1; //Toggle chest open/closed.
753 			}
754 			else
755 			{
756 				messagePlayer(chestclicked, language[460]);
757 				if (chestOpener != 0)
758 				{
759 					strcpy((char*)net_packet->data, "CCLS");  //Chest close.
760 					net_packet->address.host = net_clients[chestOpener - 1].host;
761 					net_packet->address.port = net_clients[chestOpener - 1].port;
762 					net_packet->len = 4;
763 					sendPacketSafe(net_sock, -1, net_packet, chestOpener - 1);
764 				}
765 				else
766 				{
767 					chestitemscroll = 0;
768 				}
769 				if (chestOpener != chestclicked)
770 				{
771 					messagePlayer(chestOpener, language[461]);
772 				}
773 				closeChestServer();
774 			}
775 		}
776 		else if ( chestLocked )
777 		{
778 			messagePlayer(chestclicked, language[462]);
779 			playSoundEntity(this, 152, 64);
780 		}
781 	}
782 }
783 
actChestLid(Entity * my)784 void actChestLid(Entity* my)
785 {
786 	int i;
787 
788 	Entity* parent = uidToEntity(my->parent);
789 	if ( !parent )
790 	{
791 		list_RemoveNode(my->mynode);
792 		return;
793 	}
794 
795 	if ( multiplayer != CLIENT )
796 	{
797 		my->skill[1] = parent->skill[1];
798 		if ( multiplayer == SERVER )
799 		{
800 			if ( my->skill[3] != my->skill[1] )
801 			{
802 				my->skill[3] = my->skill[1];
803 				serverUpdateEntitySkill(my, 1);
804 			}
805 		}
806 
807 		for (i = 0; i < MAXPLAYERS; ++i)
808 		{
809 			if ( (i == 0 && selectedEntity == my) || (client_selected[i] == my) )
810 			{
811 				if (inrange[i])
812 				{
813 					parent->skill[6] = i + 1;
814 				}
815 			}
816 		}
817 	}
818 
819 	if ( my->skill[1] )
820 	{
821 		// chest is open
822 		if ( !my->skill[0] )
823 		{
824 			my->skill[0] = 1;
825 			if ( multiplayer != CLIENT )
826 			{
827 				playSoundEntity(my, 21, 64);
828 			}
829 			my->fskill[0] = 0.25;
830 		}
831 		if ( my->pitch > -PI / 2 )
832 		{
833 			my->pitch -= my->fskill[0];
834 			my->fskill[0] -= 0.02;
835 			if ( my->pitch <= -PI / 2 )
836 			{
837 				my->pitch = -PI / 2;
838 				my->fskill[0] = 0;
839 			}
840 		}
841 	}
842 	else
843 	{
844 		// chest is closed
845 		if ( my->skill[0] )
846 		{
847 			my->skill[0] = 0;
848 			if ( multiplayer != CLIENT )
849 			{
850 				playSoundEntity(my, 22, 64);
851 			}
852 			my->fskill[0] = 0.025;
853 		}
854 		if ( my->pitch < 0 )
855 		{
856 			my->pitch += my->fskill[0];
857 			my->fskill[0] += 0.025;
858 			if ( my->pitch >= 0 )
859 			{
860 				my->pitch = 0;
861 				my->fskill[0] = 0;
862 			}
863 		}
864 	}
865 }
866 
closeChest()867 void Entity::closeChest()
868 {
869 	if (clientnum != 0 && multiplayer == CLIENT)
870 	{
871 		//If client, tell server the chest got closed.
872 		if (openedChest[clientnum] != NULL)
873 		{
874 			//Message server.
875 			if ( chestHealth > 0 )
876 			{
877 				messagePlayer(clientnum, language[460]);
878 			}
879 
880 			strcpy( (char*)net_packet->data, "CCLS" );
881 			net_packet->data[4] = clientnum;
882 			net_packet->address.host = net_server.host;
883 			net_packet->address.port = net_server.port;
884 			net_packet->len = 5;
885 			sendPacketSafe(net_sock, -1, net_packet, 0);
886 
887 			closeChestClientside();
888 			return;
889 		}
890 	}
891 
892 	if (chestStatus)
893 	{
894 		chestStatus = 0;
895 
896 		if ( chestHealth > 0 )
897 		{
898 			messagePlayer(clientnum, language[460]);
899 		}
900 
901 		openedChest[chestOpener] = nullptr;
902 		if (chestOpener != 0 && multiplayer == SERVER)
903 		{
904 			//Tell the client that the chest got closed.
905 			strcpy((char*)net_packet->data, "CCLS");  //Chest close.
906 			net_packet->address.host = net_clients[chestOpener - 1].host;
907 			net_packet->address.port = net_clients[chestOpener - 1].port;
908 			net_packet->len = 4;
909 			sendPacketSafe(net_sock, -1, net_packet, chestOpener - 1);
910 		}
911 		else
912 		{
913 			if ( chestOpener == clientnum )
914 			{
915 				for ( int c = 0; c < kNumChestItemsToDisplay; ++c )
916 				{
917 					invitemschest[c] = nullptr;
918 				}
919 			}
920 			chestitemscroll = 0;
921 			//Reset chest-gamepad related stuff here.
922 			selectedChestSlot = -1;
923 		}
924 	}
925 }
926 
closeChestServer()927 void Entity::closeChestServer()
928 {
929 	if (chestStatus)
930 	{
931 		chestStatus = 0;
932 		openedChest[chestOpener] = NULL;
933 		if ( chestOpener == clientnum )
934 		{
935 			for ( int c = 0; c < kNumChestItemsToDisplay; ++c )
936 			{
937 				invitemschest[c] = nullptr;
938 			}
939 		}
940 	}
941 }
942 
addItemToChest(Item * item)943 void Entity::addItemToChest(Item* item)
944 {
945 	if (!item)
946 	{
947 		return;
948 	}
949 
950 	if (clientnum != 0 && multiplayer == CLIENT)
951 	{
952 		//Tell the server.
953 		strcpy( (char*)net_packet->data, "CITM" );
954 		net_packet->data[4] = clientnum;
955 		net_packet->address.host = net_server.host;
956 		net_packet->address.port = net_server.port;
957 		SDLNet_Write32((Uint32)item->type, &net_packet->data[5]);
958 		SDLNet_Write32((Uint32)item->status, &net_packet->data[9]);
959 		SDLNet_Write32((Uint32)item->beatitude, &net_packet->data[13]);
960 		SDLNet_Write32((Uint32)item->count, &net_packet->data[17]);
961 		SDLNet_Write32((Uint32)item->appearance, &net_packet->data[21]);
962 		net_packet->data[25] = item->identified;
963 		net_packet->len = 26;
964 		sendPacketSafe(net_sock, -1, net_packet, 0);
965 
966 		addItemToChestClientside(item);
967 		return;
968 	}
969 
970 	Item* item2 = NULL;
971 
972 	//Add the item to the chest's inventory.
973 	list_t* inventory = static_cast<list_t* >(children.first->element);
974 
975 	node_t* t_node = NULL;
976 	//If item's already in the chest, add it to a pre-existing stack.
977 	for (t_node = inventory->first; t_node != NULL; t_node = t_node->next)
978 	{
979 		item2 = (Item*) t_node->element;
980 		if (!itemCompare(item, item2, false))
981 		{
982 			item2->count += item->count;
983 			return;
984 		}
985 	}
986 
987 	item->node = list_AddNodeFirst(inventory);
988 	item->node->element = item;
989 	item->node->deconstructor = &defaultDeconstructor;
990 
991 	if (chestOpener != 0 && multiplayer == SERVER)
992 	{
993 		strcpy((char*)net_packet->data, "CITM");
994 		SDLNet_Write32((Uint32)item->type, &net_packet->data[4]);
995 		SDLNet_Write32((Uint32)item->status, &net_packet->data[8]);
996 		SDLNet_Write32((Uint32)item->beatitude, &net_packet->data[12]);
997 		SDLNet_Write32((Uint32)item->count, &net_packet->data[16]);
998 		SDLNet_Write32((Uint32)item->appearance, &net_packet->data[20]);
999 		net_packet->data[24] = item->identified;
1000 		net_packet->address.host = net_clients[chestOpener - 1].host;
1001 		net_packet->address.port = net_clients[chestOpener - 1].port;
1002 		net_packet->len = 25;
1003 		sendPacketSafe(net_sock, -1, net_packet, chestOpener - 1);
1004 	}
1005 }
1006 
addItemToChestFromInventory(int player,Item * item,bool all)1007 void Entity::addItemToChestFromInventory(int player, Item* item, bool all)
1008 {
1009 	if (!item || !players[player] || !players[player]->entity)
1010 	{
1011 		return;
1012 	}
1013 
1014 	if (itemCategory(item) == SPELL_CAT)
1015 	{
1016 		return;
1017 	}
1018 
1019 	bool isEquipped = itemIsEquipped(item, player);
1020 
1021 	if ( isEquipped )
1022 	{
1023 		if ( !item->canUnequip(stats[player]) )
1024 		{
1025 			if ( shouldInvertEquipmentBeatitude(stats[player]) && item->beatitude > 0 )
1026 			{
1027 				messagePlayer(player, language[3218]);
1028 			}
1029 			else
1030 			{
1031 				messagePlayer(player, language[1087]);
1032 			}
1033 			item->identified = true;
1034 			return;
1035 		}
1036 	}
1037 	playSoundPlayer(player, 47 + rand() % 3, 64);
1038 
1039 	Item* newitem = NULL;
1040 	if ( (newitem = (Item*) malloc(sizeof(Item))) == NULL)
1041 	{
1042 		printlog( "failed to allocate memory for new item!\n" );
1043 		return; //Error or something.
1044 	}
1045 	newitem->node = NULL;
1046 	newitem->count = 1;
1047 	newitem->type = item->type;
1048 	newitem->status = item->status;
1049 	newitem->beatitude = item->beatitude;
1050 	newitem->appearance = item->appearance;
1051 	newitem->identified = item->identified;
1052 
1053 	Item** slot = itemSlot(stats[player], item);
1054 	if ( multiplayer == CLIENT )
1055 	{
1056 		// tell the server to unequip.
1057 		if ( slot != nullptr )
1058 		{
1059 			if ( slot == &stats[clientnum]->weapon )
1060 			{
1061 				playerTryEquipItemAndUpdateServer(item);
1062 			}
1063 			else if ( slot == &stats[clientnum]->shield && itemCategory(newitem) == SPELLBOOK )
1064 			{
1065 				playerTryEquipItemAndUpdateServer(item);
1066 			}
1067 			else
1068 			{
1069 				if ( slot == &stats[clientnum]->helmet )
1070 				{
1071 					clientUnequipSlotAndUpdateServer(EQUIP_ITEM_SLOT_HELM, item);
1072 				}
1073 				else if ( slot == &stats[clientnum]->breastplate )
1074 				{
1075 					clientUnequipSlotAndUpdateServer(EQUIP_ITEM_SLOT_BREASTPLATE, item);
1076 				}
1077 				else if ( slot == &stats[clientnum]->gloves )
1078 				{
1079 					clientUnequipSlotAndUpdateServer(EQUIP_ITEM_SLOT_GLOVES, item);
1080 				}
1081 				else if ( slot == &stats[clientnum]->shoes )
1082 				{
1083 					clientUnequipSlotAndUpdateServer(EQUIP_ITEM_SLOT_BOOTS, item);
1084 				}
1085 				else if ( slot == &stats[clientnum]->shield )
1086 				{
1087 					clientUnequipSlotAndUpdateServer(EQUIP_ITEM_SLOT_SHIELD, item);
1088 				}
1089 				else if ( slot == &stats[clientnum]->cloak )
1090 				{
1091 					clientUnequipSlotAndUpdateServer(EQUIP_ITEM_SLOT_CLOAK, item);
1092 				}
1093 				else if ( slot == &stats[clientnum]->amulet )
1094 				{
1095 					clientUnequipSlotAndUpdateServer(EQUIP_ITEM_SLOT_AMULET, item);
1096 				}
1097 				else if ( slot == &stats[clientnum]->ring )
1098 				{
1099 					clientUnequipSlotAndUpdateServer(EQUIP_ITEM_SLOT_RING, item);
1100 				}
1101 				else if ( slot == &stats[clientnum]->mask )
1102 				{
1103 					clientUnequipSlotAndUpdateServer(EQUIP_ITEM_SLOT_MASK, item);
1104 				}
1105 			}
1106 		}
1107 	}
1108 
1109 	// unequip the item
1110 	if ( item->count <= 1 || all)
1111 	{
1112 		if ( slot != NULL )
1113 		{
1114 			*slot = NULL;
1115 		}
1116 	}
1117 	if ( item->node != NULL )
1118 	{
1119 		if ( item->node->list == &stats[player]->inventory )
1120 		{
1121 			if (!all)
1122 			{
1123 				item->count--;
1124 				if ( item->count <= 0 )
1125 				{
1126 					list_RemoveNode(item->node);
1127 				}
1128 			}
1129 			else
1130 			{
1131 				newitem->count = item->count;
1132 				list_RemoveNode(item->node);
1133 			}
1134 		}
1135 	}
1136 	else
1137 	{
1138 		item->count--;
1139 		if ( item->count <= 0 )
1140 		{
1141 			free(item);
1142 		}
1143 	}
1144 
1145 	messagePlayer(player, language[463], newitem->getName());
1146 	addItemToChest(newitem);
1147 
1148 	return; //Do not execute the rest of this function.
1149 }
1150 
getItemFromChest(Item * item,bool all,bool getInfoOnly)1151 Item* Entity::getItemFromChest(Item* item, bool all, bool getInfoOnly)
1152 {
1153 	/*
1154 	 * getInfoOnly just returns a copy of the item at the slot, it does not actually grab the item.
1155 	 * Note that the returned memory will need to be freed.
1156 	 */
1157 	Item* newitem = NULL;
1158 
1159 	if ( item == NULL )
1160 	{
1161 		return NULL;
1162 	}
1163 
1164 	if ( clientnum != 0 && multiplayer == CLIENT)
1165 	{
1166 		if (!item || !item->node)
1167 		{
1168 			return NULL;
1169 		}
1170 
1171 		if ( (newitem = (Item*) malloc(sizeof(Item))) == NULL)
1172 		{
1173 			printlog( "failed to allocate memory for new item!\n" );
1174 			return NULL; //Error or something.
1175 		}
1176 		newitem->node = NULL;
1177 		newitem->count = 1;
1178 		newitem->type = item->type;
1179 		newitem->status = item->status;
1180 		newitem->beatitude = item->beatitude;
1181 		newitem->appearance = item->appearance;
1182 		newitem->identified = item->identified;
1183 
1184 		//Tell the server.
1185 		if ( !getInfoOnly )
1186 		{
1187 			strcpy( (char*)net_packet->data, "RCIT" );  //Have the server remove the item from the chest).
1188 			net_packet->data[4] = clientnum;
1189 			net_packet->address.host = net_server.host;
1190 			net_packet->address.port = net_server.port;
1191 			SDLNet_Write32((Uint32)item->type, &net_packet->data[5]);
1192 			SDLNet_Write32((Uint32)item->status, &net_packet->data[9]);
1193 			SDLNet_Write32((Uint32)item->beatitude, &net_packet->data[13]);
1194 			int count = 1;
1195 			if (all)
1196 			{
1197 				count = item->count;
1198 			}
1199 			SDLNet_Write32((Uint32)count, &net_packet->data[17]);
1200 			SDLNet_Write32((Uint32)item->appearance, &net_packet->data[21]);
1201 			net_packet->data[25] = item->identified;
1202 			net_packet->len = 26;
1203 			sendPacketSafe(net_sock, -1, net_packet, 0);
1204 		}
1205 	}
1206 	else
1207 	{
1208 		if ( !item )
1209 		{
1210 			return NULL;
1211 		}
1212 		if ( !item->node )
1213 		{
1214 			return NULL;
1215 		}
1216 		if ( item->node->list != children.first->element )
1217 		{
1218 			return NULL;
1219 		}
1220 
1221 		if ( (newitem = (Item*) malloc(sizeof(Item))) == NULL)
1222 		{
1223 			printlog( "failed to allocate memory for new item!\n" );
1224 			return NULL; //Error or something.
1225 		}
1226 		newitem->node = NULL;
1227 		newitem->count = 1;
1228 		newitem->type = item->type;
1229 		newitem->status = item->status;
1230 		newitem->beatitude = item->beatitude;
1231 		newitem->appearance = item->appearance;
1232 		newitem->identified = item->identified;
1233 	}
1234 
1235 	if (!all)
1236 	{
1237 		//Grab only one item from the chest.
1238 		newitem->count = 1;
1239 		if (!getInfoOnly )
1240 		{
1241 			item->count -= 1;
1242 			if ( item->count <= 0 )
1243 			{
1244 				list_RemoveNode(item->node);
1245 			}
1246 		}
1247 	}
1248 	else
1249 	{
1250 		//Grab all items from the chest.
1251 		newitem->count = item->count;
1252 		if ( !getInfoOnly )
1253 		{
1254 			list_RemoveNode(item->node);
1255 		}
1256 	}
1257 
1258 	return newitem;
1259 }
1260 
closeChestClientside()1261 void closeChestClientside()
1262 {
1263 	if (!openedChest[clientnum])
1264 	{
1265 		return;
1266 	}
1267 
1268 	if (multiplayer != CLIENT || clientnum == 0)
1269 	{
1270 		return;    //Only called for the client.
1271 	}
1272 
1273 	list_FreeAll(&chestInv);
1274 
1275 	openedChest[clientnum] = NULL;
1276 
1277 	chestitemscroll = 0;
1278 
1279 	for ( int c = 0; c < kNumChestItemsToDisplay; ++c )
1280 	{
1281 		invitemschest[c] = nullptr;
1282 	}
1283 
1284 	//Reset chest-gamepad related stuff here.
1285 	selectedChestSlot = -1;
1286 }
1287 
addItemToChestClientside(Item * item)1288 void addItemToChestClientside(Item* item)
1289 {
1290 	if (openedChest[clientnum])
1291 	{
1292 		//messagePlayer(clientnum, "Recieved item.");
1293 
1294 		//If there's an open chests, add an item to it.
1295 		//TODO: Add item to the chest.
1296 
1297 		Item* item2 = NULL;
1298 		node_t* node = NULL;
1299 
1300 		for (node = chestInv.first; node != NULL; node = node->next)
1301 		{
1302 			item2 = (Item*) node->element;
1303 			if (!itemCompare(item, item2, false))
1304 			{
1305 				item2->count += item->count;
1306 				return;
1307 			}
1308 		}
1309 
1310 		item->node = list_AddNodeFirst(&chestInv);
1311 		item->node->element = item;
1312 		item->node->deconstructor = &defaultDeconstructor;
1313 	}
1314 	//TODO: Else: Ruh-roh, error!
1315 }
1316 
1317 
1318 
addItemToChestServer(Item * item)1319 void Entity::addItemToChestServer(Item* item)
1320 {
1321 	if (!item)
1322 	{
1323 		return;
1324 	}
1325 
1326 	Item* item2 = NULL;
1327 	node_t* t_node = NULL;
1328 
1329 	//Add the item to the chest's inventory.
1330 	list_t* inventory = static_cast<list_t* >(children.first->element);
1331 
1332 	if (!inventory)
1333 	{
1334 		return;
1335 	}
1336 
1337 	//If item's already in the chest, add it to a pre-existing stack.
1338 	for (t_node = inventory->first; t_node != NULL; t_node = t_node->next)
1339 	{
1340 		item2 = (Item*) t_node->element;
1341 		if (!itemCompare(item, item2, false))
1342 		{
1343 			item2->count += item->count;
1344 			return;
1345 		}
1346 	}
1347 
1348 	item->node = list_AddNodeFirst(inventory);
1349 	item->node->element = item;
1350 	item->node->deconstructor = &defaultDeconstructor;
1351 }
1352 
removeItemFromChestServer(Item * item,int count)1353 void Entity::removeItemFromChestServer(Item* item, int count)
1354 {
1355 	if (!item)
1356 	{
1357 		return;
1358 	}
1359 
1360 	Item* item2 = NULL;
1361 	node_t* t_node = NULL;
1362 
1363 	list_t* inventory = static_cast<list_t* >(children.first->element);
1364 	if (!inventory)
1365 	{
1366 		return;
1367 	}
1368 
1369 	for (t_node = inventory->first; t_node != NULL; t_node = t_node->next)
1370 	{
1371 		item2 = (Item*) t_node->element;
1372 		if (!item2  || !item2->node || item2->node->list != children.first->element)
1373 		{
1374 			return;
1375 		}
1376 		if (!itemCompare(item, item2, false))
1377 		{
1378 			if (count < item2->count)
1379 			{
1380 				//Grab only one item from the chest.
1381 				int oldcount = item2->count;
1382 				item2->count = oldcount - count;
1383 				if ( item2->count <= 0 )
1384 				{
1385 					list_RemoveNode(item2->node);
1386 				}
1387 			}
1388 			else
1389 			{
1390 				//Grab all items from the chest.
1391 				list_RemoveNode(item2->node);
1392 			}
1393 			return;
1394 		}
1395 	}
1396 }
1397 
unlockChest()1398 void Entity::unlockChest()
1399 {
1400 	chestLocked = 0;
1401 	chestPreventLockpickCapstoneExploit = 1;
1402 }
1403 
lockChest()1404 void Entity::lockChest()
1405 {
1406 	chestLocked = 1;
1407 }
1408 
chestHandleDamageMagic(int damage,Entity & magicProjectile,Entity * caster)1409 void Entity::chestHandleDamageMagic(int damage, Entity &magicProjectile, Entity *caster)
1410 {
1411 	chestHealth -= damage; //Decrease chest health.
1412 	if ( caster )
1413 	{
1414 		if ( caster->behavior == &actPlayer )
1415 		{
1416 			if ( chestHealth <= 0 )
1417 			{
1418 				if ( magicProjectile.behavior == &actBomb )
1419 				{
1420 					messagePlayer(caster->skill[2], language[3617], items[magicProjectile.skill[21]].name_identified, language[675]);
1421 				}
1422 				else
1423 				{
1424 					messagePlayer(caster->skill[2], language[2520]);
1425 				}
1426 			}
1427 			else
1428 			{
1429 				if ( magicProjectile.behavior == &actBomb )
1430 				{
1431 					messagePlayer(caster->skill[2], language[3618], items[magicProjectile.skill[21]].name_identified, language[675]);
1432 				}
1433 				else
1434 				{
1435 					messagePlayer(caster->skill[2], language[378], language[675]);
1436 				}
1437 			}
1438 		}
1439 		updateEnemyBar(caster, this, language[675], chestHealth, chestMaxHealth);
1440 	}
1441 	playSoundEntity(this, 28, 128);
1442 }
1443