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