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