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