1 /*-------------------------------------------------------------------------------
2 
3 	BARONY
4 	File: monster_goblin.cpp
5 	Desc: implements all of the goblin monster's code
6 
7 	Copyright 2013-2016 (c) Turning Wheel LLC, all rights reserved.
8 	See LICENSE for details.
9 
10 -------------------------------------------------------------------------------*/
11 
12 #include "main.hpp"
13 #include "game.hpp"
14 #include "stat.hpp"
15 #include "entity.hpp"
16 #include "items.hpp"
17 #include "monster.hpp"
18 #include "sound.hpp"
19 #include "net.hpp"
20 #include "collision.hpp"
21 #include "player.hpp"
22 
initGoblin(Entity * my,Stat * myStats)23 void initGoblin(Entity* my, Stat* myStats)
24 {
25 	int c;
26 	node_t* node;
27 
28 	//Sprite 180 = Goblin head model.
29 	my->initMonster(180);
30 
31 	if ( multiplayer != CLIENT )
32 	{
33 		MONSTER_SPOTSND = 60;
34 		MONSTER_SPOTVAR = 3;
35 		MONSTER_IDLESND = 98;
36 		MONSTER_IDLEVAR = 3;
37 	}
38 	if ( multiplayer != CLIENT && !MONSTER_INIT )
39 	{
40 		if ( myStats != nullptr )
41 		{
42 			if ( !myStats->leader_uid )
43 			{
44 				myStats->leader_uid = 0;
45 			}
46 
47 			// apply random stat increases if set in stat_shared.cpp or editor
48 			setRandomMonsterStats(myStats);
49 
50 			// generate 6 items max, less if there are any forced items from boss variants
51 			int customItemsToGenerate = ITEM_CUSTOM_SLOT_LIMIT;
52 
53 			// boss variants
54 			if ( rand() % 50 || my->flags[USERFLAG2] || myStats->MISC_FLAGS[STAT_FLAG_DISABLE_MINIBOSS] )
55 			{
56 			}
57 			else
58 			{
59 				myStats->HP = 120;
60 				myStats->MAXHP = 120;
61 				myStats->OLDHP = myStats->HP;
62 				strcpy(myStats->name, "The Potato King");
63 				myStats->STR += 6;
64 				int status = DECREPIT + (currentlevel > 5) + (currentlevel > 15) + (currentlevel > 20);
65 				myStats->weapon = newItem(ARTIFACT_MACE, static_cast<Status>(status), 1, 1, rand(), true, nullptr);
66 				myStats->helmet = newItem(HAT_JESTER, SERVICABLE, 3 + rand() % 3, 1, rand(), false, nullptr);
67 
68 				int c;
69 				for ( c = 0; c < 3; c++ )
70 				{
71 					Entity* entity = summonMonster(GOBLIN, my->x, my->y);
72 					if ( entity )
73 					{
74 						entity->parent = my->getUID();
75 					}
76 				}
77 			}
78 
79 			// random effects
80 			if ( rand() % 8 == 0 )
81 			{
82 				myStats->EFFECTS[EFF_ASLEEP] = true;
83 				myStats->EFFECTS_TIMERS[EFF_ASLEEP] = 1800 + rand() % 1800;
84 			}
85 
86 			// generates equipment and weapons if available from editor
87 			createMonsterEquipment(myStats);
88 
89 			// create any custom inventory items from editor if available
90 			createCustomInventory(myStats, customItemsToGenerate);
91 
92 			// count if any custom inventory items from editor
93 			int customItems = countCustomItems(myStats); //max limit of 6 custom items per entity.
94 
95 			// count any inventory items set to default in edtior
96 			int defaultItems = countDefaultItems(myStats);
97 
98 			my->setHardcoreStats(*myStats);
99 
100 			// generate the default inventory items for the monster, provided the editor sprite allowed enough default slots
101 			switch ( defaultItems )
102 			{
103 				case 6:
104 				case 5:
105 				case 4:
106 				case 3:
107 				case 2:
108 				case 1:
109 					break;
110 				default:
111 					break;
112 			}
113 
114 
115 			//give weapon
116 			if ( myStats->weapon == nullptr && myStats->EDITOR_ITEMS[ITEM_SLOT_WEAPON] == 1 )
117 			{
118 				switch ( rand() % 10 )
119 				{
120 					case 0:
121 					case 1:
122 					case 2:
123 						myStats->weapon = newItem(SHORTBOW, WORN, -1 + rand() % 3, 1, rand(), false, nullptr);
124 						break;
125 					case 3:
126 					case 4:
127 					case 5:
128 						myStats->weapon = newItem(BRONZE_AXE, WORN, -1 + rand() % 3, 1, rand(), false, nullptr);
129 						break;
130 					case 6:
131 					case 7:
132 						myStats->weapon = newItem(IRON_MACE, WORN, -1 + rand() % 3, 1, rand(), false, nullptr);
133 						break;
134 					case 8:
135 						myStats->weapon = newItem(IRON_AXE, WORN, -1 + rand() % 3, 1, rand(), false, nullptr);
136 						break;
137 					case 9:
138 						myStats->weapon = newItem(MAGICSTAFF_FIRE, EXCELLENT, -1 + rand() % 3, 1, rand(), false, nullptr);
139 						break;
140 				}
141 			}
142 
143 			if ( myStats->weapon && isMeleeWeapon(*myStats->weapon) )
144 			{
145 				myStats->CHR = -3; // don't retreat
146 			}
147 
148 			//give shield
149 			if ( myStats->shield == nullptr && myStats->EDITOR_ITEMS[ITEM_SLOT_SHIELD] == 1 )
150 			{
151 				if ( myStats->weapon && isRangedWeapon(*myStats->weapon) )
152 				{
153 					my->monsterGenerateQuiverItem(myStats);
154 				}
155 				else
156 				{
157 					// give shield
158 					switch ( rand() % 10 )
159 					{
160 						case 0:
161 						case 1:
162 							myStats->shield = newItem(TOOL_TORCH, SERVICABLE, -1 + rand() % 3, 1, rand(), false, nullptr);
163 							break;
164 						case 2:
165 						case 3:
166 						case 4:
167 							break;
168 						case 5:
169 						case 6:
170 							myStats->shield = newItem(WOODEN_SHIELD, DECREPIT, -1 + rand() % 3, 1, rand(), false, nullptr);
171 							break;
172 						case 7:
173 						case 8:
174 							myStats->shield = newItem(BRONZE_SHIELD, DECREPIT, -1 + rand() % 3, 1, rand(), false, nullptr);
175 							break;
176 						case 9:
177 							myStats->shield = newItem(IRON_SHIELD, DECREPIT, -1 + rand() % 3, 1, rand(), false, nullptr);
178 							break;
179 					}
180 				}
181 			}
182 
183 			// give cloak
184 			if ( myStats->cloak == nullptr && myStats->EDITOR_ITEMS[ITEM_SLOT_CLOAK] == 1 )
185 			{
186 				switch ( rand() % 10 )
187 				{
188 					case 0:
189 					case 1:
190 					case 2:
191 					case 3:
192 					case 4:
193 					case 5:
194 						break;
195 					case 6:
196 					case 7:
197 					case 8:
198 						myStats->cloak = newItem(CLOAK, WORN, -1 + rand() % 3, 1, rand(), false, nullptr);
199 						break;
200 					case 9:
201 						myStats->cloak = newItem(CLOAK_MAGICREFLECTION, WORN, 0, 1, rand(), false, nullptr);
202 						break;
203 				}
204 			}
205 
206 			// give helmet
207 			if ( myStats->helmet == nullptr && myStats->EDITOR_ITEMS[ITEM_SLOT_HELM] == 1 )
208 			{
209 				switch ( rand() % 10 )
210 				{
211 					case 0:
212 					case 1:
213 					case 2:
214 						break;
215 					case 3:
216 					case 4:
217 						myStats->helmet = newItem(HAT_PHRYGIAN, WORN, -1 + rand() % 3, 1, rand(), false, nullptr);
218 						break;
219 					case 5:
220 						myStats->helmet = newItem(HAT_WIZARD, WORN, -1 + rand() % 3, 1, rand(), false, nullptr);
221 						break;
222 					case 6:
223 					case 7:
224 						myStats->helmet = newItem(LEATHER_HELM, WORN, -1 + rand() % 3, 1, rand(), false, nullptr);
225 						break;
226 					case 8:
227 					case 9:
228 						myStats->helmet = newItem(IRON_HELM, WORN, -1 + rand() % 3, 1, rand(), false, nullptr);
229 						break;
230 				}
231 			}
232 
233 			// give armor
234 			if ( myStats->breastplate == nullptr && myStats->EDITOR_ITEMS[ITEM_SLOT_ARMOR] == 1 )
235 			{
236 				switch ( rand() % 10 )
237 				{
238 					case 0:
239 					case 1:
240 					case 2:
241 					case 3:
242 					case 4:
243 						break;
244 					case 5:
245 					case 6:
246 					case 7:
247 						myStats->breastplate = newItem(LEATHER_BREASTPIECE, DECREPIT, -1 + rand() % 3, 1, rand(), false, nullptr);
248 						break;
249 					case 8:
250 					case 9:
251 						myStats->breastplate = newItem(IRON_BREASTPIECE, DECREPIT, -1 + rand() % 3, 1, rand(), false, nullptr);
252 						break;
253 				}
254 			}
255 		}
256 	}
257 
258 	// torso
259 	Entity* entity = newEntity(183, 0, map.entities, nullptr); //Limb entity.
260 	entity->sizex = 4;
261 	entity->sizey = 4;
262 	entity->skill[2] = my->getUID();
263 	entity->scalex = 1.01;
264 	entity->scaley = 1.01;
265 	entity->scalez = 1.01;
266 	entity->flags[PASSABLE] = true;
267 	entity->flags[NOUPDATE] = true;
268 	entity->flags[USERFLAG2] = my->flags[USERFLAG2];
269 	entity->focalx = limbs[GOBLIN][1][0]; // 0
270 	entity->focaly = limbs[GOBLIN][1][1]; // 0
271 	entity->focalz = limbs[GOBLIN][1][2]; // 0
272 	entity->behavior = &actGoblinLimb;
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 	// right leg
281 	entity = newEntity(182, 0, map.entities, nullptr); //Limb entity.
282 	entity->sizex = 4;
283 	entity->sizey = 4;
284 	entity->skill[2] = my->getUID();
285 	entity->flags[PASSABLE] = true;
286 	entity->flags[NOUPDATE] = true;
287 	entity->flags[USERFLAG2] = my->flags[USERFLAG2];
288 	entity->focalx = limbs[GOBLIN][2][0]; // 0
289 	entity->focaly = limbs[GOBLIN][2][1]; // 0
290 	entity->focalz = limbs[GOBLIN][2][2]; // 2
291 	entity->behavior = &actGoblinLimb;
292 	entity->parent = my->getUID();
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 	// left leg
300 	entity = newEntity(181, 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[USERFLAG2] = my->flags[USERFLAG2];
307 	entity->focalx = limbs[GOBLIN][3][0]; // 0
308 	entity->focaly = limbs[GOBLIN][3][1]; // 0
309 	entity->focalz = limbs[GOBLIN][3][2]; // 2
310 	entity->behavior = &actGoblinLimb;
311 	entity->parent = my->getUID();
312 	node = list_AddNodeLast(&my->children);
313 	node->element = entity;
314 	node->deconstructor = &emptyDeconstructor;
315 	node->size = sizeof(Entity*);
316 	my->bodyparts.push_back(entity);
317 
318 	// right arm
319 	entity = newEntity(178, 0, map.entities, nullptr); //Limb entity.
320 	entity->sizex = 4;
321 	entity->sizey = 4;
322 	entity->skill[2] = my->getUID();
323 	entity->flags[PASSABLE] = true;
324 	entity->flags[NOUPDATE] = true;
325 	entity->flags[USERFLAG2] = my->flags[USERFLAG2];
326 	entity->focalx = limbs[GOBLIN][4][0]; // 0
327 	entity->focaly = limbs[GOBLIN][4][1]; // 0
328 	entity->focalz = limbs[GOBLIN][4][2]; // 1.5
329 	entity->behavior = &actGoblinLimb;
330 	entity->parent = my->getUID();
331 	node = list_AddNodeLast(&my->children);
332 	node->element = entity;
333 	node->deconstructor = &emptyDeconstructor;
334 	node->size = sizeof(Entity*);
335 	my->bodyparts.push_back(entity);
336 
337 	// left arm
338 	entity = newEntity(176, 0, map.entities, nullptr); //Limb entity.
339 	entity->sizex = 4;
340 	entity->sizey = 4;
341 	entity->skill[2] = my->getUID();
342 	entity->flags[PASSABLE] = true;
343 	entity->flags[NOUPDATE] = true;
344 	entity->flags[USERFLAG2] = my->flags[USERFLAG2];
345 	entity->focalx = limbs[GOBLIN][5][0]; // 0
346 	entity->focaly = limbs[GOBLIN][5][1]; // 0
347 	entity->focalz = limbs[GOBLIN][5][2]; // 1.5
348 	entity->behavior = &actGoblinLimb;
349 	entity->parent = my->getUID();
350 	node = list_AddNodeLast(&my->children);
351 	node->element = entity;
352 	node->deconstructor = &emptyDeconstructor;
353 	node->size = sizeof(Entity*);
354 	my->bodyparts.push_back(entity);
355 
356 	// world weapon
357 	entity = newEntity(-1, 0, map.entities, nullptr); //Limb entity.
358 	entity->sizex = 4;
359 	entity->sizey = 4;
360 	entity->skill[2] = my->getUID();
361 	entity->flags[PASSABLE] = true;
362 	entity->flags[NOUPDATE] = true;
363 	entity->flags[USERFLAG2] = my->flags[USERFLAG2];
364 	entity->focalx = limbs[GOBLIN][6][0]; // 1.5
365 	entity->focaly = limbs[GOBLIN][6][1]; // 0
366 	entity->focalz = limbs[GOBLIN][6][2]; // -.5
367 	entity->behavior = &actGoblinLimb;
368 	entity->parent = my->getUID();
369 	entity->pitch = .25;
370 	node = list_AddNodeLast(&my->children);
371 	node->element = entity;
372 	node->deconstructor = &emptyDeconstructor;
373 	node->size = sizeof(Entity*);
374 	my->bodyparts.push_back(entity);
375 
376 	// shield
377 	entity = newEntity(-1, 0, map.entities, nullptr); //Limb entity.
378 	entity->sizex = 4;
379 	entity->sizey = 4;
380 	entity->skill[2] = my->getUID();
381 	entity->flags[PASSABLE] = true;
382 	entity->flags[NOUPDATE] = true;
383 	entity->flags[USERFLAG2] = my->flags[USERFLAG2];
384 	entity->focalx = limbs[GOBLIN][7][0]; // 2
385 	entity->focaly = limbs[GOBLIN][7][1]; // 0
386 	entity->focalz = limbs[GOBLIN][7][2]; // 0
387 	entity->behavior = &actGoblinLimb;
388 	entity->parent = my->getUID();
389 	node = list_AddNodeLast(&my->children);
390 	node->element = entity;
391 	node->deconstructor = &emptyDeconstructor;
392 	node->size = sizeof(Entity*);
393 	my->bodyparts.push_back(entity);
394 
395 	// cloak
396 	entity = newEntity(-1, 0, map.entities, nullptr); //Limb entity.
397 	entity->sizex = 4;
398 	entity->sizey = 4;
399 	entity->skill[2] = my->getUID();
400 	entity->flags[PASSABLE] = true;
401 	entity->flags[NOUPDATE] = true;
402 	entity->flags[USERFLAG2] = my->flags[USERFLAG2];
403 	entity->focalx = limbs[GOBLIN][8][0]; // 0
404 	entity->focaly = limbs[GOBLIN][8][1]; // 0
405 	entity->focalz = limbs[GOBLIN][8][2]; // 4
406 	entity->behavior = &actGoblinLimb;
407 	entity->parent = my->getUID();
408 	node = list_AddNodeLast(&my->children);
409 	node->element = entity;
410 	node->deconstructor = &emptyDeconstructor;
411 	node->size = sizeof(Entity*);
412 	my->bodyparts.push_back(entity);
413 
414 	// helmet
415 	entity = newEntity(-1, 0, map.entities, nullptr); //Limb entity.
416 	entity->sizex = 4;
417 	entity->sizey = 4;
418 	entity->skill[2] = my->getUID();
419 	entity->scalex = 1.01;
420 	entity->scaley = 1.01;
421 	entity->scalez = 1.01;
422 	entity->flags[PASSABLE] = true;
423 	entity->flags[NOUPDATE] = true;
424 	entity->flags[USERFLAG2] = my->flags[USERFLAG2];
425 	entity->focalx = limbs[GOBLIN][9][0]; // 0
426 	entity->focaly = limbs[GOBLIN][9][1]; // 0
427 	entity->focalz = limbs[GOBLIN][9][2]; // -2
428 	entity->behavior = &actGoblinLimb;
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 	// mask
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->flags[PASSABLE] = true;
442 	entity->flags[NOUPDATE] = true;
443 	entity->flags[USERFLAG2] = my->flags[USERFLAG2];
444 	entity->focalx = limbs[GOBLIN][10][0]; // 0
445 	entity->focaly = limbs[GOBLIN][10][1]; // 0
446 	entity->focalz = limbs[GOBLIN][10][2]; // .25
447 	entity->behavior = &actGoblinLimb;
448 	entity->parent = my->getUID();
449 	node = list_AddNodeLast(&my->children);
450 	node->element = entity;
451 	node->deconstructor = &emptyDeconstructor;
452 	node->size = sizeof(Entity*);
453 	my->bodyparts.push_back(entity);
454 
455 	if ( multiplayer == CLIENT || MONSTER_INIT )
456 	{
457 		return;
458 	}
459 }
460 
actGoblinLimb(Entity * my)461 void actGoblinLimb(Entity* my)
462 {
463 	my->actMonsterLimb(true);
464 }
465 
goblinDie(Entity * my)466 void goblinDie(Entity* my)
467 {
468 	int c;
469 	for ( c = 0; c < 5; c++ )
470 	{
471 		Entity* gib = spawnGib(my);
472 		serverSpawnGibForClient(gib);
473 	}
474 
475 	my->spawnBlood();
476 
477 	playSoundEntity(my, 63 + rand() % 3, 128);
478 
479 	my->removeMonsterDeathNodes();
480 
481 	list_RemoveNode(my->mynode);
482 	return;
483 }
484 
485 #define GOBLINWALKSPEED .13
486 
goblinMoveBodyparts(Entity * my,Stat * myStats,double dist)487 void goblinMoveBodyparts(Entity* my, Stat* myStats, double dist)
488 {
489 	node_t* node;
490 	Entity* entity = nullptr, *entity2 = nullptr;
491 	Entity* rightbody = nullptr;
492 	Entity* weaponarm = nullptr;
493 	int bodypart;
494 	bool wearingring = false;
495 
496 	// set invisibility //TODO: isInvisible()?
497 	if ( multiplayer != CLIENT )
498 	{
499 		if ( myStats->ring != nullptr )
500 			if ( myStats->ring->type == RING_INVISIBILITY )
501 			{
502 				wearingring = true;
503 			}
504 		if ( myStats->cloak != nullptr )
505 			if ( myStats->cloak->type == CLOAK_INVISIBILITY )
506 			{
507 				wearingring = true;
508 			}
509 		if ( myStats->EFFECTS[EFF_INVISIBLE] == true || wearingring == true )
510 		{
511 			my->flags[INVISIBLE] = true;
512 			my->flags[BLOCKSIGHT] = false;
513 			bodypart = 0;
514 			for (node = my->children.first; node != nullptr; node = node->next)
515 			{
516 				if ( bodypart < LIMB_HUMANOID_TORSO )
517 				{
518 					bodypart++;
519 					continue;
520 				}
521 				if ( bodypart >= 7 )
522 				{
523 					break;
524 				}
525 				entity = (Entity*)node->element;
526 				if ( !entity->flags[INVISIBLE] )
527 				{
528 					entity->flags[INVISIBLE] = true;
529 					serverUpdateEntityBodypart(my, bodypart);
530 				}
531 				bodypart++;
532 			}
533 		}
534 		else
535 		{
536 			my->flags[INVISIBLE] = false;
537 			my->flags[BLOCKSIGHT] = true;
538 			bodypart = 0;
539 			for (node = my->children.first; node != nullptr; node = node->next)
540 			{
541 				if ( bodypart < LIMB_HUMANOID_TORSO )
542 				{
543 					bodypart++;
544 					continue;
545 				}
546 				if ( bodypart >= 7 )
547 				{
548 					break;
549 				}
550 				entity = (Entity*)node->element;
551 				if ( entity->flags[INVISIBLE] )
552 				{
553 					entity->flags[INVISIBLE] = false;
554 					serverUpdateEntityBodypart(my, bodypart);
555 					serverUpdateEntityFlag(my, INVISIBLE);
556 				}
557 				bodypart++;
558 			}
559 		}
560 
561 		// sleeping
562 		if ( myStats->EFFECTS[EFF_ASLEEP] )
563 		{
564 			my->z = 2.5;
565 			my->pitch = PI / 4;
566 		}
567 		else
568 		{
569 			my->z = 0;
570 			if ( my->monsterAttack == 0 )
571 			{
572 				my->pitch = 0;
573 			}
574 		}
575 	}
576 
577 	Entity* shieldarm = nullptr;
578 	Entity* helmet = nullptr;
579 
580 	//Move bodyparts
581 	for (bodypart = 0, node = my->children.first; node != nullptr; node = node->next, bodypart++)
582 	{
583 		if ( bodypart < LIMB_HUMANOID_TORSO )
584 		{
585 			continue;
586 		}
587 		entity = (Entity*)node->element;
588 		entity->x = my->x;
589 		entity->y = my->y;
590 		entity->z = my->z;
591 		if ( MONSTER_ATTACK == MONSTER_POSE_MAGIC_WINDUP1 && bodypart == LIMB_HUMANOID_RIGHTARM )
592 		{
593 			// don't let the creatures's yaw move the casting arm
594 		}
595 		else
596 		{
597 			entity->yaw = my->yaw;
598 		}
599 
600 		if ( bodypart == LIMB_HUMANOID_RIGHTLEG || bodypart == LIMB_HUMANOID_LEFTARM )
601 		{
602 			my->humanoidAnimateWalk(entity, node, bodypart, GOBLINWALKSPEED, dist, 0.4);
603 		}
604 		else if ( bodypart == LIMB_HUMANOID_LEFTLEG || bodypart == LIMB_HUMANOID_RIGHTARM || bodypart == LIMB_HUMANOID_CLOAK )
605 		{
606 			// left leg, right arm, cloak.
607 			if ( bodypart == LIMB_HUMANOID_RIGHTARM )
608 			{
609 				weaponarm = entity;
610 				if ( my->monsterAttack > 0 )
611 				{
612 					my->handleWeaponArmAttack(entity);
613 				}
614 			}
615 			else if ( bodypart == LIMB_HUMANOID_CLOAK )
616 			{
617 				entity->pitch = entity->fskill[0];
618 			}
619 
620 			my->humanoidAnimateWalk(entity, node, bodypart, GOBLINWALKSPEED, dist, 0.4);
621 
622 			if ( bodypart == LIMB_HUMANOID_CLOAK )
623 			{
624 				entity->fskill[0] = entity->pitch;
625 				entity->roll = my->roll - fabs(entity->pitch) / 2;
626 				entity->pitch = 0;
627 			}
628 		}
629 		switch ( bodypart )
630 		{
631 			// torso
632 			case LIMB_HUMANOID_TORSO:
633 				if ( multiplayer != CLIENT )
634 				{
635 					if ( myStats->breastplate == nullptr )
636 					{
637 						entity->sprite = 183;
638 					}
639 					else
640 					{
641 						entity->sprite = itemModel(myStats->breastplate);
642 					}
643 					if ( multiplayer == SERVER )
644 					{
645 						// update sprites for clients
646 						if ( entity->skill[10] != entity->sprite )
647 						{
648 							entity->skill[10] = entity->sprite;
649 							serverUpdateEntityBodypart(my, bodypart);
650 						}
651 						if ( entity->getUID() % (TICKS_PER_SECOND * 10) == ticks % (TICKS_PER_SECOND * 10) )
652 						{
653 							serverUpdateEntityBodypart(my, bodypart);
654 						}
655 					}
656 				}
657 				my->setHumanoidLimbOffset(entity, GOBLIN, LIMB_HUMANOID_TORSO);
658 				break;
659 			// right leg
660 			case LIMB_HUMANOID_RIGHTLEG:
661 				if ( multiplayer != CLIENT )
662 				{
663 					if ( myStats->shoes == nullptr )
664 					{
665 						entity->sprite = 182;
666 					}
667 					else
668 					{
669 						my->setBootSprite(entity, SPRITE_BOOT_RIGHT_OFFSET);
670 					}
671 					if ( multiplayer == SERVER )
672 					{
673 						// update sprites for clients
674 						if ( entity->skill[10] != entity->sprite )
675 						{
676 							entity->skill[10] = entity->sprite;
677 							serverUpdateEntityBodypart(my, bodypart);
678 						}
679 						if ( entity->getUID() % (TICKS_PER_SECOND * 10) == ticks % (TICKS_PER_SECOND * 10) )
680 						{
681 							serverUpdateEntityBodypart(my, bodypart);
682 						}
683 					}
684 				}
685 				my->setHumanoidLimbOffset(entity, GOBLIN, LIMB_HUMANOID_RIGHTLEG);
686 				break;
687 			// left leg
688 			case LIMB_HUMANOID_LEFTLEG:
689 				if ( multiplayer != CLIENT )
690 				{
691 					if ( myStats->shoes == nullptr )
692 					{
693 						entity->sprite = 181;
694 					}
695 					else
696 					{
697 						my->setBootSprite(entity, SPRITE_BOOT_LEFT_OFFSET);
698 					}
699 					if ( multiplayer == SERVER )
700 					{
701 						// update sprites for clients
702 						if ( entity->skill[10] != entity->sprite )
703 						{
704 							entity->skill[10] = entity->sprite;
705 							serverUpdateEntityBodypart(my, bodypart);
706 						}
707 						if ( entity->getUID() % (TICKS_PER_SECOND * 10) == ticks % (TICKS_PER_SECOND * 10) )
708 						{
709 							serverUpdateEntityBodypart(my, bodypart);
710 						}
711 					}
712 				}
713 				my->setHumanoidLimbOffset(entity, GOBLIN, LIMB_HUMANOID_LEFTLEG);
714 				break;
715 			// right arm
716 			case LIMB_HUMANOID_RIGHTARM:
717 			{
718 				node_t* weaponNode = list_Node(&my->children, 7);
719 				if ( weaponNode )
720 				{
721 					Entity* weapon = (Entity*)weaponNode->element;
722 					if ( MONSTER_ARMBENDED || (weapon->flags[INVISIBLE] && my->monsterState == MONSTER_STATE_WAIT) )
723 					{
724 						// if weapon invisible and I'm not attacking, relax arm.
725 						entity->focalx = limbs[GOBLIN][4][0]; // 0
726 						entity->focaly = limbs[GOBLIN][4][1]; // 0
727 						entity->focalz = limbs[GOBLIN][4][2]; // 2
728 						entity->sprite = 178;
729 					}
730 					else
731 					{
732 						// else flex arm.
733 						entity->focalx = limbs[GOBLIN][4][0] + 0.75;
734 						entity->focaly = limbs[GOBLIN][4][1];
735 						entity->focalz = limbs[GOBLIN][4][2] - 0.75;
736 						entity->sprite = 179;
737 					}
738 				}
739 				my->setHumanoidLimbOffset(entity, GOBLIN, LIMB_HUMANOID_RIGHTARM);
740 				entity->yaw += MONSTER_WEAPONYAW;
741 				break;
742 			// left arm
743 			}
744 			case LIMB_HUMANOID_LEFTARM:
745 			{
746 				shieldarm = entity;
747 				node_t* shieldNode = list_Node(&my->children, 8);
748 				if ( shieldNode )
749 				{
750 					Entity* shield = (Entity*)shieldNode->element;
751 					if ( shield->flags[INVISIBLE] && my->monsterState == MONSTER_STATE_WAIT )
752 					{
753 						entity->focalx = limbs[GOBLIN][5][0]; // 0
754 						entity->focaly = limbs[GOBLIN][5][1]; // 0
755 						entity->focalz = limbs[GOBLIN][5][2]; // 2
756 						entity->sprite = 176;
757 					}
758 					else
759 					{
760 						entity->focalx = limbs[GOBLIN][5][0] + 0.75;
761 						entity->focaly = limbs[GOBLIN][5][1];
762 						entity->focalz = limbs[GOBLIN][5][2] - 0.75;
763 						entity->sprite = 177;
764 					}
765 				}
766 				my->setHumanoidLimbOffset(entity, GOBLIN, LIMB_HUMANOID_LEFTARM);
767 				if ( my->monsterDefend && my->monsterAttack == 0 )
768 				{
769 					MONSTER_SHIELDYAW = PI / 5;
770 				}
771 				else
772 				{
773 					MONSTER_SHIELDYAW = 0;
774 				}
775 				entity->yaw += MONSTER_SHIELDYAW;
776 				break;
777 			}
778 			// weapon
779 			case LIMB_HUMANOID_WEAPON:
780 				if ( multiplayer != CLIENT )
781 				{
782 					if ( myStats->weapon == nullptr || myStats->EFFECTS[EFF_INVISIBLE] || wearingring ) //TODO: isInvisible()?
783 					{
784 						entity->flags[INVISIBLE] = true;
785 					}
786 					else
787 					{
788 						entity->sprite = itemModel(myStats->weapon);
789 						if ( itemCategory(myStats->weapon) == SPELLBOOK )
790 						{
791 							entity->flags[INVISIBLE] = true;
792 						}
793 						else
794 						{
795 							entity->flags[INVISIBLE] = false;
796 						}
797 					}
798 					if ( multiplayer == SERVER )
799 					{
800 						// update sprites for clients
801 						if ( entity->skill[10] != entity->sprite )
802 						{
803 							entity->skill[10] = entity->sprite;
804 							serverUpdateEntityBodypart(my, bodypart);
805 						}
806 						if ( entity->skill[11] != entity->flags[INVISIBLE] )
807 						{
808 							entity->skill[11] = entity->flags[INVISIBLE];
809 							serverUpdateEntityBodypart(my, bodypart);
810 						}
811 						if ( entity->getUID() % (TICKS_PER_SECOND * 10) == ticks % (TICKS_PER_SECOND * 10) )
812 						{
813 							serverUpdateEntityBodypart(my, bodypart);
814 						}
815 					}
816 				}
817 				else
818 				{
819 					if ( entity->sprite <= 0 )
820 					{
821 						entity->flags[INVISIBLE] = true;
822 					}
823 				}
824 				if ( weaponarm != nullptr )
825 				{
826 					my->handleHumanoidWeaponLimb(entity, weaponarm);
827 				}
828 				break;
829 			// shield
830 			case 8:
831 				if ( multiplayer != CLIENT )
832 				{
833 					if ( myStats->shield == nullptr )
834 					{
835 						entity->flags[INVISIBLE] = true;
836 						entity->sprite = 0;
837 					}
838 					else
839 					{
840 						entity->flags[INVISIBLE] = false;
841 						entity->sprite = itemModel(myStats->shield);
842 						if ( itemTypeIsQuiver(myStats->shield->type) )
843 						{
844 							entity->handleQuiverThirdPersonModel(*myStats);
845 						}
846 					}
847 					if ( myStats->EFFECTS[EFF_INVISIBLE] || wearingring ) //TODO: isInvisible()?
848 					{
849 						entity->flags[INVISIBLE] = true;
850 					}
851 					if ( multiplayer == SERVER )
852 					{
853 						// update sprites for clients
854 						if ( entity->skill[10] != entity->sprite )
855 						{
856 							entity->skill[10] = entity->sprite;
857 							serverUpdateEntityBodypart(my, bodypart);
858 						}
859 						if ( entity->skill[11] != entity->flags[INVISIBLE] )
860 						{
861 							entity->skill[11] = entity->flags[INVISIBLE];
862 							serverUpdateEntityBodypart(my, bodypart);
863 						}
864 						if ( entity->getUID() % (TICKS_PER_SECOND * 10) == ticks % (TICKS_PER_SECOND * 10) )
865 						{
866 							serverUpdateEntityBodypart(my, bodypart);
867 						}
868 					}
869 				}
870 				else
871 				{
872 					if ( entity->sprite <= 0 )
873 					{
874 						entity->flags[INVISIBLE] = true;
875 					}
876 				}
877 				my->handleHumanoidShieldLimb(entity, shieldarm);
878 				break;
879 			// cloak
880 			case LIMB_HUMANOID_CLOAK:
881 				if ( multiplayer != CLIENT )
882 				{
883 					if ( myStats->cloak == nullptr || myStats->EFFECTS[EFF_INVISIBLE] || wearingring ) //TODO: isInvisible()?
884 					{
885 						entity->flags[INVISIBLE] = true;
886 					}
887 					else
888 					{
889 						entity->flags[INVISIBLE] = false;
890 						entity->sprite = itemModel(myStats->cloak);
891 					}
892 					if ( multiplayer == SERVER )
893 					{
894 						// update sprites for clients
895 						if ( entity->skill[10] != entity->sprite )
896 						{
897 							entity->skill[10] = entity->sprite;
898 							serverUpdateEntityBodypart(my, bodypart);
899 						}
900 						if ( entity->skill[11] != entity->flags[INVISIBLE] )
901 						{
902 							entity->skill[11] = entity->flags[INVISIBLE];
903 							serverUpdateEntityBodypart(my, bodypart);
904 						}
905 						if ( entity->getUID() % (TICKS_PER_SECOND * 10) == ticks % (TICKS_PER_SECOND * 10) )
906 						{
907 							serverUpdateEntityBodypart(my, bodypart);
908 						}
909 					}
910 				}
911 				else
912 				{
913 					if ( entity->sprite <= 0 )
914 					{
915 						entity->flags[INVISIBLE] = true;
916 					}
917 				}
918 				entity->x -= cos(my->yaw);
919 				entity->y -= sin(my->yaw);
920 				entity->yaw += PI / 2;
921 				break;
922 			// helm
923 			case LIMB_HUMANOID_HELMET:
924 				helmet = entity;
925 				entity->focalx = limbs[GOBLIN][9][0]; // 0
926 				entity->focaly = limbs[GOBLIN][9][1]; // 0
927 				entity->focalz = limbs[GOBLIN][9][2]; // -2
928 				entity->pitch = my->pitch;
929 				entity->roll = 0;
930 				if ( multiplayer != CLIENT )
931 				{
932 					entity->sprite = itemModel(myStats->helmet);
933 					if ( myStats->helmet == nullptr || myStats->EFFECTS[EFF_INVISIBLE] || wearingring ) //TODO: isInvisible()?
934 					{
935 						entity->flags[INVISIBLE] = true;
936 					}
937 					else
938 					{
939 						entity->flags[INVISIBLE] = false;
940 					}
941 					if ( multiplayer == SERVER )
942 					{
943 						// update sprites for clients
944 						if ( entity->skill[10] != entity->sprite )
945 						{
946 							entity->skill[10] = entity->sprite;
947 							serverUpdateEntityBodypart(my, bodypart);
948 						}
949 						if ( entity->skill[11] != entity->flags[INVISIBLE] )
950 						{
951 							entity->skill[11] = entity->flags[INVISIBLE];
952 							serverUpdateEntityBodypart(my, bodypart);
953 						}
954 						if ( entity->getUID() % (TICKS_PER_SECOND * 10) == ticks % (TICKS_PER_SECOND * 10) )
955 						{
956 							serverUpdateEntityBodypart(my, bodypart);
957 						}
958 					}
959 				}
960 				else
961 				{
962 					if ( entity->sprite <= 0 )
963 					{
964 						entity->flags[INVISIBLE] = true;
965 					}
966 				}
967 				my->setHelmetLimbOffset(entity);
968 				break;
969 			// mask
970 			case LIMB_HUMANOID_MASK:
971 				entity->focalx = limbs[GOBLIN][10][0]; // 0
972 				entity->focaly = limbs[GOBLIN][10][1]; // 0
973 				entity->focalz = limbs[GOBLIN][10][2]; // .25
974 				entity->pitch = my->pitch;
975 				entity->roll = PI / 2;
976 				if ( multiplayer != CLIENT )
977 				{
978 					bool hasSteelHelm = false;
979 					if ( myStats->helmet )
980 					{
981 						if ( myStats->helmet->type == STEEL_HELM
982 							|| myStats->helmet->type == CRYSTAL_HELM
983 							|| myStats->helmet->type == ARTIFACT_HELM )
984 						{
985 							hasSteelHelm = true;
986 						}
987 					}
988 					if ( myStats->mask == nullptr || myStats->EFFECTS[EFF_INVISIBLE] || wearingring || hasSteelHelm ) //TODO: isInvisible()?
989 					{
990 						entity->flags[INVISIBLE] = true;
991 					}
992 					else
993 					{
994 						entity->flags[INVISIBLE] = false;
995 					}
996 					if ( myStats->mask != nullptr )
997 					{
998 						if ( myStats->mask->type == TOOL_GLASSES )
999 						{
1000 							entity->sprite = 165; // GlassesWorn.vox
1001 						}
1002 						else
1003 						{
1004 							entity->sprite = itemModel(myStats->mask);
1005 						}
1006 					}
1007 					if ( multiplayer == SERVER )
1008 					{
1009 						// update sprites for clients
1010 						if ( entity->skill[10] != entity->sprite )
1011 						{
1012 							entity->skill[10] = entity->sprite;
1013 							serverUpdateEntityBodypart(my, bodypart);
1014 						}
1015 						if ( entity->skill[11] != entity->flags[INVISIBLE] )
1016 						{
1017 							entity->skill[11] = entity->flags[INVISIBLE];
1018 							serverUpdateEntityBodypart(my, bodypart);
1019 						}
1020 						if ( entity->getUID() % (TICKS_PER_SECOND * 10) == ticks % (TICKS_PER_SECOND * 10) )
1021 						{
1022 							serverUpdateEntityBodypart(my, bodypart);
1023 						}
1024 					}
1025 				}
1026 				else
1027 				{
1028 					if ( entity->sprite <= 0 )
1029 					{
1030 						entity->flags[INVISIBLE] = true;
1031 					}
1032 				}
1033 
1034 				if ( entity->sprite != 165 )
1035 				{
1036 					if ( entity->sprite == items[MASK_SHAMAN].index )
1037 					{
1038 						entity->roll = 0;
1039 						my->setHelmetLimbOffset(entity);
1040 						my->setHelmetLimbOffsetWithMask(helmet, entity);
1041 					}
1042 					else
1043 					{
1044 						entity->focalx = limbs[GOBLIN][10][0] + .35; // .35
1045 						entity->focaly = limbs[GOBLIN][10][1] - 2; // -2
1046 						entity->focalz = limbs[GOBLIN][10][2]; // .25
1047 					}
1048 				}
1049 				else
1050 				{
1051 					entity->focalx = limbs[GOBLIN][10][0] + .25; // .25
1052 					entity->focaly = limbs[GOBLIN][10][1] - 2.25; // -2.25
1053 					entity->focalz = limbs[GOBLIN][10][2]; // .25
1054 				}
1055 				break;
1056 		}
1057 	}
1058 	// rotate shield a bit
1059 	node_t* shieldNode = list_Node(&my->children, 8);
1060 	if ( shieldNode )
1061 	{
1062 		Entity* shieldEntity = (Entity*)shieldNode->element;
1063 		if ( shieldEntity->sprite != items[TOOL_TORCH].index && shieldEntity->sprite != items[TOOL_LANTERN].index && shieldEntity->sprite != items[TOOL_CRYSTALSHARD].index )
1064 		{
1065 			shieldEntity->yaw -= PI / 6;
1066 		}
1067 	}
1068 	if ( MONSTER_ATTACK > 0 && MONSTER_ATTACK <= MONSTER_POSE_MAGIC_CAST3 )
1069 	{
1070 		MONSTER_ATTACKTIME++;
1071 	}
1072 	else if ( MONSTER_ATTACK == 0 )
1073 	{
1074 		MONSTER_ATTACKTIME = 0;
1075 	}
1076 	else
1077 	{
1078 		// do nothing, don't reset attacktime or increment it.
1079 	}
1080 }
1081 
goblinCanWieldItem(const Item & item) const1082 bool Entity::goblinCanWieldItem(const Item& item) const
1083 {
1084 	Stat* myStats = getStats();
1085 	if ( !myStats )
1086 	{
1087 		return false;
1088 	}
1089 
1090 	if ( monsterAllyIndex >= 0 && (monsterAllyClass != ALLY_CLASS_MIXED || item.interactNPCUid == getUID()) )
1091 	{
1092 		return monsterAllyEquipmentInClass(item);
1093 	}
1094 
1095 	switch ( itemCategory(&item) )
1096 	{
1097 		case WEAPON:
1098 			return true;
1099 		case ARMOR:
1100 			return true;
1101 		case MAGICSTAFF:
1102 			return true;
1103 		case THROWN:
1104 			return true;
1105 		case TOOL:
1106 			if ( itemTypeIsQuiver(item.type) )
1107 			{
1108 				return true;
1109 			}
1110 			break;
1111 		default:
1112 			return false;
1113 	}
1114 
1115 	return false;
1116 }
1117