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