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