1 /*-------------------------------------------------------------------------------
2
3 BARONY
4 File: monster_incubus.cpp
5 Desc: implements all of the incubus 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 #include "magic/magic.hpp"
23
initIncubus(Entity * my,Stat * myStats)24 void initIncubus(Entity* my, Stat* myStats)
25 {
26 int c;
27 node_t* node;
28
29 //Sprite 445 = incubus head sprite
30 my->initMonster(445);
31
32 if ( multiplayer != CLIENT )
33 {
34 MONSTER_SPOTSND = 282;
35 MONSTER_SPOTVAR = 3;
36 MONSTER_IDLESND = 276;
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 if ( !strncmp(myStats->name, "inner demon", strlen("inner demon")) )
49 {
50 Entity* parent = uidToEntity(my->parent);
51 if ( !parent )
52 {
53 myStats->HP = 0;
54 }
55 else
56 {
57 Stat* parentStats = parent->getStats();
58 if ( !parentStats )
59 {
60 myStats->HP = 0;
61 }
62 else
63 {
64 myStats->HP = parentStats->HP;
65 myStats->MAXHP = myStats->HP;
66 myStats->OLDHP = myStats->HP;
67 myStats->STR = -999;
68 myStats->DEX = std::min(parentStats->DEX, 15);
69 // pretend the parent wasn't defending as this gets added in AC() call.
70 bool wasDefending = parentStats->defending;
71 parentStats->defending = false;
72 myStats->CON = AC(parentStats);
73 parentStats->defending = wasDefending;
74
75 myStats->INT = parentStats->INT;
76 myStats->PER = parentStats->PER;
77 myStats->CHR = parentStats->CHR;
78 myStats->LVL = parentStats->LVL;
79 myStats->GOLD = 0;
80
81 myStats->weapon = newItem(TOOL_WHIP, EXCELLENT, 0, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, true, nullptr);
82 /*if ( parentStats->shield )
83 {
84 myStats->shield = newItem(BRONZE_SWORD, EXCELLENT, 0, 1, 0, true, nullptr);
85 copyItem(myStats->shield, parentStats->shield);
86 myStats->shield->appearance = MONSTER_ITEM_UNDROPPABLE_APPEARANCE;
87 }
88 if ( parentStats->helmet )
89 {
90 myStats->helmet = newItem(BRONZE_SWORD, EXCELLENT, 0, 1, 0, true, nullptr);
91 copyItem(myStats->helmet, parentStats->helmet);
92 myStats->helmet->appearance = MONSTER_ITEM_UNDROPPABLE_APPEARANCE;
93 }
94 if ( parentStats->breastplate )
95 {
96 myStats->breastplate = newItem(BRONZE_SWORD, EXCELLENT, 0, 1, 0, true, nullptr);
97 copyItem(myStats->breastplate, parentStats->breastplate);
98 myStats->breastplate->appearance = MONSTER_ITEM_UNDROPPABLE_APPEARANCE;
99 }
100 if ( parentStats->shoes )
101 {
102 myStats->shoes = newItem(BRONZE_SWORD, EXCELLENT, 0, 1, 0, true, nullptr);
103 copyItem(myStats->shoes, parentStats->shoes);
104 myStats->shoes->appearance = MONSTER_ITEM_UNDROPPABLE_APPEARANCE;
105 }
106 if ( parentStats->gloves )
107 {
108 myStats->gloves = newItem(BRONZE_SWORD, EXCELLENT, 0, 1, 0, true, nullptr);
109 copyItem(myStats->gloves, parentStats->gloves);
110 myStats->gloves->appearance = MONSTER_ITEM_UNDROPPABLE_APPEARANCE;
111 }
112 if ( parentStats->cloak )
113 {
114 myStats->cloak = newItem(BRONZE_SWORD, EXCELLENT, 0, 1, 0, true, nullptr);
115 copyItem(myStats->cloak, parentStats->cloak);
116 myStats->cloak->appearance = MONSTER_ITEM_UNDROPPABLE_APPEARANCE;
117 }
118 if ( parentStats->ring )
119 {
120 myStats->ring = newItem(BRONZE_SWORD, EXCELLENT, 0, 1, 0, true, nullptr);
121 copyItem(myStats->ring, parentStats->ring);
122 myStats->ring->appearance = MONSTER_ITEM_UNDROPPABLE_APPEARANCE;
123 }
124 if ( parentStats->amulet )
125 {
126 myStats->amulet = newItem(BRONZE_SWORD, EXCELLENT, 0, 1, 0, true, nullptr);
127 copyItem(myStats->amulet, parentStats->amulet);
128 myStats->amulet->appearance = MONSTER_ITEM_UNDROPPABLE_APPEARANCE;
129 }
130 if ( parentStats->mask )
131 {
132 myStats->mask = newItem(BRONZE_SWORD, EXCELLENT, 0, 1, 0, true, nullptr);
133 copyItem(myStats->mask, parentStats->mask);
134 myStats->mask->appearance = MONSTER_ITEM_UNDROPPABLE_APPEARANCE;
135 }*/
136 }
137 }
138 }
139 else
140 {
141 bool lesserMonster = false;
142 if ( !strncmp(myStats->name, "lesser incubus", strlen("lesser incubus")) )
143 {
144 lesserMonster = true;
145 myStats->HP = 80;
146 myStats->MAXHP = myStats->HP;
147 myStats->OLDHP = myStats->HP;
148 myStats->STR = 12;
149 myStats->DEX = 6;
150 myStats->CON = 3;
151 myStats->INT = -2;
152 myStats->PER = 5;
153 myStats->CHR = 5;
154 myStats->EXP = 0;
155 myStats->LVL = 15;
156 }
157 // apply random stat increases if set in stat_shared.cpp or editor
158 setRandomMonsterStats(myStats);
159
160 // generate 6 items max, less if there are any forced items from boss variants
161 int customItemsToGenerate = ITEM_CUSTOM_SLOT_LIMIT;
162
163 // boss variants
164 if ( rand() % 50 || my->flags[USERFLAG2] || myStats->MISC_FLAGS[STAT_FLAG_DISABLE_MINIBOSS] )
165 {
166
167 }
168 else
169 {
170 /*myStats->DEX = 10;
171 strcpy(myStats->name, "Lilith");
172 for ( c = 0; c < 2; c++ )
173 {
174 Entity* entity = summonMonster(SUCCUBUS, my->x, my->y);
175 if ( entity )
176 {
177 entity->parent = my->getUID();
178 }
179 }*/
180 }
181
182 // random effects
183
184 // generates equipment and weapons if available from editor
185 createMonsterEquipment(myStats);
186
187 // create any custom inventory items from editor if available
188 createCustomInventory(myStats, customItemsToGenerate);
189
190 // count if any custom inventory items from editor
191 int customItems = countCustomItems(myStats); //max limit of 6 custom items per entity.
192
193 // count any inventory items set to default in edtior
194 int defaultItems = countDefaultItems(myStats);
195
196 my->setHardcoreStats(*myStats);
197
198 // always give special spell to incubus, undroppable.
199 newItem(SPELLBOOK_STEAL_WEAPON, DECREPIT, 0, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, &myStats->inventory);
200 newItem(SPELLBOOK_CHARM_MONSTER, DECREPIT, 0, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, &myStats->inventory);
201
202 if ( rand() % 4 == 0 ) // 1 in 4
203 {
204 newItem(POTION_CONFUSION, SERVICABLE, 0, 0 + rand() % 3, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, &myStats->inventory);
205 }
206 else // 3 in 4
207 {
208 newItem(POTION_BOOZE, SERVICABLE, 0, 1 + rand() % 3, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, &myStats->inventory);
209 }
210
211
212 // generate the default inventory items for the monster, provided the editor sprite allowed enough default slots
213 switch ( defaultItems )
214 {
215 case 6:
216 case 5:
217 case 4:
218 case 3:
219 if ( rand() % 2 == 0 && !lesserMonster ) // 1 in 2
220 {
221 newItem(MAGICSTAFF_COLD, SERVICABLE, 0, 1, rand(), false, &myStats->inventory);
222 }
223 case 2:
224 if ( rand() % 5 == 0 ) // 1 in 5
225 {
226 newItem(POTION_CONFUSION, SERVICABLE, 0, 1 + rand() % 2, rand(), false, &myStats->inventory);
227 }
228 case 1:
229 if ( rand() % 3 == 0 ) // 1 in 3
230 {
231 newItem(POTION_BOOZE, SERVICABLE, 0, 1 + rand() % 3, rand(), false, &myStats->inventory);
232 }
233 break;
234 default:
235 break;
236 }
237
238 //give weapon
239 if ( lesserMonster )
240 {
241 if ( myStats->weapon == nullptr && myStats->EDITOR_ITEMS[ITEM_SLOT_WEAPON] == 1 )
242 {
243 switch ( rand() % 10 )
244 {
245 case 0:
246 case 1:
247 myStats->weapon = newItem(CROSSBOW, static_cast<Status>(WORN + rand() % 2), -1 + rand() % 3, 1, rand(), false, nullptr);
248 break;
249 case 2:
250 case 3:
251 case 4:
252 case 5:
253 case 6:
254 case 7:
255 case 8:
256 myStats->weapon = newItem(STEEL_HALBERD, static_cast<Status>(WORN + rand() % 2), -1 + rand() % 3, 1, rand(), false, nullptr);
257 break;
258 case 9:
259 myStats->weapon = newItem(MAGICSTAFF_COLD, static_cast<Status>(WORN + rand() % 2), -1 + rand() % 3, 1, rand(), false, nullptr);
260 break;
261 }
262 }
263 }
264 else if ( myStats->weapon == nullptr && myStats->EDITOR_ITEMS[ITEM_SLOT_WEAPON] == 1 )
265 {
266 switch ( rand() % 10 )
267 {
268 case 0:
269 case 1:
270 case 2:
271 case 3:
272 myStats->weapon = newItem(CRYSTAL_SPEAR, static_cast<Status>(WORN + rand() % 2), -1 + rand() % 3, 1, rand(), false, nullptr);
273 break;
274 case 4:
275 case 5:
276 case 6:
277 myStats->weapon = newItem(CROSSBOW, static_cast<Status>(WORN + rand() % 2), -1 + rand() % 3, 1, rand(), false, nullptr);
278 break;
279 case 7:
280 case 8:
281 myStats->weapon = newItem(STEEL_HALBERD, static_cast<Status>(WORN + rand() % 2), -1 + rand() % 3, 1, rand(), false, nullptr);
282 break;
283 case 9:
284 myStats->weapon = newItem(MAGICSTAFF_COLD, static_cast<Status>(WORN + rand() % 2), -1 + rand() % 3, 1, rand(), false, nullptr);
285 break;
286 }
287 }
288
289 // give shield
290 if ( myStats->shield == nullptr && myStats->EDITOR_ITEMS[ITEM_SLOT_SHIELD] == 1 )
291 {
292 if ( myStats->weapon && isRangedWeapon(*myStats->weapon) )
293 {
294 my->monsterGenerateQuiverItem(myStats);
295 }
296 else
297 {
298 switch ( rand() % 10 )
299 {
300 case 0:
301 case 1:
302 case 2:
303 case 3:
304 case 4:
305 case 5:
306 case 6:
307 case 7:
308 break;
309 case 8:
310 case 9:
311 myStats->shield = newItem(MIRROR_SHIELD, static_cast<Status>(WORN + rand() % 2), -1 + rand() % 3, 1, rand(), false, nullptr);
312 break;
313 }
314 }
315 }
316 }
317 }
318 }
319
320 // torso
321 Entity* entity = newEntity(446, 0, map.entities, nullptr); //Limb entity.
322 entity->sizex = 4;
323 entity->sizey = 4;
324 entity->skill[2] = my->getUID();
325 entity->flags[PASSABLE] = true;
326 entity->flags[NOUPDATE] = true;
327 entity->flags[USERFLAG2] = my->flags[USERFLAG2];
328 entity->focalx = limbs[INCUBUS][1][0]; // 0
329 entity->focaly = limbs[INCUBUS][1][1]; // 0
330 entity->focalz = limbs[INCUBUS][1][2]; // 0
331 entity->behavior = &actIncubusLimb;
332 entity->parent = my->getUID();
333 node = list_AddNodeLast(&my->children);
334 node->element = entity;
335 node->deconstructor = &emptyDeconstructor;
336 node->size = sizeof(Entity*);
337 my->bodyparts.push_back(entity);
338
339 // right leg
340 entity = newEntity(450, 0, map.entities, nullptr); //Limb entity.
341 entity->sizex = 4;
342 entity->sizey = 4;
343 entity->skill[2] = my->getUID();
344 entity->flags[PASSABLE] = true;
345 entity->flags[NOUPDATE] = true;
346 entity->flags[USERFLAG2] = my->flags[USERFLAG2];
347 entity->focalx = limbs[INCUBUS][2][0]; // 1
348 entity->focaly = limbs[INCUBUS][2][1]; // 0
349 entity->focalz = limbs[INCUBUS][2][2]; // 2
350 entity->behavior = &actIncubusLimb;
351 entity->parent = my->getUID();
352 node = list_AddNodeLast(&my->children);
353 node->element = entity;
354 node->deconstructor = &emptyDeconstructor;
355 node->size = sizeof(Entity*);
356 my->bodyparts.push_back(entity);
357
358 // left leg
359 entity = newEntity(449, 0, map.entities, nullptr); //Limb entity.
360 entity->sizex = 4;
361 entity->sizey = 4;
362 entity->skill[2] = my->getUID();
363 entity->flags[PASSABLE] = true;
364 entity->flags[NOUPDATE] = true;
365 entity->flags[USERFLAG2] = my->flags[USERFLAG2];
366 entity->focalx = limbs[INCUBUS][3][0]; // 1
367 entity->focaly = limbs[INCUBUS][3][1]; // 0
368 entity->focalz = limbs[INCUBUS][3][2]; // 2
369 entity->behavior = &actIncubusLimb;
370 entity->parent = my->getUID();
371 node = list_AddNodeLast(&my->children);
372 node->element = entity;
373 node->deconstructor = &emptyDeconstructor;
374 node->size = sizeof(Entity*);
375 my->bodyparts.push_back(entity);
376
377 // right arm
378 entity = newEntity(448, 0, map.entities, nullptr); //595 //Limb entity.
379 entity->sizex = 4;
380 entity->sizey = 4;
381 entity->skill[2] = my->getUID();
382 entity->flags[PASSABLE] = true;
383 entity->flags[NOUPDATE] = true;
384 entity->flags[USERFLAG2] = my->flags[USERFLAG2];
385 entity->focalx = limbs[INCUBUS][4][0]; // -.25
386 entity->focaly = limbs[INCUBUS][4][1]; // 0
387 entity->focalz = limbs[INCUBUS][4][2]; // 1.5
388 entity->behavior = &actIncubusLimb;
389 entity->parent = my->getUID();
390 node = list_AddNodeLast(&my->children);
391 node->element = entity;
392 node->deconstructor = &emptyDeconstructor;
393 node->size = sizeof(Entity*);
394 my->bodyparts.push_back(entity);
395
396 // left arm
397 entity = newEntity(447, 0, map.entities, nullptr); //447 //Limb entity.
398 entity->sizex = 4;
399 entity->sizey = 4;
400 entity->skill[2] = my->getUID();
401 entity->flags[PASSABLE] = true;
402 entity->flags[NOUPDATE] = true;
403 entity->flags[USERFLAG2] = my->flags[USERFLAG2];
404 entity->focalx = limbs[INCUBUS][5][0]; // -.25
405 entity->focaly = limbs[INCUBUS][5][1]; // 0
406 entity->focalz = limbs[INCUBUS][5][2]; // 1.5
407 entity->behavior = &actIncubusLimb;
408 entity->parent = my->getUID();
409 node = list_AddNodeLast(&my->children);
410 node->element = entity;
411 node->deconstructor = &emptyDeconstructor;
412 node->size = sizeof(Entity*);
413 my->bodyparts.push_back(entity);
414
415 // world weapon
416 entity = newEntity(-1, 0, map.entities, nullptr); //Limb entity.
417 entity->sizex = 4;
418 entity->sizey = 4;
419 entity->skill[2] = my->getUID();
420 entity->flags[PASSABLE] = true;
421 entity->flags[NOUPDATE] = true;
422 entity->flags[USERFLAG2] = my->flags[USERFLAG2];
423 entity->focalx = limbs[INCUBUS][6][0]; //
424 entity->focaly = limbs[INCUBUS][6][1]; //
425 entity->focalz = limbs[INCUBUS][6][2]; //
426 entity->behavior = &actIncubusLimb;
427 entity->parent = my->getUID();
428 entity->pitch = .25;
429 node = list_AddNodeLast(&my->children);
430 node->element = entity;
431 node->deconstructor = &emptyDeconstructor;
432 node->size = sizeof(Entity*);
433 my->bodyparts.push_back(entity);
434
435 // shield
436 entity = newEntity(-1, 0, map.entities, nullptr); //Limb entity.
437 entity->sizex = 4;
438 entity->sizey = 4;
439 entity->skill[2] = my->getUID();
440 entity->flags[PASSABLE] = true;
441 entity->flags[NOUPDATE] = true;
442 entity->flags[USERFLAG2] = my->flags[USERFLAG2];
443 entity->focalx = limbs[INCUBUS][7][0]; //
444 entity->focaly = limbs[INCUBUS][7][1]; //
445 entity->focalz = limbs[INCUBUS][7][2]; //
446 entity->behavior = &actIncubusLimb;
447 entity->parent = my->getUID();
448 node = list_AddNodeLast(&my->children);
449 node->element = entity;
450 node->deconstructor = &emptyDeconstructor;
451 node->size = sizeof(Entity*);
452 my->bodyparts.push_back(entity);
453
454 // cloak
455 entity = newEntity(-1, 0, map.entities, nullptr); //Limb entity.
456 entity->sizex = 4;
457 entity->sizey = 4;
458 entity->skill[2] = my->getUID();
459 entity->scalex = 1.01;
460 entity->scaley = 1.01;
461 entity->scalez = 1.01;
462 entity->flags[PASSABLE] = true;
463 entity->flags[NOUPDATE] = true;
464 entity->flags[INVISIBLE] = true;
465 entity->flags[USERFLAG2] = my->flags[USERFLAG2];
466 entity->focalx = limbs[INCUBUS][8][0]; // 0
467 entity->focaly = limbs[INCUBUS][8][1]; // 0
468 entity->focalz = limbs[INCUBUS][8][2]; // 4
469 entity->behavior = &actIncubusLimb;
470 entity->parent = my->getUID();
471 node = list_AddNodeLast(&my->children);
472 node->element = entity;
473 node->deconstructor = &emptyDeconstructor;
474 node->size = sizeof(Entity*);
475 my->bodyparts.push_back(entity);
476
477 // helmet
478 entity = newEntity(-1, 0, map.entities, nullptr); //Limb entity.
479 entity->sizex = 4;
480 entity->sizey = 4;
481 entity->skill[2] = my->getUID();
482 entity->scalex = 1.01;
483 entity->scaley = 1.01;
484 entity->scalez = 1.01;
485 entity->flags[PASSABLE] = true;
486 entity->flags[NOUPDATE] = true;
487 entity->flags[INVISIBLE] = true;
488 entity->flags[USERFLAG2] = my->flags[USERFLAG2];
489 entity->focalx = limbs[INCUBUS][9][0]; // 0
490 entity->focaly = limbs[INCUBUS][9][1]; // 0
491 entity->focalz = limbs[INCUBUS][9][2]; // -2
492 entity->behavior = &actIncubusLimb;
493 entity->parent = my->getUID();
494 node = list_AddNodeLast(&my->children);
495 node->element = entity;
496 node->deconstructor = &emptyDeconstructor;
497 node->size = sizeof(Entity*);
498 my->bodyparts.push_back(entity);
499
500 // mask
501 entity = newEntity(-1, 0, map.entities, nullptr); //Limb entity.
502 entity->sizex = 4;
503 entity->sizey = 4;
504 entity->skill[2] = my->getUID();
505 entity->flags[PASSABLE] = true;
506 entity->flags[NOUPDATE] = true;
507 entity->flags[INVISIBLE] = true;
508 entity->flags[USERFLAG2] = my->flags[USERFLAG2];
509 entity->focalx = limbs[INCUBUS][10][0]; // 0
510 entity->focaly = limbs[INCUBUS][10][1]; // 0
511 entity->focalz = limbs[INCUBUS][10][2]; // .5
512 entity->behavior = &actIncubusLimb;
513 entity->parent = my->getUID();
514 node = list_AddNodeLast(&my->children);
515 node->element = entity;
516 node->deconstructor = &emptyDeconstructor;
517 node->size = sizeof(Entity*);
518 my->bodyparts.push_back(entity);
519 }
520
actIncubusLimb(Entity * my)521 void actIncubusLimb(Entity* my)
522 {
523 my->actMonsterLimb();
524 }
525
incubusDie(Entity * my)526 void incubusDie(Entity* my)
527 {
528 int c;
529
530 Stat* myStats = my->getStats();
531 if ( myStats && !strncmp(myStats->name, "inner demon", strlen("inner demon")) )
532 {
533 // die, no blood.
534 //spawnMagicEffectParticles(my->x, my->y, my->z, 171);
535 createParticleErupt(my, 983);
536 serverSpawnMiscParticles(my, PARTICLE_EFFECT_ERUPT, 983);
537 //playSoundEntity(my, 279 + rand() % 3, 128);
538 playSoundEntity(my, 178, 128);
539 my->removeMonsterDeathNodes();
540 list_RemoveNode(my->mynode);
541 return;
542 }
543
544 for ( c = 0; c < 5; c++ )
545 {
546 Entity* gib = spawnGib(my);
547 serverSpawnGibForClient(gib);
548 }
549
550 my->spawnBlood();
551
552 playSoundEntity(my, 279 + rand() % 3, 128);
553
554 my->removeMonsterDeathNodes();
555
556 list_RemoveNode(my->mynode);
557 return;
558 }
559
560 #define INCUBUSWALKSPEED .07
561
incubusMoveBodyparts(Entity * my,Stat * myStats,double dist)562 void incubusMoveBodyparts(Entity* my, Stat* myStats, double dist)
563 {
564 node_t* node;
565 Entity* entity = nullptr, *entity2 = nullptr;
566 Entity* rightbody = nullptr;
567 Entity* weaponarm = nullptr;
568 int bodypart;
569 bool wearingring = false;
570
571 // set invisibility //TODO: isInvisible()?
572 if ( multiplayer != CLIENT )
573 {
574 if ( myStats->ring != nullptr )
575 {
576 if ( myStats->ring->type == RING_INVISIBILITY )
577 {
578 wearingring = true;
579 }
580 }
581 if ( myStats->cloak != nullptr )
582 {
583 if ( myStats->cloak->type == CLOAK_INVISIBILITY )
584 {
585 wearingring = true;
586 }
587 }
588 if ( myStats->EFFECTS[EFF_INVISIBLE] == true )
589 {
590 my->flags[INVISIBLE] = true;
591 my->flags[BLOCKSIGHT] = false;
592 bodypart = 0;
593 for (node = my->children.first; node != nullptr; node = node->next)
594 {
595 if ( bodypart < 2 )
596 {
597 bodypart++;
598 continue;
599 }
600 if ( bodypart >= 7 )
601 {
602 break;
603 }
604 entity = (Entity*)node->element;
605 if ( !entity->flags[INVISIBLE] )
606 {
607 entity->flags[INVISIBLE] = true;
608 serverUpdateEntityBodypart(my, bodypart);
609 }
610 bodypart++;
611 }
612 }
613 else
614 {
615 my->flags[INVISIBLE] = false;
616 my->flags[BLOCKSIGHT] = true;
617 bodypart = 0;
618 for (node = my->children.first; node != nullptr; node = node->next)
619 {
620 if ( bodypart < 2 )
621 {
622 bodypart++;
623 continue;
624 }
625 if ( bodypart >= 7 )
626 {
627 break;
628 }
629 entity = (Entity*)node->element;
630 if ( entity->flags[INVISIBLE] )
631 {
632 entity->flags[INVISIBLE] = false;
633 serverUpdateEntityBodypart(my, bodypart);
634 serverUpdateEntityFlag(my, INVISIBLE);
635 }
636 bodypart++;
637 }
638 }
639
640 if ( !strncmp(myStats->name, "inner demon", strlen("inner demon")) )
641 {
642 Entity* parent = uidToEntity(my->parent);
643 if ( !parent )
644 {
645 myStats->HP = 0;
646 }
647 else if ( my->ticks > TICKS_PER_SECOND * 5 )
648 {
649 myStats->HP = 0;
650 }
651 }
652
653 // sleeping
654 if ( myStats->EFFECTS[EFF_ASLEEP] )
655 {
656 my->z = 1.5;
657 }
658 else
659 {
660 my->z = -1;
661 }
662 }
663
664 Entity* shieldarm = nullptr;
665 Entity* helmet = nullptr;
666
667 //Move bodyparts
668 for (bodypart = 0, node = my->children.first; node != nullptr; node = node->next, bodypart++)
669 {
670 if ( bodypart < LIMB_HUMANOID_TORSO )
671 {
672 // post-swing head animation. client doesn't need to adjust the entity pitch, server will handle.
673 if ( multiplayer != CLIENT && bodypart == 1 )
674 {
675 if ( my->monsterAttack != MONSTER_POSE_MAGIC_WINDUP3
676 && my->monsterAttack != MONSTER_POSE_INCUBUS_TELEPORT
677 && my->monsterAttack != MONSTER_POSE_INCUBUS_TAUNT )
678 {
679 limbAnimateToLimit(my, ANIMATE_PITCH, 0.1, 0, false, 0.0);
680 }
681 }
682 continue;
683 }
684 entity = (Entity*)node->element;
685 entity->x = my->x;
686 entity->y = my->y;
687 entity->z = my->z;
688 if ( MONSTER_ATTACK == MONSTER_POSE_MAGIC_WINDUP1 && bodypart == LIMB_HUMANOID_RIGHTARM )
689 {
690 // don't let the creatures's yaw move the casting arm
691 }
692 else
693 {
694 entity->yaw = my->yaw;
695 }
696 if ( bodypart == LIMB_HUMANOID_RIGHTLEG || bodypart == LIMB_HUMANOID_LEFTARM )
697 {
698 if ( bodypart == LIMB_HUMANOID_LEFTARM &&
699 ((my->monsterSpecialState == INCUBUS_STEAL && my->monsterAttack != 0 ) ||
700 my->monsterAttack == MONSTER_POSE_INCUBUS_TELEPORT
701 || my->monsterAttack == MONSTER_POSE_INCUBUS_TAUNT) )
702 {
703 // leftarm follows the right arm during special steal state/teleport attack
704 // will not work when shield is visible
705 // else animate normally.
706 node_t* shieldNode = list_Node(&my->children, 8);
707 if ( shieldNode )
708 {
709 Entity* shield = (Entity*)shieldNode->element;
710 if ( shield->flags[INVISIBLE] )
711 {
712 Entity* weaponarm = nullptr;
713 node_t* weaponarmNode = list_Node(&my->children, LIMB_HUMANOID_RIGHTARM);
714 if ( weaponarmNode )
715 {
716 weaponarm = (Entity*)weaponarmNode->element;
717 }
718 else
719 {
720 return;
721 }
722 entity->pitch = weaponarm->pitch;
723 entity->roll = -weaponarm->roll;
724 }
725 }
726 }
727 else
728 {
729 my->humanoidAnimateWalk(entity, node, bodypart, INCUBUSWALKSPEED, dist, 0.4);
730 }
731 }
732 else if ( bodypart == LIMB_HUMANOID_LEFTLEG || bodypart == LIMB_HUMANOID_RIGHTARM || bodypart == LIMB_HUMANOID_CLOAK )
733 {
734 // left leg, right arm, cloak.
735 if ( bodypart == LIMB_HUMANOID_RIGHTARM )
736 {
737 weaponarm = entity;
738 if ( my->monsterAttack > 0 )
739 {
740 Entity* rightbody = nullptr;
741 // set rightbody to left leg.
742 node_t* rightbodyNode = list_Node(&my->children, LIMB_HUMANOID_LEFTLEG);
743 if ( rightbodyNode )
744 {
745 rightbody = (Entity*)rightbodyNode->element;
746 }
747 else
748 {
749 return;
750 }
751
752 // potion special throw
753 if ( my->monsterAttack == MONSTER_POSE_SPECIAL_WINDUP1 )
754 {
755 if ( my->monsterAttackTime == 0 )
756 {
757 // init rotations
758 weaponarm->pitch = 0;
759 my->monsterArmbended = 0;
760 my->monsterWeaponYaw = 0;
761 weaponarm->roll = 0;
762 createParticleDot(my);
763 }
764
765 limbAnimateToLimit(weaponarm, ANIMATE_PITCH, -0.25, 5 * PI / 4, false, 0.0);
766
767 if ( my->monsterAttackTime >= ANIMATE_DURATION_WINDUP * 4 / (monsterGlobalAnimationMultiplier / 10.0) )
768 {
769 if ( multiplayer != CLIENT )
770 {
771 my->attack(1, 0, nullptr);
772 }
773 }
774 ++my->monsterAttackTime;
775 }
776 else if ( my->monsterAttack == MONSTER_POSE_MAGIC_WINDUP3 )
777 {
778 if ( my->monsterAttackTime == 0 )
779 {
780 // init rotations
781 weaponarm->pitch = 0;
782 my->monsterArmbended = 0;
783 my->monsterWeaponYaw = 0;
784 weaponarm->roll = 0;
785 weaponarm->skill[1] = 0;
786 createParticleDot(my);
787 // play casting sound
788 playSoundEntityLocal(my, 170, 64);
789 // monster scream
790 playSoundEntityLocal(my, MONSTER_SPOTSND + 1, 128);
791 }
792
793 limbAnimateToLimit(weaponarm, ANIMATE_PITCH, -0.25, 5 * PI / 4, false, 0.0);
794 if ( multiplayer != CLIENT )
795 {
796 // move the head and weapon yaw
797 limbAnimateToLimit(my, ANIMATE_PITCH, -0.1, 14 * PI / 8, true, 0.1);
798 limbAnimateToLimit(my, ANIMATE_WEAPON_YAW, 0.25, 1 * PI / 8, false, 0.0);
799 }
800
801 if ( my->monsterAttackTime >= 3 * ANIMATE_DURATION_WINDUP / (monsterGlobalAnimationMultiplier / 10.0) )
802 {
803 if ( multiplayer != CLIENT )
804 {
805 // throw the spell
806 my->attack(MONSTER_POSE_MELEE_WINDUP1, 0, nullptr);
807 }
808 }
809 }
810 // teleport animation
811 else if( my->monsterAttack == MONSTER_POSE_INCUBUS_TELEPORT
812 || my->monsterAttack == MONSTER_POSE_INCUBUS_TAUNT )
813 {
814 if ( my->monsterAttackTime == 0 )
815 {
816 // init rotations
817 weaponarm->pitch = 0;
818 my->monsterArmbended = 0;
819 my->monsterWeaponYaw = 0;
820 weaponarm->roll = 0;
821 weaponarm->skill[1] = 0; // use this for direction of animation
822 // monster scream
823 if ( my->monsterAttack == MONSTER_POSE_INCUBUS_TAUNT )
824 {
825 playSoundEntityLocal(my, 276 + rand() % 3, 128);
826 }
827 else
828 {
829 playSoundEntityLocal(my, 282 + 2, 128);
830 }
831 if ( multiplayer != CLIENT )
832 {
833 // set overshoot for head, freeze incubus in place
834 my->monsterAnimationLimbOvershoot = ANIMATE_OVERSHOOT_TO_SETPOINT;
835 myStats->EFFECTS[EFF_PARALYZED] = true;
836 if ( my->monsterAttack == MONSTER_POSE_INCUBUS_TAUNT )
837 {
838 myStats->EFFECTS_TIMERS[EFF_PARALYZED] = 250;
839 }
840 else
841 {
842 myStats->EFFECTS_TIMERS[EFF_PARALYZED] = 100;
843 }
844 }
845 }
846
847 if ( weaponarm->skill[1] == 0 )
848 {
849 // wind up arm, change direction when setpoint reached.
850 if ( limbAnimateToLimit(weaponarm, ANIMATE_PITCH, -0.2, 13 * PI / 8, false, 0.0) )
851 {
852 weaponarm->skill[1] = 1;
853 }
854 }
855 else
856 {
857 // swing and flare out arm.
858 limbAnimateToLimit(weaponarm, ANIMATE_PITCH, 0.1, 2 * PI / 16, false, 0.0);
859 limbAnimateToLimit(weaponarm, ANIMATE_ROLL, -0.1, 31 * PI / 16, false, 0.0);
860 }
861
862 if ( multiplayer != CLIENT )
863 {
864 // move the head back and forth.
865 // keeps between PI and 0 (2PI) so we can lower the head at completion to 0 (2PI).
866 if ( my->monsterAnimationLimbOvershoot >= ANIMATE_OVERSHOOT_TO_SETPOINT )
867 {
868 if ( my->monsterAttack == MONSTER_POSE_INCUBUS_TAUNT )
869 {
870 limbAnimateWithOvershoot(my, ANIMATE_PITCH, -0.05, 7 * PI / 4, -0.05, 15 * PI / 8, ANIMATE_DIR_POSITIVE);
871 }
872 else
873 {
874 limbAnimateWithOvershoot(my, ANIMATE_PITCH, -0.1, 7 * PI / 4, -0.1, 15 * PI / 8, ANIMATE_DIR_POSITIVE);
875 }
876 }
877 else
878 {
879 // after 1 cycle is complete, reset the overshoot flag and repeat the animation.
880 my->monsterAnimationLimbOvershoot = ANIMATE_OVERSHOOT_TO_SETPOINT;
881 }
882 }
883
884 // animation takes roughly 2 seconds.
885 int duration = 10;
886 if ( my->monsterAttack == MONSTER_POSE_INCUBUS_TAUNT )
887 {
888 duration = 50;
889 }
890 if ( my->monsterAttackTime >= ANIMATE_DURATION_WINDUP * duration / (monsterGlobalAnimationMultiplier / 10.0) )
891 {
892 weaponarm->skill[0] = rightbody->skill[0];
893 weaponarm->pitch = rightbody->pitch;
894 weaponarm->roll = -PI / 32;
895 my->monsterArmbended = 0;
896 my->monsterAttack = 0;
897 Entity* leftarm = nullptr;
898 node_t* leftarmNode = list_Node(&my->children, LIMB_HUMANOID_LEFTARM);
899 if ( leftarmNode )
900 {
901 leftarm = (Entity*)leftarmNode->element;
902 leftarm->roll = PI / 32;
903 }
904 else
905 {
906 return;
907 }
908 if ( multiplayer != CLIENT )
909 {
910 my->monsterAnimationLimbOvershoot = ANIMATE_OVERSHOOT_NONE;
911 }
912 }
913 ++my->monsterAttackTime; // manually increment timer
914 }
915 else
916 {
917 my->handleWeaponArmAttack(weaponarm);
918 if ( my->monsterAttack != MONSTER_POSE_MELEE_WINDUP2 && my->monsterAttack != 2 )
919 {
920 // flare out the weapon arm to match neutral arm position.
921 // breaks the horizontal chop attack animation so we skip it.
922 weaponarm->roll = -PI / 32;
923 }
924 }
925 }
926 }
927 else if ( bodypart == LIMB_HUMANOID_CLOAK )
928 {
929 entity->pitch = entity->fskill[0];
930 }
931
932 my->humanoidAnimateWalk(entity, node, bodypart, INCUBUSWALKSPEED, dist, 0.4);
933
934 if ( bodypart == LIMB_HUMANOID_CLOAK )
935 {
936 entity->fskill[0] = entity->pitch;
937 entity->roll = my->roll - fabs(entity->pitch) / 2;
938 entity->pitch = 0;
939 }
940 }
941 switch ( bodypart )
942 {
943 // torso
944 case LIMB_HUMANOID_TORSO:
945 my->setHumanoidLimbOffset(entity, INCUBUS, LIMB_HUMANOID_TORSO);
946 break;
947 // right leg
948 case LIMB_HUMANOID_RIGHTLEG:
949 if ( multiplayer != CLIENT )
950 {
951 if ( myStats->shoes == nullptr )
952 {
953 entity->sprite = 450;
954 }
955 else
956 {
957 my->setBootSprite(entity, SPRITE_BOOT_RIGHT_OFFSET);
958 }
959 if ( multiplayer == SERVER )
960 {
961 // update sprites for clients
962 if ( entity->skill[10] != entity->sprite )
963 {
964 entity->skill[10] = entity->sprite;
965 serverUpdateEntityBodypart(my, bodypart);
966 }
967 if ( entity->getUID() % (TICKS_PER_SECOND * 10) == ticks % (TICKS_PER_SECOND * 10) )
968 {
969 serverUpdateEntityBodypart(my, bodypart);
970 }
971 }
972 }
973 my->setHumanoidLimbOffset(entity, INCUBUS, LIMB_HUMANOID_RIGHTLEG);
974 break;
975 // left leg
976 case LIMB_HUMANOID_LEFTLEG:
977 if ( multiplayer != CLIENT )
978 {
979 if ( myStats->shoes == nullptr )
980 {
981 entity->sprite = 449;
982 }
983 else
984 {
985 my->setBootSprite(entity, SPRITE_BOOT_LEFT_OFFSET);
986 }
987 if ( multiplayer == SERVER )
988 {
989 // update sprites for clients
990 if ( entity->skill[10] != entity->sprite )
991 {
992 entity->skill[10] = entity->sprite;
993 serverUpdateEntityBodypart(my, bodypart);
994 }
995 if ( entity->getUID() % (TICKS_PER_SECOND * 10) == ticks % (TICKS_PER_SECOND * 10) )
996 {
997 serverUpdateEntityBodypart(my, bodypart);
998 }
999 }
1000 }
1001 my->setHumanoidLimbOffset(entity, INCUBUS, LIMB_HUMANOID_LEFTLEG);
1002 break;
1003 // right arm
1004 case LIMB_HUMANOID_RIGHTARM:
1005 {
1006 node_t* weaponNode = list_Node(&my->children, LIMB_HUMANOID_WEAPON);
1007 if ( weaponNode )
1008 {
1009 Entity* weapon = (Entity*)weaponNode->element;
1010 if ( MONSTER_ARMBENDED || (weapon->flags[INVISIBLE] && my->monsterState != MONSTER_STATE_ATTACK) )
1011 {
1012 // if weapon invisible and I'm not attacking, relax arm.
1013 entity->focalx = limbs[INCUBUS][4][0] - 0.25; // 0
1014 entity->focaly = limbs[INCUBUS][4][1] - 0.25; // 0
1015 entity->focalz = limbs[INCUBUS][4][2]; // 2
1016 entity->sprite = 448;
1017 if ( my->monsterAttack == 0 )
1018 {
1019 entity->roll = -PI / 32;
1020 }
1021 }
1022 else
1023 {
1024 // else flex arm.
1025 entity->focalx = limbs[INCUBUS][4][0];
1026 entity->focaly = limbs[INCUBUS][4][1];
1027 entity->focalz = limbs[INCUBUS][4][2];
1028 entity->sprite = 595;
1029 }
1030 }
1031 my->setHumanoidLimbOffset(entity, INCUBUS, LIMB_HUMANOID_RIGHTARM);
1032 entity->yaw += MONSTER_WEAPONYAW;
1033 break;
1034 }
1035 // left arm
1036 case LIMB_HUMANOID_LEFTARM:
1037 {
1038 shieldarm = entity;
1039 node_t* shieldNode = list_Node(&my->children, 8);
1040 if ( shieldNode )
1041 {
1042 Entity* shield = (Entity*)shieldNode->element;
1043 if ( shield->flags[INVISIBLE] && (my->monsterState != MONSTER_STATE_ATTACK) )
1044 {
1045 // if weapon invisible and I'm not attacking, relax arm.
1046 entity->focalx = limbs[INCUBUS][5][0] - 0.25; // 0
1047 entity->focaly = limbs[INCUBUS][5][1] + 0.25; // 0
1048 entity->focalz = limbs[INCUBUS][5][2]; // 2
1049 entity->sprite = 447;
1050 if ( my->monsterAttack == 0 )
1051 {
1052 entity->roll = PI / 32;
1053 }
1054 }
1055 else
1056 {
1057 // else flex arm.
1058 entity->focalx = limbs[INCUBUS][5][0];
1059 entity->focaly = limbs[INCUBUS][5][1];
1060 entity->focalz = limbs[INCUBUS][5][2];
1061 entity->sprite = 594;
1062 if ( my->monsterSpecialState == INCUBUS_STEAL )
1063 {
1064 entity->yaw -= MONSTER_WEAPONYAW;
1065 }
1066 }
1067 }
1068 my->setHumanoidLimbOffset(entity, INCUBUS, LIMB_HUMANOID_LEFTARM);
1069 if ( my->monsterDefend && my->monsterAttack == 0 )
1070 {
1071 MONSTER_SHIELDYAW = PI / 5;
1072 }
1073 else
1074 {
1075 MONSTER_SHIELDYAW = 0;
1076 }
1077 entity->yaw += MONSTER_SHIELDYAW;
1078 break;
1079 }
1080 // weapon
1081 case LIMB_HUMANOID_WEAPON:
1082 {
1083 if ( multiplayer != CLIENT )
1084 {
1085 if ( myStats->weapon == nullptr || myStats->EFFECTS[EFF_INVISIBLE] || wearingring ) //TODO: isInvisible()?
1086 {
1087 entity->flags[INVISIBLE] = true;
1088 }
1089 else
1090 {
1091 entity->sprite = itemModel(myStats->weapon);
1092 if ( itemCategory(myStats->weapon) == SPELLBOOK )
1093 {
1094 entity->flags[INVISIBLE] = true;
1095 }
1096 else
1097 {
1098 entity->flags[INVISIBLE] = false;
1099 }
1100 }
1101 if ( multiplayer == SERVER )
1102 {
1103 // update sprites for clients
1104 if ( entity->skill[10] != entity->sprite )
1105 {
1106 entity->skill[10] = entity->sprite;
1107 serverUpdateEntityBodypart(my, bodypart);
1108 }
1109 if ( entity->skill[11] != entity->flags[INVISIBLE] )
1110 {
1111 entity->skill[11] = entity->flags[INVISIBLE];
1112 serverUpdateEntityBodypart(my, bodypart);
1113 }
1114 if ( entity->getUID() % (TICKS_PER_SECOND * 10) == ticks % (TICKS_PER_SECOND * 10) )
1115 {
1116 serverUpdateEntityBodypart(my, bodypart);
1117 }
1118 }
1119 }
1120 else
1121 {
1122 if ( entity->sprite <= 0 )
1123 {
1124 entity->flags[INVISIBLE] = true;
1125 }
1126 }
1127 if ( weaponarm != nullptr )
1128 {
1129 my->handleHumanoidWeaponLimb(entity, weaponarm);
1130 }
1131 break;
1132 }
1133 // shield
1134 case LIMB_HUMANOID_SHIELD:
1135 {
1136 if ( multiplayer != CLIENT )
1137 {
1138 if ( myStats->shield == nullptr )
1139 {
1140 entity->flags[INVISIBLE] = true;
1141 entity->sprite = 0;
1142 }
1143 else
1144 {
1145 entity->flags[INVISIBLE] = false;
1146 entity->sprite = itemModel(myStats->shield);
1147 if ( itemTypeIsQuiver(myStats->shield->type) )
1148 {
1149 entity->handleQuiverThirdPersonModel(*myStats);
1150 }
1151 }
1152 if ( myStats->EFFECTS[EFF_INVISIBLE] || wearingring ) //TODO: isInvisible()?
1153 {
1154 entity->flags[INVISIBLE] = true;
1155 }
1156 if ( multiplayer == SERVER )
1157 {
1158 // update sprites for clients
1159 if ( entity->skill[10] != entity->sprite )
1160 {
1161 entity->skill[10] = entity->sprite;
1162 serverUpdateEntityBodypart(my, bodypart);
1163 }
1164 if ( entity->skill[11] != entity->flags[INVISIBLE] )
1165 {
1166 entity->skill[11] = entity->flags[INVISIBLE];
1167 serverUpdateEntityBodypart(my, bodypart);
1168 }
1169 if ( entity->getUID() % (TICKS_PER_SECOND * 10) == ticks % (TICKS_PER_SECOND * 10) )
1170 {
1171 serverUpdateEntityBodypart(my, bodypart);
1172 }
1173 }
1174 }
1175 else
1176 {
1177 if ( entity->sprite <= 0 )
1178 {
1179 entity->flags[INVISIBLE] = true;
1180 }
1181 }
1182 my->handleHumanoidShieldLimb(entity, shieldarm);
1183 break;
1184 // cloak
1185 case LIMB_HUMANOID_CLOAK:
1186 if ( multiplayer != CLIENT )
1187 {
1188 if ( myStats->cloak == nullptr || myStats->EFFECTS[EFF_INVISIBLE] || wearingring ) //TODO: isInvisible()?
1189 {
1190 entity->flags[INVISIBLE] = true;
1191 }
1192 else
1193 {
1194 entity->flags[INVISIBLE] = false;
1195 entity->sprite = itemModel(myStats->cloak);
1196 }
1197 if ( multiplayer == SERVER )
1198 {
1199 // update sprites for clients
1200 if ( entity->skill[10] != entity->sprite )
1201 {
1202 entity->skill[10] = entity->sprite;
1203 serverUpdateEntityBodypart(my, bodypart);
1204 }
1205 if ( entity->skill[11] != entity->flags[INVISIBLE] )
1206 {
1207 entity->skill[11] = entity->flags[INVISIBLE];
1208 serverUpdateEntityBodypart(my, bodypart);
1209 }
1210 if ( entity->getUID() % (TICKS_PER_SECOND * 10) == ticks % (TICKS_PER_SECOND * 10) )
1211 {
1212 serverUpdateEntityBodypart(my, bodypart);
1213 }
1214 }
1215 }
1216 else
1217 {
1218 if ( entity->sprite <= 0 )
1219 {
1220 entity->flags[INVISIBLE] = true;
1221 }
1222 }
1223 entity->x -= cos(my->yaw);
1224 entity->y -= sin(my->yaw);
1225 entity->yaw += PI / 2;
1226 break;
1227 // helm
1228 case LIMB_HUMANOID_HELMET:
1229 helmet = entity;
1230 entity->focalx = limbs[INCUBUS][9][0]; // 0
1231 entity->focaly = limbs[INCUBUS][9][1]; // 0
1232 entity->focalz = limbs[INCUBUS][9][2]; // -2
1233 entity->pitch = my->pitch;
1234 entity->roll = 0;
1235 if ( multiplayer != CLIENT )
1236 {
1237 entity->sprite = itemModel(myStats->helmet);
1238 if ( myStats->helmet == nullptr || myStats->EFFECTS[EFF_INVISIBLE] || wearingring ) //TODO: isInvisible()?
1239 {
1240 entity->flags[INVISIBLE] = true;
1241 }
1242 else
1243 {
1244 entity->flags[INVISIBLE] = false;
1245 }
1246 if ( multiplayer == SERVER )
1247 {
1248 // update sprites for clients
1249 if ( entity->skill[10] != entity->sprite )
1250 {
1251 entity->skill[10] = entity->sprite;
1252 serverUpdateEntityBodypart(my, bodypart);
1253 }
1254 if ( entity->skill[11] != entity->flags[INVISIBLE] )
1255 {
1256 entity->skill[11] = entity->flags[INVISIBLE];
1257 serverUpdateEntityBodypart(my, bodypart);
1258 }
1259 if ( entity->getUID() % (TICKS_PER_SECOND * 10) == ticks % (TICKS_PER_SECOND * 10) )
1260 {
1261 serverUpdateEntityBodypart(my, bodypart);
1262 }
1263 }
1264 }
1265 else
1266 {
1267 if ( entity->sprite <= 0 )
1268 {
1269 entity->flags[INVISIBLE] = true;
1270 }
1271 }
1272 my->setHelmetLimbOffset(entity);
1273 break;
1274 // mask
1275 case LIMB_HUMANOID_MASK:
1276 entity->focalx = limbs[INCUBUS][10][0]; // 0
1277 entity->focaly = limbs[INCUBUS][10][1]; // 0
1278 entity->focalz = limbs[INCUBUS][10][2]; // .5
1279 entity->pitch = my->pitch;
1280 entity->roll = PI / 2;
1281 if ( multiplayer != CLIENT )
1282 {
1283 bool hasSteelHelm = false;
1284 if ( myStats->helmet )
1285 {
1286 if ( myStats->helmet->type == STEEL_HELM
1287 || myStats->helmet->type == CRYSTAL_HELM
1288 || myStats->helmet->type == ARTIFACT_HELM )
1289 {
1290 hasSteelHelm = true;
1291 }
1292 }
1293 if ( myStats->mask == nullptr || myStats->EFFECTS[EFF_INVISIBLE] || wearingring || hasSteelHelm ) //TODO: isInvisible()?
1294 {
1295 entity->flags[INVISIBLE] = true;
1296 }
1297 else
1298 {
1299 entity->flags[INVISIBLE] = false;
1300 }
1301 if ( myStats->mask != nullptr )
1302 {
1303 if ( myStats->mask->type == TOOL_GLASSES )
1304 {
1305 entity->sprite = 165; // GlassesWorn.vox
1306 }
1307 else
1308 {
1309 entity->sprite = itemModel(myStats->mask);
1310 }
1311 }
1312 if ( multiplayer == SERVER )
1313 {
1314 // update sprites for clients
1315 if ( entity->skill[10] != entity->sprite )
1316 {
1317 entity->skill[10] = entity->sprite;
1318 serverUpdateEntityBodypart(my, bodypart);
1319 }
1320 if ( entity->skill[11] != entity->flags[INVISIBLE] )
1321 {
1322 entity->skill[11] = entity->flags[INVISIBLE];
1323 serverUpdateEntityBodypart(my, bodypart);
1324 }
1325 if ( entity->getUID() % (TICKS_PER_SECOND * 10) == ticks % (TICKS_PER_SECOND * 10) )
1326 {
1327 serverUpdateEntityBodypart(my, bodypart);
1328 }
1329 }
1330 }
1331 else
1332 {
1333 if ( entity->sprite <= 0 )
1334 {
1335 entity->flags[INVISIBLE] = true;
1336 }
1337 }
1338 if ( entity->sprite != 165 )
1339 {
1340 if ( entity->sprite == items[MASK_SHAMAN].index )
1341 {
1342 entity->roll = 0;
1343 my->setHelmetLimbOffset(entity);
1344 my->setHelmetLimbOffsetWithMask(helmet, entity);
1345 }
1346 else
1347 {
1348 entity->focalx = limbs[INCUBUS][10][0] + .35; // .35
1349 entity->focaly = limbs[INCUBUS][10][1] - 2; // -2
1350 entity->focalz = limbs[INCUBUS][10][2]; // .5
1351 }
1352 }
1353 else
1354 {
1355 entity->focalx = limbs[INCUBUS][10][0] + .25; // .25
1356 entity->focaly = limbs[INCUBUS][10][1] - 2.25; // -2.25
1357 entity->focalz = limbs[INCUBUS][10][2]; // .5
1358 }
1359 break;
1360 }
1361 }
1362 }
1363 // rotate shield a bit
1364 node_t* shieldNode = list_Node(&my->children, LIMB_HUMANOID_SHIELD);
1365 if ( shieldNode )
1366 {
1367 Entity* shieldEntity = (Entity*)shieldNode->element;
1368 if ( shieldEntity->sprite != items[TOOL_TORCH].index && shieldEntity->sprite != items[TOOL_LANTERN].index && shieldEntity->sprite != items[TOOL_CRYSTALSHARD].index )
1369 {
1370 shieldEntity->yaw -= PI / 6;
1371 }
1372 }
1373 if ( MONSTER_ATTACK > 0 && MONSTER_ATTACK <= MONSTER_POSE_MAGIC_CAST3 )
1374 {
1375 MONSTER_ATTACKTIME++;
1376 }
1377 else if ( MONSTER_ATTACK == 0 )
1378 {
1379 MONSTER_ATTACKTIME = 0;
1380 }
1381 else
1382 {
1383 // do nothing, don't reset attacktime or increment it.
1384 }
1385 }
1386
incubusChooseWeapon(const Entity * target,double dist)1387 void Entity::incubusChooseWeapon(const Entity* target, double dist)
1388 {
1389 if ( monsterSpecialState != 0 )
1390 {
1391 //Holding a weapon assigned from the special attack. Don't switch weapons.
1392 if ( monsterSpecialState == INCUBUS_TELEPORT_STEAL && monsterSpecialTimer == 0 )
1393 {
1394 // handle steal weapon random teleportation
1395 incubusTeleportRandom();
1396 monsterSpecialState = 0;
1397 serverUpdateEntitySkill(this, 33); // for clients to keep track of animation
1398 attack(MONSTER_POSE_INCUBUS_TELEPORT, 0, nullptr);
1399 }
1400 else if ( monsterSpecialState == INCUBUS_TELEPORT && monsterSpecialTimer == 0 )
1401 {
1402 // handle idle teleporting to target
1403 monsterSpecialState = 0;
1404 serverUpdateEntitySkill(this, 33); // for clients to keep track of animation
1405 }
1406 return;
1407 }
1408
1409 Stat *myStats = getStats();
1410 if ( !myStats )
1411 {
1412 return;
1413 }
1414
1415 if ( !strncmp(myStats->name, "inner demon", strlen("inner demon")) )
1416 {
1417 return;
1418 }
1419
1420 int specialRoll = -1;
1421 int bonusFromHP = 0;
1422
1423 if ( ticks % 10 == 0 && monsterSpecialState != INCUBUS_TELEPORT_STEAL )
1424 {
1425 // teleport to target, higher chance at greater distance or lower HP
1426 specialRoll = rand() % 50;
1427 if ( specialRoll < (1 + (dist > 80 ? 4 : 0) + (myStats->HP <= myStats->MAXHP * 0.8 ? 4 : 0)) )
1428 {
1429 monsterSpecialState = INCUBUS_TELEPORT;
1430 incubusTeleportToTarget(target);
1431 return;
1432 }
1433 }
1434
1435 if ( monsterSpecialTimer == 0 && (ticks % 10 == 0) && monsterAttack == 0 )
1436 {
1437 Stat* targetStats = target->getStats();
1438 if ( !targetStats )
1439 {
1440 return;
1441 }
1442
1443 bool tryCharm = true;
1444 bool trySteal = true;
1445 if ( targetStats->EFFECTS[EFF_PACIFY] )
1446 {
1447 tryCharm = false;
1448 }
1449 if ( !targetStats->weapon )
1450 {
1451 trySteal = false;
1452 }
1453
1454 if ( trySteal || tryCharm )
1455 {
1456 if ( myStats->HP <= myStats->MAXHP * 0.8 )
1457 {
1458 bonusFromHP += 1; // +2.5% chance if on low health
1459 }
1460 if ( myStats->HP <= myStats->MAXHP * 0.4 )
1461 {
1462 bonusFromHP += 1; // +extra 2.5% chance if on lower health
1463 }
1464
1465 int requiredRoll = (1 + bonusFromHP + (targetStats->EFFECTS[EFF_CONFUSED] ? 4 : 0)
1466 + (targetStats->EFFECTS[EFF_DRUNK] ? 2 : 0)
1467 + (targetStats->EFFECTS[EFF_PACIFY] ? 2 : 0)); // +2.5% base, + extra if target is inebriated
1468
1469 if ( trySteal && tryCharm )
1470 {
1471 if ( rand() % 8 == 0 )
1472 {
1473 trySteal = false; // try charm 12.5% of the time.
1474 }
1475 else
1476 {
1477 tryCharm = false;
1478 }
1479 }
1480
1481 if ( trySteal )
1482 {
1483 // try to steal weapon if target is holding.
1484 // occurs less often against fellow monsters.
1485 specialRoll = rand() % (40 + 40 * (target->behavior == &actMonster));
1486 }
1487 else if ( tryCharm )
1488 {
1489 specialRoll = rand() % 40;
1490 }
1491
1492 if ( trySteal )
1493 {
1494 requiredRoll += (myStats->weapon == nullptr ? 3 : 0); // bonus if no weapon held
1495 }
1496
1497 if ( specialRoll < requiredRoll )
1498 {
1499 node_t* node = nullptr;
1500 if ( trySteal )
1501 {
1502 node = itemNodeInInventory(myStats, SPELLBOOK_STEAL_WEAPON, static_cast<Category>(-1));
1503 if ( node != nullptr )
1504 {
1505 swapMonsterWeaponWithInventoryItem(this, myStats, node, true, true);
1506 monsterSpecialState = INCUBUS_STEAL;
1507 serverUpdateEntitySkill(this, 33); // for clients to keep track of animation
1508 return;
1509 }
1510 }
1511 else if ( tryCharm )
1512 {
1513 node = itemNodeInInventory(myStats, SPELLBOOK_CHARM_MONSTER, static_cast<Category>(-1));
1514 if ( node != nullptr )
1515 {
1516 swapMonsterWeaponWithInventoryItem(this, myStats, node, true, true);
1517 monsterSpecialState = INCUBUS_CHARM;
1518 serverUpdateEntitySkill(this, 33); // for clients to keep track of animation
1519 return;
1520 }
1521 }
1522 }
1523 }
1524
1525 // try new roll for alternate potion throw special.
1526 // occurs less often against fellow monsters.
1527 specialRoll = rand() % (30 + 30 * (target->behavior == &actMonster));
1528 if ( specialRoll < (2 + bonusFromHP) ) // +5% base
1529 {
1530 node_t* node = nullptr;
1531 node = itemNodeInInventory(myStats, static_cast<ItemType>(-1), POTION);
1532 if ( node != nullptr )
1533 {
1534 swapMonsterWeaponWithInventoryItem(this, myStats, node, true, true);
1535 monsterSpecialState = INCUBUS_CONFUSION;
1536 return;
1537 }
1538 }
1539 }
1540
1541 bool inMeleeRange = monsterInMeleeRange(target, dist);
1542
1543 if ( inMeleeRange )
1544 {
1545 //Switch to a melee weapon if not already wielding one. Unless monster special state is overriding the AI.
1546 if ( !myStats->weapon || !isMeleeWeapon(*myStats->weapon) )
1547 {
1548 node_t* weaponNode = getMeleeWeaponItemNodeInInventory(myStats);
1549 if ( !weaponNode )
1550 {
1551 return; //Resort to fists.
1552 }
1553
1554 bool swapped = swapMonsterWeaponWithInventoryItem(this, myStats, weaponNode, false, false);
1555 if ( !swapped )
1556 {
1557 //Don't return so that monsters will at least equip ranged weapons in melee range if they don't have anything else.
1558 }
1559 else
1560 {
1561 return;
1562 }
1563 }
1564 else
1565 {
1566 return;
1567 }
1568 }
1569
1570 //Switch to a thrown weapon or a ranged weapon.
1571 if ( !myStats->weapon || isMeleeWeapon(*myStats->weapon) )
1572 {
1573 node_t *weaponNode = getRangedWeaponItemNodeInInventory(myStats, true);
1574 if ( !weaponNode )
1575 {
1576 return; //Nothing available
1577 }
1578 bool swapped = swapMonsterWeaponWithInventoryItem(this, myStats, weaponNode, false, false);
1579 return;
1580 }
1581 return;
1582 }
1583
incubusTeleportToTarget(const Entity * target)1584 void Entity::incubusTeleportToTarget(const Entity* target)
1585 {
1586 Entity* spellTimer = createParticleTimer(this, 40, 593);
1587 spellTimer->particleTimerEndAction = PARTICLE_EFFECT_INCUBUS_TELEPORT_TARGET; // teleport behavior of timer.
1588 spellTimer->particleTimerEndSprite = 593; // sprite to use for end of timer function.
1589 spellTimer->particleTimerCountdownAction = PARTICLE_TIMER_ACTION_SHOOT_PARTICLES;
1590 spellTimer->particleTimerCountdownSprite = 593;
1591 if ( target != nullptr )
1592 {
1593 spellTimer->particleTimerTarget = static_cast<Sint32>(target->getUID()); // get the target to teleport around.
1594 }
1595 spellTimer->particleTimerVariable1 = 3; // distance of teleport in tiles
1596 if ( multiplayer == SERVER )
1597 {
1598 serverSpawnMiscParticles(this, PARTICLE_EFFECT_INCUBUS_TELEPORT_TARGET, 593);
1599 }
1600 }
1601
incubusTeleportRandom()1602 void Entity::incubusTeleportRandom()
1603 {
1604 Entity* spellTimer = createParticleTimer(this, 80, 593);
1605 spellTimer->particleTimerEndAction = PARTICLE_EFFECT_INCUBUS_TELEPORT_STEAL; // teleport behavior of timer.
1606 spellTimer->particleTimerEndSprite = 593; // sprite to use for end of timer function.
1607 spellTimer->particleTimerCountdownAction = PARTICLE_TIMER_ACTION_SHOOT_PARTICLES;
1608 spellTimer->particleTimerCountdownSprite = 593;
1609 spellTimer->particleTimerPreDelay = 40;
1610 if ( multiplayer == SERVER )
1611 {
1612 serverSpawnMiscParticles(this, PARTICLE_EFFECT_INCUBUS_TELEPORT_STEAL, 593);
1613 }
1614 }
1615