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