1 /*-------------------------------------------------------------------------------
2
3 BARONY
4 File: monster_skeleton.cpp
5 Desc: implements all of the skeleton 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
initSkeleton(Entity * my,Stat * myStats)24 void initSkeleton(Entity* my, Stat* myStats)
25 {
26 int c;
27 node_t* node;
28
29 //Sprite 229 = Skeleton head model
30 my->initMonster(229);
31
32 if ( multiplayer != CLIENT )
33 {
34 MONSTER_SPOTSND = -1;
35 MONSTER_SPOTVAR = 1;
36 MONSTER_IDLESND = -1;
37 MONSTER_IDLEVAR = 1;
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
50 if ( my->monsterAllySummonRank != 0 )
51 {
52 int rank = std::min(my->monsterAllySummonRank, 7);
53 bool secondarySummon = true;
54 if ( !strcmp(myStats->name, "skeleton knight") )
55 {
56 secondarySummon = false;
57 }
58 my->skeletonSummonSetEquipment(myStats, rank);
59 myStats->sex = MALE;
60 myStats->GOLD = 0;
61 my->light = lightSphereShadow(my->x / 16, my->y / 16, 3, 64);
62
63 Entity* leader = uidToEntity(myStats->leader_uid);
64 if ( leader )
65 {
66 Stat* leaderStats = leader->getStats();
67 if ( leaderStats )
68 {
69 if ( !secondarySummon )
70 {
71 if ( leaderStats->playerSummonLVLHP != 0 ) // first stat initialisation if equal to 0
72 {
73 myStats->LVL = (leaderStats->playerSummonLVLHP & 0xFFFF0000) >> 16;
74 myStats->MAXHP = leaderStats->playerSummonLVLHP & 0x0000FFFF;
75 myStats->HP = myStats->MAXHP;
76 myStats->OLDHP = myStats->MAXHP;
77
78 myStats->STR = (leaderStats->playerSummonSTRDEXCONINT & 0xFF000000) >> 24;
79 myStats->DEX = (leaderStats->playerSummonSTRDEXCONINT & 0x00FF0000) >> 16;
80 myStats->CON = (leaderStats->playerSummonSTRDEXCONINT & 0x0000FF00) >> 8;
81 myStats->INT = (leaderStats->playerSummonSTRDEXCONINT & 0x000000FF) >> 0;
82
83 myStats->PER = (leaderStats->playerSummonPERCHR & 0xFF000000) >> 24;
84 myStats->CHR = (leaderStats->playerSummonPERCHR & 0x00FF0000) >> 16;
85 }
86 else
87 {
88 // set variables for first time cast
89 myStats->HP = 60;
90 myStats->MAXHP = myStats->HP;
91 myStats->OLDHP = myStats->HP;
92
93 leaderStats->playerSummonLVLHP = (myStats->LVL << 16);
94 leaderStats->playerSummonLVLHP |= (myStats->MAXHP);
95
96 myStats->STR = std::max(1, myStats->STR);
97 myStats->DEX = std::max(2, myStats->DEX);
98 myStats->CON = std::max(0, myStats->CON);
99 myStats->INT = std::max(0, myStats->INT);
100 myStats->PER = std::max(0, myStats->PER);
101 myStats->CHR = std::max(0, myStats->CHR);
102 leaderStats->playerSummonSTRDEXCONINT = (myStats->STR << 24);
103 leaderStats->playerSummonSTRDEXCONINT |= (myStats->DEX << 16);
104 leaderStats->playerSummonSTRDEXCONINT |= (myStats->CON << 8);
105 leaderStats->playerSummonSTRDEXCONINT |= (myStats->INT);
106
107 leaderStats->playerSummonPERCHR = (myStats->PER << 24);
108 leaderStats->playerSummonPERCHR |= (myStats->CHR << 16);
109 leaderStats->playerSummonPERCHR |= (my->monsterAllySummonRank << 8);
110 }
111 }
112 else
113 {
114 if ( leaderStats->playerSummon2LVLHP != 0 ) // first stat initialisation if equal to 0
115 {
116 myStats->LVL = (leaderStats->playerSummon2LVLHP & 0xFFFF0000) >> 16;
117 myStats->MAXHP = leaderStats->playerSummon2LVLHP & 0x0000FFFF;
118 myStats->HP = myStats->MAXHP;
119 myStats->OLDHP = myStats->MAXHP;
120
121 myStats->STR = (leaderStats->playerSummon2STRDEXCONINT & 0xFF000000) >> 24;
122 myStats->DEX = (leaderStats->playerSummon2STRDEXCONINT & 0x00FF0000) >> 16;
123 myStats->CON = (leaderStats->playerSummon2STRDEXCONINT & 0x0000FF00) >> 8;
124 myStats->INT = (leaderStats->playerSummon2STRDEXCONINT & 0x000000FF) >> 0;
125
126 myStats->PER = (leaderStats->playerSummon2PERCHR & 0xFF000000) >> 24;
127 myStats->CHR = (leaderStats->playerSummon2PERCHR & 0x00FF0000) >> 16;
128 }
129 else
130 {
131 // set variables for first time cast
132 // make up level deficit from primary summon.
133 int levelUps = 0;
134 if ( leaderStats->playerSummonLVLHP > 0 )
135 {
136 levelUps = std::max(3, static_cast<int>((leaderStats->playerSummonLVLHP & 0xFFFF0000) >> 16) - 3);
137 }
138 levelUps = std::max(0, levelUps - myStats->LVL);
139 int increasestat[3] = { 0, 0, 0 };
140 for ( int i = 0; i < levelUps; ++i )
141 {
142 myStats->LVL++;
143 myStats->HP += HP_MOD;
144 myStats->MAXHP += HP_MOD;
145 myStats->HP = std::min(myStats->HP, myStats->MAXHP);
146 my->playerStatIncrease(CLASS_ROGUE, increasestat); // rogue weighting
147 for ( int j = 0; j < 3; j++ )
148 {
149 switch ( increasestat[j] )
150 {
151 case STAT_STR:
152 myStats->STR++;
153 break;
154 case STAT_DEX:
155 myStats->DEX++;
156 break;
157 case STAT_CON:
158 myStats->CON++;
159 break;
160 case STAT_INT:
161 myStats->INT++;
162 break;
163 case STAT_PER:
164 myStats->PER++;
165 break;
166 case STAT_CHR:
167 myStats->CHR++;
168 break;
169 default:
170 break;
171 }
172 }
173 }
174
175 my->skeletonSummonSetEquipment(myStats, std::min(7, 1 + (myStats->LVL / 5)));
176
177 leaderStats->playerSummon2LVLHP = (myStats->LVL << 16);
178 leaderStats->playerSummon2LVLHP |= (myStats->MAXHP);
179
180 myStats->STR = std::max(0, myStats->STR);
181 myStats->DEX = std::max(3, myStats->DEX);
182 myStats->CON = std::max(0, myStats->CON);
183 myStats->INT = std::max(0, myStats->INT);
184 myStats->PER = std::max(1, myStats->PER);
185 myStats->CHR = std::max(0, myStats->CHR);
186 leaderStats->playerSummon2STRDEXCONINT = (myStats->STR << 24);
187 leaderStats->playerSummon2STRDEXCONINT |= (myStats->DEX << 16);
188 leaderStats->playerSummon2STRDEXCONINT |= (myStats->CON << 8);
189 leaderStats->playerSummon2STRDEXCONINT |= (myStats->INT);
190
191 leaderStats->playerSummon2PERCHR = (myStats->PER << 24);
192 leaderStats->playerSummon2PERCHR |= (myStats->CHR << 16);
193 leaderStats->playerSummon2PERCHR |= (my->monsterAllySummonRank << 8);
194 }
195 }
196
197 myStats->MAXMP = getCostOfSpell(&spell_summon, leader);
198 myStats->MP = 0;
199
200 if ( multiplayer == SERVER && leader->behavior == &actPlayer )
201 {
202 serverUpdateAllyStat(leader->skill[2], my->getUID(), myStats->LVL, myStats->HP, myStats->MAXHP, myStats->type);
203 serverUpdatePlayerSummonStrength(leader->skill[2]);
204 serverUpdateEntitySkill(my, 50); // update the rank of the monster.
205 }
206 }
207 else
208 {
209 myStats->HP = 0;
210 }
211 }
212 else
213 {
214 myStats->HP = 0;
215 }
216
217
218 }
219 else
220 {
221 setRandomMonsterStats(myStats);
222
223 // generate 6 items max, less if there are any forced items from boss variants
224 int customItemsToGenerate = ITEM_CUSTOM_SLOT_LIMIT;
225
226 // boss variants
227 if ( rand() % 50 > 0 || my->flags[USERFLAG2] || strcmp(myStats->name, "") || myStats->MISC_FLAGS[STAT_FLAG_DISABLE_MINIBOSS] )
228 {
229 // not boss if a follower, or name has already been set to something other than blank.
230 if ( strncmp(map.name, "Underworld", 10) )
231 {
232 //give weapon
233 if ( myStats->weapon == nullptr && myStats->EDITOR_ITEMS[ITEM_SLOT_WEAPON] == 1 )
234 {
235 switch ( rand() % 10 )
236 {
237 case 0:
238 case 1:
239 myStats->weapon = newItem(BRONZE_AXE, WORN, -1 + rand() % 2, 1, rand(), false, nullptr);
240 break;
241 case 2:
242 case 3:
243 myStats->weapon = newItem(BRONZE_SWORD, WORN, -1 + rand() % 2, 1, rand(), false, nullptr);
244 break;
245 case 4:
246 case 5:
247 myStats->weapon = newItem(IRON_SPEAR, WORN, -1 + rand() % 2, 1, rand(), false, nullptr);
248 break;
249 case 6:
250 case 7:
251 myStats->weapon = newItem(IRON_AXE, WORN, -1 + rand() % 2, 1, rand(), false, nullptr);
252 break;
253 case 8:
254 case 9:
255 myStats->weapon = newItem(IRON_SWORD, WORN, -1 + rand() % 2, 1, rand(), false, nullptr);
256 break;
257 }
258 }
259 }
260 }
261 else
262 {
263 myStats->HP = 100;
264 myStats->MAXHP = 100;
265 strcpy(myStats->name, "Funny Bones");
266 myStats->STR += 6;
267 int status = DECREPIT + (currentlevel > 5) + (currentlevel > 15) + (currentlevel > 20);
268 myStats->weapon = newItem(ARTIFACT_AXE, static_cast<Status>(status), 1, 1, rand(), true, nullptr);
269 myStats->cloak = newItem(CLOAK_PROTECTION, WORN, 0, 1, 2, true, nullptr);
270 }
271
272 // random effects
273
274 // generates equipment and weapons if available from editor
275 createMonsterEquipment(myStats);
276
277 // create any custom inventory items from editor if available
278 createCustomInventory(myStats, customItemsToGenerate);
279
280 // count if any custom inventory items from editor
281 int customItems = countCustomItems(myStats); //max limit of 6 custom items per entity.
282
283 // count any inventory items set to default in edtior
284 int defaultItems = countDefaultItems(myStats);
285
286 my->setHardcoreStats(*myStats);
287
288 // generate the default inventory items for the monster, provided the editor sprite allowed enough default slots
289 switch ( defaultItems )
290 {
291 case 6:
292 case 5:
293 case 4:
294 case 3:
295 case 2:
296 case 1:
297 break;
298 default:
299 break;
300 }
301
302 //give weapon
303 if ( myStats->weapon == nullptr && myStats->EDITOR_ITEMS[ITEM_SLOT_WEAPON] == 1 )
304 {
305 switch ( rand() % 10 )
306 {
307 case 0:
308 case 1:
309 case 2:
310 case 3:
311 myStats->weapon = newItem(SHORTBOW, WORN, -1 + rand() % 2, 1, rand(), false, nullptr);
312 break;
313 case 4:
314 case 5:
315 case 6:
316 case 7:
317 myStats->weapon = newItem(CROSSBOW, WORN, -1 + rand() % 2, 1, rand(), false, nullptr);
318 break;
319 case 8:
320 case 9:
321 myStats->weapon = newItem(MAGICSTAFF_COLD, EXCELLENT, -1 + rand() % 2, 1, rand(), false, nullptr);
322 break;
323 }
324 }
325
326 //give helmet
327 if ( myStats->helmet == nullptr && myStats->EDITOR_ITEMS[ITEM_SLOT_HELM] == 1 )
328 {
329 switch ( rand() % 10 )
330 {
331 case 0:
332 case 1:
333 case 2:
334 case 3:
335 case 4:
336 break;
337 case 5:
338 myStats->helmet = newItem(LEATHER_HELM, DECREPIT, -1 + rand() % 2, 1, rand(), false, nullptr);
339 break;
340 case 6:
341 case 7:
342 case 8:
343 case 9:
344 myStats->helmet = newItem(IRON_HELM, DECREPIT, -1 + rand() % 2, 1, rand(), false, nullptr);
345 break;
346 }
347 }
348
349 //give shield
350 if ( myStats->shield == nullptr && myStats->EDITOR_ITEMS[ITEM_SLOT_SHIELD] == 1 )
351 {
352 if ( myStats->weapon && isRangedWeapon(*myStats->weapon) )
353 {
354 my->monsterGenerateQuiverItem(myStats);
355 }
356 else
357 {
358 switch ( rand() % 10 )
359 {
360 case 0:
361 case 1:
362 case 2:
363 case 3:
364 case 4:
365 case 5:
366 break;
367 case 6:
368 case 7:
369 myStats->shield = newItem(WOODEN_SHIELD, DECREPIT, -1 + rand() % 2, 1, rand(), false, nullptr);
370 break;
371 case 8:
372 myStats->shield = newItem(BRONZE_SHIELD, DECREPIT, -1 + rand() % 2, 1, rand(), false, nullptr);
373 break;
374 case 9:
375 myStats->shield = newItem(IRON_SHIELD, DECREPIT, -1 + rand() % 2, 1, rand(), false, nullptr);
376 break;
377 }
378 }
379 }
380 }
381 }
382 }
383
384 // torso
385 Entity* entity = newEntity(230, 0, map.entities, nullptr); //Limb entity.
386 entity->sizex = 4;
387 entity->sizey = 4;
388 entity->skill[2] = my->getUID();
389 entity->flags[PASSABLE] = true;
390 entity->flags[NOUPDATE] = true;
391 entity->flags[USERFLAG2] = my->flags[USERFLAG2];
392 entity->focalx = limbs[SKELETON][1][0]; // 0
393 entity->focaly = limbs[SKELETON][1][1]; // 0
394 entity->focalz = limbs[SKELETON][1][2]; // 0
395 entity->behavior = &actSkeletonLimb;
396 entity->parent = my->getUID();
397 node = list_AddNodeLast(&my->children);
398 node->element = entity;
399 node->deconstructor = &emptyDeconstructor;
400 node->size = sizeof(Entity*);
401 my->bodyparts.push_back(entity);
402
403 // right leg
404 entity = newEntity(236, 0, map.entities, nullptr); //Limb entity.
405 entity->sizex = 4;
406 entity->sizey = 4;
407 entity->skill[2] = my->getUID();
408 entity->flags[PASSABLE] = true;
409 entity->flags[NOUPDATE] = true;
410 entity->flags[USERFLAG2] = my->flags[USERFLAG2];
411 entity->focalx = limbs[SKELETON][2][0]; // 0
412 entity->focaly = limbs[SKELETON][2][1]; // 0
413 entity->focalz = limbs[SKELETON][2][2]; // 2
414 entity->behavior = &actSkeletonLimb;
415 entity->parent = my->getUID();
416 node = list_AddNodeLast(&my->children);
417 node->element = entity;
418 node->deconstructor = &emptyDeconstructor;
419 node->size = sizeof(Entity*);
420 my->bodyparts.push_back(entity);
421
422 // left leg
423 entity = newEntity(235, 0, map.entities, nullptr); //Limb entity.
424 entity->sizex = 4;
425 entity->sizey = 4;
426 entity->skill[2] = my->getUID();
427 entity->flags[PASSABLE] = true;
428 entity->flags[NOUPDATE] = true;
429 entity->flags[USERFLAG2] = my->flags[USERFLAG2];
430 entity->focalx = limbs[SKELETON][3][0]; // 0
431 entity->focaly = limbs[SKELETON][3][1]; // 0
432 entity->focalz = limbs[SKELETON][3][2]; // 2
433 entity->behavior = &actSkeletonLimb;
434 entity->parent = my->getUID();
435 node = list_AddNodeLast(&my->children);
436 node->element = entity;
437 node->deconstructor = &emptyDeconstructor;
438 node->size = sizeof(Entity*);
439 my->bodyparts.push_back(entity);
440
441 // right arm
442 entity = newEntity(233, 0, map.entities, nullptr); //Limb entity.
443 entity->sizex = 4;
444 entity->sizey = 4;
445 entity->skill[2] = my->getUID();
446 entity->flags[PASSABLE] = true;
447 entity->flags[NOUPDATE] = true;
448 entity->flags[USERFLAG2] = my->flags[USERFLAG2];
449 entity->focalx = limbs[SKELETON][4][0]; // 0
450 entity->focaly = limbs[SKELETON][4][1]; // 0
451 entity->focalz = limbs[SKELETON][4][2]; // 2
452 entity->behavior = &actSkeletonLimb;
453 entity->parent = my->getUID();
454 node = list_AddNodeLast(&my->children);
455 node->element = entity;
456 node->deconstructor = &emptyDeconstructor;
457 node->size = sizeof(Entity*);
458 my->bodyparts.push_back(entity);
459
460 // left arm
461 entity = newEntity(231, 0, map.entities, nullptr); //Limb entity.
462 entity->sizex = 4;
463 entity->sizey = 4;
464 entity->skill[2] = my->getUID();
465 entity->flags[PASSABLE] = true;
466 entity->flags[NOUPDATE] = true;
467 entity->flags[USERFLAG2] = my->flags[USERFLAG2];
468 entity->focalx = limbs[SKELETON][5][0]; // 0
469 entity->focaly = limbs[SKELETON][5][1]; // 0
470 entity->focalz = limbs[SKELETON][5][2]; // 2
471 entity->behavior = &actSkeletonLimb;
472 entity->parent = my->getUID();
473 node = list_AddNodeLast(&my->children);
474 node->element = entity;
475 node->deconstructor = &emptyDeconstructor;
476 node->size = sizeof(Entity*);
477 my->bodyparts.push_back(entity);
478
479 // world weapon
480 entity = newEntity(-1, 0, map.entities, nullptr); //Limb entity.
481 entity->sizex = 4;
482 entity->sizey = 4;
483 entity->skill[2] = my->getUID();
484 entity->flags[PASSABLE] = true;
485 entity->flags[NOUPDATE] = true;
486 entity->flags[INVISIBLE] = true;
487 entity->flags[USERFLAG2] = my->flags[USERFLAG2];
488 entity->focalx = limbs[SKELETON][6][0]; // 2.5
489 entity->focaly = limbs[SKELETON][6][1]; // 0
490 entity->focalz = limbs[SKELETON][6][2]; // 0
491 entity->behavior = &actSkeletonLimb;
492 entity->parent = my->getUID();
493 entity->pitch = .25;
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 // shield
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[SKELETON][7][0]; // 2
510 entity->focaly = limbs[SKELETON][7][1]; // 0
511 entity->focalz = limbs[SKELETON][7][2]; // 0
512 entity->behavior = &actSkeletonLimb;
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 // cloak
521 entity = newEntity(-1, 0, map.entities, nullptr); //Limb entity.
522 entity->sizex = 4;
523 entity->sizey = 4;
524 entity->skill[2] = my->getUID();
525 entity->scalex = 1.01;
526 entity->scaley = 1.01;
527 entity->scalez = 1.01;
528 entity->flags[PASSABLE] = true;
529 entity->flags[NOUPDATE] = true;
530 entity->flags[INVISIBLE] = true;
531 entity->flags[USERFLAG2] = my->flags[USERFLAG2];
532 entity->focalx = limbs[SKELETON][8][0]; // 0
533 entity->focaly = limbs[SKELETON][8][1]; // 0
534 entity->focalz = limbs[SKELETON][8][2]; // 4
535 entity->behavior = &actSkeletonLimb;
536 entity->parent = my->getUID();
537 node = list_AddNodeLast(&my->children);
538 node->element = entity;
539 node->deconstructor = &emptyDeconstructor;
540 node->size = sizeof(Entity*);
541 my->bodyparts.push_back(entity);
542
543 // helmet
544 entity = newEntity(-1, 0, map.entities, nullptr); //Limb entity.
545 entity->sizex = 4;
546 entity->sizey = 4;
547 entity->skill[2] = my->getUID();
548 entity->scalex = 1.01;
549 entity->scaley = 1.01;
550 entity->scalez = 1.01;
551 entity->flags[PASSABLE] = true;
552 entity->flags[NOUPDATE] = true;
553 entity->flags[INVISIBLE] = true;
554 entity->flags[USERFLAG2] = my->flags[USERFLAG2];
555 entity->focalx = limbs[SKELETON][9][0]; // 0
556 entity->focaly = limbs[SKELETON][9][1]; // 0
557 entity->focalz = limbs[SKELETON][9][2]; // -2
558 entity->behavior = &actSkeletonLimb;
559 entity->parent = my->getUID();
560 node = list_AddNodeLast(&my->children);
561 node->element = entity;
562 node->deconstructor = &emptyDeconstructor;
563 node->size = sizeof(Entity*);
564 my->bodyparts.push_back(entity);
565
566 // mask
567 entity = newEntity(-1, 0, map.entities, nullptr); //Limb entity.
568 entity->sizex = 4;
569 entity->sizey = 4;
570 entity->skill[2] = my->getUID();
571 entity->flags[PASSABLE] = true;
572 entity->flags[NOUPDATE] = true;
573 entity->flags[INVISIBLE] = true;
574 entity->flags[USERFLAG2] = my->flags[USERFLAG2];
575 entity->focalx = limbs[SKELETON][10][0]; // 0
576 entity->focaly = limbs[SKELETON][10][1]; // 0
577 entity->focalz = limbs[SKELETON][10][2]; // .5
578 entity->behavior = &actSkeletonLimb;
579 entity->parent = my->getUID();
580 node = list_AddNodeLast(&my->children);
581 node->element = entity;
582 node->deconstructor = &emptyDeconstructor;
583 node->size = sizeof(Entity*);
584 my->bodyparts.push_back(entity);
585
586 if ( multiplayer == CLIENT || MONSTER_INIT )
587 {
588 return;
589 }
590 }
591
actSkeletonLimb(Entity * my)592 void actSkeletonLimb(Entity* my)
593 {
594 my->actMonsterLimb(true);
595 }
596
skeletonDie(Entity * my)597 void skeletonDie(Entity* my)
598 {
599 if ( multiplayer != CLIENT && my->monsterAllySummonRank != 0 )
600 {
601 Stat* myStats = my->getStats();
602 Entity* leader = uidToEntity(myStats->leader_uid);
603 if ( leader )
604 {
605 Stat* leaderStats = leader->getStats();
606 if ( leaderStats )
607 {
608 // refund mana to caster.
609 int spellCost = getCostOfSpell(&spell_summon, leader);
610 if ( (leader->getINT() + leaderStats->PROFICIENCIES[PRO_MAGIC]) >= SKILL_LEVEL_EXPERT )
611 {
612 // we summoned 2 units, halve the return rate.
613 spellCost /= 2;
614 }
615 //for ( node_t* node = leaderStats->FOLLOWERS.first; node != nullptr; node = node->next )
616 //{
617 // Uint32* c = (Uint32*)node->element;
618 // Entity* otherSummon = uidToEntity(*c);
619 // if ( otherSummon && otherSummon->monsterAllySummonRank != 0 && otherSummon->monsterAllyGetPlayerLeader() == leader )
620 // {
621 // spellCost /= 2;
622 // Stat* otherSummonStat = otherSummon->getStats();
623 // if ( !strcmp(otherSummonStat->name, "skeleton sentinel") )
624 // {
625 // otherSummonStat->MAXMP /= 2; // halve the other summon's possible return MP.
626 // otherSummonStat->MAXMP = std::max(1, otherSummonStat->MAXMP);
627 // otherSummonStat->MP /= 2;
628 // }
629 // break;
630 // }
631 //}
632 int manaToRefund = std::min(spellCost, static_cast<int>(myStats->MP / static_cast<float>(myStats->MAXMP) * spellCost)); // MP to restore
633 if ( manaToRefund > 0 )
634 {
635 manaToRefund -= rand() % (std::max(1, manaToRefund / 3));
636 if ( leaderStats->HP <= 0 )
637 {
638 manaToRefund = 0;
639 }
640 Entity* spellEntity = createParticleSapCenter(leader, my, SPELL_SUMMON, 599, 791);
641 if ( spellEntity )
642 {
643 playSoundEntity(my, 167, 128); // succeeded spell sound
644 spellEntity->skill[7] = manaToRefund;
645 if ( leader->behavior == &actPlayer )
646 {
647 messagePlayerMonsterEvent(leader->skill[2], 0xFFFFFFFF, *myStats, language[3194], language[3195], MSG_COMBAT);
648 }
649 }
650 }
651 }
652 }
653 }
654
655 my->removeMonsterDeathNodes();
656
657 int c;
658 for ( c = 0; c < 6; c++ )
659 {
660 Entity* entity = spawnGib(my);
661 if ( entity )
662 {
663 switch ( c )
664 {
665 case 0:
666 entity->sprite = 229;
667 break;
668 case 1:
669 entity->sprite = 230;
670 break;
671 case 2:
672 entity->sprite = 231;
673 break;
674 case 3:
675 entity->sprite = 233;
676 break;
677 case 4:
678 entity->sprite = 235;
679 break;
680 case 5:
681 entity->sprite = 236;
682 break;
683 }
684 serverSpawnGibForClient(entity);
685 }
686 }
687 playSoundEntity(my, 94, 128);
688 list_RemoveNode(my->mynode);
689 return;
690 }
691
692 #define SKELETONWALKSPEED .13
693
skeletonMoveBodyparts(Entity * my,Stat * myStats,double dist)694 void skeletonMoveBodyparts(Entity* my, Stat* myStats, double dist)
695 {
696 node_t* node;
697 Entity* entity = nullptr, *entity2 = nullptr;
698 Entity* rightbody = nullptr;
699 Entity* weaponarm = nullptr;
700 int bodypart;
701 bool wearingring = false;
702
703 // set invisibility //TODO: isInvisible()?
704 if ( multiplayer != CLIENT )
705 {
706 if ( myStats->ring != nullptr )
707 if ( myStats->ring->type == RING_INVISIBILITY )
708 {
709 wearingring = true;
710 }
711 if ( myStats->cloak != nullptr )
712 if ( myStats->cloak->type == CLOAK_INVISIBILITY )
713 {
714 wearingring = true;
715 }
716 if ( myStats->EFFECTS[EFF_INVISIBLE] == true || wearingring == true )
717 {
718 my->flags[INVISIBLE] = true;
719 my->flags[BLOCKSIGHT] = false;
720 bodypart = 0;
721 for (node = my->children.first; node != nullptr; node = node->next)
722 {
723 if ( bodypart < LIMB_HUMANOID_TORSO )
724 {
725 bodypart++;
726 continue;
727 }
728 if ( bodypart >= LIMB_HUMANOID_WEAPON )
729 {
730 break;
731 }
732 entity = (Entity*)node->element;
733 if ( !entity->flags[INVISIBLE] )
734 {
735 entity->flags[INVISIBLE] = true;
736 serverUpdateEntityBodypart(my, bodypart);
737 }
738 bodypart++;
739 }
740 }
741 else
742 {
743 my->flags[INVISIBLE] = false;
744 my->flags[BLOCKSIGHT] = true;
745 bodypart = 0;
746 for (node = my->children.first; node != nullptr; node = node->next)
747 {
748 if ( bodypart < LIMB_HUMANOID_TORSO )
749 {
750 bodypart++;
751 continue;
752 }
753 if ( bodypart >= LIMB_HUMANOID_WEAPON )
754 {
755 break;
756 }
757 entity = (Entity*)node->element;
758 if ( entity->flags[INVISIBLE] )
759 {
760 entity->flags[INVISIBLE] = false;
761 serverUpdateEntityBodypart(my, bodypart);
762 serverUpdateEntityFlag(my, INVISIBLE);
763 }
764 bodypart++;
765 }
766 }
767
768 // sleeping
769 if ( myStats->EFFECTS[EFF_ASLEEP] )
770 {
771 my->z = 2;
772 my->pitch = PI / 4;
773 }
774 else
775 {
776 my->z = -.5;
777 my->pitch = 0;
778 }
779 }
780
781 Entity* shieldarm = nullptr;
782 Entity* helmet = nullptr;
783 //Move bodyparts
784 for (bodypart = 0, node = my->children.first; node != nullptr; node = node->next, bodypart++)
785 {
786 if ( bodypart < LIMB_HUMANOID_TORSO )
787 {
788 continue;
789 }
790 entity = (Entity*)node->element;
791 entity->x = my->x;
792 entity->y = my->y;
793 entity->z = my->z;
794 if ( (MONSTER_ATTACK == MONSTER_POSE_MAGIC_WINDUP1) && bodypart == LIMB_HUMANOID_RIGHTARM )
795 {
796 // don't let the creatures's yaw move the casting arm
797 }
798 else
799 {
800 entity->yaw = my->yaw;
801 }
802 if ( bodypart == LIMB_HUMANOID_RIGHTLEG || bodypart == LIMB_HUMANOID_LEFTARM )
803 {
804 my->humanoidAnimateWalk(entity, node, bodypart, SKELETONWALKSPEED, dist, 0.1);
805 }
806 else if ( bodypart == LIMB_HUMANOID_LEFTLEG || bodypart == LIMB_HUMANOID_RIGHTARM || bodypart == LIMB_HUMANOID_CLOAK )
807 {
808 // left leg, right arm, cloak.
809 if ( bodypart == LIMB_HUMANOID_RIGHTARM )
810 {
811 weaponarm = entity;
812 if ( MONSTER_ATTACK > 0 )
813 {
814 my->handleWeaponArmAttack(entity);
815 }
816 }
817 else if ( bodypart == LIMB_HUMANOID_CLOAK )
818 {
819 entity->pitch = entity->fskill[0];
820 }
821
822 my->humanoidAnimateWalk(entity, node, bodypart, SKELETONWALKSPEED, dist, 0.1);
823
824 if ( bodypart == LIMB_HUMANOID_CLOAK )
825 {
826 entity->fskill[0] = entity->pitch;
827 entity->roll = my->roll - fabs(entity->pitch) / 2;
828 entity->pitch = 0;
829 }
830 }
831 switch ( bodypart )
832 {
833 // torso
834 case LIMB_HUMANOID_TORSO:
835 if ( multiplayer != CLIENT )
836 {
837 if ( myStats->breastplate == nullptr )
838 {
839 entity->sprite = 230;
840 }
841 else
842 {
843 entity->sprite = itemModel(myStats->breastplate);
844 }
845 if ( multiplayer == SERVER )
846 {
847 // update sprites for clients
848 if ( entity->skill[10] != entity->sprite )
849 {
850 entity->skill[10] = entity->sprite;
851 serverUpdateEntityBodypart(my, bodypart);
852 }
853 if ( entity->getUID() % (TICKS_PER_SECOND * 10) == ticks % (TICKS_PER_SECOND * 10) )
854 {
855 serverUpdateEntityBodypart(my, bodypart);
856 }
857 }
858 }
859 my->setHumanoidLimbOffset(entity, SKELETON, LIMB_HUMANOID_TORSO);
860 break;
861 // right leg
862 case LIMB_HUMANOID_RIGHTLEG:
863 if ( multiplayer != CLIENT )
864 {
865 if ( myStats->shoes == nullptr )
866 {
867 entity->sprite = 236;
868 }
869 else
870 {
871 my->setBootSprite(entity, SPRITE_BOOT_RIGHT_OFFSET);
872 }
873 if ( multiplayer == SERVER )
874 {
875 // update sprites for clients
876 if ( entity->skill[10] != entity->sprite )
877 {
878 entity->skill[10] = entity->sprite;
879 serverUpdateEntityBodypart(my, bodypart);
880 }
881 if ( entity->getUID() % (TICKS_PER_SECOND * 10) == ticks % (TICKS_PER_SECOND * 10) )
882 {
883 serverUpdateEntityBodypart(my, bodypart);
884 }
885 }
886 }
887 my->setHumanoidLimbOffset(entity, SKELETON, LIMB_HUMANOID_RIGHTLEG);
888 break;
889 // left leg
890 case LIMB_HUMANOID_LEFTLEG:
891 if ( multiplayer != CLIENT )
892 {
893 if ( myStats->shoes == nullptr )
894 {
895 entity->sprite = 235;
896 }
897 else
898 {
899 my->setBootSprite(entity, SPRITE_BOOT_LEFT_OFFSET);
900 }
901 if ( multiplayer == SERVER )
902 {
903 // update sprites for clients
904 if ( entity->skill[10] != entity->sprite )
905 {
906 entity->skill[10] = entity->sprite;
907 serverUpdateEntityBodypart(my, bodypart);
908 }
909 if ( entity->getUID() % (TICKS_PER_SECOND * 10) == ticks % (TICKS_PER_SECOND * 10) )
910 {
911 serverUpdateEntityBodypart(my, bodypart);
912 }
913 }
914 }
915 my->setHumanoidLimbOffset(entity, SKELETON, LIMB_HUMANOID_LEFTLEG);
916 break;
917 // right arm
918 case LIMB_HUMANOID_RIGHTARM:
919 {
920 if ( multiplayer != CLIENT )
921 {
922 if ( myStats->gloves == nullptr )
923 {
924 entity->sprite = 233;
925 }
926 else
927 {
928 if ( setGloveSprite(myStats, entity, SPRITE_GLOVE_RIGHT_OFFSET) != 0 )
929 {
930 // successfully set sprite for the human model
931 }
932 }
933 if ( multiplayer == SERVER )
934 {
935 // update sprites for clients
936 if ( entity->skill[10] != entity->sprite )
937 {
938 entity->skill[10] = entity->sprite;
939 serverUpdateEntityBodypart(my, bodypart);
940 }
941 if ( entity->getUID() % (TICKS_PER_SECOND * 10) == ticks % (TICKS_PER_SECOND * 10) )
942 {
943 serverUpdateEntityBodypart(my, bodypart);
944 }
945 }
946 }
947
948 if ( multiplayer == CLIENT )
949 {
950 if ( entity->skill[7] == 0 )
951 {
952 if ( entity->sprite == 233 )
953 {
954 // these are the default arms.
955 // chances are they may be wrong if sent by the server,
956 }
957 else
958 {
959 // otherwise we're being sent gloves armor etc so it's probably right.
960 entity->skill[7] = entity->sprite;
961 }
962 }
963 if ( entity->skill[7] == 0 )
964 {
965 // we set this ourselves until proper initialisation.
966 entity->sprite = 233;
967 }
968 else
969 {
970 entity->sprite = entity->skill[7];
971 }
972 }
973
974 node_t* weaponNode = list_Node(&my->children, LIMB_HUMANOID_WEAPON);
975 if ( weaponNode )
976 {
977 Entity* weapon = (Entity*)weaponNode->element;
978 if ( MONSTER_ARMBENDED || (weapon->flags[INVISIBLE] && my->monsterAttack == 0 ) )
979 {
980 // if weapon invisible and I'm not attacking, relax arm.
981 entity->focalx = limbs[SKELETON][4][0]; // 0
982 entity->focaly = limbs[SKELETON][4][1]; // 0
983 entity->focalz = limbs[SKELETON][4][2]; // 2
984 //entity->sprite = 233;
985 }
986 else
987 {
988 // else flex arm.
989 entity->focalx = limbs[SKELETON][4][0] + 1; // 1
990 entity->focaly = limbs[SKELETON][4][1]; // 0
991 entity->focalz = limbs[SKELETON][4][2] - 1; // 1
992 if ( entity->sprite == 233 )
993 {
994 entity->sprite = 234;
995 }
996 else
997 {
998 entity->sprite += 2;
999 }
1000 }
1001 }
1002 my->setHumanoidLimbOffset(entity, SKELETON, LIMB_HUMANOID_RIGHTARM);
1003 entity->yaw += MONSTER_WEAPONYAW;
1004 break;
1005 // left arm
1006 }
1007 case LIMB_HUMANOID_LEFTARM:
1008 {
1009 if ( multiplayer != CLIENT )
1010 {
1011 if ( myStats->gloves == nullptr )
1012 {
1013 entity->sprite = 231;
1014 }
1015 else
1016 {
1017 if ( setGloveSprite(myStats, entity, SPRITE_GLOVE_LEFT_OFFSET) != 0 )
1018 {
1019 // successfully set sprite for the human model
1020 }
1021 }
1022 if ( multiplayer == SERVER )
1023 {
1024 // update sprites for clients
1025 if ( entity->skill[10] != entity->sprite )
1026 {
1027 entity->skill[10] = entity->sprite;
1028 serverUpdateEntityBodypart(my, bodypart);
1029 }
1030 if ( entity->getUID() % (TICKS_PER_SECOND * 10) == ticks % (TICKS_PER_SECOND * 10) )
1031 {
1032 serverUpdateEntityBodypart(my, bodypart);
1033 }
1034 }
1035 }
1036
1037 if ( multiplayer == CLIENT )
1038 {
1039 if ( entity->skill[7] == 0 )
1040 {
1041 if ( entity->sprite == 231 )
1042 {
1043 // these are the default arms.
1044 // chances are they may be wrong if sent by the server,
1045 }
1046 else
1047 {
1048 // otherwise we're being sent gloves armor etc so it's probably right.
1049 entity->skill[7] = entity->sprite;
1050 }
1051 }
1052 if ( entity->skill[7] == 0 )
1053 {
1054 // we set this ourselves until proper initialisation.
1055 entity->sprite = 231;
1056 }
1057 else
1058 {
1059 entity->sprite = entity->skill[7];
1060 }
1061 }
1062
1063 shieldarm = entity;
1064 node_t* shieldNode = list_Node(&my->children, 8);
1065 if ( shieldNode )
1066 {
1067 Entity* shield = (Entity*)shieldNode->element;
1068 if ( shield->flags[INVISIBLE] )
1069 {
1070 // if shield invisible, relax arm.
1071 //entity->sprite = 231;
1072 entity->focalx = limbs[SKELETON][5][0]; // 0
1073 entity->focaly = limbs[SKELETON][5][1]; // 0
1074 entity->focalz = limbs[SKELETON][5][2]; // 2
1075 }
1076 else
1077 {
1078 // else flex arm.
1079 entity->focalx = limbs[SKELETON][5][0] + 1; // 1
1080 entity->focaly = limbs[SKELETON][5][1]; // 0
1081 entity->focalz = limbs[SKELETON][5][2] - 1; // 1
1082 if ( entity->sprite == 231 )
1083 {
1084 entity->sprite = 232;
1085 }
1086 else
1087 {
1088 entity->sprite += 2;
1089 }
1090 }
1091 }
1092 my->setHumanoidLimbOffset(entity, SKELETON, LIMB_HUMANOID_LEFTARM);
1093 if ( my->monsterDefend && my->monsterAttack == 0 )
1094 {
1095 MONSTER_SHIELDYAW = PI / 5;
1096 }
1097 else
1098 {
1099 MONSTER_SHIELDYAW = 0;
1100 }
1101 entity->yaw += MONSTER_SHIELDYAW;
1102 break;
1103 }
1104 // weapon
1105 case LIMB_HUMANOID_WEAPON:
1106 if ( multiplayer != CLIENT )
1107 {
1108 if ( myStats->weapon == nullptr || myStats->EFFECTS[EFF_INVISIBLE] || wearingring ) //TODO: isInvisible()?
1109 {
1110 entity->flags[INVISIBLE] = true;
1111 }
1112 else
1113 {
1114 entity->sprite = itemModel(myStats->weapon);
1115 if ( itemCategory(myStats->weapon) == SPELLBOOK )
1116 {
1117 entity->flags[INVISIBLE] = true;
1118 }
1119 else
1120 {
1121 entity->flags[INVISIBLE] = false;
1122 }
1123 }
1124 if ( multiplayer == SERVER )
1125 {
1126 // update sprites for clients
1127 if ( entity->skill[10] != entity->sprite )
1128 {
1129 entity->skill[10] = entity->sprite;
1130 serverUpdateEntityBodypart(my, bodypart);
1131 }
1132 if ( entity->skill[11] != entity->flags[INVISIBLE] )
1133 {
1134 entity->skill[11] = entity->flags[INVISIBLE];
1135 serverUpdateEntityBodypart(my, bodypart);
1136 }
1137 if ( entity->getUID() % (TICKS_PER_SECOND * 10) == ticks % (TICKS_PER_SECOND * 10) )
1138 {
1139 serverUpdateEntityBodypart(my, bodypart);
1140 }
1141 }
1142 }
1143 else
1144 {
1145 if ( entity->sprite <= 0 )
1146 {
1147 entity->flags[INVISIBLE] = true;
1148 }
1149 }
1150 if ( weaponarm != nullptr )
1151 {
1152 my->handleHumanoidWeaponLimb(entity, weaponarm);
1153 }
1154 break;
1155 // shield
1156 case LIMB_HUMANOID_SHIELD:
1157 if ( multiplayer != CLIENT )
1158 {
1159 if ( myStats->shield == nullptr )
1160 {
1161 entity->flags[INVISIBLE] = true;
1162 entity->sprite = 0;
1163 }
1164 else
1165 {
1166 entity->flags[INVISIBLE] = false;
1167 entity->sprite = itemModel(myStats->shield);
1168 if ( itemTypeIsQuiver(myStats->shield->type) )
1169 {
1170 entity->handleQuiverThirdPersonModel(*myStats);
1171 }
1172 }
1173 if ( myStats->EFFECTS[EFF_INVISIBLE] || wearingring ) //TODO: isInvisible()?
1174 {
1175 entity->flags[INVISIBLE] = true;
1176 }
1177 if ( multiplayer == SERVER )
1178 {
1179 // update sprites for clients
1180 if ( entity->skill[10] != entity->sprite )
1181 {
1182 entity->skill[10] = entity->sprite;
1183 serverUpdateEntityBodypart(my, bodypart);
1184 }
1185 if ( entity->skill[11] != entity->flags[INVISIBLE] )
1186 {
1187 entity->skill[11] = entity->flags[INVISIBLE];
1188 serverUpdateEntityBodypart(my, bodypart);
1189 }
1190 if ( entity->getUID() % (TICKS_PER_SECOND * 10) == ticks % (TICKS_PER_SECOND * 10) )
1191 {
1192 serverUpdateEntityBodypart(my, bodypart);
1193 }
1194 }
1195 }
1196 else
1197 {
1198 if ( entity->sprite <= 0 )
1199 {
1200 entity->flags[INVISIBLE] = true;
1201 }
1202 }
1203 my->handleHumanoidShieldLimb(entity, shieldarm);
1204 break;
1205 // cloak
1206 case LIMB_HUMANOID_CLOAK:
1207 if ( multiplayer != CLIENT )
1208 {
1209 if ( myStats->cloak == nullptr || myStats->EFFECTS[EFF_INVISIBLE] || wearingring ) //TODO: isInvisible()?
1210 {
1211 entity->flags[INVISIBLE] = true;
1212 }
1213 else
1214 {
1215 entity->flags[INVISIBLE] = false;
1216 entity->sprite = itemModel(myStats->cloak);
1217 }
1218 if ( multiplayer == SERVER )
1219 {
1220 // update sprites for clients
1221 if ( entity->skill[10] != entity->sprite )
1222 {
1223 entity->skill[10] = entity->sprite;
1224 serverUpdateEntityBodypart(my, bodypart);
1225 }
1226 if ( entity->skill[11] != entity->flags[INVISIBLE] )
1227 {
1228 entity->skill[11] = entity->flags[INVISIBLE];
1229 serverUpdateEntityBodypart(my, bodypart);
1230 }
1231 if ( entity->getUID() % (TICKS_PER_SECOND * 10) == ticks % (TICKS_PER_SECOND * 10) )
1232 {
1233 serverUpdateEntityBodypart(my, bodypart);
1234 }
1235 }
1236 }
1237 else
1238 {
1239 if ( entity->sprite <= 0 )
1240 {
1241 entity->flags[INVISIBLE] = true;
1242 }
1243 }
1244 entity->x -= cos(my->yaw);
1245 entity->y -= sin(my->yaw);
1246 entity->yaw += PI / 2;
1247 break;
1248 // helm
1249 case LIMB_HUMANOID_HELMET:
1250 helmet = entity;
1251 entity->focalx = limbs[SKELETON][9][0]; // 0
1252 entity->focaly = limbs[SKELETON][9][1]; // 0
1253 entity->focalz = limbs[SKELETON][9][2]; // -2
1254 entity->pitch = my->pitch;
1255 entity->roll = 0;
1256 if ( multiplayer != CLIENT )
1257 {
1258 entity->sprite = itemModel(myStats->helmet);
1259 if ( myStats->helmet == nullptr || myStats->EFFECTS[EFF_INVISIBLE] || wearingring ) //TODO: isInvisible()?
1260 {
1261 entity->flags[INVISIBLE] = true;
1262 }
1263 else
1264 {
1265 entity->flags[INVISIBLE] = false;
1266 }
1267 if ( multiplayer == SERVER )
1268 {
1269 // update sprites for clients
1270 if ( entity->skill[10] != entity->sprite )
1271 {
1272 entity->skill[10] = entity->sprite;
1273 serverUpdateEntityBodypart(my, bodypart);
1274 }
1275 if ( entity->skill[11] != entity->flags[INVISIBLE] )
1276 {
1277 entity->skill[11] = entity->flags[INVISIBLE];
1278 serverUpdateEntityBodypart(my, bodypart);
1279 }
1280 if ( entity->getUID() % (TICKS_PER_SECOND * 10) == ticks % (TICKS_PER_SECOND * 10) )
1281 {
1282 serverUpdateEntityBodypart(my, bodypart);
1283 }
1284 }
1285 }
1286 else
1287 {
1288 if ( entity->sprite <= 0 )
1289 {
1290 entity->flags[INVISIBLE] = true;
1291 }
1292 }
1293 my->setHelmetLimbOffset(entity);
1294 break;
1295 // mask
1296 case LIMB_HUMANOID_MASK:
1297 entity->focalx = limbs[SKELETON][10][0]; // 0
1298 entity->focaly = limbs[SKELETON][10][1]; // 0
1299 entity->focalz = limbs[SKELETON][10][2]; // .5
1300 entity->pitch = my->pitch;
1301 entity->roll = PI / 2;
1302 if ( multiplayer != CLIENT )
1303 {
1304 bool hasSteelHelm = false;
1305 if ( myStats->helmet )
1306 {
1307 if ( myStats->helmet->type == STEEL_HELM
1308 || myStats->helmet->type == CRYSTAL_HELM
1309 || myStats->helmet->type == ARTIFACT_HELM )
1310 {
1311 hasSteelHelm = true;
1312 }
1313 }
1314 if ( myStats->mask == nullptr || myStats->EFFECTS[EFF_INVISIBLE] || wearingring || hasSteelHelm ) //TODO: isInvisible()?
1315 {
1316 entity->flags[INVISIBLE] = true;
1317 }
1318 else
1319 {
1320 entity->flags[INVISIBLE] = false;
1321 }
1322 if ( myStats->mask != nullptr )
1323 {
1324 if ( myStats->mask->type == TOOL_GLASSES )
1325 {
1326 entity->sprite = 165; // GlassesWorn.vox
1327 }
1328 else
1329 {
1330 entity->sprite = itemModel(myStats->mask);
1331 }
1332 }
1333 if ( multiplayer == SERVER )
1334 {
1335 // update sprites for clients
1336 if ( entity->skill[10] != entity->sprite )
1337 {
1338 entity->skill[10] = entity->sprite;
1339 serverUpdateEntityBodypart(my, bodypart);
1340 }
1341 if ( entity->skill[11] != entity->flags[INVISIBLE] )
1342 {
1343 entity->skill[11] = entity->flags[INVISIBLE];
1344 serverUpdateEntityBodypart(my, bodypart);
1345 }
1346 if ( entity->getUID() % (TICKS_PER_SECOND * 10) == ticks % (TICKS_PER_SECOND * 10) )
1347 {
1348 serverUpdateEntityBodypart(my, bodypart);
1349 }
1350 }
1351 }
1352 else
1353 {
1354 if ( entity->sprite <= 0 )
1355 {
1356 entity->flags[INVISIBLE] = true;
1357 }
1358 }
1359 if ( entity->sprite != 165 )
1360 {
1361 if ( entity->sprite == items[MASK_SHAMAN].index )
1362 {
1363 entity->roll = 0;
1364 my->setHelmetLimbOffset(entity);
1365 my->setHelmetLimbOffsetWithMask(helmet, entity);
1366 }
1367 else
1368 {
1369 entity->focalx = limbs[SKELETON][10][0] + .35; // .35
1370 entity->focaly = limbs[SKELETON][10][1] - 2; // -2
1371 entity->focalz = limbs[SKELETON][10][2]; // .5
1372 }
1373 }
1374 else
1375 {
1376 entity->focalx = limbs[SKELETON][10][0] + .25; // .25
1377 entity->focaly = limbs[SKELETON][10][1] - 2.25; // -2.25
1378 entity->focalz = limbs[SKELETON][10][2]; // .5
1379 }
1380 break;
1381 }
1382 }
1383 // rotate shield a bit
1384 node_t* shieldNode = list_Node(&my->children, LIMB_HUMANOID_SHIELD);
1385 if ( shieldNode )
1386 {
1387 Entity* shieldEntity = (Entity*)shieldNode->element;
1388 if ( shieldEntity->sprite != items[TOOL_TORCH].index && shieldEntity->sprite != items[TOOL_LANTERN].index && shieldEntity->sprite != items[TOOL_CRYSTALSHARD].index )
1389 {
1390 shieldEntity->yaw -= PI / 6;
1391 }
1392 }
1393 if ( MONSTER_ATTACK > 0 && MONSTER_ATTACK <= MONSTER_POSE_MAGIC_CAST3 )
1394 {
1395 MONSTER_ATTACKTIME++;
1396 }
1397 else if ( MONSTER_ATTACK == 0 )
1398 {
1399 MONSTER_ATTACKTIME = 0;
1400 }
1401 else
1402 {
1403 // do nothing, don't reset attacktime or increment it.
1404 }
1405 }
1406
skeletonSummonSetEquipment(Stat * myStats,int rank)1407 void Entity::skeletonSummonSetEquipment(Stat* myStats, int rank)
1408 {
1409 if ( !strcmp(myStats->name, "skeleton knight") )
1410 {
1411 switch ( rank )
1412 {
1413 case 1:
1414 if ( !myStats->weapon )
1415 {
1416 myStats->weapon = newItem(BRONZE_SWORD, EXCELLENT, 0, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, nullptr);
1417 }
1418 else
1419 {
1420 myStats->weapon->type = BRONZE_SWORD;
1421 }
1422 if ( !myStats->breastplate )
1423 {
1424 myStats->breastplate = newItem(LEATHER_BREASTPIECE, DECREPIT, 0, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, nullptr);
1425 }
1426 else
1427 {
1428 myStats->breastplate->type = LEATHER_BREASTPIECE;
1429 }
1430 if ( !myStats->shield )
1431 {
1432 myStats->shield = newItem(BRONZE_SHIELD, DECREPIT, 0, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, nullptr);
1433 }
1434 else
1435 {
1436 myStats->shield->type = BRONZE_SHIELD;
1437 }
1438 break;
1439 case 2:
1440 if ( !myStats->weapon )
1441 {
1442 myStats->weapon = newItem(IRON_SPEAR, EXCELLENT, 0, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, nullptr);
1443 }
1444 else
1445 {
1446 myStats->weapon->type = IRON_SPEAR;
1447 }
1448 if ( !myStats->breastplate )
1449 {
1450 myStats->breastplate = newItem(LEATHER_BREASTPIECE, DECREPIT, 0, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, nullptr);
1451 }
1452 else
1453 {
1454 myStats->breastplate->type = LEATHER_BREASTPIECE;
1455 }
1456 if ( !myStats->shield )
1457 {
1458 myStats->shield = newItem(BRONZE_SHIELD, DECREPIT, 0, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, nullptr);
1459 }
1460 else
1461 {
1462 myStats->shield->type = BRONZE_SHIELD;
1463 }
1464 if ( !myStats->shoes )
1465 {
1466 myStats->shoes = newItem(IRON_BOOTS, DECREPIT, 0, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, nullptr);
1467 }
1468 else
1469 {
1470 myStats->shoes->type = IRON_BOOTS;
1471 }
1472 break;
1473 case 3:
1474 if ( !myStats->weapon )
1475 {
1476 myStats->weapon = newItem(IRON_SPEAR, EXCELLENT, 0, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, nullptr);
1477 }
1478 else
1479 {
1480 myStats->weapon->type = IRON_SPEAR;
1481 }
1482 if ( !myStats->breastplate )
1483 {
1484 myStats->breastplate = newItem(IRON_BREASTPIECE, DECREPIT, 0, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, nullptr);
1485 }
1486 else
1487 {
1488 myStats->breastplate->type = IRON_BREASTPIECE;
1489 }
1490 if ( !myStats->shield )
1491 {
1492 myStats->shield = newItem(IRON_SHIELD, DECREPIT, 0, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, nullptr);
1493 }
1494 else
1495 {
1496 myStats->shield->type = IRON_SHIELD;
1497 }
1498 if ( !myStats->shoes )
1499 {
1500 myStats->shoes = newItem(IRON_BOOTS, DECREPIT, 0, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, nullptr);
1501 }
1502 else
1503 {
1504 myStats->shoes->type = IRON_BOOTS;
1505 }
1506 break;
1507 case 4:
1508 if ( !myStats->weapon )
1509 {
1510 myStats->weapon = newItem(STEEL_HALBERD, EXCELLENT, 0, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, nullptr);
1511 }
1512 else
1513 {
1514 myStats->weapon->type = STEEL_HALBERD;
1515 }
1516 if ( !myStats->breastplate )
1517 {
1518 myStats->breastplate = newItem(IRON_BREASTPIECE, DECREPIT, 0, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, nullptr);
1519 }
1520 else
1521 {
1522 myStats->breastplate->type = IRON_BREASTPIECE;
1523 }
1524 if ( !myStats->shield )
1525 {
1526 myStats->shield = newItem(IRON_SHIELD, DECREPIT, 0, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, nullptr);
1527 }
1528 else
1529 {
1530 myStats->shield->type = IRON_SHIELD;
1531 }
1532 if ( !myStats->shoes )
1533 {
1534 myStats->shoes = newItem(STEEL_BOOTS, DECREPIT, 0, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, nullptr);
1535 }
1536 else
1537 {
1538 myStats->shoes->type = STEEL_BOOTS;
1539 }
1540 break;
1541 case 5:
1542 if ( !myStats->weapon )
1543 {
1544 myStats->weapon = newItem(STEEL_HALBERD, EXCELLENT, 0, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, nullptr);
1545 }
1546 else
1547 {
1548 myStats->weapon->type = STEEL_HALBERD;
1549 }
1550 if ( !myStats->breastplate )
1551 {
1552 myStats->breastplate = newItem(STEEL_BREASTPIECE, DECREPIT, 0, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, nullptr);
1553 }
1554 else
1555 {
1556 myStats->breastplate->type = STEEL_BREASTPIECE;
1557 }
1558 if ( !myStats->shield )
1559 {
1560 myStats->shield = newItem(STEEL_SHIELD, DECREPIT, 0, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, nullptr);
1561 }
1562 else
1563 {
1564 myStats->shield->type = STEEL_SHIELD;
1565 }
1566 if ( !myStats->shoes )
1567 {
1568 myStats->shoes = newItem(STEEL_BOOTS, DECREPIT, 0, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, nullptr);
1569 }
1570 else
1571 {
1572 myStats->shoes->type = STEEL_BOOTS;
1573 }
1574 break;
1575 case 6:
1576 if ( !myStats->weapon )
1577 {
1578 myStats->weapon = newItem(CRYSTAL_SPEAR, EXCELLENT, 0, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, nullptr);
1579 }
1580 else
1581 {
1582 myStats->weapon->type = CRYSTAL_SPEAR;
1583 }
1584 if ( !myStats->breastplate )
1585 {
1586 myStats->breastplate = newItem(STEEL_BREASTPIECE, DECREPIT, 0, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, nullptr);
1587 }
1588 else
1589 {
1590 myStats->breastplate->type = STEEL_BREASTPIECE;
1591 }
1592 if ( !myStats->shield )
1593 {
1594 myStats->shield = newItem(STEEL_SHIELD, DECREPIT, 0, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, nullptr);
1595 }
1596 else
1597 {
1598 myStats->shield->type = STEEL_SHIELD;
1599 }
1600 if ( !myStats->shoes )
1601 {
1602 myStats->shoes = newItem(CRYSTAL_BOOTS, DECREPIT, 0, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, nullptr);
1603 }
1604 else
1605 {
1606 myStats->shoes->type = CRYSTAL_BOOTS;
1607 }
1608 break;
1609 case 7:
1610 if ( !myStats->weapon )
1611 {
1612 myStats->weapon = newItem(CRYSTAL_SPEAR, EXCELLENT, 0, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, nullptr);
1613 }
1614 else
1615 {
1616 myStats->weapon->type = CRYSTAL_SPEAR;
1617 }
1618 if ( !myStats->breastplate )
1619 {
1620 myStats->breastplate = newItem(CRYSTAL_BREASTPIECE, DECREPIT, 0, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, nullptr);
1621 }
1622 else
1623 {
1624 myStats->breastplate->type = CRYSTAL_BREASTPIECE;
1625 }
1626 if ( !myStats->shield )
1627 {
1628 myStats->shield = newItem(CRYSTAL_SHIELD, DECREPIT, 0, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, nullptr);
1629 }
1630 else
1631 {
1632 myStats->shield->type = CRYSTAL_SHIELD;
1633 }
1634 if ( !myStats->shoes )
1635 {
1636 myStats->shoes = newItem(CRYSTAL_BOOTS, DECREPIT, 0, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, nullptr);
1637 }
1638 else
1639 {
1640 myStats->shoes->type = CRYSTAL_BOOTS;
1641 }
1642 break;
1643 default:
1644 break;
1645 }
1646 }
1647 else if ( !strcmp(myStats->name, "skeleton sentinel") )
1648 {
1649 switch ( rank )
1650 {
1651 case 1:
1652 if ( !myStats->weapon )
1653 {
1654 myStats->weapon = newItem(SLING, EXCELLENT, 0, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, nullptr);
1655 }
1656 else
1657 {
1658 myStats->weapon->type = SLING;
1659 }
1660 if ( !myStats->helmet )
1661 {
1662 myStats->helmet = newItem(HAT_HOOD, DECREPIT, 0, 1, 1, false, nullptr);
1663 }
1664 else
1665 {
1666 myStats->helmet->type = HAT_HOOD;
1667 }
1668 if ( !myStats->cloak )
1669 {
1670 myStats->cloak = newItem(CLOAK, DECREPIT, 0, 1, 1, false, nullptr);
1671 }
1672 else
1673 {
1674 myStats->cloak->type = CLOAK;
1675 }
1676 break;
1677 case 2:
1678 if ( !myStats->weapon )
1679 {
1680 myStats->weapon = newItem(SLING, EXCELLENT, 0, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, nullptr);
1681 }
1682 else
1683 {
1684 myStats->weapon->type = SLING;
1685 }
1686 if ( !myStats->helmet )
1687 {
1688 myStats->helmet = newItem(HAT_HOOD, DECREPIT, 0, 1, 1, false, nullptr);
1689 }
1690 else
1691 {
1692 myStats->helmet->type = HAT_HOOD;
1693 }
1694 if ( !myStats->cloak )
1695 {
1696 myStats->cloak = newItem(CLOAK, DECREPIT, 0, 1, 1, false, nullptr);
1697 }
1698 else
1699 {
1700 myStats->cloak->type = CLOAK;
1701 }
1702 if ( !myStats->shoes )
1703 {
1704 myStats->shoes = newItem(LEATHER_BOOTS, DECREPIT, 0, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, nullptr);
1705 }
1706 else
1707 {
1708 myStats->shoes->type = LEATHER_BOOTS;
1709 }
1710 break;
1711 case 3:
1712 if ( !myStats->weapon )
1713 {
1714 myStats->weapon = newItem(SHORTBOW, EXCELLENT, 0, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, nullptr);
1715 }
1716 else
1717 {
1718 myStats->weapon->type = SHORTBOW;
1719 }
1720 if ( !myStats->helmet )
1721 {
1722 myStats->helmet = newItem(HAT_HOOD, DECREPIT, 0, 1, 1, false, nullptr);
1723 }
1724 else
1725 {
1726 myStats->helmet->type = HAT_HOOD;
1727 }
1728 if ( !myStats->cloak )
1729 {
1730 myStats->cloak = newItem(CLOAK, DECREPIT, 0, 1, 1, false, nullptr);
1731 }
1732 else
1733 {
1734 myStats->cloak->type = CLOAK;
1735 }
1736 if ( !myStats->shoes )
1737 {
1738 myStats->shoes = newItem(LEATHER_BOOTS, DECREPIT, 0, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, nullptr);
1739 }
1740 else
1741 {
1742 myStats->shoes->type = LEATHER_BOOTS;
1743 }
1744 break;
1745 case 4:
1746 if ( !myStats->weapon )
1747 {
1748 myStats->weapon = newItem(SHORTBOW, EXCELLENT, 0, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, nullptr);
1749 }
1750 else
1751 {
1752 myStats->weapon->type = SHORTBOW;
1753 }
1754 if ( !myStats->helmet )
1755 {
1756 myStats->helmet = newItem(HAT_HOOD, DECREPIT, 0, 1, 1, false, nullptr);
1757 }
1758 else
1759 {
1760 myStats->helmet->type = HAT_HOOD;
1761 }
1762 if ( !myStats->cloak )
1763 {
1764 myStats->cloak = newItem(CLOAK, DECREPIT, 0, 1, 1, false, nullptr);
1765 }
1766 else
1767 {
1768 myStats->cloak->type = CLOAK;
1769 }
1770 if ( !myStats->shoes )
1771 {
1772 myStats->shoes = newItem(IRON_BOOTS, DECREPIT, 0, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, nullptr);
1773 }
1774 else
1775 {
1776 myStats->shoes->type = IRON_BOOTS;
1777 }
1778 break;
1779 case 5:
1780 if ( !myStats->weapon )
1781 {
1782 myStats->weapon = newItem(CROSSBOW, EXCELLENT, 0, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, nullptr);
1783 }
1784 else
1785 {
1786 myStats->weapon->type = CROSSBOW;
1787 }
1788 if ( !myStats->helmet )
1789 {
1790 myStats->helmet = newItem(HAT_HOOD, DECREPIT, 0, 1, 1, false, nullptr);
1791 }
1792 else
1793 {
1794 myStats->helmet->type = HAT_HOOD;
1795 }
1796 if ( !myStats->cloak )
1797 {
1798 myStats->cloak = newItem(CLOAK, DECREPIT, 0, 1, 1, false, nullptr);
1799 }
1800 else
1801 {
1802 myStats->cloak->type = CLOAK;
1803 }
1804 if ( !myStats->gloves )
1805 {
1806 myStats->gloves = newItem(BRACERS, DECREPIT, 0, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, nullptr);
1807 }
1808 else
1809 {
1810 myStats->gloves->type = BRACERS;
1811 }
1812 if ( !myStats->shoes )
1813 {
1814 myStats->shoes = newItem(IRON_BOOTS, DECREPIT, 0, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, nullptr);
1815 }
1816 else
1817 {
1818 myStats->shoes->type = IRON_BOOTS;
1819 }
1820 break;
1821 case 6:
1822 if ( !myStats->weapon )
1823 {
1824 myStats->weapon = newItem(CROSSBOW, EXCELLENT, 0, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, nullptr);
1825 }
1826 else
1827 {
1828 myStats->weapon->type = CROSSBOW;
1829 }
1830 if ( !myStats->helmet )
1831 {
1832 myStats->helmet = newItem(HAT_HOOD, DECREPIT, 0, 1, 1, false, nullptr);
1833 }
1834 else
1835 {
1836 myStats->helmet->type = HAT_HOOD;
1837 }
1838 if ( !myStats->cloak )
1839 {
1840 myStats->cloak = newItem(CLOAK, DECREPIT, 0, 1, 1, false, nullptr);
1841 }
1842 else
1843 {
1844 myStats->cloak->type = CLOAK;
1845 }
1846 if ( !myStats->gloves )
1847 {
1848 myStats->gloves = newItem(BRACERS, DECREPIT, 0, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, nullptr);
1849 }
1850 else
1851 {
1852 myStats->gloves->type = BRACERS;
1853 }
1854 if ( !myStats->shoes )
1855 {
1856 myStats->shoes = newItem(STEEL_BOOTS_FEATHER, DECREPIT, 0, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, nullptr);
1857 }
1858 else
1859 {
1860 myStats->shoes->type = STEEL_BOOTS_FEATHER;
1861 }
1862 break;
1863 case 7:
1864 if ( !myStats->weapon )
1865 {
1866 myStats->weapon = newItem(CROSSBOW, EXCELLENT, 0, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, nullptr);
1867 }
1868 else
1869 {
1870 myStats->weapon->type = CROSSBOW;
1871 }
1872 if ( !myStats->helmet )
1873 {
1874 myStats->helmet = newItem(HAT_HOOD, DECREPIT, 0, 1, 2, false, nullptr);
1875 }
1876 else
1877 {
1878 myStats->helmet->type = HAT_HOOD;
1879 myStats->helmet->appearance = 2;
1880 }
1881 if ( !myStats->cloak )
1882 {
1883 myStats->cloak = newItem(CLOAK_BLACK, DECREPIT, 0, 1, 1, false, nullptr);
1884 }
1885 else
1886 {
1887 myStats->cloak->type = CLOAK_BLACK;
1888 }
1889 if ( !myStats->gloves )
1890 {
1891 myStats->gloves = newItem(CRYSTAL_GLOVES, DECREPIT, 0, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, nullptr);
1892 }
1893 else
1894 {
1895 myStats->gloves->type = CRYSTAL_GLOVES;
1896 }
1897 if ( !myStats->shoes )
1898 {
1899 myStats->shoes = newItem(STEEL_BOOTS_FEATHER, DECREPIT, 0, 1, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, nullptr);
1900 }
1901 else
1902 {
1903 myStats->shoes->type = STEEL_BOOTS_FEATHER;
1904 }
1905 break;
1906 default:
1907 break;
1908 }
1909 }
1910 }