1 /*-------------------------------------------------------------------------------
2 
3 	BARONY
4 	File: monster_kobold.cpp
5 	Desc: implements all of the kobold 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 #include "magic/magic.hpp"
24 
initKobold(Entity * my,Stat * myStats)25 void initKobold(Entity* my, Stat* myStats)
26 {
27 	node_t* node;
28 
29 	//Sprite 421 = Kobold head model
30 	my->initMonster(421);
31 
32 	if ( multiplayer != CLIENT )
33 	{
34 		MONSTER_SPOTSND = 302;
35 		MONSTER_SPOTVAR = 2;
36 		MONSTER_IDLESND = 295;
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 			// 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 			int cultist = 0;
76 
77 			if ( !strncmp(myStats->name, "kobold cultist", 14) )
78 			{
79 				cultist = 1 + rand() % 2;
80 			}
81 
82 			my->setHardcoreStats(*myStats);
83 
84 			//give weapon
85 			if ( myStats->weapon == nullptr && myStats->EDITOR_ITEMS[ITEM_SLOT_WEAPON] == 1 )
86 			{
87 				if ( cultist > 0 )
88 				{
89 					if ( cultist == 1 )
90 					{
91 						// mage
92 						switch ( rand() % 5 )
93 						{
94 							case 0:
95 								myStats->weapon = newItem(SPELLBOOK_COLD, static_cast<Status>(WORN + rand() % 3), -1 + rand() % 3, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, nullptr);
96 								break;
97 							case 1:
98 								myStats->weapon = newItem(SPELLBOOK_FIREBALL, static_cast<Status>(WORN + rand() % 3), -1 + rand() % 3, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, nullptr);
99 								break;
100 							case 2:
101 								myStats->weapon = newItem(SPELLBOOK_BLEED, static_cast<Status>(WORN + rand() % 3), -1 + rand() % 3, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, nullptr);
102 								break;
103 							case 3:
104 								newItem(SPELLBOOK_STONEBLOOD, static_cast<Status>(WORN + rand() % 3), -1 + rand() % 3, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, &myStats->inventory);
105 								break;
106 							case 4:
107 								myStats->weapon = newItem(MAGICSTAFF_BLEED, static_cast<Status>(WORN + rand() % 3), -1 + rand() % 3, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, nullptr);
108 								break;
109 						}
110 					}
111 					else
112 					{
113 						// ranged
114 						switch ( rand() % 5 )
115 						{
116 							case 0:
117 							case 1:
118 								myStats->weapon = newItem(CROSSBOW, static_cast<Status>(WORN + rand() % 3), -1 + rand() % 3, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, nullptr);
119 								break;
120 							case 2:
121 								myStats->weapon = newItem(CROSSBOW, static_cast<Status>(WORN + rand() % 3), -1 + rand() % 3, 1, rand(), false, nullptr);
122 								break;
123 							case 3:
124 							case 4:
125 								myStats->weapon = newItem(SHORTBOW, static_cast<Status>(WORN + rand() % 3), -1 + rand() % 3, 1, rand(), false, nullptr);
126 								break;
127 						}
128 					}
129 				}
130 				else
131 				{
132 					switch ( rand() % 10 )
133 					{
134 						case 0:
135 						case 1:
136 						case 2:
137 							myStats->weapon = newItem(STEEL_SWORD, static_cast<Status>(WORN + rand() % 3), -1 + rand() % 3, 1, rand(), false, nullptr);
138 							break;
139 						case 3:
140 						case 4:
141 							myStats->weapon = newItem(STEEL_HALBERD, static_cast<Status>(WORN + rand() % 3), -1 + rand() % 3, 1, rand(), false, nullptr);
142 							break;
143 						case 5:
144 						case 6:
145 						case 7:
146 						case 8:
147 							myStats->weapon = newItem(CROSSBOW, static_cast<Status>(WORN + rand() % 3), -1 + rand() % 3, 1, rand(), false, nullptr);
148 							break;
149 						case 9:
150 							myStats->weapon = newItem(IRON_AXE, static_cast<Status>(DECREPIT + rand() % 4), -2 + rand() % 5, 1, rand(), false, nullptr);
151 							break;
152 					}
153 				}
154 			}
155 
156 			// generate the default inventory items for the monster, provided the editor sprite allowed enough default slots
157 			switch ( defaultItems )
158 			{
159 				case 6:
160 				case 5:
161 				case 4:
162 				case 3:
163 				case 2:
164 					if ( rand() % 20 == 0 )
165 					{
166 						newItem(ENCHANTED_FEATHER, SERVICABLE, 0, 1, (2 * (ENCHANTED_FEATHER_MAX_DURABILITY - 1)) / 4, false, &myStats->inventory);
167 					}
168 					else if ( rand() % 5 == 0 ) // 20% chance
169 					{
170 						if ( rand() % 2 )
171 						{
172 							newItem(TOOL_TINOPENER, WORN, -1 + rand() % 3, 1, rand(), false, &myStats->inventory);
173 						}
174 						else
175 						{
176 							newItem(TOOL_TOWEL, WORN, -1 + rand() % 3, 1, rand(), false, &myStats->inventory);
177 						}
178 					}
179 				case 1:
180 					if ( my->hasRangedWeapon() )
181 					{
182 						if ( rand() % 5 > 0 ) // 80% chance
183 						{
184 							newItem(SPELLBOOK_SLOW, DECREPIT, 0, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, &myStats->inventory);
185 						}
186 					}
187 					break;
188 				default:
189 					break;
190 			}
191 
192 			//give shield
193 			if ( myStats->shield == nullptr && myStats->EDITOR_ITEMS[ITEM_SLOT_SHIELD] == 1 )
194 			{
195 				if ( myStats->weapon && isRangedWeapon(*myStats->weapon) )
196 				{
197 					my->monsterGenerateQuiverItem(myStats);
198 				}
199 				else
200 				{
201 					if ( cultist > 0 )
202 					{
203 						if ( cultist == 1 )
204 						{
205 							myStats->shield = newItem(TOOL_CRYSTALSHARD, EXCELLENT, -1 + rand() % 3, 1, rand(), false, nullptr);
206 						}
207 						else
208 						{
209 							myStats->shield = newItem(TOOL_LANTERN, EXCELLENT, -1 + rand() % 3, 1, rand(), false, nullptr);
210 						}
211 					}
212 					else
213 					{
214 						switch ( rand() % 10 )
215 						{
216 							case 0:
217 							case 1:
218 								myStats->shield = newItem(IRON_SHIELD, static_cast<Status>(WORN + rand() % 2), -2 + rand() % 5, 1, rand(), false, nullptr);
219 								break;
220 							case 2:
221 							case 3:
222 							case 4:
223 							case 5:
224 								myStats->shield = newItem(STEEL_SHIELD, static_cast<Status>(DECREPIT + rand() % 4), -1 + rand() % 3, 1, rand(), false, nullptr);
225 								break;
226 							case 6:
227 							case 7:
228 								myStats->shield = newItem(TOOL_LANTERN, EXCELLENT, -1 + rand() % 3, 1, rand(), false, nullptr);
229 								break;
230 							case 8:
231 								myStats->shield = newItem(TOOL_CRYSTALSHARD, SERVICABLE, -1 + rand() % 3, 1, rand(), false, nullptr);
232 								break;
233 							case 9:
234 								// nothing
235 								break;
236 						}
237 					}
238 				}
239 			}
240 
241 			// give cloak
242 			if ( myStats->cloak == nullptr && myStats->EDITOR_ITEMS[ITEM_SLOT_CLOAK] == 1 )
243 			{
244 				if ( cultist > 0 )
245 				{
246 					myStats->cloak = newItem(CLOAK, static_cast<Status>(WORN + rand() % 3), -1 + rand() % 3, 1, cultist - 1, false, nullptr);
247 				}
248 				else
249 				{
250 					switch ( rand() % 10 )
251 					{
252 						case 0:
253 						case 1:
254 						case 2:
255 						case 3:
256 						case 4:
257 							break;
258 						case 5:
259 						case 6:
260 						case 7:
261 						case 8:
262 						case 9:
263 							myStats->cloak = newItem(CLOAK, static_cast<Status>(WORN + rand() % 3), -1 + rand() % 3, 1, rand(), false, nullptr);
264 							break;
265 					}
266 				}
267 			}
268 
269 			// give helm
270 			if ( cultist > 0 && myStats->helmet == nullptr && myStats->EDITOR_ITEMS[ITEM_SLOT_HELM] == 1 )
271 			{
272 				myStats->helmet = newItem(HAT_HOOD, static_cast<Status>(WORN + rand() % 3), -1 + rand() % 3, 1, cultist - 1, false, nullptr);
273 			}
274 		}
275 	}
276 
277 	// torso
278 	Entity* entity = newEntity(422, 0, map.entities, nullptr); //Limb entity.
279 	entity->sizex = 4;
280 	entity->sizey = 4;
281 	entity->skill[2] = my->getUID();
282 	entity->flags[PASSABLE] = true;
283 	entity->flags[NOUPDATE] = true;
284 	entity->flags[USERFLAG2] = my->flags[USERFLAG2];
285 	entity->focalx = limbs[KOBOLD][1][0]; // 0
286 	entity->focaly = limbs[KOBOLD][1][1]; // 0
287 	entity->focalz = limbs[KOBOLD][1][2]; // 0
288 	entity->behavior = &actKoboldLimb;
289 	entity->parent = my->getUID();
290 	node = list_AddNodeLast(&my->children);
291 	node->element = entity;
292 	node->deconstructor = &emptyDeconstructor;
293 	node->size = sizeof(Entity*);
294 	my->bodyparts.push_back(entity);
295 
296 	// right leg
297 	entity = newEntity(423, 0, map.entities, nullptr); //Limb entity.
298 	entity->sizex = 4;
299 	entity->sizey = 4;
300 	entity->skill[2] = my->getUID();
301 	entity->flags[PASSABLE] = true;
302 	entity->flags[NOUPDATE] = true;
303 	entity->flags[USERFLAG2] = my->flags[USERFLAG2];
304 	entity->focalx = limbs[KOBOLD][2][0]; // .25
305 	entity->focaly = limbs[KOBOLD][2][1]; // 0
306 	entity->focalz = limbs[KOBOLD][2][2]; // 1.5
307 	entity->behavior = &actKoboldLimb;
308 	entity->parent = my->getUID();
309 	node = list_AddNodeLast(&my->children);
310 	node->element = entity;
311 	node->deconstructor = &emptyDeconstructor;
312 	node->size = sizeof(Entity*);
313 	my->bodyparts.push_back(entity);
314 
315 	// left leg
316 	entity = newEntity(424, 0, map.entities, nullptr); //Limb entity.
317 	entity->sizex = 4;
318 	entity->sizey = 4;
319 	entity->skill[2] = my->getUID();
320 	entity->flags[PASSABLE] = true;
321 	entity->flags[NOUPDATE] = true;
322 	entity->flags[USERFLAG2] = my->flags[USERFLAG2];
323 	entity->focalx = limbs[KOBOLD][3][0]; // .25
324 	entity->focaly = limbs[KOBOLD][3][1]; // 0
325 	entity->focalz = limbs[KOBOLD][3][2]; // 1.5
326 	entity->behavior = &actKoboldLimb;
327 	entity->parent = my->getUID();
328 	node = list_AddNodeLast(&my->children);
329 	node->element = entity;
330 	node->deconstructor = &emptyDeconstructor;
331 	node->size = sizeof(Entity*);
332 	my->bodyparts.push_back(entity);
333 
334 	// right arm
335 	entity = newEntity(425, 0, map.entities, nullptr); //Limb entity.
336 	entity->sizex = 4;
337 	entity->sizey = 4;
338 	entity->skill[2] = my->getUID();
339 	entity->flags[PASSABLE] = true;
340 	entity->flags[NOUPDATE] = true;
341 	entity->flags[USERFLAG2] = my->flags[USERFLAG2];
342 	entity->focalx = limbs[KOBOLD][4][0]; // 0
343 	entity->focaly = limbs[KOBOLD][4][1]; // 0
344 	entity->focalz = limbs[KOBOLD][4][2]; // 2
345 	entity->behavior = &actKoboldLimb;
346 	entity->parent = my->getUID();
347 	node = list_AddNodeLast(&my->children);
348 	node->element = entity;
349 	node->deconstructor = &emptyDeconstructor;
350 	node->size = sizeof(Entity*);
351 	my->bodyparts.push_back(entity);
352 
353 	// left arm
354 	entity = newEntity(427, 0, map.entities, nullptr); //Limb entity.
355 	entity->sizex = 4;
356 	entity->sizey = 4;
357 	entity->skill[2] = my->getUID();
358 	entity->flags[PASSABLE] = true;
359 	entity->flags[NOUPDATE] = true;
360 	entity->flags[USERFLAG2] = my->flags[USERFLAG2];
361 	entity->focalx = limbs[KOBOLD][5][0]; // 0
362 	entity->focaly = limbs[KOBOLD][5][1]; // 0
363 	entity->focalz = limbs[KOBOLD][5][2]; // 2
364 	entity->behavior = &actKoboldLimb;
365 	entity->parent = my->getUID();
366 	node = list_AddNodeLast(&my->children);
367 	node->element = entity;
368 	node->deconstructor = &emptyDeconstructor;
369 	node->size = sizeof(Entity*);
370 	my->bodyparts.push_back(entity);
371 
372 	// world weapon
373 	entity = newEntity(-1, 0, map.entities, nullptr); //Limb entity.
374 	entity->sizex = 4;
375 	entity->sizey = 4;
376 	entity->skill[2] = my->getUID();
377 	entity->flags[PASSABLE] = true;
378 	entity->flags[NOUPDATE] = true;
379 	entity->flags[INVISIBLE] = true;
380 	entity->flags[USERFLAG2] = my->flags[USERFLAG2];
381 	entity->focalx = limbs[KOBOLD][6][0]; // 2
382 	entity->focaly = limbs[KOBOLD][6][1]; // 0
383 	entity->focalz = limbs[KOBOLD][6][2]; // -.5
384 	entity->behavior = &actKoboldLimb;
385 	entity->parent = my->getUID();
386 	entity->pitch = .25;
387 	node = list_AddNodeLast(&my->children);
388 	node->element = entity;
389 	node->deconstructor = &emptyDeconstructor;
390 	node->size = sizeof(Entity*);
391 	my->bodyparts.push_back(entity);
392 
393 	// shield
394 	entity = newEntity(-1, 0, map.entities, nullptr); //Limb entity.
395 	entity->sizex = 4;
396 	entity->sizey = 4;
397 	entity->skill[2] = my->getUID();
398 	entity->flags[PASSABLE] = true;
399 	entity->flags[NOUPDATE] = true;
400 	entity->flags[INVISIBLE] = true;
401 	entity->flags[USERFLAG2] = my->flags[USERFLAG2];
402 	entity->focalx = limbs[KOBOLD][7][0]; // 0
403 	entity->focaly = limbs[KOBOLD][7][1]; // 0
404 	entity->focalz = limbs[KOBOLD][7][2]; // 1.5
405 	entity->behavior = &actKoboldLimb;
406 	entity->parent = my->getUID();
407 	node = list_AddNodeLast(&my->children);
408 	node->element = entity;
409 	node->deconstructor = &emptyDeconstructor;
410 	node->size = sizeof(Entity*);
411 	my->bodyparts.push_back(entity);
412 
413 	// cloak
414 	entity = newEntity(-1, 0, map.entities, nullptr); //Limb entity.
415 	entity->sizex = 4;
416 	entity->sizey = 4;
417 	entity->skill[2] = my->getUID();
418 	entity->scalex = 1.01;
419 	entity->scaley = 1.01;
420 	entity->scalez = 1.01;
421 	entity->flags[PASSABLE] = true;
422 	entity->flags[NOUPDATE] = true;
423 	entity->flags[INVISIBLE] = true;
424 	entity->flags[USERFLAG2] = my->flags[USERFLAG2];
425 	entity->focalx = limbs[KOBOLD][8][0]; // 0
426 	entity->focaly = limbs[KOBOLD][8][1]; // 0
427 	entity->focalz = limbs[KOBOLD][8][2]; // 4
428 	entity->behavior = &actKoboldLimb;
429 	entity->parent = my->getUID();
430 	node = list_AddNodeLast(&my->children);
431 	node->element = entity;
432 	node->deconstructor = &emptyDeconstructor;
433 	node->size = sizeof(Entity*);
434 	my->bodyparts.push_back(entity);
435 
436 	// helmet
437 	entity = newEntity(-1, 0, map.entities, nullptr); //Limb entity.
438 	entity->sizex = 4;
439 	entity->sizey = 4;
440 	entity->skill[2] = my->getUID();
441 	entity->scalex = 1.1;
442 	entity->scaley = 1.1;
443 	entity->scalez = 1.1;
444 	entity->flags[PASSABLE] = true;
445 	entity->flags[NOUPDATE] = true;
446 	entity->flags[USERFLAG2] = my->flags[USERFLAG2];
447 	entity->focalx = limbs[KOBOLD][9][0]; // 0
448 	entity->focaly = limbs[KOBOLD][9][1]; // 0
449 	entity->focalz = limbs[KOBOLD][9][2]; // -2
450 	entity->behavior = &actKoboldLimb;
451 	entity->parent = my->getUID();
452 	node = list_AddNodeLast(&my->children);
453 	node->element = entity;
454 	node->deconstructor = &emptyDeconstructor;
455 	node->size = sizeof(Entity*);
456 	my->bodyparts.push_back(entity);
457 
458 	if ( multiplayer == CLIENT || MONSTER_INIT )
459 	{
460 		return;
461 	}
462 }
463 
actKoboldLimb(Entity * my)464 void actKoboldLimb(Entity* my)
465 {
466 	my->actMonsterLimb(true);
467 }
468 
koboldDie(Entity * my)469 void koboldDie(Entity* my)
470 {
471 	int c;
472 	for ( c = 0; c < 6; ++c )
473 	{
474 		Entity* entity = spawnGib(my);
475 		if ( entity )
476 		{
477 			serverSpawnGibForClient(entity);
478 		}
479 	}
480 
481 	my->spawnBlood();
482 
483 	my->removeMonsterDeathNodes();
484 
485 	playSoundEntity(my, 298 + rand() % 4, 128);
486 	list_RemoveNode(my->mynode);
487 	return;
488 }
489 
490 #define KOBOLDWALKSPEED .13
491 
koboldMoveBodyparts(Entity * my,Stat * myStats,double dist)492 void koboldMoveBodyparts(Entity* my, Stat* myStats, double dist)
493 {
494 	node_t* node;
495 	Entity* entity = nullptr, *entity2 = nullptr;
496 	Entity* rightbody = nullptr;
497 	Entity* weaponarm = nullptr;
498 	int bodypart;
499 	bool wearingring = false;
500 
501 	// set invisibility //TODO: isInvisible()?
502 	if ( multiplayer != CLIENT )
503 	{
504 		if ( myStats->ring != nullptr )
505 			if ( myStats->ring->type == RING_INVISIBILITY )
506 			{
507 				wearingring = true;
508 			}
509 		if ( myStats->cloak != nullptr )
510 			if ( myStats->cloak->type == CLOAK_INVISIBILITY )
511 			{
512 				wearingring = true;
513 			}
514 		if ( myStats->EFFECTS[EFF_INVISIBLE] == true || wearingring == true )
515 		{
516 			my->flags[INVISIBLE] = true;
517 			my->flags[BLOCKSIGHT] = false;
518 			bodypart = 0;
519 			for (node = my->children.first; node != nullptr; node = node->next)
520 			{
521 				if ( bodypart < LIMB_HUMANOID_TORSO )
522 				{
523 					++bodypart;
524 					continue;
525 				}
526 				if ( bodypart >= LIMB_HUMANOID_WEAPON )
527 				{
528 					break;
529 				}
530 				entity = (Entity*)node->element;
531 				if ( !entity->flags[INVISIBLE] )
532 				{
533 					entity->flags[INVISIBLE] = true;
534 					serverUpdateEntityBodypart(my, bodypart);
535 				}
536 				++bodypart;
537 			}
538 		}
539 		else
540 		{
541 			my->flags[INVISIBLE] = false;
542 			my->flags[BLOCKSIGHT] = true;
543 			bodypart = 0;
544 			for (node = my->children.first; node != nullptr; node = node->next)
545 			{
546 				if ( bodypart < LIMB_HUMANOID_TORSO )
547 				{
548 					++bodypart;
549 					continue;
550 				}
551 				if ( bodypart >= LIMB_HUMANOID_WEAPON )
552 				{
553 					break;
554 				}
555 				entity = (Entity*)node->element;
556 				if ( entity->flags[INVISIBLE] )
557 				{
558 					entity->flags[INVISIBLE] = false;
559 					serverUpdateEntityBodypart(my, bodypart);
560 					serverUpdateEntityFlag(my, INVISIBLE);
561 				}
562 				++bodypart;
563 			}
564 		}
565 
566 		// sleeping
567 		if ( myStats->EFFECTS[EFF_ASLEEP] )
568 		{
569 			my->z = 4;
570 			my->pitch = PI / 4;
571 		}
572 		else
573 		{
574 			my->z = 2.25;
575 			my->pitch = 0;
576 		}
577 	}
578 
579 	Entity* shieldarm = nullptr;
580 
581 	//Move bodyparts
582 	for (bodypart = 0, node = my->children.first; node != nullptr; node = node->next, ++bodypart)
583 	{
584 		if ( bodypart < LIMB_HUMANOID_TORSO )
585 		{
586 			continue;
587 		}
588 		entity = (Entity*)node->element;
589 		entity->x = my->x;
590 		entity->y = my->y;
591 		entity->z = my->z;
592 		if ( MONSTER_ATTACK == MONSTER_POSE_MAGIC_WINDUP1 && bodypart == LIMB_HUMANOID_RIGHTARM )
593 		{
594 			// don't let the creatures's yaw move the casting arm
595 		}
596 		else
597 		{
598 			entity->yaw = my->yaw;
599 		}
600 
601 		if ( bodypart == LIMB_HUMANOID_RIGHTLEG || bodypart == LIMB_HUMANOID_LEFTARM )
602 		{
603 			my->humanoidAnimateWalk(entity, node, bodypart, KOBOLDWALKSPEED, dist, 0.4);
604 		}
605 		else if ( bodypart == LIMB_HUMANOID_LEFTLEG || bodypart == LIMB_HUMANOID_RIGHTARM || bodypart == LIMB_HUMANOID_CLOAK )
606 		{
607 			// left leg, right arm, cloak.
608 			if ( bodypart == LIMB_HUMANOID_RIGHTARM )
609 			{
610 				weaponarm = entity;
611 				if ( my->monsterAttack > 0 )
612 				{
613 					my->handleWeaponArmAttack(entity);
614 				}
615 			}
616 			else if ( bodypart == LIMB_HUMANOID_CLOAK )
617 			{
618 				entity->pitch = entity->fskill[0];
619 			}
620 
621 			my->humanoidAnimateWalk(entity, node, bodypart, KOBOLDWALKSPEED, dist, 0.4);
622 
623 			if ( bodypart == LIMB_HUMANOID_CLOAK )
624 			{
625 				entity->fskill[0] = entity->pitch;
626 				entity->roll = my->roll - fabs(entity->pitch) / 2;
627 				entity->pitch = 0;
628 			}
629 		}
630 		switch ( bodypart )
631 		{
632 			// torso
633 			case LIMB_HUMANOID_TORSO:
634 				entity->x -= .25 * cos(my->yaw);
635 				entity->y -= .25 * sin(my->yaw);
636 				entity->z += 1.25;
637 				break;
638 			// right leg
639 			case LIMB_HUMANOID_RIGHTLEG:
640 				if ( multiplayer != CLIENT )
641 				{
642 					if ( myStats->shoes == nullptr )
643 					{
644 						entity->sprite = 423;
645 					}
646 					else
647 					{
648 						my->setBootSprite(entity, SPRITE_BOOT_RIGHT_OFFSET);
649 					}
650 					if ( multiplayer == SERVER )
651 					{
652 						// update sprites for clients
653 						if ( entity->skill[10] != entity->sprite )
654 						{
655 							entity->skill[10] = entity->sprite;
656 							serverUpdateEntityBodypart(my, bodypart);
657 						}
658 						if ( entity->getUID() % (TICKS_PER_SECOND * 10) == ticks % (TICKS_PER_SECOND * 10) )
659 						{
660 							serverUpdateEntityBodypart(my, bodypart);
661 						}
662 					}
663 				}
664 				entity->x += 1.25 * cos(my->yaw + PI / 2);
665 				entity->y += 1.25 * sin(my->yaw + PI / 2);
666 				entity->z += 2.75;
667 				if ( my->z >= 3.9 && my->z <= 4.1 )
668 				{
669 					entity->yaw += PI / 8;
670 					entity->pitch = -PI / 2;
671 				}
672 				break;
673 			// left leg
674 			case LIMB_HUMANOID_LEFTLEG:
675 				if ( multiplayer != CLIENT )
676 				{
677 					if ( myStats->shoes == nullptr )
678 					{
679 						entity->sprite = 424;
680 					}
681 					else
682 					{
683 						my->setBootSprite(entity, SPRITE_BOOT_LEFT_OFFSET);
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->getUID() % (TICKS_PER_SECOND * 10) == ticks % (TICKS_PER_SECOND * 10) )
694 						{
695 							serverUpdateEntityBodypart(my, bodypart);
696 						}
697 					}
698 				}
699 				entity->x -= 1.25 * cos(my->yaw + PI / 2);
700 				entity->y -= 1.25 * sin(my->yaw + PI / 2);
701 				entity->z += 2.75;
702 				if ( my->z >= 3.9 && my->z <= 4.1 )
703 				{
704 					entity->yaw -= PI / 8;
705 					entity->pitch = -PI / 2;
706 				}
707 				break;
708 			// right arm
709 			case LIMB_HUMANOID_RIGHTARM:
710 			{
711 				node_t* weaponNode = list_Node(&my->children, 7);
712 				if ( weaponNode )
713 				{
714 					Entity* weapon = (Entity*)weaponNode->element;
715 					if ( my->monsterArmbended || (weapon->flags[INVISIBLE] && my->monsterState == MONSTER_STATE_WAIT) )
716 					{
717 						// if weapon invisible and I'm not moving, relax arm.
718 						entity->focalx = limbs[KOBOLD][4][0]; // 0
719 						entity->focaly = limbs[KOBOLD][4][1]; // 0
720 						entity->focalz = limbs[KOBOLD][4][2]; // 2
721 						entity->sprite = 425;
722 					}
723 					else
724 					{
725 						// else flex arm.
726 						entity->focalx = limbs[KOBOLD][4][0] + 1; // 1
727 						entity->focaly = limbs[KOBOLD][4][1]; // 0
728 						entity->focalz = limbs[KOBOLD][4][2] - 1; // 1
729 						entity->sprite = 426;
730 					}
731 				}
732 				entity->x += 2.5 * cos(my->yaw + PI / 2) - .75 * cos(my->yaw);
733 				entity->y += 2.5 * sin(my->yaw + PI / 2) - .75 * sin(my->yaw);
734 				entity->z -= .25;
735 				entity->yaw += MONSTER_WEAPONYAW;
736 				if ( my->z >= 3.9 && my->z <= 4.1 )
737 				{
738 					entity->pitch = 0;
739 				}
740 				break;
741 			}
742 			// left arm
743 			case LIMB_HUMANOID_LEFTARM:
744 			{
745 				shieldarm = entity;
746 				node_t* shieldNode = list_Node(&my->children, 8);
747 				if ( shieldNode )
748 				{
749 					Entity* shield = (Entity*)shieldNode->element;
750 					if ( shield->flags[INVISIBLE] && my->monsterState == MONSTER_STATE_WAIT )
751 					{
752 						// if shield invisible and I'm not moving, relax arm.
753 						entity->focalx = limbs[KOBOLD][5][0]; // 0
754 						entity->focaly = limbs[KOBOLD][5][1]; // 0
755 						entity->focalz = limbs[KOBOLD][5][2]; // 2
756 						entity->sprite = 427;
757 					}
758 					else
759 					{
760 						// else flex arm.
761 						entity->focalx = limbs[KOBOLD][5][0] + 1; // 1
762 						entity->focaly = limbs[KOBOLD][5][1]; // 0
763 						entity->focalz = limbs[KOBOLD][5][2] - 1; // 1
764 						entity->sprite = 428;
765 					}
766 				}
767 				entity->x -= 2.5 * cos(my->yaw + PI / 2) + .75 * cos(my->yaw);
768 				entity->y -= 2.5 * sin(my->yaw + PI / 2) + .75 * sin(my->yaw);
769 				entity->z -= .25;
770 				if ( my->z >= 3.9 && my->z <= 4.1 )
771 				{
772 					entity->pitch = 0;
773 				}
774 				if ( my->monsterDefend && my->monsterAttack == 0 )
775 				{
776 					MONSTER_SHIELDYAW = PI / 5;
777 				}
778 				else
779 				{
780 					MONSTER_SHIELDYAW = 0;
781 				}
782 				entity->yaw += MONSTER_SHIELDYAW;
783 				break;
784 			}
785 			// weapon
786 			case LIMB_HUMANOID_WEAPON:
787 				if ( multiplayer != CLIENT )
788 				{
789 					if ( myStats->weapon == nullptr || myStats->EFFECTS[EFF_INVISIBLE] || wearingring ) //TODO: isInvisible()?
790 					{
791 						entity->flags[INVISIBLE] = true;
792 					}
793 					else
794 					{
795 						entity->sprite = itemModel(myStats->weapon);
796 						if ( itemCategory(myStats->weapon) == SPELLBOOK )
797 						{
798 							entity->flags[INVISIBLE] = true;
799 						}
800 						else
801 						{
802 							entity->flags[INVISIBLE] = false;
803 						}
804 					}
805 					if ( multiplayer == SERVER )
806 					{
807 						// update sprites for clients
808 						if ( entity->skill[10] != entity->sprite )
809 						{
810 							entity->skill[10] = entity->sprite;
811 							serverUpdateEntityBodypart(my, bodypart);
812 						}
813 						if ( entity->skill[11] != entity->flags[INVISIBLE] )
814 						{
815 							entity->skill[11] = entity->flags[INVISIBLE];
816 							serverUpdateEntityBodypart(my, bodypart);
817 						}
818 						if ( entity->getUID() % (TICKS_PER_SECOND * 10) == ticks % (TICKS_PER_SECOND * 10) )
819 						{
820 							serverUpdateEntityBodypart(my, bodypart);
821 						}
822 					}
823 				}
824 				else
825 				{
826 					if ( entity->sprite <= 0 )
827 					{
828 						entity->flags[INVISIBLE] = true;
829 					}
830 				}
831 				if ( weaponarm != nullptr )
832 				{
833 					my->handleHumanoidWeaponLimb(entity, weaponarm);
834 				}
835 				break;
836 			// shield
837 			case LIMB_HUMANOID_SHIELD:
838 				if ( multiplayer != CLIENT )
839 				{
840 					if ( myStats->shield == nullptr )
841 					{
842 						entity->flags[INVISIBLE] = true;
843 						entity->sprite = 0;
844 					}
845 					else
846 					{
847 						entity->flags[INVISIBLE] = false;
848 						entity->sprite = itemModel(myStats->shield);
849 						if ( itemTypeIsQuiver(myStats->shield->type) )
850 						{
851 							entity->handleQuiverThirdPersonModel(*myStats);
852 						}
853 					}
854 					if ( myStats->EFFECTS[EFF_INVISIBLE] || wearingring ) //TODO: isInvisible()?
855 					{
856 						entity->flags[INVISIBLE] = true;
857 					}
858 					if ( multiplayer == SERVER )
859 					{
860 						// update sprites for clients
861 						if ( entity->skill[10] != entity->sprite )
862 						{
863 							entity->skill[10] = entity->sprite;
864 							serverUpdateEntityBodypart(my, bodypart);
865 						}
866 						if ( entity->skill[11] != entity->flags[INVISIBLE] )
867 						{
868 							entity->skill[11] = entity->flags[INVISIBLE];
869 							serverUpdateEntityBodypart(my, bodypart);
870 						}
871 						if ( entity->getUID() % (TICKS_PER_SECOND * 10) == ticks % (TICKS_PER_SECOND * 10) )
872 						{
873 							serverUpdateEntityBodypart(my, bodypart);
874 						}
875 					}
876 				}
877 				else
878 				{
879 					if ( entity->sprite <= 0 )
880 					{
881 						entity->flags[INVISIBLE] = true;
882 					}
883 				}
884 				entity->x -= 2.5 * cos(my->yaw + PI / 2) + .20 * cos(my->yaw);
885 				entity->y -= 2.5 * sin(my->yaw + PI / 2) + .20 * sin(my->yaw);
886 				entity->z += 1;
887 				entity->yaw = shieldarm->yaw;
888 				entity->roll = 0;
889 				entity->pitch = 0;
890 				if ( entity->sprite == items[TOOL_TORCH].index )
891 				{
892 					entity2 = spawnFlame(entity, SPRITE_FLAME);
893 					entity2->x += 2 * cos(entity->yaw);
894 					entity2->y += 2 * sin(entity->yaw);
895 					entity2->z -= 2;
896 				}
897 				else if ( entity->sprite == items[TOOL_CRYSTALSHARD].index )
898 				{
899 					entity2 = spawnFlame(entity, SPRITE_CRYSTALFLAME);
900 					entity2->x += 2 * cos(entity->yaw);
901 					entity2->y += 2 * sin(entity->yaw);
902 					entity2->z -= 2;
903 				}
904 				else if ( entity->sprite == items[TOOL_LANTERN].index )
905 				{
906 					entity->z += 2;
907 					entity2 = spawnFlame(entity, SPRITE_FLAME);
908 					entity2->x += 2 * cos(entity->yaw);
909 					entity2->y += 2 * sin(entity->yaw);
910 					entity2->z += 1;
911 				}
912 				if ( MONSTER_SHIELDYAW > PI / 32 )
913 				{
914 					if ( entity->sprite != items[TOOL_TORCH].index && entity->sprite != items[TOOL_LANTERN].index && entity->sprite != items[TOOL_CRYSTALSHARD].index )
915 					{
916 						// shield, so rotate a little.
917 						entity->roll += PI / 64;
918 						entity->x += 2 * cos(my->yaw); // stick out shield a bit when defending to minimise model clipping
919 						entity->y += 2 * sin(my->yaw);
920 					}
921 					else
922 					{
923 						entity->x += 0.25 * cos(my->yaw);
924 						entity->y += 0.25 * sin(my->yaw);
925 						entity->pitch += PI / 16;
926 						if ( entity2 )
927 						{
928 							entity2->x += 0.75 * cos(shieldarm->yaw);
929 							entity2->y += 0.75 * sin(shieldarm->yaw);
930 						}
931 					}
932 				}
933 				if ( itemSpriteIsQuiverThirdPersonModel(entity->sprite) )
934 				{
935 					/*shieldLimb->x -= -0.25 * cos(this->yaw + PI / 2) + 1.25 * cos(this->yaw);
936 					shieldLimb->y -= -0.25 * sin(this->yaw + PI / 2) + 1.25 * sin(this->yaw);*/
937 					entity->x -= 0.25 * cos(my->yaw + PI / 2);
938 					entity->y -= 0.25 * sin(my->yaw + PI / 2);
939 					entity->z += 1;
940 					entity->yaw += PI / 6;
941 				}
942 				break;
943 			// cloak
944 			case LIMB_HUMANOID_CLOAK:
945 				if ( multiplayer != CLIENT )
946 				{
947 					if ( myStats->cloak == nullptr || myStats->EFFECTS[EFF_INVISIBLE] || wearingring ) //TODO: isInvisible()?
948 					{
949 						entity->flags[INVISIBLE] = true;
950 					}
951 					else
952 					{
953 						entity->flags[INVISIBLE] = false;
954 						entity->sprite = itemModel(myStats->cloak);
955 					}
956 					if ( multiplayer == SERVER )
957 					{
958 						// update sprites for clients
959 						if ( entity->skill[10] != entity->sprite )
960 						{
961 							entity->skill[10] = entity->sprite;
962 							serverUpdateEntityBodypart(my, bodypart);
963 						}
964 						if ( entity->skill[11] != entity->flags[INVISIBLE] )
965 						{
966 							entity->skill[11] = entity->flags[INVISIBLE];
967 							serverUpdateEntityBodypart(my, bodypart);
968 						}
969 						if ( entity->getUID() % (TICKS_PER_SECOND * 10) == ticks % (TICKS_PER_SECOND * 10) )
970 						{
971 							serverUpdateEntityBodypart(my, bodypart);
972 						}
973 					}
974 				}
975 				else
976 				{
977 					if ( entity->sprite <= 0 )
978 					{
979 						entity->flags[INVISIBLE] = true;
980 					}
981 				}
982 				entity->x -= cos(my->yaw) * 1.5;
983 				entity->y -= sin(my->yaw) * 1.5;
984 				entity->yaw += PI / 2;
985 				break;
986 			// helmet
987 			case LIMB_HUMANOID_HELMET:
988 				entity->focalx = limbs[KOBOLD][9][0]; // 0
989 				entity->focaly = limbs[KOBOLD][9][1]; // 0
990 				entity->focalz = limbs[KOBOLD][9][2]; // -2
991 				entity->pitch = my->pitch;
992 				entity->roll = 0;
993 				if ( multiplayer != CLIENT )
994 				{
995 					entity->sprite = itemModel(myStats->helmet);
996 					if ( myStats->helmet == NULL || myStats->EFFECTS[EFF_INVISIBLE] || wearingring ) //TODO: isInvisible()?
997 					{
998 						entity->flags[INVISIBLE] = true;
999 					}
1000 					else
1001 					{
1002 						entity->flags[INVISIBLE] = false;
1003 					}
1004 					if ( multiplayer == SERVER )
1005 					{
1006 						// update sprites for clients
1007 						if ( entity->skill[10] != entity->sprite )
1008 						{
1009 							entity->skill[10] = entity->sprite;
1010 							serverUpdateEntityBodypart(my, bodypart);
1011 						}
1012 						if ( entity->skill[11] != entity->flags[INVISIBLE] )
1013 						{
1014 							entity->skill[11] = entity->flags[INVISIBLE];
1015 							serverUpdateEntityBodypart(my, bodypart);
1016 						}
1017 						if ( entity->getUID() % (TICKS_PER_SECOND * 10) == ticks % (TICKS_PER_SECOND * 10) )
1018 						{
1019 							serverUpdateEntityBodypart(my, bodypart);
1020 						}
1021 					}
1022 				}
1023 				else
1024 				{
1025 					if ( entity->sprite <= 0 )
1026 					{
1027 						entity->flags[INVISIBLE] = true;
1028 					}
1029 				}
1030 				my->setHelmetLimbOffset(entity);
1031 				break;
1032 		}
1033 	}
1034 	// rotate shield a bit
1035 	node_t* shieldNode = list_Node(&my->children, 8);
1036 	if ( shieldNode )
1037 	{
1038 		Entity* shieldEntity = (Entity*)shieldNode->element;
1039 		if ( shieldEntity->sprite != items[TOOL_TORCH].index && shieldEntity->sprite != items[TOOL_LANTERN].index && shieldEntity->sprite != items[TOOL_CRYSTALSHARD].index )
1040 		{
1041 			shieldEntity->yaw -= PI / 6;
1042 		}
1043 	}
1044 	if ( MONSTER_ATTACK > 0 && MONSTER_ATTACK <= MONSTER_POSE_MAGIC_CAST3 )
1045 	{
1046 		MONSTER_ATTACKTIME++;
1047 	}
1048 	else if ( MONSTER_ATTACK == 0 )
1049 	{
1050 		MONSTER_ATTACKTIME = 0;
1051 	}
1052 	else
1053 	{
1054 		// do nothing, don't reset attacktime or increment it.
1055 	}
1056 }
1057