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