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