1 /*-------------------------------------------------------------------------------
2 
3 	BARONY
4 	File: monster_gnome.cpp
5 	Desc: implements all of the gnome 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 "book.hpp"
20 #include "net.hpp"
21 #include "collision.hpp"
22 #include "player.hpp"
23 
initGnome(Entity * my,Stat * myStats)24 void initGnome(Entity* my, Stat* myStats)
25 {
26 	int c;
27 	node_t* node;
28 
29 	//Sprite 295 = Gnome head model
30 	my->initMonster(295);
31 
32 	if ( multiplayer != CLIENT )
33 	{
34 		MONSTER_SPOTSND = 220;
35 		MONSTER_SPOTVAR = 5;
36 		MONSTER_IDLESND = 217;
37 		MONSTER_IDLEVAR = 3;
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 			// apply random stat increases if set in stat_shared.cpp or editor
49 			setRandomMonsterStats(myStats);
50 
51 			// generate 6 items max, less if there are any forced items from boss variants
52 			int customItemsToGenerate = ITEM_CUSTOM_SLOT_LIMIT;
53 
54 			// boss variants
55 
56 			// random effects
57 			if ( rand() % 8 == 0 )
58 			{
59 				myStats->EFFECTS[EFF_ASLEEP] = true;
60 				myStats->EFFECTS_TIMERS[EFF_ASLEEP] = 1800 + rand() % 1800;
61 			}
62 
63 			// generates equipment and weapons if available from editor
64 			createMonsterEquipment(myStats);
65 
66 			// create any custom inventory items from editor if available
67 			createCustomInventory(myStats, customItemsToGenerate);
68 
69 			// count if any custom inventory items from editor
70 			int customItems = countCustomItems(myStats); //max limit of 6 custom items per entity.
71 
72 			// count any inventory items set to default in edtior
73 			int defaultItems = countDefaultItems(myStats);
74 
75 			my->setHardcoreStats(*myStats);
76 
77 			// generate the default inventory items for the monster, provided the editor sprite allowed enough default slots
78 			switch ( defaultItems )
79 			{
80 				case 6:
81 				case 5:
82 				case 4:
83 				case 3:
84 					if ( rand() % 50 == 0 )
85 					{
86 						if ( rand() % 2 == 0 )
87 						{
88 							newItem(ENCHANTED_FEATHER, WORN, 0, 1, (2 * (ENCHANTED_FEATHER_MAX_DURABILITY - 1)) / 4, false, &myStats->inventory);
89 						}
90 						else
91 						{
92 							newItem(READABLE_BOOK, EXCELLENT, 0, 1, getBook("Winny's Report"), false, &myStats->inventory);
93 						}
94 					}
95 				case 2:
96 					if ( rand() % 10 == 0 )
97 					{
98 						int i = 1 + rand() % 4;
99 						for ( c = 0; c < i; c++ )
100 						{
101 							newItem(static_cast<ItemType>(GEM_GARNET + rand() % 15), static_cast<Status>(1 + rand() % 4), 0, 1, rand(), false, &myStats->inventory);
102 						}
103 					}
104 				case 1:
105 					if ( rand() % 3 == 0 )
106 					{
107 						newItem(FOOD_FISH, EXCELLENT, 0, 1, rand(), false, &myStats->inventory);
108 					}
109 					break;
110 				default:
111 					break;
112 			}
113 
114 			//give shield
115 			if ( myStats->shield == nullptr && myStats->EDITOR_ITEMS[ITEM_SLOT_SHIELD] == 1 )
116 			{
117 				switch ( rand() % 10 )
118 				{
119 					case 0:
120 					case 1:
121 						myStats->shield = newItem(TOOL_LANTERN, EXCELLENT, -1 + rand() % 3, 1, rand(), false, nullptr);
122 						break;
123 					case 2:
124 					case 3:
125 					case 4:
126 					case 5:
127 					case 6:
128 						break;
129 					case 7:
130 					case 8:
131 					case 9:
132 						myStats->shield = newItem(WOODEN_SHIELD, static_cast<Status>(WORN + rand() % 2), -1 + rand() % 3, 1, rand(), false, nullptr);
133 						break;
134 				}
135 			}
136 
137 			//give weapon
138 			if ( myStats->weapon == nullptr && myStats->EDITOR_ITEMS[ITEM_SLOT_WEAPON] == 1 )
139 			{
140 				switch ( rand() % 10 )
141 				{
142 					case 0:
143 					case 1:
144 					case 2:
145 					case 3:
146 					case 4:
147 						myStats->weapon = newItem(TOOL_PICKAXE, EXCELLENT, -1 + rand() % 3, 1, rand(), false, nullptr);
148 						break;
149 					case 5:
150 					case 6:
151 					case 7:
152 					case 8:
153 					case 9:
154 						myStats->GOLD += 100;
155 						myStats->weapon = newItem(MAGICSTAFF_LIGHTNING, EXCELLENT, -1 + rand() % 3, 1, rand(), false, nullptr);
156 						break;
157 				}
158 			}
159 
160 			// give cloak
161 			if ( myStats->cloak == nullptr && myStats->EDITOR_ITEMS[ITEM_SLOT_CLOAK] == 1 )
162 			{
163 				switch ( rand() % 10 )
164 				{
165 					case 0:
166 					case 1:
167 					case 2:
168 					case 3:
169 					case 4:
170 					case 5:
171 						break;
172 					case 6:
173 					case 7:
174 					case 8:
175 					case 9:
176 						myStats->cloak = newItem(CLOAK, SERVICABLE, -1 + rand() % 3, 1, rand(), false, nullptr);
177 						break;
178 				}
179 			}
180 		}
181 	}
182 
183 	// torso
184 	Entity* entity = newEntity(296, 0, map.entities, nullptr); //Limb entity.
185 	entity->sizex = 4;
186 	entity->sizey = 4;
187 	entity->skill[2] = my->getUID();
188 	entity->flags[PASSABLE] = true;
189 	entity->flags[NOUPDATE] = true;
190 	entity->flags[USERFLAG2] = my->flags[USERFLAG2];
191 	entity->focalx = limbs[GNOME][1][0]; // 0
192 	entity->focaly = limbs[GNOME][1][1]; // 0
193 	entity->focalz = limbs[GNOME][1][2]; // 0
194 	entity->behavior = &actGnomeLimb;
195 	entity->parent = my->getUID();
196 	node = list_AddNodeLast(&my->children);
197 	node->element = entity;
198 	node->deconstructor = &emptyDeconstructor;
199 	node->size = sizeof(Entity*);
200 	my->bodyparts.push_back(entity);
201 
202 	// right leg
203 	entity = newEntity(297, 0, map.entities, nullptr); //Limb entity.
204 	entity->sizex = 4;
205 	entity->sizey = 4;
206 	entity->skill[2] = my->getUID();
207 	entity->flags[PASSABLE] = true;
208 	entity->flags[NOUPDATE] = true;
209 	entity->flags[USERFLAG2] = my->flags[USERFLAG2];
210 	entity->focalx = limbs[GNOME][2][0]; // .25
211 	entity->focaly = limbs[GNOME][2][1]; // 0
212 	entity->focalz = limbs[GNOME][2][2]; // 1.5
213 	entity->behavior = &actGnomeLimb;
214 	entity->parent = my->getUID();
215 	node = list_AddNodeLast(&my->children);
216 	node->element = entity;
217 	node->deconstructor = &emptyDeconstructor;
218 	node->size = sizeof(Entity*);
219 	my->bodyparts.push_back(entity);
220 
221 	// left leg
222 	entity = newEntity(298, 0, map.entities, nullptr); //Limb entity.
223 	entity->sizex = 4;
224 	entity->sizey = 4;
225 	entity->skill[2] = my->getUID();
226 	entity->flags[PASSABLE] = true;
227 	entity->flags[NOUPDATE] = true;
228 	entity->flags[USERFLAG2] = my->flags[USERFLAG2];
229 	entity->focalx = limbs[GNOME][3][0]; // .25
230 	entity->focaly = limbs[GNOME][3][1]; // 0
231 	entity->focalz = limbs[GNOME][3][2]; // 1.5
232 	entity->behavior = &actGnomeLimb;
233 	entity->parent = my->getUID();
234 	node = list_AddNodeLast(&my->children);
235 	node->element = entity;
236 	node->deconstructor = &emptyDeconstructor;
237 	node->size = sizeof(Entity*);
238 	my->bodyparts.push_back(entity);
239 
240 	// right arm
241 	entity = newEntity(299, 0, map.entities, nullptr); //Limb entity.
242 	entity->sizex = 4;
243 	entity->sizey = 4;
244 	entity->skill[2] = my->getUID();
245 	entity->flags[PASSABLE] = true;
246 	entity->flags[NOUPDATE] = true;
247 	entity->flags[USERFLAG2] = my->flags[USERFLAG2];
248 	entity->focalx = limbs[GNOME][4][0]; // 0
249 	entity->focaly = limbs[GNOME][4][1]; // 0
250 	entity->focalz = limbs[GNOME][4][2]; // 2
251 	entity->behavior = &actGnomeLimb;
252 	entity->parent = my->getUID();
253 	node = list_AddNodeLast(&my->children);
254 	node->element = entity;
255 	node->deconstructor = &emptyDeconstructor;
256 	node->size = sizeof(Entity*);
257 	my->bodyparts.push_back(entity);
258 
259 	// left arm
260 	entity = newEntity(301, 0, map.entities, nullptr); //Limb entity.
261 	entity->sizex = 4;
262 	entity->sizey = 4;
263 	entity->skill[2] = my->getUID();
264 	entity->flags[PASSABLE] = true;
265 	entity->flags[NOUPDATE] = true;
266 	entity->flags[USERFLAG2] = my->flags[USERFLAG2];
267 	entity->focalx = limbs[GNOME][5][0]; // 0
268 	entity->focaly = limbs[GNOME][5][1]; // 0
269 	entity->focalz = limbs[GNOME][5][2]; // 2
270 	entity->behavior = &actGnomeLimb;
271 	entity->parent = my->getUID();
272 	node = list_AddNodeLast(&my->children);
273 	node->element = entity;
274 	node->deconstructor = &emptyDeconstructor;
275 	node->size = sizeof(Entity*);
276 	my->bodyparts.push_back(entity);
277 
278 	// world weapon
279 	entity = newEntity(-1, 0, map.entities, nullptr); //Limb entity.
280 	entity->sizex = 4;
281 	entity->sizey = 4;
282 	entity->skill[2] = my->getUID();
283 	entity->flags[PASSABLE] = true;
284 	entity->flags[NOUPDATE] = true;
285 	entity->flags[INVISIBLE] = true;
286 	entity->flags[USERFLAG2] = my->flags[USERFLAG2];
287 	entity->focalx = limbs[GNOME][6][0]; // 2
288 	entity->focaly = limbs[GNOME][6][1]; // 0
289 	entity->focalz = limbs[GNOME][6][2]; // -.5
290 	entity->behavior = &actGnomeLimb;
291 	entity->parent = my->getUID();
292 	entity->pitch = .25;
293 	node = list_AddNodeLast(&my->children);
294 	node->element = entity;
295 	node->deconstructor = &emptyDeconstructor;
296 	node->size = sizeof(Entity*);
297 	my->bodyparts.push_back(entity);
298 
299 	// shield
300 	entity = newEntity(-1, 0, map.entities, nullptr); //Limb entity.
301 	entity->sizex = 4;
302 	entity->sizey = 4;
303 	entity->skill[2] = my->getUID();
304 	entity->flags[PASSABLE] = true;
305 	entity->flags[NOUPDATE] = true;
306 	entity->flags[INVISIBLE] = true;
307 	entity->flags[USERFLAG2] = my->flags[USERFLAG2];
308 	entity->focalx = limbs[GNOME][7][0]; // 0
309 	entity->focaly = limbs[GNOME][7][1]; // 0
310 	entity->focalz = limbs[GNOME][7][2]; // 1.5
311 	entity->behavior = &actGnomeLimb;
312 	entity->parent = my->getUID();
313 	node = list_AddNodeLast(&my->children);
314 	node->element = entity;
315 	node->deconstructor = &emptyDeconstructor;
316 	node->size = sizeof(Entity*);
317 	my->bodyparts.push_back(entity);
318 
319 	// cloak
320 	entity = newEntity(-1, 0, map.entities, nullptr); //Limb entity.
321 	entity->sizex = 4;
322 	entity->sizey = 4;
323 	entity->skill[2] = my->getUID();
324 	entity->scalex = 1.01;
325 	entity->scaley = 1.01;
326 	entity->scalez = 1.01;
327 	entity->flags[PASSABLE] = true;
328 	entity->flags[NOUPDATE] = true;
329 	entity->flags[INVISIBLE] = true;
330 	entity->flags[USERFLAG2] = my->flags[USERFLAG2];
331 	entity->focalx = limbs[GNOME][8][0]; // 0
332 	entity->focaly = limbs[GNOME][8][1]; // 0
333 	entity->focalz = limbs[GNOME][8][2]; // 4
334 	entity->behavior = &actGnomeLimb;
335 	entity->parent = my->getUID();
336 	node = list_AddNodeLast(&my->children);
337 	node->element = entity;
338 	node->deconstructor = &emptyDeconstructor;
339 	node->size = sizeof(Entity*);
340 	my->bodyparts.push_back(entity);
341 
342 	if ( multiplayer == CLIENT || MONSTER_INIT )
343 	{
344 		return;
345 	}
346 }
347 
actGnomeLimb(Entity * my)348 void actGnomeLimb(Entity* my)
349 {
350 	my->actMonsterLimb(true);
351 }
352 
gnomeDie(Entity * my)353 void gnomeDie(Entity* my)
354 {
355 	int c;
356 	for ( c = 0; c < 6; c++ )
357 	{
358 		Entity* entity = spawnGib(my);
359 		if ( entity )
360 		{
361 			serverSpawnGibForClient(entity);
362 		}
363 	}
364 
365 	my->spawnBlood();
366 
367 	my->removeMonsterDeathNodes();
368 
369 	playSoundEntity(my, 225 + rand() % 4, 128);
370 	list_RemoveNode(my->mynode);
371 	return;
372 }
373 
374 #define GNOMEWALKSPEED .13
375 
gnomeMoveBodyparts(Entity * my,Stat * myStats,double dist)376 void gnomeMoveBodyparts(Entity* my, Stat* myStats, double dist)
377 {
378 	node_t* node;
379 	Entity* entity = nullptr, *entity2 = nullptr;
380 	Entity* rightbody = nullptr;
381 	Entity* weaponarm = nullptr;
382 	int bodypart;
383 	bool wearingring = false;
384 
385 	// set invisibility //TODO: isInvisible()?
386 	if ( multiplayer != CLIENT )
387 	{
388 		if ( myStats->ring != nullptr )
389 			if ( myStats->ring->type == RING_INVISIBILITY )
390 			{
391 				wearingring = true;
392 			}
393 		if ( myStats->cloak != nullptr )
394 			if ( myStats->cloak->type == CLOAK_INVISIBILITY )
395 			{
396 				wearingring = true;
397 			}
398 		if ( myStats->EFFECTS[EFF_INVISIBLE] == true || wearingring == true )
399 		{
400 			my->flags[INVISIBLE] = true;
401 			my->flags[BLOCKSIGHT] = false;
402 			bodypart = 0;
403 			for (node = my->children.first; node != nullptr; node = node->next)
404 			{
405 				if ( bodypart < LIMB_HUMANOID_TORSO )
406 				{
407 					++bodypart;
408 					continue;
409 				}
410 				if ( bodypart >= LIMB_HUMANOID_WEAPON )
411 				{
412 					break;
413 				}
414 				entity = (Entity*)node->element;
415 				if ( !entity->flags[INVISIBLE] )
416 				{
417 					entity->flags[INVISIBLE] = true;
418 					serverUpdateEntityBodypart(my, bodypart);
419 				}
420 				bodypart++;
421 			}
422 		}
423 		else
424 		{
425 			my->flags[INVISIBLE] = false;
426 			my->flags[BLOCKSIGHT] = true;
427 			bodypart = 0;
428 			for (node = my->children.first; node != nullptr; node = node->next)
429 			{
430 				if ( bodypart < 2 )
431 				{
432 					bodypart++;
433 					continue;
434 				}
435 				if ( bodypart >= 7 )
436 				{
437 					break;
438 				}
439 				entity = (Entity*)node->element;
440 				if ( entity->flags[INVISIBLE] )
441 				{
442 					entity->flags[INVISIBLE] = false;
443 					serverUpdateEntityBodypart(my, bodypart);
444 					serverUpdateEntityFlag(my, INVISIBLE);
445 				}
446 				bodypart++;
447 			}
448 		}
449 
450 		// sleeping
451 		if ( myStats->EFFECTS[EFF_ASLEEP] )
452 		{
453 			my->z = 4;
454 			my->pitch = PI / 4;
455 		}
456 		else
457 		{
458 			my->z = 2.25;
459 			my->pitch = 0;
460 		}
461 	}
462 
463 	Entity* shieldarm = nullptr;
464 
465 	//Move bodyparts
466 	for (bodypart = 0, node = my->children.first; node != nullptr; node = node->next, bodypart++)
467 	{
468 		if ( bodypart < LIMB_HUMANOID_TORSO )
469 		{
470 			continue;
471 		}
472 		entity = (Entity*)node->element;
473 		entity->x = my->x;
474 		entity->y = my->y;
475 		entity->z = my->z;
476 		if ( MONSTER_ATTACK == MONSTER_POSE_MAGIC_WINDUP1 && bodypart == LIMB_HUMANOID_RIGHTARM )
477 		{
478 			// don't let the creatures's yaw move the casting arm
479 		}
480 		else
481 		{
482 			entity->yaw = my->yaw;
483 		}
484 		if ( bodypart == LIMB_HUMANOID_RIGHTLEG || bodypart == LIMB_HUMANOID_LEFTARM )
485 		{
486 			my->humanoidAnimateWalk(entity, node, bodypart, GNOMEWALKSPEED, dist, 0.4);
487 		}
488 		else if ( bodypart == LIMB_HUMANOID_LEFTLEG || bodypart == LIMB_HUMANOID_RIGHTARM || bodypart == LIMB_HUMANOID_CLOAK )
489 		{
490 			// left leg, right arm, cloak.
491 			if ( bodypart == LIMB_HUMANOID_RIGHTARM )
492 			{
493 				weaponarm = entity;
494 				if ( my->monsterAttack > 0 )
495 				{
496 					my->handleWeaponArmAttack(entity);
497 				}
498 			}
499 			else if ( bodypart == LIMB_HUMANOID_CLOAK )
500 			{
501 				entity->pitch = entity->fskill[0];
502 			}
503 
504 			my->humanoidAnimateWalk(entity, node, bodypart, GNOMEWALKSPEED, dist, 0.4);
505 
506 			if ( bodypart == LIMB_HUMANOID_CLOAK )
507 			{
508 				entity->fskill[0] = entity->pitch;
509 				entity->roll = my->roll - fabs(entity->pitch) / 2;
510 				entity->pitch = 0;
511 			}
512 		}
513 		switch ( bodypart )
514 		{
515 			// torso
516 			case LIMB_HUMANOID_TORSO:
517 				entity->x -= .25 * cos(my->yaw);
518 				entity->y -= .25 * sin(my->yaw);
519 				entity->z += 1.25;
520 				break;
521 			// right leg
522 			case LIMB_HUMANOID_RIGHTLEG:
523 				if ( multiplayer != CLIENT )
524 				{
525 					if ( myStats->shoes == nullptr )
526 					{
527 						entity->sprite = 297;
528 					}
529 					else
530 					{
531 						my->setBootSprite(entity, SPRITE_BOOT_RIGHT_OFFSET);
532 					}
533 					if ( multiplayer == SERVER )
534 					{
535 						// update sprites for clients
536 						if ( entity->skill[10] != entity->sprite )
537 						{
538 							entity->skill[10] = entity->sprite;
539 							serverUpdateEntityBodypart(my, bodypart);
540 						}
541 						if ( entity->getUID() % (TICKS_PER_SECOND * 10) == ticks % (TICKS_PER_SECOND * 10) )
542 						{
543 							serverUpdateEntityBodypart(my, bodypart);
544 						}
545 					}
546 				}
547 				entity->x += 1.25 * cos(my->yaw + PI / 2);
548 				entity->y += 1.25 * sin(my->yaw + PI / 2);
549 				entity->z += 2.75;
550 				if ( my->z >= 3.9 && my->z <= 4.1 )
551 				{
552 					entity->yaw += PI / 8;
553 					entity->pitch = -PI / 2;
554 				}
555 				break;
556 			// left leg
557 			case LIMB_HUMANOID_LEFTLEG:
558 				if ( multiplayer != CLIENT )
559 				{
560 					if ( myStats->shoes == nullptr )
561 					{
562 						entity->sprite = 298;
563 					}
564 					else
565 					{
566 						my->setBootSprite(entity, SPRITE_BOOT_LEFT_OFFSET);
567 					}
568 					if ( multiplayer == SERVER )
569 					{
570 						// update sprites for clients
571 						if ( entity->skill[10] != entity->sprite )
572 						{
573 							entity->skill[10] = entity->sprite;
574 							serverUpdateEntityBodypart(my, bodypart);
575 						}
576 						if ( entity->getUID() % (TICKS_PER_SECOND * 10) == ticks % (TICKS_PER_SECOND * 10) )
577 						{
578 							serverUpdateEntityBodypart(my, bodypart);
579 						}
580 					}
581 				}
582 				entity->x -= 1.25 * cos(my->yaw + PI / 2);
583 				entity->y -= 1.25 * sin(my->yaw + PI / 2);
584 				entity->z += 2.75;
585 				if ( my->z >= 3.9 && my->z <= 4.1 )
586 				{
587 					entity->yaw -= PI / 8;
588 					entity->pitch = -PI / 2;
589 				}
590 				break;
591 			// right arm
592 			case LIMB_HUMANOID_RIGHTARM:
593 			{
594 				;
595 				node_t* weaponNode = list_Node(&my->children, 7);
596 				if ( weaponNode )
597 				{
598 					Entity* weapon = (Entity*)weaponNode->element;
599 					if ( my->monsterArmbended || (weapon->flags[INVISIBLE] && my->monsterState == MONSTER_STATE_WAIT) )
600 					{
601 						entity->focalx = limbs[GNOME][4][0]; // 0
602 						entity->focaly = limbs[GNOME][4][1]; // 0
603 						entity->focalz = limbs[GNOME][4][2]; // 2
604 						entity->sprite = 299;
605 					}
606 					else
607 					{
608 						entity->focalx = limbs[GNOME][4][0] + 1; // 1
609 						entity->focaly = limbs[GNOME][4][1]; // 0
610 						entity->focalz = limbs[GNOME][4][2] - 1; // 1
611 						entity->sprite = 300;
612 					}
613 				}
614 				entity->x += 2.5 * cos(my->yaw + PI / 2) - .75 * cos(my->yaw);
615 				entity->y += 2.5 * sin(my->yaw + PI / 2) - .75 * sin(my->yaw);
616 				entity->z -= .25;
617 				entity->yaw += MONSTER_WEAPONYAW;
618 				if ( my->z >= 3.9 && my->z <= 4.1 )
619 				{
620 					entity->pitch = 0;
621 				}
622 				break;
623 			// left arm
624 			}
625 			case LIMB_HUMANOID_LEFTARM:
626 			{
627 				shieldarm = entity;
628 				node_t* shieldNode = list_Node(&my->children, 8);
629 				if ( shieldNode )
630 				{
631 					Entity* shield = (Entity*)shieldNode->element;
632 					if ( shield->flags[INVISIBLE] && my->monsterState == MONSTER_STATE_WAIT )
633 					{
634 						entity->focalx = limbs[GNOME][5][0]; // 0
635 						entity->focaly = limbs[GNOME][5][1]; // 0
636 						entity->focalz = limbs[GNOME][5][2]; // 2
637 						entity->sprite = 301;
638 					}
639 					else
640 					{
641 						entity->focalx = limbs[GNOME][5][0] + 1; // 1
642 						entity->focaly = limbs[GNOME][5][1]; // 0
643 						entity->focalz = limbs[GNOME][5][2] - 1; // 1
644 						entity->sprite = 302;
645 					}
646 				}
647 				entity->x -= 2.5 * cos(my->yaw + PI / 2) + .75 * cos(my->yaw);
648 				entity->y -= 2.5 * sin(my->yaw + PI / 2) + .75 * sin(my->yaw);
649 				entity->z -= .25;
650 				if ( my->z >= 3.9 && my->z <= 4.1 )
651 				{
652 					entity->pitch = 0;
653 				}
654 				if ( my->monsterDefend && my->monsterAttack == 0 )
655 				{
656 					MONSTER_SHIELDYAW = PI / 5;
657 				}
658 				else
659 				{
660 					MONSTER_SHIELDYAW = 0;
661 				}
662 				entity->yaw += MONSTER_SHIELDYAW;
663 				break;
664 			}
665 			// weapon
666 			case LIMB_HUMANOID_WEAPON:
667 				if ( multiplayer != CLIENT )
668 				{
669 					if ( myStats->weapon == nullptr || myStats->EFFECTS[EFF_INVISIBLE] || wearingring ) //TODO: isInvisible()?
670 					{
671 						entity->flags[INVISIBLE] = true;
672 					}
673 					else
674 					{
675 						entity->sprite = itemModel(myStats->weapon);
676 						if ( itemCategory(myStats->weapon) == SPELLBOOK )
677 						{
678 							entity->flags[INVISIBLE] = true;
679 						}
680 						else
681 						{
682 							entity->flags[INVISIBLE] = false;
683 						}
684 					}
685 					if ( multiplayer == SERVER )
686 					{
687 						// update sprites for clients
688 						if ( entity->skill[10] != entity->sprite )
689 						{
690 							entity->skill[10] = entity->sprite;
691 							serverUpdateEntityBodypart(my, bodypart);
692 						}
693 						if ( entity->skill[11] != entity->flags[INVISIBLE] )
694 						{
695 							entity->skill[11] = entity->flags[INVISIBLE];
696 							serverUpdateEntityBodypart(my, bodypart);
697 						}
698 						if ( entity->getUID() % (TICKS_PER_SECOND * 10) == ticks % (TICKS_PER_SECOND * 10) )
699 						{
700 							serverUpdateEntityBodypart(my, bodypart);
701 						}
702 					}
703 				}
704 				else
705 				{
706 					if ( entity->sprite <= 0 )
707 					{
708 						entity->flags[INVISIBLE] = true;
709 					}
710 				}
711 				if ( weaponarm != nullptr )
712 				{
713 					my->handleHumanoidWeaponLimb(entity, weaponarm);
714 				}
715 				break;
716 			// shield
717 			case LIMB_HUMANOID_SHIELD:
718 				if ( multiplayer != CLIENT )
719 				{
720 					if ( myStats->shield == nullptr )
721 					{
722 						entity->flags[INVISIBLE] = true;
723 						entity->sprite = 0;
724 					}
725 					else
726 					{
727 						entity->flags[INVISIBLE] = false;
728 						entity->sprite = itemModel(myStats->shield);
729 						if ( itemTypeIsQuiver(myStats->shield->type) )
730 						{
731 							entity->handleQuiverThirdPersonModel(*myStats);
732 						}
733 					}
734 					if ( myStats->EFFECTS[EFF_INVISIBLE] || wearingring ) //TODO: isInvisible()?
735 					{
736 						entity->flags[INVISIBLE] = true;
737 					}
738 					if ( multiplayer == SERVER )
739 					{
740 						// update sprites for clients
741 						if ( entity->skill[10] != entity->sprite )
742 						{
743 							entity->skill[10] = entity->sprite;
744 							serverUpdateEntityBodypart(my, bodypart);
745 						}
746 						if ( entity->skill[11] != entity->flags[INVISIBLE] )
747 						{
748 							entity->skill[11] = entity->flags[INVISIBLE];
749 							serverUpdateEntityBodypart(my, bodypart);
750 						}
751 						if ( entity->getUID() % (TICKS_PER_SECOND * 10) == ticks % (TICKS_PER_SECOND * 10) )
752 						{
753 							serverUpdateEntityBodypart(my, bodypart);
754 						}
755 					}
756 				}
757 				else
758 				{
759 					if ( entity->sprite <= 0 )
760 					{
761 						entity->flags[INVISIBLE] = true;
762 					}
763 				}
764 				entity->x -= 2.5 * cos(my->yaw + PI / 2) + .20 * cos(my->yaw);
765 				entity->y -= 2.5 * sin(my->yaw + PI / 2) + .20 * sin(my->yaw);
766 				entity->z += 1;
767 				entity->yaw = shieldarm->yaw;
768 				entity->roll = 0;
769 				entity->pitch = 0;
770 				if ( entity->sprite == items[TOOL_TORCH].index )
771 				{
772 					entity2 = spawnFlame(entity, SPRITE_FLAME);
773 					entity2->x += 2 * cos(entity->yaw);
774 					entity2->y += 2 * sin(entity->yaw);
775 					entity2->z -= 2;
776 				}
777 				else if ( entity->sprite == items[TOOL_CRYSTALSHARD].index )
778 				{
779 					entity2 = spawnFlame(entity, SPRITE_CRYSTALFLAME);
780 					entity2->x += 2 * cos(entity->yaw);
781 					entity2->y += 2 * sin(entity->yaw);
782 					entity2->z -= 2;
783 				}
784 				else if ( entity->sprite == items[TOOL_LANTERN].index )
785 				{
786 					entity->z += 2;
787 					entity2 = spawnFlame(entity, SPRITE_FLAME);
788 					entity2->x += 2 * cos(entity->yaw);
789 					entity2->y += 2 * sin(entity->yaw);
790 					entity2->z += 1;
791 				}
792 				if ( MONSTER_SHIELDYAW > PI / 32 )
793 				{
794 					if ( entity->sprite != items[TOOL_TORCH].index && entity->sprite != items[TOOL_LANTERN].index && entity->sprite != items[TOOL_CRYSTALSHARD].index )
795 					{
796 						// shield, so rotate a little.
797 						entity->roll += PI / 64;
798 					}
799 					else
800 					{
801 						entity->x += 0.25 * cos(my->yaw);
802 						entity->y += 0.25 * sin(my->yaw);
803 						entity->pitch += PI / 16;
804 						if ( entity2 )
805 						{
806 							entity2->x += 0.75 * cos(shieldarm->yaw);
807 							entity2->y += 0.75 * sin(shieldarm->yaw);
808 						}
809 					}
810 				}
811 				break;
812 			// cloak
813 			case LIMB_HUMANOID_CLOAK:
814 				if ( multiplayer != CLIENT )
815 				{
816 					if ( myStats->cloak == nullptr || myStats->EFFECTS[EFF_INVISIBLE] || wearingring ) //TODO: isInvisible()?
817 					{
818 						entity->flags[INVISIBLE] = true;
819 					}
820 					else
821 					{
822 						entity->flags[INVISIBLE] = false;
823 						entity->sprite = itemModel(myStats->cloak);
824 					}
825 					if ( multiplayer == SERVER )
826 					{
827 						// update sprites for clients
828 						if ( entity->skill[10] != entity->sprite )
829 						{
830 							entity->skill[10] = entity->sprite;
831 							serverUpdateEntityBodypart(my, bodypart);
832 						}
833 						if ( entity->skill[11] != entity->flags[INVISIBLE] )
834 						{
835 							entity->skill[11] = entity->flags[INVISIBLE];
836 							serverUpdateEntityBodypart(my, bodypart);
837 						}
838 						if ( entity->getUID() % (TICKS_PER_SECOND * 10) == ticks % (TICKS_PER_SECOND * 10) )
839 						{
840 							serverUpdateEntityBodypart(my, bodypart);
841 						}
842 					}
843 				}
844 				else
845 				{
846 					if ( entity->sprite <= 0 )
847 					{
848 						entity->flags[INVISIBLE] = true;
849 					}
850 				}
851 				entity->x -= cos(my->yaw) * 1.5;
852 				entity->y -= sin(my->yaw) * 1.5;
853 				entity->yaw += PI / 2;
854 				break;
855 		}
856 	}
857 	// rotate shield a bit
858 	node_t* shieldNode = list_Node(&my->children, 8);
859 	if ( shieldNode )
860 	{
861 		Entity* shieldEntity = (Entity*)shieldNode->element;
862 		if ( shieldEntity->sprite != items[TOOL_TORCH].index && shieldEntity->sprite != items[TOOL_LANTERN].index && shieldEntity->sprite != items[TOOL_CRYSTALSHARD].index )
863 		{
864 			shieldEntity->yaw -= PI / 6;
865 		}
866 	}
867 	if ( MONSTER_ATTACK > 0 && MONSTER_ATTACK <= MONSTER_POSE_MAGIC_CAST3 )
868 	{
869 		MONSTER_ATTACKTIME++;
870 	}
871 	else if ( MONSTER_ATTACK == 0 )
872 	{
873 		MONSTER_ATTACKTIME = 0;
874 	}
875 	else
876 	{
877 		// do nothing, don't reset attacktime or increment it.
878 	}
879 }
880