1 /*-------------------------------------------------------------------------------
2 
3 	BARONY
4 	File: monster_shopkeeper.cpp
5 	Desc: implements all of the shopkeeper's 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 "items.hpp"
17 #include "monster.hpp"
18 #include "sound.hpp"
19 #include "net.hpp"
20 #include "collision.hpp"
21 #include "player.hpp"
22 #include "magic/magic.hpp"
23 #include "shops.hpp"
24 #include "mod_tools.hpp"
25 
initShopkeeper(Entity * my,Stat * myStats)26 void initShopkeeper(Entity* my, Stat* myStats)
27 {
28 	int c;
29 	node_t* node;
30 
31 	my->initMonster(217);
32 
33 	if ( multiplayer != CLIENT )
34 	{
35 		MONSTER_SPOTSND = -1;
36 		MONSTER_SPOTVAR = 1;
37 		MONSTER_IDLESND = -1;
38 		MONSTER_IDLEVAR = 1;
39 	}
40 	if ( multiplayer != CLIENT && !MONSTER_INIT )
41 	{
42 		my->createPathBoundariesNPC();
43 
44 		for ( int x = my->monsterPathBoundaryXStart - 16; x <= my->monsterPathBoundaryXEnd + 16; x += 16 )
45 		{
46 			for ( int y = my->monsterPathBoundaryYStart - 16; y <= my->monsterPathBoundaryYEnd + 16; y += 16 )
47 			{
48 				if ( x / 16 >= 0 && x / 16 < map.width && y / 16 >= 0 && y / 16 < map.height )
49 				{
50 					shoparea[y / 16 + (x / 16)*map.height] = true;
51 				}
52 			}
53 		}
54 
55 		if ( myStats != NULL )
56 		{
57 			if ( !myStats->leader_uid )
58 			{
59 				myStats->leader_uid = 0;
60 			}
61 
62 			if ( !strcmp(myStats->name, "") )
63 			{
64 				strcpy(myStats->name, language[158 + rand() % 26]);
65 			}
66 
67 			// apply random stat increases if set in stat_shared.cpp or editor
68 			setRandomMonsterStats(myStats);
69 
70 			if ( currentlevel >= 25 && myStats->HP == 300 && myStats->MAXHP == 300 )
71 			{
72 				myStats->HP *= 2;
73 				myStats->MAXHP *= 2;
74 				myStats->OLDHP = myStats->HP;
75 			}
76 
77 			// generate 6 items max, less if there are any forced items from boss variants
78 			int customItemsToGenerate = ITEM_CUSTOM_SLOT_LIMIT;
79 
80 			// boss variants
81 
82 			// random effects
83 			if ( rand() % 20 == 0 )
84 			{
85 				myStats->EFFECTS[EFF_ASLEEP] = true;
86 				myStats->EFFECTS_TIMERS[EFF_ASLEEP] = 1800 + rand() % 3600;
87 			}
88 
89 			// generates equipment and weapons if available from editor
90 			createMonsterEquipment(myStats);
91 
92 			// create any custom inventory items from editor if available
93 			createCustomInventory(myStats, customItemsToGenerate);
94 
95 			// count if any custom inventory items from editor
96 			int customItems = countCustomItems(myStats); //max limit of 6 custom items per entity.
97 
98 														 // count any inventory items set to default in edtior
99 			int defaultItems = countDefaultItems(myStats);
100 
101 			my->setHardcoreStats(*myStats);
102 
103 			// generate the default inventory items for the monster, provided the editor sprite allowed enough default slots
104 			switch ( defaultItems )
105 			{
106 				case 6:
107 				case 5:
108 				case 4:
109 				case 3:
110 				case 2:
111 				case 1:
112 					break;
113 				default:
114 					break;
115 			}
116 
117 			//give weapon
118 			if ( myStats->weapon == NULL && myStats->EDITOR_ITEMS[ITEM_SLOT_WEAPON] == 1 )
119 			{
120 				if ( currentlevel < 25 )
121 				{
122 					myStats->weapon = newItem(SPELLBOOK_MAGICMISSILE, EXCELLENT, 0, 1, 0, false, NULL);
123 				}
124 				else
125 				{
126 					if ( rand() % 2 == 0 )
127 					{
128 						myStats->weapon = newItem(SPELLBOOK_DRAIN_SOUL, EXCELLENT, 0, 1, 0, false, NULL);
129 					}
130 					else
131 					{
132 						myStats->weapon = newItem(SPELLBOOK_BLEED, EXCELLENT, 0, 1, 0, false, NULL);
133 					}
134 				}
135 			}
136 
137 			// give shopkeeper items
138 			if ( myStats->MISC_FLAGS[STAT_FLAG_NPC] == 14 )
139 			{
140 				myStats->MISC_FLAGS[STAT_FLAG_MYSTERIOUS_SHOPKEEP] = 1;
141 				my->monsterStoreType = 10;
142 			}
143 			else if ( myStats->MISC_FLAGS[STAT_FLAG_NPC] > 0 )
144 			{
145 				my->monsterStoreType = myStats->MISC_FLAGS[STAT_FLAG_NPC] - 1;
146 				if ( my->monsterStoreType > 9 )
147 				{
148 					my->monsterStoreType = rand() % 9;
149 					if ( my->monsterStoreType == 8 )
150 					{
151 						my->monsterStoreType++;
152 					}
153 				}
154 			}
155 			else
156 			{
157 				my->monsterStoreType = rand() % 10;
158 			}
159 			int numitems = 10 + rand() % 5;
160 			int blessedShopkeeper = 1; // bless important pieces of gear like armor, jewelry, weapons..
161 			if ( currentlevel >= 30 )
162 			{
163 				if ( rand() % 3 == 0 )
164 				{
165 					blessedShopkeeper = 3;
166 				}
167 				else
168 				{
169 					blessedShopkeeper = 2;
170 				}
171 			}
172 			else if ( currentlevel >= 25 )
173 			{
174 				if ( rand() % 4 == 0 )
175 				{
176 					blessedShopkeeper = 3;
177 				}
178 				else
179 				{
180 					blessedShopkeeper = 2;
181 				}
182 			}
183 			else if ( currentlevel >= 18 )
184 			{
185 				if ( rand() % 3 == 0 )
186 				{
187 					blessedShopkeeper = 2;
188 				}
189 			}
190 
191 			int customShopkeeperInUse = ((myStats->MISC_FLAGS[STAT_FLAG_SHOPKEEPER_CUSTOM_PROPERTIES] >> 12) & 0xF);
192 			int oldMonsterStoreType = my->monsterStoreType;
193 			if ( customShopkeeperInUse == MonsterStatCustomManager::StatEntry::ShopkeeperCustomFlags::ENABLE_GEN_ITEMS )
194 			{
195 				if ( (myStats->MISC_FLAGS[STAT_FLAG_SHOPKEEPER_CUSTOM_PROPERTIES] & 0xFF) > 1 )
196 				{
197 					numitems = (myStats->MISC_FLAGS[STAT_FLAG_SHOPKEEPER_CUSTOM_PROPERTIES] & 0xFF) - 1;
198 				}
199 				if ( ((myStats->MISC_FLAGS[STAT_FLAG_SHOPKEEPER_CUSTOM_PROPERTIES] >> 8) & 0xF) > 0 )
200 				{
201 					blessedShopkeeper = std::max(1, (myStats->MISC_FLAGS[STAT_FLAG_SHOPKEEPER_CUSTOM_PROPERTIES] >> 8) & 0xF);
202 				}
203 			}
204 			else if ( customShopkeeperInUse == MonsterStatCustomManager::StatEntry::ShopkeeperCustomFlags::DISABLE_GEN_ITEMS )
205 			{
206 				my->monsterStoreType = -1;
207 			}
208 
209 			bool sellVampireBlood = false;
210 			for ( c = 0; c < MAXPLAYERS; ++c )
211 			{
212 				if ( players[c] && players[c]->entity )
213 				{
214 					if ( players[c]->entity->playerRequiresBloodToSustain() )
215 					{
216 						sellVampireBlood = true;
217 						break;
218 					}
219 				}
220 			}
221 
222 			Item* tmpItem = nullptr;
223 			bool doneAlembic = false;
224 			bool doneLockpick = false;
225 			bool doneBackpack = false;
226 			bool doneTinkeringKit = false;
227 			bool doneFeather = false;
228 			switch ( my->monsterStoreType )
229 			{
230 				case -1:
231 					my->monsterStoreType = oldMonsterStoreType; // don't generate any items.
232 					break;
233 				case 0:
234 					// arms & armor store
235 					if ( blessedShopkeeper > 0 )
236 					{
237 						numitems += rand() % 5; // offset some of the quantity reduction.
238 					}
239 					for ( c = 0; c < numitems; c++ )
240 					{
241 						if ( currentlevel >= 18 )
242 						{
243 							if ( rand() % 2 )
244 							{
245 								if ( rand() % 10 == 0 )
246 								{
247 									tmpItem = newItem(itemLevelCurve(THROWN, 8, currentlevel), static_cast<Status>(SERVICABLE + rand() % 2), 0, 3 + rand() % 3, rand(), false, &myStats->inventory);
248 								}
249 								else
250 								{
251 									tmpItem = newItem(itemLevelCurve(ARMOR, 5, currentlevel), static_cast<Status>(WORN + rand() % 3), rand() % blessedShopkeeper, 1 + rand() % 4, rand(), false, &myStats->inventory);
252 								}
253 							}
254 							else
255 							{
256 								tmpItem = newItem(itemLevelCurve(WEAPON, 10, currentlevel), static_cast<Status>(WORN + rand() % 3), rand() % blessedShopkeeper, 1 + rand() % 4, rand(), false, &myStats->inventory);
257 							}
258 						}
259 						else
260 						{
261 							if ( rand() % 2 )
262 							{
263 								if ( rand() % 8 == 0 )
264 								{
265 									tmpItem = newItem(itemLevelCurve(THROWN, 0, currentlevel + 20), static_cast<Status>(WORN + rand() % 3), 0, 3 + rand() % 3, rand(), false, &myStats->inventory);
266 								}
267 								else
268 								{
269 									tmpItem = newItem(static_cast<ItemType>(rand() % 20), static_cast<Status>(WORN + rand() % 3), 0, 1 + rand() % 4, rand(), false, &myStats->inventory);
270 								}
271 							}
272 							else
273 							{
274 								int i = rand() % 23;
275 								if ( i < 18 )
276 								{
277 									tmpItem = newItem(static_cast<ItemType>(GLOVES + i), static_cast<Status>(WORN + rand() % 3), 0, 1 + rand() % 4, rand(), false, &myStats->inventory);
278 								}
279 								else if ( i < 21 )
280 								{
281 									tmpItem = newItem(static_cast<ItemType>(GLOVES + i + 4), static_cast<Status>(WORN + rand() % 3), 0, 1 + rand() % 6, rand(), false, &myStats->inventory);
282 								}
283 								else
284 								{
285 									// punching armaments
286 									tmpItem = newItem(static_cast<ItemType>(BRASS_KNUCKLES + rand() % 3), static_cast<Status>(WORN + rand() % 3), 0, 1 + rand() % 2, rand(), false, &myStats->inventory);
287 								}
288 							}
289 						}
290 						// post-processing
291 						if ( tmpItem )
292 						{
293 							if ( tmpItem->beatitude > 0 )
294 							{
295 								tmpItem->count = 1;
296 								tmpItem->status = static_cast<Status>(SERVICABLE + rand() % 2);
297 							}
298 							if ( tmpItem->type >= BRONZE_TOMAHAWK && tmpItem->type <= CRYSTAL_SHURIKEN )
299 							{
300 								// thrown weapons always fixed status. (tomahawk = decrepit, shuriken = excellent)
301 								tmpItem->status = std::min(static_cast<Status>(DECREPIT + (tmpItem->type - BRONZE_TOMAHAWK)), EXCELLENT);
302 							}
303 						}
304 					}
305 					break;
306 				case 1:
307 					// hat store
308 					for ( c = 0; c < numitems; c++ )
309 					{
310 						tmpItem = newItem(static_cast<ItemType>(HAT_PHRYGIAN + rand() % 7), static_cast<Status>(WORN + rand() % 3), rand() % blessedShopkeeper, 1 + rand() % 6, rand(), false, &myStats->inventory);
311 						// post-processing
312 						if ( tmpItem && tmpItem->beatitude > 0 )
313 						{
314 							tmpItem->count = 1;
315 							tmpItem->status = static_cast<Status>(SERVICABLE + rand() % 2);
316 						}
317 					}
318 					break;
319 				case 2:
320 					// jewelry store
321 					for ( c = 0; c < numitems; c++ )
322 					{
323 						switch ( rand() % 3 )
324 						{
325 							case 0:
326 								tmpItem = newItem(itemLevelCurve(AMULET, 0, currentlevel + 5), static_cast<Status>(WORN + rand() % 3), rand() % blessedShopkeeper, 1 + rand() % 2, rand(), false, &myStats->inventory);
327 								break;
328 							case 1:
329 								tmpItem = newItem(itemLevelCurve(RING, 0, currentlevel + 5), static_cast<Status>(WORN + rand() % 3), rand() % blessedShopkeeper, 1 + rand() % 2, rand(), false, &myStats->inventory);
330 								break;
331 							case 2:
332 								tmpItem = newItem(static_cast<ItemType>(GEM_GARNET + rand() % 16), static_cast<Status>(WORN + rand() % 3), 0, 1 + rand() % 2, rand(), false, &myStats->inventory);
333 								break;
334 						}
335 						// post-processing
336 						if ( tmpItem && tmpItem->beatitude > 0 )
337 						{
338 							tmpItem->count = 1;
339 							tmpItem->status = static_cast<Status>(SERVICABLE + rand() % 2);
340 						}
341 					}
342 					break;
343 				case 3:
344 					// bookstore
345 					for ( c = 0; c < numitems; c++ )
346 					{
347 						switch ( rand() % 3 )
348 						{
349 							case 0:
350 								if ( currentlevel >= 18 )
351 								{
352 									tmpItem = newItem(itemLevelCurve(SPELLBOOK, 0, currentlevel), static_cast<Status>(WORN + rand() % 3), rand() % blessedShopkeeper, 1 + rand() % 2, rand(), true, &myStats->inventory);
353 								}
354 								else
355 								{
356 									tmpItem = newItem(static_cast<ItemType>(SPELLBOOK_FORCEBOLT + rand() % 21), static_cast<Status>(WORN + rand() % 3), 0, 1 + rand() % 2, rand(), true, &myStats->inventory);
357 								}
358 								break;
359 							case 1:
360 								tmpItem = newItem(itemLevelCurve(SCROLL, 0, 35), static_cast<Status>(WORN + rand() % 3), 0, 1 + rand() % 2, rand(), true, &myStats->inventory);
361 								break;
362 							case 2:
363 								if ( rand() % 3 == 0 )
364 								{
365 									tmpItem = newItem(itemLevelCurve(SCROLL, 0, 35), static_cast<Status>(WORN + rand() % 3), 0, 1 + rand() % 2, rand(), true, &myStats->inventory);
366 								}
367 								else
368 								{
369 									tmpItem = newItem(READABLE_BOOK, static_cast<Status>(WORN + rand() % 3), 0, 1 + rand() % 3, rand(), false, &myStats->inventory);
370 								}
371 								break;
372 						}
373 						// post-processing
374 						if ( rand() % blessedShopkeeper > 0 )
375 						{
376 							tmpItem->status = static_cast<Status>(SERVICABLE + rand() % 2);
377 						}
378 					}
379 					if ( !doneFeather && rand() % 20 == 0 )
380 					{
381 						if ( rand() % 5 == 0 )
382 						{
383 							newItem(ENCHANTED_FEATHER, EXCELLENT, 0, 1, ENCHANTED_FEATHER_MAX_DURABILITY - 1, true, &myStats->inventory);
384 						}
385 						else
386 						{
387 							newItem(ENCHANTED_FEATHER, SERVICABLE, 0, 1, (3 * (ENCHANTED_FEATHER_MAX_DURABILITY - 1)) / 4, true, &myStats->inventory);
388 						}
389 						tmpItem = newItem(SCROLL_BLANK, static_cast<Status>(WORN + rand() % 3), 0, 1 + rand() % 3, rand(), true, &myStats->inventory);
390 						doneFeather = true;
391 					}
392 					break;
393 				case 4:
394 					// apothecary
395 					for ( c = 0; c < numitems; c++ )
396 					{
397 						if ( !doneAlembic && rand() % 2 == 0 )
398 						{
399 							if ( rand() % 2 == 0 )
400 							{
401 								tmpItem = newItem(TOOL_ALEMBIC, static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), true, &myStats->inventory);
402 								if ( rand() % blessedShopkeeper > 0 )
403 								{
404 									tmpItem->status = static_cast<Status>(SERVICABLE + rand() % 2);
405 								}
406 							}
407 							if ( rand() % 2 == 0 )
408 							{
409 								tmpItem = newItem(TOOL_ALEMBIC, static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), true, &myStats->inventory);
410 								if ( rand() % blessedShopkeeper > 0 )
411 								{
412 									tmpItem->status = static_cast<Status>(SERVICABLE + rand() % 2);
413 								}
414 							}
415 							tmpItem = newItem(TOOL_ALEMBIC, static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), true, &myStats->inventory);
416 							doneAlembic = true;
417 						}
418 						else
419 						{
420 							tmpItem = newItem(static_cast<ItemType>(POTION_WATER + rand() % 15), static_cast<Status>(WORN + rand() % 3), 0, 1 + rand() % 5, rand(), true, &myStats->inventory);
421 						}
422 						// post-processing
423 						if ( rand() % blessedShopkeeper > 0 )
424 						{
425 							tmpItem->status = static_cast<Status>(SERVICABLE + rand() % 2);
426 						}
427 					}
428 					newItem(POTION_EMPTY, SERVICABLE, 0, 2 + rand() % 5, 0, true, &myStats->inventory);
429 					if ( sellVampireBlood )
430 					{
431 						tmpItem = newItem(FOOD_BLOOD, EXCELLENT, 0, 2 + rand() % 3, rand(), false, &myStats->inventory);
432 					}
433 					break;
434 				case 5:
435 					// staff shop
436 					for ( c = 0; c < numitems; c++ )
437 					{
438 						if ( currentlevel >= 18 )
439 						{
440 							tmpItem = newItem(itemLevelCurve(MAGICSTAFF, 0, currentlevel), static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), true, &myStats->inventory);
441 						}
442 						else
443 						{
444 							tmpItem = newItem(itemLevelCurve(MAGICSTAFF, 0, 15), static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), true, &myStats->inventory);
445 						}
446 						// post-processing
447 						if ( rand() % blessedShopkeeper > 0 )
448 						{
449 							tmpItem->status = static_cast<Status>(SERVICABLE + rand() % 2);
450 						}
451 					}
452 					break;
453 				case 6:
454 					// food store
455 					for ( c = 0; c < numitems; c++ )
456 					{
457 						tmpItem = newItem(static_cast<ItemType>(FOOD_BREAD + rand() % 7), static_cast<Status>(SERVICABLE + rand() % 2), 0, 1 + rand() % 3, rand(), false, &myStats->inventory);
458 						// post-processing
459 						if ( rand() % blessedShopkeeper > 0 )
460 						{
461 							tmpItem->status = static_cast<Status>(SERVICABLE + rand() % 2);
462 						}
463 					}
464 					break;
465 				case 7:
466 					// hardware store
467 					for ( c = 0; c < numitems; c++ )
468 					{
469 						if ( rand() % 20 == 0 )
470 						{
471 							tmpItem = newItem(itemLevelCurve(THROWN, 0, currentlevel + 20), static_cast<Status>(SERVICABLE + rand() % 2), 0, 3 + rand() % 3, rand(), false, &myStats->inventory);
472 						}
473 						else
474 						{
475 							tmpItem = newItem(static_cast<ItemType>(TOOL_PICKAXE + rand() % 11), static_cast<Status>(WORN + rand() % 3), 0, 1 + rand() % 3, rand(), false, &myStats->inventory);
476 						}
477 						// post-processing
478 						if ( rand() % blessedShopkeeper > 0 )
479 						{
480 							tmpItem->status = static_cast<Status>(SERVICABLE + rand() % 2);
481 						}
482 						if ( tmpItem->type >= BRONZE_TOMAHAWK && tmpItem->type <= CRYSTAL_SHURIKEN )
483 						{
484 							// thrown weapons always fixed status. (tomahawk = decrepit, shuriken = excellent)
485 							tmpItem->status = std::min(static_cast<Status>(DECREPIT + (tmpItem->type - BRONZE_TOMAHAWK)), EXCELLENT);
486 						}
487 
488 						if ( !doneLockpick && rand() % 2 == 0 )
489 						{
490 							tmpItem = newItem(TOOL_LOCKPICK, static_cast<Status>(WORN + rand() % 3), 0, 1 + rand() % 3, rand(), true, &myStats->inventory);
491 							if ( rand() % blessedShopkeeper > 0 )
492 							{
493 								tmpItem->status = static_cast<Status>(SERVICABLE + rand() % 2);
494 							}
495 							doneLockpick = true;
496 						}
497 
498 						if ( !doneTinkeringKit && rand() % 5 == 0 )
499 						{
500 							newItem(TOOL_TINKERING_KIT, DECREPIT, 0, 1, rand(), true, &myStats->inventory);
501 							doneTinkeringKit = true;
502 						}
503 
504 						if ( !doneAlembic && rand() % 2 == 0 )
505 						{
506 							tmpItem = newItem(TOOL_ALEMBIC, static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), true, &myStats->inventory);
507 							if ( rand() % blessedShopkeeper > 0 )
508 							{
509 								tmpItem->status = static_cast<Status>(SERVICABLE + rand() % 2);
510 							}
511 							if ( rand() % 2 == 0 )
512 							{
513 								tmpItem = newItem(TOOL_ALEMBIC, static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), true, &myStats->inventory);
514 								if ( rand() % blessedShopkeeper > 0 )
515 								{
516 									tmpItem->status = static_cast<Status>(SERVICABLE + rand() % 2);
517 								}
518 							}
519 							if ( rand() % 2 == 0 )
520 							{
521 								tmpItem = newItem(TOOL_ALEMBIC, static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), true, &myStats->inventory);
522 								if ( rand() % blessedShopkeeper > 0 )
523 								{
524 									tmpItem->status = static_cast<Status>(SERVICABLE + rand() % 2);
525 								}
526 							}
527 							doneAlembic = true;
528 						}
529 
530 					}
531 					if ( !doneBackpack && rand() % 10 == 0 )
532 					{
533 						newItem(CLOAK_BACKPACK, static_cast<Status>(WORN + rand() % 3), 0, 1, rand(), true, &myStats->inventory);
534 						doneBackpack = true;
535 					}
536 					break;
537 				case 8:
538 					// weapon/hunting store
539 					if ( currentlevel < 10 && customShopkeeperInUse == 0 )
540 					{
541 						numitems = 7 + rand() % 4;
542 					}
543 					for ( c = 0; c < numitems; c++ )
544 					{
545 						switch ( rand() % 20 )
546 						{
547 							case 0:
548 							case 1:
549 							case 2:
550 							case 3:
551 							{
552 								// ranged weapons
553 								std::vector<ItemType> rangedWeapons;
554 								rangedWeapons.push_back(SHORTBOW);
555 								if ( currentlevel < 5 )
556 								{
557 									rangedWeapons.push_back(SLING);
558 								}
559 								if ( currentlevel >= 8 )
560 								{
561 									rangedWeapons.push_back(CROSSBOW);
562 								}
563 								if ( currentlevel >= 13 )
564 								{
565 									rangedWeapons.push_back(LONGBOW);
566 								}
567 								if ( currentlevel >= 15 )
568 								{
569 									rangedWeapons.push_back(HEAVY_CROSSBOW);
570 									rangedWeapons.push_back(COMPOUND_BOW);
571 								}
572 								ItemType chosenType = rangedWeapons[rand() % rangedWeapons.size()];
573 								tmpItem = newItem(chosenType, static_cast<Status>(WORN + rand() % 3), rand() % blessedShopkeeper, 1, rand(), false, &myStats->inventory);
574 								break;
575 							}
576 							case 4:
577 							case 5:
578 							case 6:
579 							case 7:
580 							case 8:
581 							case 9:
582 							case 10:
583 							case 11:
584 								// standard weapons
585 								if ( currentlevel >= 18 )
586 								{
587 									tmpItem = newItem(itemLevelCurve(WEAPON, 10, currentlevel + 5), static_cast<Status>(WORN + rand() % 3), rand() % blessedShopkeeper, 1, rand(), false, &myStats->inventory);
588 								}
589 								else
590 								{
591 									tmpItem = newItem(itemLevelCurve(WEAPON, 0, currentlevel + 5), static_cast<Status>(WORN + rand() % 3), rand() % blessedShopkeeper, 1, rand(), false, &myStats->inventory);
592 								}
593 								break;
594 							case 12:
595 							case 13:
596 								// thrown weapons (10%), sometime punching things
597 								if ( rand() % 10 == 0 )
598 								{
599 									// punching stuff (5%)
600 									std::vector<ItemType> gloveWeapons;
601 									gloveWeapons.push_back(BRASS_KNUCKLES);
602 									if ( currentlevel >= items[SPIKED_GAUNTLETS].level )
603 									{
604 										gloveWeapons.push_back(SPIKED_GAUNTLETS);
605 									}
606 									if ( currentlevel >= items[IRON_KNUCKLES].level )
607 									{
608 										gloveWeapons.push_back(IRON_KNUCKLES);
609 									}
610 									ItemType chosenType = gloveWeapons[rand() % gloveWeapons.size()];
611 									tmpItem = newItem(chosenType, static_cast<Status>(WORN + rand() % 3), rand() % blessedShopkeeper, 1, rand(), false, &myStats->inventory);
612 								}
613 								else
614 								{
615 									if ( currentlevel >= 18 )
616 									{
617 										tmpItem = newItem(itemLevelCurve(THROWN, 0, currentlevel + 20), static_cast<Status>(WORN + rand() % 3), 0, 3 + rand() % 3, rand(), false, &myStats->inventory);
618 									}
619 									else
620 									{
621 										tmpItem = newItem(itemLevelCurve(THROWN, 0, 8), static_cast<Status>(SERVICABLE + rand() % 2), 0, 3 + rand() % 3, rand(), false, &myStats->inventory);
622 									}
623 								}
624 								break;
625 							case 14:
626 							case 15:
627 							case 16:
628 							case 17:
629 							case 18:
630 							case 19:
631 							{
632 								// quivers (30%)
633 								std::vector<ItemType> quivers;
634 								quivers.push_back(QUIVER_SILVER);
635 								quivers.push_back(QUIVER_LIGHTWEIGHT);
636 								if ( currentlevel >= 5 )
637 								{
638 									quivers.push_back(QUIVER_KNOCKBACK);
639 								}
640 								if ( currentlevel >= 10 )
641 								{
642 									quivers.push_back(QUIVER_FIRE);
643 									quivers.push_back(QUIVER_HUNTING);
644 								}
645 								if ( currentlevel >= 18 )
646 								{
647 									quivers.push_back(QUIVER_PIERCE);
648 									quivers.push_back(QUIVER_CRYSTAL);
649 								}
650 								ItemType chosenType = quivers[rand() % quivers.size()];
651 								tmpItem = newItem(chosenType, EXCELLENT, 0, 10 + rand() % 6, 0, true, &myStats->inventory); // 10-15 arrows.
652 								break;
653 							}
654 							default:
655 								break;
656 						}
657 						// post-processing
658 						if ( tmpItem )
659 						{
660 							if ( tmpItem->beatitude > 0 )
661 							{
662 								tmpItem->count = 1;
663 								tmpItem->status = static_cast<Status>(SERVICABLE + rand() % 2);
664 							}
665 							if ( tmpItem->type >= BRONZE_TOMAHAWK && tmpItem->type <= CRYSTAL_SHURIKEN )
666 							{
667 								// thrown weapons always fixed status. (tomahawk = decrepit, shuriken = excellent)
668 								tmpItem->status = std::min(static_cast<Status>(DECREPIT + (tmpItem->type - BRONZE_TOMAHAWK)), EXCELLENT);
669 							}
670 						}
671 					}
672 					break;
673 				case 9:
674 					// general store
675 					for ( c = 0; c < numitems; c++ )
676 					{
677 						Category cat = static_cast<Category>(rand() % (NUMCATEGORIES - 1));
678 						tmpItem = newItem(itemLevelCurve(cat, 0, currentlevel + 5), static_cast<Status>(WORN + rand() % 3), 0, 1 + rand() % 3, rand(), false, &myStats->inventory);
679 						if ( tmpItem && (itemCategory(tmpItem) == WEAPON || itemCategory(tmpItem) == ARMOR || itemCategory(tmpItem) == RING || itemCategory(tmpItem) == AMULET) )
680 						{
681 							tmpItem->beatitude += rand() % blessedShopkeeper;
682 							// post-processing
683 							if ( tmpItem->beatitude > 0 )
684 							{
685 								tmpItem->count = 1;
686 							}
687 						}
688 						if ( tmpItem && tmpItem->type >= BRONZE_TOMAHAWK && tmpItem->type <= CRYSTAL_SHURIKEN )
689 						{
690 							// thrown weapons always fixed status. (tomahawk = decrepit, shuriken = excellent)
691 							tmpItem->status = std::min(static_cast<Status>(DECREPIT + (tmpItem->type - BRONZE_TOMAHAWK)), EXCELLENT);
692 						}
693 					}
694 					if ( !doneTinkeringKit && rand() % 20 == 0 )
695 					{
696 						if ( rand() % 5 == 0 )
697 						{
698 							newItem(TOOL_TINKERING_KIT, WORN, 0, 1, rand(), true, &myStats->inventory);
699 						}
700 						else
701 						{
702 							newItem(TOOL_TINKERING_KIT, DECREPIT, 0, 1, rand(), true, &myStats->inventory);
703 						}
704 						doneTinkeringKit = true;
705 					}
706 					if ( sellVampireBlood )
707 					{
708 						tmpItem = newItem(FOOD_BLOOD, EXCELLENT, 0, 1 + rand() % 4, rand(), false, &myStats->inventory);
709 					}
710 					break;
711 				case 10:
712 					// mysterious merchant.
713 					// general store
714 					numitems = 15;
715 					while ( numitems > 0 )
716 					{
717 						for ( auto orbCategories : shopkeeperMysteriousItems )
718 						{
719 							for (auto itemInCategory : orbCategories.second)
720 							{
721 								if (itemInCategory == ENCHANTED_FEATHER)
722 								{
723 									newItem(static_cast<ItemType>(itemInCategory), EXCELLENT, 0, 1, ENCHANTED_FEATHER_MAX_DURABILITY - 1, true, &myStats->inventory);
724 								}
725 								else if (itemTypeIsQuiver(static_cast<ItemType>(itemInCategory)))
726 								{
727 									newItem(static_cast<ItemType>(itemInCategory), SERVICABLE, 0, 50, ITEM_GENERATED_QUIVER_APPEARANCE, true, &myStats->inventory);
728 								}
729 								else
730 								{
731 									int bless = 0;
732 									Status status = SERVICABLE;
733 									if (itemInCategory >= CRYSTAL_SWORD && itemInCategory <= CRYSTAL_MACE)
734 									{
735 										bless = 3;
736 										status = EXCELLENT;
737 									}
738 									else if (itemInCategory >= ARTIFACT_SWORD && itemInCategory <= ARTIFACT_BOW)
739 									{
740 										status = SERVICABLE;
741 									}
742 									newItem(static_cast<ItemType>(itemInCategory), status, bless, 1, rand(), true, &myStats->inventory);
743 								}
744 								--numitems;
745 							}
746 						}
747 						break;
748 					}
749 					break;
750 				default:
751 					break;
752 			}
753 		}
754 	}
755 
756 	// torso
757 	Entity* entity = newEntity(218, 0, map.entities, nullptr); //Limb entity.
758 	entity->sizex = 4;
759 	entity->sizey = 4;
760 	entity->skill[2] = my->getUID();
761 	entity->flags[PASSABLE] = true;
762 	entity->flags[NOUPDATE] = true;
763 	entity->flags[USERFLAG2] = my->flags[USERFLAG2];
764 	entity->focalx = limbs[SHOPKEEPER][1][0]; // 0
765 	entity->focaly = limbs[SHOPKEEPER][1][1]; // 0
766 	entity->focalz = limbs[SHOPKEEPER][1][2]; // 0
767 	entity->behavior = &actShopkeeperLimb;
768 	entity->parent = my->getUID();
769 	node = list_AddNodeLast(&my->children);
770 	node->element = entity;
771 	node->deconstructor = &emptyDeconstructor;
772 	node->size = sizeof(Entity*);
773 	my->bodyparts.push_back(entity);
774 
775 	// right leg
776 	entity = newEntity(222, 0, map.entities, nullptr); //Limb entity.
777 	entity->sizex = 4;
778 	entity->sizey = 4;
779 	entity->skill[2] = my->getUID();
780 	entity->flags[PASSABLE] = true;
781 	entity->flags[NOUPDATE] = true;
782 	entity->flags[USERFLAG2] = my->flags[USERFLAG2];
783 	entity->focalx = limbs[SHOPKEEPER][2][0]; // 0
784 	entity->focaly = limbs[SHOPKEEPER][2][1]; // 0
785 	entity->focalz = limbs[SHOPKEEPER][2][2]; // 2
786 	entity->behavior = &actShopkeeperLimb;
787 	entity->parent = my->getUID();
788 	node = list_AddNodeLast(&my->children);
789 	node->element = entity;
790 	node->deconstructor = &emptyDeconstructor;
791 	node->size = sizeof(Entity*);
792 	my->bodyparts.push_back(entity);
793 
794 	// left leg
795 	entity = newEntity(221, 0, map.entities, nullptr); //Limb entity.
796 	entity->sizex = 4;
797 	entity->sizey = 4;
798 	entity->skill[2] = my->getUID();
799 	entity->flags[PASSABLE] = true;
800 	entity->flags[NOUPDATE] = true;
801 	entity->flags[USERFLAG2] = my->flags[USERFLAG2];
802 	entity->focalx = limbs[SHOPKEEPER][3][0]; // 0
803 	entity->focaly = limbs[SHOPKEEPER][3][1]; // 0
804 	entity->focalz = limbs[SHOPKEEPER][3][2]; // 2
805 	entity->behavior = &actShopkeeperLimb;
806 	entity->parent = my->getUID();
807 	node = list_AddNodeLast(&my->children);
808 	node->element = entity;
809 	node->deconstructor = &emptyDeconstructor;
810 	node->size = sizeof(Entity*);
811 	my->bodyparts.push_back(entity);
812 
813 	// right arm
814 	entity = newEntity(220, 0, map.entities, nullptr); //Limb entity.
815 	entity->sizex = 4;
816 	entity->sizey = 4;
817 	entity->skill[2] = my->getUID();
818 	entity->flags[PASSABLE] = true;
819 	entity->flags[NOUPDATE] = true;
820 	entity->flags[USERFLAG2] = my->flags[USERFLAG2];
821 	entity->focalx = limbs[SHOPKEEPER][4][0]; // 0
822 	entity->focaly = limbs[SHOPKEEPER][4][1]; // 0
823 	entity->focalz = limbs[SHOPKEEPER][4][2]; // 1.5
824 	entity->behavior = &actShopkeeperLimb;
825 	entity->parent = my->getUID();
826 	node = list_AddNodeLast(&my->children);
827 	node->element = entity;
828 	node->deconstructor = &emptyDeconstructor;
829 	node->size = sizeof(Entity*);
830 	my->bodyparts.push_back(entity);
831 
832 	// left arm
833 	entity = newEntity(219, 0, map.entities, nullptr); //Limb entity.
834 	entity->sizex = 4;
835 	entity->sizey = 4;
836 	entity->skill[2] = my->getUID();
837 	entity->flags[PASSABLE] = true;
838 	entity->flags[NOUPDATE] = true;
839 	entity->flags[USERFLAG2] = my->flags[USERFLAG2];
840 	entity->focalx = limbs[SHOPKEEPER][5][0]; // 0
841 	entity->focaly = limbs[SHOPKEEPER][5][1]; // 0
842 	entity->focalz = limbs[SHOPKEEPER][5][2]; // 1.5
843 	entity->behavior = &actShopkeeperLimb;
844 	entity->parent = my->getUID();
845 	node = list_AddNodeLast(&my->children);
846 	node->element = entity;
847 	node->deconstructor = &emptyDeconstructor;
848 	node->size = sizeof(Entity*);
849 	my->bodyparts.push_back(entity);
850 
851 	// world weapon
852 	entity = newEntity(-1, 0, map.entities, nullptr); //Limb entity.
853 	entity->sizex = 4;
854 	entity->sizey = 4;
855 	entity->skill[2] = my->getUID();
856 	entity->flags[PASSABLE] = true;
857 	entity->flags[NOUPDATE] = true;
858 	entity->flags[USERFLAG2] = my->flags[USERFLAG2];
859 	entity->focalx = limbs[SHOPKEEPER][6][0]; // 1.5
860 	entity->focaly = limbs[SHOPKEEPER][6][1]; // 0
861 	entity->focalz = limbs[SHOPKEEPER][6][2]; // -.5
862 	entity->behavior = &actShopkeeperLimb;
863 	entity->parent = my->getUID();
864 	entity->pitch = .25;
865 	node = list_AddNodeLast(&my->children);
866 	node->element = entity;
867 	node->deconstructor = &emptyDeconstructor;
868 	node->size = sizeof(Entity*);
869 	my->bodyparts.push_back(entity);
870 
871 	// shield
872 	entity = newEntity(-1, 0, map.entities, nullptr); //Limb entity.
873 	entity->sizex = 4;
874 	entity->sizey = 4;
875 	entity->skill[2] = my->getUID();
876 	entity->flags[PASSABLE] = true;
877 	entity->flags[NOUPDATE] = true;
878 	entity->flags[USERFLAG2] = my->flags[USERFLAG2];
879 	entity->focalx = limbs[SHOPKEEPER][7][0]; // 2
880 	entity->focaly = limbs[SHOPKEEPER][7][1]; // 0
881 	entity->focalz = limbs[SHOPKEEPER][7][2]; // 0
882 	entity->behavior = &actShopkeeperLimb;
883 	entity->parent = my->getUID();
884 	node = list_AddNodeLast(&my->children);
885 	node->element = entity;
886 	node->deconstructor = &emptyDeconstructor;
887 	node->size = sizeof(Entity*);
888 	my->bodyparts.push_back(entity);
889 
890 	// cloak
891 	entity = newEntity(-1, 0, map.entities, nullptr); //Limb entity.
892 	entity->sizex = 4;
893 	entity->sizey = 4;
894 	entity->skill[2] = my->getUID();
895 	entity->scalex = 1.01;
896 	entity->scaley = 1.01;
897 	entity->scalez = 1.01;
898 	entity->flags[PASSABLE] = true;
899 	entity->flags[NOUPDATE] = true;
900 	entity->flags[USERFLAG2] = my->flags[USERFLAG2];
901 	entity->focalx = limbs[SHOPKEEPER][8][0]; // 0
902 	entity->focaly = limbs[SHOPKEEPER][8][1]; // 0
903 	entity->focalz = limbs[SHOPKEEPER][8][2]; // 4
904 	entity->behavior = &actShopkeeperLimb;
905 	entity->parent = my->getUID();
906 	node = list_AddNodeLast(&my->children);
907 	node->element = entity;
908 	node->deconstructor = &emptyDeconstructor;
909 	node->size = sizeof(Entity*);
910 	my->bodyparts.push_back(entity);
911 
912 	// helmet
913 	entity = newEntity(-1, 0, map.entities, nullptr); //Limb entity.
914 	entity->sizex = 4;
915 	entity->sizey = 4;
916 	entity->skill[2] = my->getUID();
917 	entity->scalex = 1.01;
918 	entity->scaley = 1.01;
919 	entity->scalez = 1.01;
920 	entity->flags[PASSABLE] = true;
921 	entity->flags[NOUPDATE] = true;
922 	entity->flags[USERFLAG2] = my->flags[USERFLAG2];
923 	entity->focalx = limbs[SHOPKEEPER][9][0]; // 0
924 	entity->focaly = limbs[SHOPKEEPER][9][1]; // 0
925 	entity->focalz = limbs[SHOPKEEPER][9][2]; // -1.75
926 	entity->behavior = &actShopkeeperLimb;
927 	entity->parent = my->getUID();
928 	node = list_AddNodeLast(&my->children);
929 	node->element = entity;
930 	node->deconstructor = &emptyDeconstructor;
931 	node->size = sizeof(Entity*);
932 	my->bodyparts.push_back(entity);
933 
934 	// mask
935 	entity = newEntity(-1, 0, map.entities, nullptr); //Limb entity.
936 	entity->sizex = 4;
937 	entity->sizey = 4;
938 	entity->skill[2] = my->getUID();
939 	entity->scalex = .99;
940 	entity->scaley = .99;
941 	entity->scalez = .99;
942 	entity->flags[PASSABLE] = true;
943 	entity->flags[NOUPDATE] = true;
944 	entity->flags[USERFLAG2] = my->flags[USERFLAG2];
945 	entity->focalx = limbs[SHOPKEEPER][10][0]; // 0
946 	entity->focaly = limbs[SHOPKEEPER][10][1]; // 0
947 	entity->focalz = limbs[SHOPKEEPER][10][2]; // .5
948 	entity->behavior = &actShopkeeperLimb;
949 	entity->parent = my->getUID();
950 	node = list_AddNodeLast(&my->children);
951 	node->element = entity;
952 	node->deconstructor = &emptyDeconstructor;
953 	node->size = sizeof(Entity*);
954 	my->bodyparts.push_back(entity);
955 }
956 
actShopkeeperLimb(Entity * my)957 void actShopkeeperLimb(Entity* my)
958 {
959 	my->actMonsterLimb();
960 }
961 
shopkeeperDie(Entity * my)962 void shopkeeperDie(Entity* my)
963 {
964 	int c;
965 	for ( c = 0; c < 5; c++ )
966 	{
967 		Entity* gib = spawnGib(my);
968 		serverSpawnGibForClient(gib);
969 	}
970 
971 	my->spawnBlood();
972 
973 	my->removeMonsterDeathNodes();
974 
975 	list_RemoveNode(my->mynode);
976 	return;
977 }
978 
979 #define SHOPKEEPERWALKSPEED .15
980 
shopkeeperMoveBodyparts(Entity * my,Stat * myStats,double dist)981 void shopkeeperMoveBodyparts(Entity* my, Stat* myStats, double dist)
982 {
983 	node_t* node;
984 	Entity* entity = nullptr, *entity2 = nullptr;
985 	Entity* rightbody = nullptr;
986 	Entity* weaponarm = nullptr;
987 	int bodypart;
988 	bool wearingring = false;
989 
990 	if ( multiplayer != CLIENT && myStats->MISC_FLAGS[STAT_FLAG_MYSTERIOUS_SHOPKEEP] > 0 )
991 	{
992 		if ( my->ticks == 5 * TICKS_PER_SECOND )
993 		{
994 			serverUpdateEntitySkill(my, 18); // update the store type for clients.
995 		}
996 		if ( myStats->HP < ((3 * myStats->MAXHP) / 4) )
997 		{
998 			playSoundEntity(my, 77, 64);
999 			createParticleErupt(my, 593);
1000 			serverSpawnMiscParticles(my, PARTICLE_EFFECT_ERUPT, 593);
1001 			my->removeMonsterDeathNodes();
1002 			list_RemoveNode(my->mynode);
1003 			return;
1004 		}
1005 	}
1006 
1007 	// set invisibility //TODO: isInvisible()?
1008 	if ( multiplayer != CLIENT )
1009 	{
1010 		if ( myStats->ring != nullptr )
1011 			if ( myStats->ring->type == RING_INVISIBILITY )
1012 			{
1013 				wearingring = true;
1014 			}
1015 		if ( myStats->cloak != nullptr )
1016 			if ( myStats->cloak->type == CLOAK_INVISIBILITY )
1017 			{
1018 				wearingring = true;
1019 			}
1020 		if ( myStats->EFFECTS[EFF_INVISIBLE] == true || wearingring == true )
1021 		{
1022 			my->flags[INVISIBLE] = true;
1023 			my->flags[BLOCKSIGHT] = false;
1024 			bodypart = 0;
1025 			for ( node = my->children.first; node != nullptr; node = node->next )
1026 			{
1027 				if ( bodypart < 2 )
1028 				{
1029 					bodypart++;
1030 					continue;
1031 				}
1032 				if ( bodypart >= 7 )
1033 				{
1034 					break;
1035 				}
1036 				entity = (Entity*)node->element;
1037 				if ( !entity->flags[INVISIBLE] )
1038 				{
1039 					entity->flags[INVISIBLE] = true;
1040 					serverUpdateEntityBodypart(my, bodypart);
1041 				}
1042 				bodypart++;
1043 			}
1044 		}
1045 		else
1046 		{
1047 			my->flags[INVISIBLE] = false;
1048 			my->flags[BLOCKSIGHT] = true;
1049 			bodypart = 0;
1050 			for ( node = my->children.first; node != nullptr; node = node->next )
1051 			{
1052 				if ( bodypart < 2 )
1053 				{
1054 					bodypart++;
1055 					continue;
1056 				}
1057 				if ( bodypart >= 7 )
1058 				{
1059 					break;
1060 				}
1061 				entity = (Entity*)node->element;
1062 				if ( entity->flags[INVISIBLE] )
1063 				{
1064 					entity->flags[INVISIBLE] = false;
1065 					serverUpdateEntityBodypart(my, bodypart);
1066 					serverUpdateEntityFlag(my, INVISIBLE);
1067 				}
1068 				bodypart++;
1069 			}
1070 		}
1071 
1072 		// sleeping
1073 		if ( myStats->EFFECTS[EFF_ASLEEP] )
1074 		{
1075 			my->z = 1.5;
1076 			my->pitch = PI / 4;
1077 		}
1078 		else
1079 		{
1080 			my->z = -1;
1081 			my->pitch = 0;
1082 		}
1083 	}
1084 
1085 	Entity* helmet = nullptr;
1086 
1087 	//Move bodyparts
1088 	for (bodypart = 0, node = my->children.first; node != NULL; node = node->next, bodypart++)
1089 	{
1090 		if ( bodypart < LIMB_HUMANOID_TORSO )
1091 		{
1092 			continue;
1093 		}
1094 		entity = (Entity*)node->element;
1095 		entity->x = my->x;
1096 		entity->y = my->y;
1097 		entity->z = my->z;
1098 		if ( MONSTER_ATTACK == MONSTER_POSE_MAGIC_WINDUP1 && bodypart == LIMB_HUMANOID_RIGHTARM )
1099 		{
1100 			// don't let the creatures's yaw move the casting arm
1101 		}
1102 		else
1103 		{
1104 			entity->yaw = my->yaw;
1105 		}
1106 		if ( bodypart == LIMB_HUMANOID_RIGHTLEG || bodypart == LIMB_HUMANOID_LEFTARM )
1107 		{
1108 			my->humanoidAnimateWalk(entity, node, bodypart, SHOPKEEPERWALKSPEED, dist, 0.4);
1109 		}
1110 		else if ( bodypart == LIMB_HUMANOID_LEFTLEG || bodypart == LIMB_HUMANOID_RIGHTARM || bodypart == LIMB_HUMANOID_CLOAK )
1111 		{
1112 			// left leg, right arm, cloak.
1113 			if ( bodypart == LIMB_HUMANOID_RIGHTARM )
1114 			{
1115 				weaponarm = entity;
1116 				if ( my->monsterAttack > 0 )
1117 				{
1118 					my->handleWeaponArmAttack(weaponarm);
1119 				}
1120 			}
1121 			my->humanoidAnimateWalk(entity, node, bodypart, SHOPKEEPERWALKSPEED, dist, 0.4);
1122 
1123 			if ( bodypart == LIMB_HUMANOID_CLOAK )
1124 			{
1125 				entity->fskill[0] = entity->pitch;
1126 				entity->roll = my->roll - fabs(entity->pitch) / 2;
1127 				entity->pitch = 0;
1128 			}
1129 		}
1130 		switch ( bodypart )
1131 		{
1132 			// torso
1133 			case 2:
1134 				if ( multiplayer != CLIENT )
1135 				{
1136 					if ( myStats->breastplate == nullptr )
1137 					{
1138 						entity->sprite = 218;
1139 					}
1140 					else
1141 					{
1142 						entity->sprite = itemModel(myStats->breastplate);
1143 					}
1144 					if ( multiplayer == SERVER )
1145 					{
1146 						// update sprites for clients
1147 						if ( entity->skill[10] != entity->sprite )
1148 						{
1149 							entity->skill[10] = entity->sprite;
1150 							serverUpdateEntityBodypart(my, bodypart);
1151 						}
1152 						if ( entity->getUID() % (TICKS_PER_SECOND * 10) == ticks % (TICKS_PER_SECOND * 10) )
1153 						{
1154 							serverUpdateEntityBodypart(my, bodypart);
1155 						}
1156 					}
1157 				}
1158 				entity->x -= .25 * cos(my->yaw);
1159 				entity->y -= .25 * sin(my->yaw);
1160 				entity->z += 2.5;
1161 				break;
1162 			// right leg
1163 			case LIMB_HUMANOID_RIGHTLEG:
1164 				if ( multiplayer != CLIENT )
1165 				{
1166 					if ( myStats->shoes == nullptr )
1167 					{
1168 						entity->sprite = 222;
1169 					}
1170 					else
1171 					{
1172 						my->setBootSprite(entity, SPRITE_BOOT_RIGHT_OFFSET);
1173 					}
1174 					if ( multiplayer == SERVER )
1175 					{
1176 						// update sprites for clients
1177 						if ( entity->skill[10] != entity->sprite )
1178 						{
1179 							entity->skill[10] = entity->sprite;
1180 							serverUpdateEntityBodypart(my, bodypart);
1181 						}
1182 						if ( entity->getUID() % (TICKS_PER_SECOND * 10) == ticks % (TICKS_PER_SECOND * 10) )
1183 						{
1184 							serverUpdateEntityBodypart(my, bodypart);
1185 						}
1186 					}
1187 				}
1188 				entity->x += 1 * cos(my->yaw + PI / 2) + .25 * cos(my->yaw);
1189 				entity->y += 1 * sin(my->yaw + PI / 2) + .25 * sin(my->yaw);
1190 				entity->z += 5;
1191 				if ( my->z >= 1.4 && my->z <= 1.6 )
1192 				{
1193 					entity->yaw += PI / 8;
1194 					entity->pitch = -PI / 2;
1195 				}
1196 				break;
1197 			// left leg
1198 			case LIMB_HUMANOID_LEFTLEG:
1199 				if ( multiplayer != CLIENT )
1200 				{
1201 					if ( myStats->shoes == nullptr )
1202 					{
1203 						entity->sprite = 221;
1204 					}
1205 					else
1206 					{
1207 						my->setBootSprite(entity, SPRITE_BOOT_LEFT_OFFSET);
1208 					}
1209 					if ( multiplayer == SERVER )
1210 					{
1211 						// update sprites for clients
1212 						if ( entity->skill[10] != entity->sprite )
1213 						{
1214 							entity->skill[10] = entity->sprite;
1215 							serverUpdateEntityBodypart(my, bodypart);
1216 						}
1217 						if ( entity->getUID() % (TICKS_PER_SECOND * 10) == ticks % (TICKS_PER_SECOND * 10) )
1218 						{
1219 							serverUpdateEntityBodypart(my, bodypart);
1220 						}
1221 					}
1222 				}
1223 				entity->x -= 1 * cos(my->yaw + PI / 2) - .25 * cos(my->yaw);
1224 				entity->y -= 1 * sin(my->yaw + PI / 2) - .25 * sin(my->yaw);
1225 				entity->z += 5;
1226 				if ( my->z >= 1.4 && my->z <= 1.6 )
1227 				{
1228 					entity->yaw -= PI / 8;
1229 					entity->pitch = -PI / 2;
1230 				}
1231 				break;
1232 			// right arm
1233 			case LIMB_HUMANOID_RIGHTARM:
1234 			{
1235 				node_t* weaponNode = list_Node(&my->children, LIMB_HUMANOID_WEAPON);
1236 				if ( weaponNode )
1237 				{
1238 					Entity* weapon = (Entity*)weaponNode->element;
1239 					if ( MONSTER_ARMBENDED || (weapon->flags[INVISIBLE] && my->monsterState != MONSTER_STATE_ATTACK) )
1240 					{
1241 						// if weapon invisible and I'm not attacking, relax arm.
1242 						entity->focalx = limbs[SHOPKEEPER][4][0]; // 0
1243 						entity->focaly = limbs[SHOPKEEPER][4][1]; // 0
1244 						entity->focalz = limbs[SHOPKEEPER][4][2]; // 1.5
1245 						entity->sprite = 220;
1246 					}
1247 					else
1248 					{
1249 						// else flex arm.
1250 						entity->focalx = limbs[SHOPKEEPER][4][0] + 0.75;
1251 						entity->focaly = limbs[SHOPKEEPER][4][1];
1252 						entity->focalz = limbs[SHOPKEEPER][4][2] - 0.75;
1253 						entity->sprite = 111;
1254 					}
1255 				}
1256 				entity->x += 2.25 * cos(my->yaw + PI / 2) - .20 * cos(my->yaw);
1257 				entity->y += 2.25 * sin(my->yaw + PI / 2) - .20 * sin(my->yaw);
1258 				entity->z += 1.5;
1259 				entity->yaw += MONSTER_WEAPONYAW;
1260 				if ( my->z >= 1.4 && my->z <= 1.6 )
1261 				{
1262 					entity->pitch = 0;
1263 				}
1264 				break;
1265 			}
1266 			// left arm
1267 			case LIMB_HUMANOID_LEFTARM:
1268 			{
1269 				node_t* shieldNode = list_Node(&my->children, 8);
1270 				if ( shieldNode )
1271 				{
1272 					Entity* shield = (Entity*)shieldNode->element;
1273 					if ( shield->flags[INVISIBLE] && (my->monsterState != MONSTER_STATE_ATTACK) )
1274 					{
1275 						// if shield invisible and I'm not attacking, relax arm.
1276 						entity->focalx = limbs[SHOPKEEPER][5][0]; // 0
1277 						entity->focaly = limbs[SHOPKEEPER][5][1]; // 0
1278 						entity->focalz = limbs[SHOPKEEPER][5][2]; // 1.5
1279 						entity->sprite = 219;
1280 					}
1281 					else
1282 					{
1283 						// else flex arm.
1284 						entity->focalx = limbs[SHOPKEEPER][5][0] + 0.75;
1285 						entity->focaly = limbs[SHOPKEEPER][5][1];
1286 						entity->focalz = limbs[SHOPKEEPER][5][2] - 0.75;
1287 						entity->sprite = 112;
1288 					}
1289 				}
1290 				entity->x -= 2.25 * cos(my->yaw + PI / 2) + .20 * cos(my->yaw);
1291 				entity->y -= 2.25 * sin(my->yaw + PI / 2) + .20 * sin(my->yaw);
1292 				entity->z += 1.5;
1293 				if ( my->z >= 1.4 && my->z <= 1.6 )
1294 				{
1295 					entity->pitch = 0;
1296 				}
1297 				break;
1298 			}
1299 				// weapon
1300 			case LIMB_HUMANOID_WEAPON:
1301 				if ( multiplayer != CLIENT )
1302 				{
1303 					if ( myStats->weapon == nullptr || myStats->EFFECTS[EFF_INVISIBLE] || wearingring ) //TODO: isInvisible()?
1304 					{
1305 						entity->flags[INVISIBLE] = true;
1306 					}
1307 					else
1308 					{
1309 						entity->sprite = itemModel(myStats->weapon);
1310 						if ( itemCategory(myStats->weapon) == SPELLBOOK )
1311 						{
1312 							entity->flags[INVISIBLE] = true;
1313 						}
1314 						else
1315 						{
1316 							entity->flags[INVISIBLE] = false;
1317 						}
1318 					}
1319 					if ( multiplayer == SERVER )
1320 					{
1321 						// update sprites for clients
1322 						if ( entity->skill[10] != entity->sprite )
1323 						{
1324 							entity->skill[10] = entity->sprite;
1325 							serverUpdateEntityBodypart(my, bodypart);
1326 						}
1327 						if ( entity->skill[11] != entity->flags[INVISIBLE] )
1328 						{
1329 							entity->skill[11] = entity->flags[INVISIBLE];
1330 							serverUpdateEntityBodypart(my, bodypart);
1331 						}
1332 						if ( entity->getUID() % (TICKS_PER_SECOND * 10) == ticks % (TICKS_PER_SECOND * 10) )
1333 						{
1334 							serverUpdateEntityBodypart(my, bodypart);
1335 						}
1336 					}
1337 				}
1338 				else
1339 				{
1340 					if ( entity->sprite <= 0 )
1341 					{
1342 						entity->flags[INVISIBLE] = true;
1343 					}
1344 				}
1345 				if ( weaponarm != nullptr )
1346 				{
1347 					my->handleHumanoidWeaponLimb(entity, weaponarm);
1348 				}
1349 				break;
1350 				// shield
1351 			case LIMB_HUMANOID_SHIELD:
1352 				if ( multiplayer != CLIENT )
1353 				{
1354 					if ( myStats->shield == nullptr )
1355 					{
1356 						entity->flags[INVISIBLE] = true;
1357 						entity->sprite = 0;
1358 					}
1359 					else
1360 					{
1361 						entity->flags[INVISIBLE] = false;
1362 						entity->sprite = itemModel(myStats->shield);
1363 						if ( itemTypeIsQuiver(myStats->shield->type) )
1364 						{
1365 							entity->handleQuiverThirdPersonModel(*myStats);
1366 						}
1367 					}
1368 					if ( myStats->EFFECTS[EFF_INVISIBLE] || wearingring ) //TODO: isInvisible()?
1369 					{
1370 						entity->flags[INVISIBLE] = true;
1371 					}
1372 					if ( multiplayer == SERVER )
1373 					{
1374 						// update sprites for clients
1375 						if ( entity->skill[10] != entity->sprite )
1376 						{
1377 							entity->skill[10] = entity->sprite;
1378 							serverUpdateEntityBodypart(my, bodypart);
1379 						}
1380 						if ( entity->skill[11] != entity->flags[INVISIBLE] )
1381 						{
1382 							entity->skill[11] = entity->flags[INVISIBLE];
1383 							serverUpdateEntityBodypart(my, bodypart);
1384 						}
1385 						if ( entity->getUID() % (TICKS_PER_SECOND * 10) == ticks % (TICKS_PER_SECOND * 10) )
1386 						{
1387 							serverUpdateEntityBodypart(my, bodypart);
1388 						}
1389 					}
1390 				}
1391 				else
1392 				{
1393 					if ( entity->sprite <= 0 )
1394 					{
1395 						entity->flags[INVISIBLE] = true;
1396 					}
1397 				}
1398 				entity->x -= 2.5 * cos(my->yaw + PI / 2) + .20 * cos(my->yaw);
1399 				entity->y -= 2.5 * sin(my->yaw + PI / 2) + .20 * sin(my->yaw);
1400 				entity->z += 2.5;
1401 				if ( entity->sprite == items[TOOL_TORCH].index )
1402 				{
1403 					entity2 = spawnFlame(entity, SPRITE_FLAME);
1404 					entity2->x += 2 * cos(my->yaw);
1405 					entity2->y += 2 * sin(my->yaw);
1406 					entity2->z -= 2;
1407 				}
1408 				else if ( entity->sprite == items[TOOL_CRYSTALSHARD].index )
1409 				{
1410 					entity2 = spawnFlame(entity, SPRITE_CRYSTALFLAME);
1411 					entity2->x += 2 * cos(my->yaw);
1412 					entity2->y += 2 * sin(my->yaw);
1413 					entity2->z -= 2;
1414 				}
1415 				else if ( entity->sprite == items[TOOL_LANTERN].index )
1416 				{
1417 					entity->z += 2;
1418 					entity2 = spawnFlame(entity, SPRITE_FLAME);
1419 					entity2->x += 2 * cos(my->yaw);
1420 					entity2->y += 2 * sin(my->yaw);
1421 					entity2->z += 1;
1422 				}
1423 				break;
1424 				// cloak
1425 			case LIMB_HUMANOID_CLOAK:
1426 				if ( multiplayer != CLIENT )
1427 				{
1428 					if ( myStats->cloak == nullptr || myStats->EFFECTS[EFF_INVISIBLE] || wearingring ) //TODO: isInvisible()?
1429 					{
1430 						entity->flags[INVISIBLE] = true;
1431 					}
1432 					else
1433 					{
1434 						entity->flags[INVISIBLE] = false;
1435 						entity->sprite = itemModel(myStats->cloak);
1436 					}
1437 					if ( multiplayer == SERVER )
1438 					{
1439 						// update sprites for clients
1440 						if ( entity->skill[10] != entity->sprite )
1441 						{
1442 							entity->skill[10] = entity->sprite;
1443 							serverUpdateEntityBodypart(my, bodypart);
1444 						}
1445 						if ( entity->skill[11] != entity->flags[INVISIBLE] )
1446 						{
1447 							entity->skill[11] = entity->flags[INVISIBLE];
1448 							serverUpdateEntityBodypart(my, bodypart);
1449 						}
1450 						if ( entity->getUID() % (TICKS_PER_SECOND * 10) == ticks % (TICKS_PER_SECOND * 10) )
1451 						{
1452 							serverUpdateEntityBodypart(my, bodypart);
1453 						}
1454 					}
1455 				}
1456 				else
1457 				{
1458 					if ( entity->sprite <= 0 )
1459 					{
1460 						entity->flags[INVISIBLE] = true;
1461 					}
1462 				}
1463 				entity->x -= cos(my->yaw);
1464 				entity->y -= sin(my->yaw);
1465 				entity->yaw += PI / 2;
1466 				break;
1467 			// helm
1468 			case LIMB_HUMANOID_HELMET:
1469 				helmet = entity;
1470 				entity->focalx = limbs[SHOPKEEPER][9][0]; // 0
1471 				entity->focaly = limbs[SHOPKEEPER][9][1]; // 0
1472 				entity->focalz = limbs[SHOPKEEPER][9][2]; // -1.75
1473 				entity->pitch = my->pitch;
1474 				entity->roll = 0;
1475 				if ( multiplayer != CLIENT )
1476 				{
1477 					entity->sprite = itemModel(myStats->helmet);
1478 					if ( myStats->helmet == nullptr || myStats->EFFECTS[EFF_INVISIBLE] || wearingring ) //TODO: isInvisible()?
1479 					{
1480 						entity->flags[INVISIBLE] = true;
1481 					}
1482 					else
1483 					{
1484 						entity->flags[INVISIBLE] = false;
1485 					}
1486 					if ( multiplayer == SERVER )
1487 					{
1488 						// update sprites for clients
1489 						if ( entity->skill[10] != entity->sprite )
1490 						{
1491 							entity->skill[10] = entity->sprite;
1492 							serverUpdateEntityBodypart(my, bodypart);
1493 						}
1494 						if ( entity->skill[11] != entity->flags[INVISIBLE] )
1495 						{
1496 							entity->skill[11] = entity->flags[INVISIBLE];
1497 							serverUpdateEntityBodypart(my, bodypart);
1498 						}
1499 						if ( entity->getUID() % (TICKS_PER_SECOND * 10) == ticks % (TICKS_PER_SECOND * 10) )
1500 						{
1501 							serverUpdateEntityBodypart(my, bodypart);
1502 						}
1503 					}
1504 				}
1505 				else
1506 				{
1507 					if ( entity->sprite <= 0 )
1508 					{
1509 						entity->flags[INVISIBLE] = true;
1510 					}
1511 				}
1512 				my->setHelmetLimbOffset(entity);
1513 				break;
1514 			// mask
1515 			case LIMB_HUMANOID_MASK:
1516 				entity->focalx = limbs[SHOPKEEPER][10][0]; // 0
1517 				entity->focaly = limbs[SHOPKEEPER][10][1]; // 0
1518 				entity->focalz = limbs[SHOPKEEPER][10][2]; // .5
1519 				entity->pitch = my->pitch;
1520 				entity->roll = PI / 2;
1521 				if ( multiplayer != CLIENT )
1522 				{
1523 					bool hasSteelHelm = false;
1524 					if ( myStats->helmet )
1525 					{
1526 						if ( myStats->helmet->type == STEEL_HELM
1527 							|| myStats->helmet->type == CRYSTAL_HELM
1528 							|| myStats->helmet->type == ARTIFACT_HELM )
1529 						{
1530 							hasSteelHelm = true;
1531 						}
1532 					}
1533 					if ( myStats->mask == nullptr || myStats->EFFECTS[EFF_INVISIBLE] || wearingring || hasSteelHelm ) //TODO: isInvisible()?
1534 					{
1535 						entity->flags[INVISIBLE] = true;
1536 					}
1537 					else
1538 					{
1539 						entity->flags[INVISIBLE] = false;
1540 					}
1541 					if ( myStats->mask != nullptr )
1542 					{
1543 						if ( myStats->mask->type == TOOL_GLASSES )
1544 						{
1545 							entity->sprite = 165; // GlassesWorn.vox
1546 						}
1547 						else
1548 						{
1549 							entity->sprite = itemModel(myStats->mask);
1550 						}
1551 					}
1552 					if ( multiplayer == SERVER )
1553 					{
1554 						// update sprites for clients
1555 						if ( entity->skill[10] != entity->sprite )
1556 						{
1557 							entity->skill[10] = entity->sprite;
1558 							serverUpdateEntityBodypart(my, bodypart);
1559 						}
1560 						if ( entity->skill[11] != entity->flags[INVISIBLE] )
1561 						{
1562 							entity->skill[11] = entity->flags[INVISIBLE];
1563 							serverUpdateEntityBodypart(my, bodypart);
1564 						}
1565 						if ( entity->getUID() % (TICKS_PER_SECOND * 10) == ticks % (TICKS_PER_SECOND * 10) )
1566 						{
1567 							serverUpdateEntityBodypart(my, bodypart);
1568 						}
1569 					}
1570 				}
1571 				else
1572 				{
1573 					if ( entity->sprite <= 0 )
1574 					{
1575 						entity->flags[INVISIBLE] = true;
1576 					}
1577 				}
1578 
1579 				if ( entity->sprite != 165 )
1580 				{
1581 					if ( entity->sprite == items[MASK_SHAMAN].index )
1582 					{
1583 						entity->roll = 0;
1584 						my->setHelmetLimbOffset(entity);
1585 						my->setHelmetLimbOffsetWithMask(helmet, entity);
1586 					}
1587 					else
1588 					{
1589 						entity->focalx = limbs[SHOPKEEPER][10][0] + .35; // .35
1590 						entity->focaly = limbs[SHOPKEEPER][10][1] - 2; // -2
1591 						entity->focalz = limbs[SHOPKEEPER][10][2]; // .5
1592 					}
1593 				}
1594 				else
1595 				{
1596 					entity->focalx = limbs[SHOPKEEPER][10][0] + .25; // .25
1597 					entity->focaly = limbs[SHOPKEEPER][10][1] - 2.25; // -2.25
1598 					entity->focalz = limbs[SHOPKEEPER][10][2]; // .5
1599 				}
1600 				break;
1601 		}
1602 	}
1603 	// rotate shield a bit
1604 	node_t* shieldNode = list_Node(&my->children, LIMB_HUMANOID_SHIELD);
1605 	if ( shieldNode )
1606 	{
1607 		Entity* shieldEntity = (Entity*)shieldNode->element;
1608 		if ( shieldEntity->sprite != items[TOOL_TORCH].index && shieldEntity->sprite != items[TOOL_LANTERN].index && shieldEntity->sprite != items[TOOL_CRYSTALSHARD].index )
1609 		{
1610 			shieldEntity->yaw -= PI / 6;
1611 		}
1612 	}
1613 	if ( MONSTER_ATTACK > 0 && MONSTER_ATTACK <= MONSTER_POSE_MAGIC_CAST3 )
1614 	{
1615 		MONSTER_ATTACKTIME++;
1616 	}
1617 	else if ( MONSTER_ATTACK == 0 )
1618 	{
1619 		MONSTER_ATTACKTIME = 0;
1620 	}
1621 	else
1622 	{
1623 		// do nothing, don't reset attacktime or increment it.
1624 	}
1625 }
1626 
shopkeeperMysteriousGenerateInventory(ItemType orb)1627 void shopkeeperMysteriousGenerateInventory(ItemType orb)
1628 {
1629 	/*std::vector<int> items;
1630 	if ( orb == ARTIFACT_ORB_GREEN )
1631 	{
1632 		items.push_back(QUIVER_CRYSTAL);
1633 		items.push_back(QUIVER_LIGHTWEIGHT);
1634 		items.push_back(QUIVER_HUNTING);
1635 		items.push_back(HEAVY_CROSSBOW);
1636 		items.push_back(BOOMERANG);
1637 		items.push_back(ARTIFACT_BOW);
1638 	}
1639 	else if ( orb == ARTIFACT_ORB_BLUE )
1640 	{
1641 		items.push_back(ARTIFACT_MACE);
1642 		items.push_back(ENCHANTED_FEATHER);
1643 	}
1644 	else if ( orb == ARTIFACT_ORB_RED )
1645 	{
1646 		items.push_back(ARTIFACT_AXE);
1647 		items.push_back(ARTIFACT_SWORD);
1648 		items.push_back(ARTIFACT_SPEAR);
1649 	}
1650 
1651 	if ( !items.empty() )
1652 	{
1653 		if ( shopkeeperMysteriousItems.find(orb) == shopkeeperMysteriousItems.end() )
1654 		{
1655 			shopkeeperMysteriousItems.insert(std::make_pair(orb, items));
1656 		}
1657 		else
1658 		{
1659 			shopkeeperMysteriousItems[orb] = items;
1660 		}
1661 	}*/
1662 }