1 /*-------------------------------------------------------------------------------
2
3 BARONY
4 File: monster_minotaur.cpp
5 Desc: implements all of the minotaur 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 "magic/magic.hpp"
22 #include "player.hpp"
23 #include "colors.hpp"
24
initMinotaur(Entity * my,Stat * myStats)25 void initMinotaur(Entity* my, Stat* myStats)
26 {
27 int c;
28 node_t* node;
29
30 my->initMonster(239);
31
32 if ( multiplayer != CLIENT )
33 {
34 MONSTER_SPOTSND = 107;
35 MONSTER_SPOTVAR = 3;
36 MONSTER_IDLESND = 110;
37 MONSTER_IDLEVAR = 3;
38 }
39 if ( multiplayer != CLIENT && !MONSTER_INIT )
40 {
41 if ( myStats != NULL )
42 {
43 if ( !myStats->leader_uid )
44 {
45 myStats->leader_uid = 0;
46 }
47
48 // apply random stat increases if set in stat_shared.cpp or editor
49 setRandomMonsterStats(myStats);
50
51 // generate 6 items max, less if there are any forced items from boss variants
52 int customItemsToGenerate = ITEM_CUSTOM_SLOT_LIMIT;
53
54 // boss variants
55 if ( strcmp(map.name, "Hell Boss") == 0 )
56 {
57 myStats->STR = 50;
58 myStats->DEX = 20;
59 myStats->CON = 20;
60 }
61 else if ( currentlevel >= 25 )
62 {
63 myStats->HP += 400;
64 myStats->MAXHP += 400;
65 myStats->STR = 60;
66 myStats->DEX = 20;
67 myStats->CON = 20;
68 myStats->EFFECTS[EFF_VAMPIRICAURA] = true;
69 myStats->EFFECTS_TIMERS[EFF_VAMPIRICAURA] = -1;
70 }
71
72
73 // random effects
74 // minotaurs can traverse waters and pits (pits with magic :))
75 myStats->EFFECTS[EFF_LEVITATING] = true;
76 myStats->EFFECTS_TIMERS[EFF_LEVITATING] = 0;
77
78 // generates equipment and weapons if available from editor
79 createMonsterEquipment(myStats);
80
81 // create any custom inventory items from editor if available
82 createCustomInventory(myStats, customItemsToGenerate);
83
84 // count if any custom inventory items from editor
85 int customItems = countCustomItems(myStats); //max limit of 6 custom items per entity.
86
87 // count any inventory items set to default in edtior
88 int defaultItems = countDefaultItems(myStats);
89
90 my->setHardcoreStats(*myStats);
91
92 // generate the default inventory items for the monster, provided the editor sprite allowed enough default slots
93
94 ItemType gemtype = GEM_RUBY;
95
96 switch ( defaultItems )
97 {
98 case 6:
99 case 5:
100 case 4:
101 case 3:
102 case 2:
103 case 1:
104 switch ( rand() % 4 )
105 {
106 case 0:
107 gemtype = GEM_RUBY;
108 break;
109 case 1:
110 gemtype = GEM_EMERALD;
111 break;
112 case 2:
113 gemtype = GEM_SAPPHIRE;
114 break;
115 case 3:
116 gemtype = GEM_DIAMOND;
117 break;
118 }
119 newItem(gemtype, EXCELLENT, 0, 1, rand(), true, &myStats->inventory);
120 break;
121 default:
122 break;
123 }
124 }
125 }
126
127 // head
128 Entity* entity = newEntity(237, 0, map.entities, nullptr); //Limb entity.
129 entity->sizex = 4;
130 entity->sizey = 4;
131 entity->skill[2] = my->getUID();
132 entity->flags[PASSABLE] = true;
133 entity->flags[NOUPDATE] = true;
134 entity->flags[USERFLAG2] = my->flags[USERFLAG2];
135 entity->focalx = limbs[MINOTAUR][1][0]; // 0
136 entity->focaly = limbs[MINOTAUR][1][1]; // 0
137 entity->focalz = limbs[MINOTAUR][1][2]; // 0
138 entity->behavior = &actMinotaurLimb;
139 entity->parent = my->getUID();
140 node = list_AddNodeLast(&my->children);
141 node->element = entity;
142 node->deconstructor = &emptyDeconstructor;
143 node->size = sizeof(Entity*);
144 my->bodyparts.push_back(entity);
145
146 // chest
147 entity = newEntity(238, 0, map.entities, nullptr); //Limb entity.
148 entity->sizex = 4;
149 entity->sizey = 4;
150 entity->skill[2] = my->getUID();
151 entity->flags[PASSABLE] = true;
152 entity->flags[NOUPDATE] = true;
153 entity->flags[USERFLAG2] = my->flags[USERFLAG2];
154 entity->focalx = limbs[MINOTAUR][2][0]; // 0
155 entity->focaly = limbs[MINOTAUR][2][1]; // 0
156 entity->focalz = limbs[MINOTAUR][2][2]; // 0
157 entity->behavior = &actMinotaurLimb;
158 entity->parent = my->getUID();
159 node = list_AddNodeLast(&my->children);
160 node->element = entity;
161 node->deconstructor = &emptyDeconstructor;
162 node->size = sizeof(Entity*);
163 my->bodyparts.push_back(entity);
164
165 // right leg
166 entity = newEntity(243, 0, map.entities, nullptr); //Limb entity.
167 entity->sizex = 4;
168 entity->sizey = 4;
169 entity->skill[2] = my->getUID();
170 entity->flags[PASSABLE] = true;
171 entity->flags[NOUPDATE] = true;
172 entity->flags[USERFLAG2] = my->flags[USERFLAG2];
173 entity->focalx = limbs[MINOTAUR][3][0]; // 1
174 entity->focaly = limbs[MINOTAUR][3][1]; // 0
175 entity->focalz = limbs[MINOTAUR][3][2]; // 5
176 entity->behavior = &actMinotaurLimb;
177 entity->parent = my->getUID();
178 node = list_AddNodeLast(&my->children);
179 node->element = entity;
180 node->deconstructor = &emptyDeconstructor;
181 node->size = sizeof(Entity*);
182 my->bodyparts.push_back(entity);
183
184 // left leg
185 entity = newEntity(242, 0, map.entities, nullptr); //Limb entity.
186 entity->sizex = 4;
187 entity->sizey = 4;
188 entity->skill[2] = my->getUID();
189 entity->flags[PASSABLE] = true;
190 entity->flags[NOUPDATE] = true;
191 entity->flags[USERFLAG2] = my->flags[USERFLAG2];
192 entity->focalx = limbs[MINOTAUR][4][0]; // 1
193 entity->focaly = limbs[MINOTAUR][4][1]; // 0
194 entity->focalz = limbs[MINOTAUR][4][2]; // 5
195 entity->behavior = &actMinotaurLimb;
196 entity->parent = my->getUID();
197 node = list_AddNodeLast(&my->children);
198 node->element = entity;
199 node->deconstructor = &emptyDeconstructor;
200 node->size = sizeof(Entity*);
201 my->bodyparts.push_back(entity);
202
203 // right arm
204 entity = newEntity(241, 0, map.entities, nullptr); //Limb entity.
205 entity->sizex = 4;
206 entity->sizey = 4;
207 entity->skill[2] = my->getUID();
208 entity->flags[PASSABLE] = true;
209 entity->flags[NOUPDATE] = true;
210 entity->flags[USERFLAG2] = my->flags[USERFLAG2];
211 entity->focalx = limbs[MINOTAUR][5][0]; // 2.5
212 entity->focaly = limbs[MINOTAUR][5][1]; // 7
213 entity->focalz = limbs[MINOTAUR][5][2]; // 3.5
214 entity->behavior = &actMinotaurLimb;
215 entity->parent = my->getUID();
216 node = list_AddNodeLast(&my->children);
217 node->element = entity;
218 node->deconstructor = &emptyDeconstructor;
219 node->size = sizeof(Entity*);
220 my->bodyparts.push_back(entity);
221
222 // left arm
223 entity = newEntity(240, 0, map.entities, nullptr); //Limb entity.
224 entity->sizex = 4;
225 entity->sizey = 4;
226 entity->skill[2] = my->getUID();
227 entity->flags[PASSABLE] = true;
228 entity->flags[NOUPDATE] = true;
229 entity->flags[USERFLAG2] = my->flags[USERFLAG2];
230 entity->focalx = limbs[MINOTAUR][6][0]; // 2.5
231 entity->focaly = limbs[MINOTAUR][6][1]; // -7
232 entity->focalz = limbs[MINOTAUR][6][2]; // 3.5
233 entity->behavior = &actMinotaurLimb;
234 entity->parent = my->getUID();
235 node = list_AddNodeLast(&my->children);
236 node->element = entity;
237 node->deconstructor = &emptyDeconstructor;
238 node->size = sizeof(Entity*);
239 my->bodyparts.push_back(entity);
240 }
241
actMinotaurLimb(Entity * my)242 void actMinotaurLimb(Entity* my)
243 {
244 my->actMonsterLimb();
245 }
246
minotaurDie(Entity * my)247 void minotaurDie(Entity* my)
248 {
249 int c;
250 for ( c = 0; c < 5; c++ )
251 {
252 Entity* gib = spawnGib(my);
253 serverSpawnGibForClient(gib);
254 }
255
256 my->spawnBlood();
257
258 for ( c = 0; c < MAXPLAYERS; c++ )
259 {
260 playSoundPlayer(c, 114, 128);
261 }
262
263 my->removeMonsterDeathNodes();
264
265 list_RemoveNode(my->mynode);
266 return;
267 }
268
269 #define MINOTAURWALKSPEED .07
270
minotaurMoveBodyparts(Entity * my,Stat * myStats,double dist)271 void minotaurMoveBodyparts(Entity* my, Stat* myStats, double dist)
272 {
273 node_t* node;
274 Entity* entity = NULL;
275 Entity* rightbody = NULL;
276 Entity* head = NULL;
277 Entity* chest = NULL;
278 int bodypart;
279
280 // set invisibility //TODO: isInvisible()?
281 if ( multiplayer != CLIENT )
282 {
283 if ( myStats->EFFECTS[EFF_INVISIBLE] == true )
284 {
285 my->flags[INVISIBLE] = true;
286 my->flags[BLOCKSIGHT] = false;
287 bodypart = 0;
288 for (node = my->children.first; node != NULL; node = node->next)
289 {
290 if ( bodypart < 2 )
291 {
292 bodypart++;
293 continue;
294 }
295 if ( bodypart >= 7 )
296 {
297 break;
298 }
299 entity = (Entity*)node->element;
300 if ( !entity->flags[INVISIBLE] )
301 {
302 entity->flags[INVISIBLE] = true;
303 serverUpdateEntityBodypart(my, bodypart);
304 }
305 bodypart++;
306 }
307 }
308 else
309 {
310 my->flags[INVISIBLE] = false;
311 my->flags[BLOCKSIGHT] = true;
312 bodypart = 0;
313 for (node = my->children.first; node != NULL; node = node->next)
314 {
315 if ( bodypart < 2 )
316 {
317 bodypart++;
318 continue;
319 }
320 if ( bodypart >= 7 )
321 {
322 break;
323 }
324 entity = (Entity*)node->element;
325 if ( entity->flags[INVISIBLE] )
326 {
327 entity->flags[INVISIBLE] = false;
328 serverUpdateEntityBodypart(my, bodypart);
329 serverUpdateEntityFlag(my, INVISIBLE);
330 }
331 bodypart++;
332 }
333 }
334 }
335
336 //Move bodyparts
337 for (bodypart = 0, node = my->children.first; node != NULL; node = node->next, bodypart++)
338 {
339 if ( bodypart < 2 )
340 {
341 continue;
342 }
343 entity = (Entity*)node->element;
344 entity->x = my->x;
345 entity->y = my->y;
346 entity->z = my->z;
347 entity->yaw = my->yaw;
348 if ( bodypart == 2 )
349 {
350 head = entity;
351 }
352 else if ( bodypart == 3 )
353 {
354 chest = entity;
355 }
356 if ( bodypart == 4 || bodypart == 7 )
357 {
358 if ( bodypart == 4 )
359 {
360 rightbody = (Entity*)node->next->element;
361 }
362 if ( dist > 0.1 )
363 {
364 if ( !rightbody->skill[0] )
365 {
366 entity->pitch -= dist * MINOTAURWALKSPEED;
367 if ( entity->pitch < -PI / 4.0 )
368 {
369 entity->pitch = -PI / 4.0;
370 if (bodypart == 4 && dist > .4)
371 {
372 playSound(115, 128);
373 }
374 }
375 }
376 else
377 {
378 entity->pitch += dist * MINOTAURWALKSPEED;
379 if ( entity->pitch > PI / 4.0 )
380 {
381 entity->pitch = PI / 4.0;
382 if (bodypart == 4 && dist > .4)
383 {
384 playSound(115, 128);
385 }
386 }
387 }
388 }
389 else
390 {
391 if ( entity->pitch < 0 )
392 {
393 entity->pitch += 1 / fmax(dist * .1, 10.0);
394 if ( entity->pitch > 0 )
395 {
396 entity->pitch = 0;
397 }
398 }
399 else if ( entity->pitch > 0 )
400 {
401 entity->pitch -= 1 / fmax(dist * .1, 10.0);
402 if ( entity->pitch < 0 )
403 {
404 entity->pitch = 0;
405 }
406 }
407 }
408 }
409 else if ( bodypart == 5 || bodypart == 6 )
410 {
411 if ( bodypart == 6 )
412 {
413 if ( my->monsterAttack > 0 )
414 {
415 // vertical chop windup
416 if ( my->monsterAttack == MONSTER_POSE_MELEE_WINDUP1 )
417 {
418 if ( my->monsterAttackTime == 0 )
419 {
420 // init rotations
421 entity->pitch = 0;
422 my->monsterArmbended = 0;
423 my->monsterWeaponYaw = 0;
424 entity->roll = 0;
425 entity->skill[1] = 0;
426 }
427
428 limbAnimateToLimit(entity, ANIMATE_PITCH, -0.25, 5 * PI / 4, false, 0.0);
429
430 if ( my->monsterAttackTime >= ANIMATE_DURATION_WINDUP / (monsterGlobalAnimationMultiplier / 10.0) )
431 {
432 if ( multiplayer != CLIENT )
433 {
434 my->attack(1, 0, nullptr);
435 }
436 }
437 }
438 // ceiling buster chop windup
439 if ( my->monsterAttack == MONSTER_POSE_MELEE_WINDUP2 )
440 {
441 if ( my->monsterAttackTime == 0 )
442 {
443 // init rotations
444 entity->pitch = 0;
445 my->monsterArmbended = 0;
446 my->monsterWeaponYaw = 0;
447 entity->roll = 0;
448 entity->skill[1] = 0;
449 }
450
451 limbAnimateToLimit(entity, ANIMATE_PITCH, -0.25, 5 * PI / 4, false, 0.0);
452
453 if ( my->monsterAttackTime >= ANIMATE_DURATION_WINDUP / (monsterGlobalAnimationMultiplier / 10.0) )
454 {
455 my->monsterAttack = 1;
456 }
457 }
458 // vertical chop attack
459 else if ( my->monsterAttack == 1 )
460 {
461 if ( entity->pitch >= 3 * PI / 2 )
462 {
463 my->monsterArmbended = 1;
464 }
465
466 if ( entity->skill[1] == 0 )
467 {
468 // chop forwards
469 if ( limbAnimateToLimit(entity, ANIMATE_PITCH, 0.4, PI / 3, false, 0.0) )
470 {
471 entity->skill[1] = 1;
472 }
473 }
474 else if ( entity->skill[1] == 1 )
475 {
476 // return to neutral
477 if ( limbAnimateToLimit(entity, ANIMATE_PITCH, -0.25, 7 * PI / 4, false, 0.0) )
478 {
479 entity->skill[0] = rightbody->skill[0];
480 my->monsterWeaponYaw = 0;
481 entity->pitch = rightbody->pitch;
482 entity->roll = 0;
483 my->monsterArmbended = 0;
484 my->monsterAttack = 0;
485 }
486 }
487 }
488 }
489 }
490
491 if ( bodypart != 6 || (MONSTER_ATTACK == 0 && MONSTER_ATTACKTIME == 0) )
492 {
493 if ( dist > 0.1 )
494 {
495 if ( entity->skill[0] )
496 {
497 entity->pitch -= dist * MINOTAURWALKSPEED;
498 if ( entity->pitch < -PI / 4.0 )
499 {
500 entity->skill[0] = 0;
501 entity->pitch = -PI / 4.0;
502 }
503 }
504 else
505 {
506 entity->pitch += dist * MINOTAURWALKSPEED;
507 if ( entity->pitch > PI / 4.0 )
508 {
509 entity->skill[0] = 1;
510 entity->pitch = PI / 4.0;
511 }
512 }
513 }
514 else
515 {
516 if ( entity->pitch < 0 )
517 {
518 entity->pitch += 1 / fmax(dist * .1, 10.0);
519 if ( entity->pitch > 0 )
520 {
521 entity->pitch = 0;
522 }
523 }
524 else if ( entity->pitch > 0 )
525 {
526 entity->pitch -= 1 / fmax(dist * .1, 10.0);
527 if ( entity->pitch < 0 )
528 {
529 entity->pitch = 0;
530 }
531 }
532 }
533 }
534 }
535 if ( !MONSTER_ATTACK )
536 {
537 if ( bodypart == 6 )
538 {
539 entity->yaw += entity->pitch;
540 head->yaw = entity->yaw;
541 chest->yaw = entity->yaw;
542 }
543 else if ( bodypart == 7 )
544 {
545 entity->yaw -= entity->pitch;
546 }
547 }
548 switch ( bodypart )
549 {
550 // head
551 case 2:
552 entity->z -= 11;
553 break;
554 // chest
555 case 3:
556 entity->z -= 4.5;
557 break;
558 // right leg
559 case 4:
560 entity->x += 2.5 * cos(my->yaw + PI / 2) - .5 * cos(my->yaw);
561 entity->y += 2.5 * sin(my->yaw + PI / 2) - .5 * sin(my->yaw);
562 entity->z += 2.5;
563 break;
564 // left leg
565 case 5:
566 entity->x -= 2.5 * cos(my->yaw + PI / 2) + .5 * cos(my->yaw);
567 entity->y -= 2.5 * sin(my->yaw + PI / 2) + .5 * sin(my->yaw);
568 entity->z += 2.5;
569 break;
570 // right arm
571 case 6:
572 entity->z -= 6;
573 entity->yaw += MONSTER_WEAPONYAW;
574 break;
575 // left arm
576 case 7:
577 entity->z -= 6;
578 break;
579 }
580 }
581 if ( MONSTER_ATTACK != 0 )
582 {
583 MONSTER_ATTACKTIME++;
584 }
585 else
586 {
587 MONSTER_ATTACKTIME = 0;
588 }
589 }
590
591 /*-------------------------------------------------------------------------------
592
593 act*
594
595 The following function describes an entity behavior. The function
596 takes a pointer to the entity that uses it as an argument.
597
598 -------------------------------------------------------------------------------*/
599
600 #define MINOTAURTRAP_FIRED my->skill[0]
601
actMinotaurTrap(Entity * my)602 void actMinotaurTrap(Entity* my)
603 {
604 if ( !my->skill[28] )
605 {
606 return;
607 }
608
609 // received on signal
610 if ( my->skill[28] == 2)
611 {
612 if ( !MINOTAURTRAP_FIRED )
613 {
614 Entity* monster = summonMonster(MINOTAUR, my->x, my->y);
615 if ( monster )
616 {
617 MINOTAURTRAP_FIRED = 1;
618 if ( strcmp(map.name, "Hell Boss") )
619 {
620 int c;
621 for ( c = 0; c < MAXPLAYERS; c++ )
622 {
623 playSoundPlayer( c, 107 + rand() % 3, 128 );
624 Uint32 color = SDL_MapRGB(mainsurface->format, 255, 128, 0);
625 messagePlayerColor(c, color, language[1113]);
626 }
627 }
628 }
629 }
630 }
631 }
632
633 #define MINOTAURTIMER_LIFE my->skill[0]
634 #define MINOTAURTIMER_ACTIVE my->skill[1]
635
actMinotaurTimer(Entity * my)636 void actMinotaurTimer(Entity* my)
637 {
638 node_t* node;
639
640 MINOTAURTIMER_LIFE++;
641 if (( (currentlevel < 25 && MINOTAURTIMER_LIFE == TICKS_PER_SECOND * 120)
642 || (currentlevel >= 25 && MINOTAURTIMER_LIFE == TICKS_PER_SECOND * 180)
643 )
644 && rand() % 5 == 0 ) // two minutes if currentlevel < 25, else 3 minutes.
645 {
646 int c;
647 bool spawnedsomebody = false;
648 for ( c = 0; c < 9; c++ )
649 {
650 Uint32 zapLeaderUid = 0;
651 Entity* monster = summonMonster(HUMAN, my->x, my->y);
652 if ( monster )
653 {
654 monster->skill[29] = 1; // so we spawn a zap brigadier
655 spawnedsomebody = true;
656 if ( !zapLeaderUid )
657 {
658 zapLeaderUid = monster->getUID();
659 }
660 else
661 {
662 Stat* monsterStats = monster->getStats();
663 monsterStats->leader_uid = zapLeaderUid;
664 }
665 }
666 }
667
668 if ( spawnedsomebody )
669 {
670 #ifdef MUSIC
671 fadein_increment = default_fadein_increment * 20;
672 fadeout_increment = default_fadeout_increment * 5;
673 playmusic( sounds[175], false, true, false);
674 #endif
675 for ( c = 0; c < MAXPLAYERS; c++ )
676 {
677 Uint32 color = SDL_MapRGB(mainsurface->format, 0, 255, 255);
678 if ( stats[c]->type == HUMAN )
679 {
680 messagePlayerColor(c, color, language[1114], stats[c]->name);
681 }
682 else
683 {
684 messagePlayerColor(c, color, language[3285]);
685 }
686 }
687 }
688 }
689 else if (( (currentlevel < 25 && MINOTAURTIMER_LIFE >= TICKS_PER_SECOND * 150)
690 || (currentlevel >= 25 && MINOTAURTIMER_LIFE >= TICKS_PER_SECOND * 210)
691 )
692 && !MINOTAURTIMER_ACTIVE ) // two and a half minutes if currentlevel < 25, else 3.5 minutes
693 {
694 Entity* monster = summonMonster(MINOTAUR, my->x, my->y);
695 if ( monster )
696 {
697 int c;
698 for ( c = 0; c < MAXPLAYERS; c++ )
699 {
700 playSoundPlayer( c, 107 + rand() % 3, 128 );
701 Uint32 color = SDL_MapRGB(mainsurface->format, 255, 128, 0);
702 messagePlayerColor(c, color, language[1115]);
703 }
704 MINOTAURTIMER_ACTIVE = MINOTAURTIMER_LIFE;
705 }
706 }
707 if ( MINOTAURTIMER_ACTIVE && MINOTAURTIMER_LIFE >= MINOTAURTIMER_ACTIVE + TICKS_PER_SECOND * 3 )
708 {
709 int c;
710 for ( c = 0; c < MAXPLAYERS; c++ )
711 {
712 if ( currentlevel < 25 )
713 {
714 playSoundPlayer(c, 120 + rand() % 3, 128);
715 Uint32 color = SDL_MapRGB(mainsurface->format, 255, 0, 255);
716 messagePlayerColor(c, color, language[1116]);
717 messagePlayerColor(c, color, language[73]);
718 }
719 else
720 {
721 playSoundPlayer(c, 375, 128);
722 playSoundPlayer(c, 379, 128);
723 messagePlayerColor(c, uint32ColorOrange(*mainsurface), language[1116]);
724 messagePlayerColor(c, uint32ColorOrange(*mainsurface), language[73]);
725 messagePlayerColor(c, uint32ColorBaronyBlue(*mainsurface), language[73]);
726 }
727 }
728 list_RemoveNode(my->mynode);
729 return;
730 }
731 }
732
actMinotaurCeilingBuster(Entity * my)733 void actMinotaurCeilingBuster(Entity* my)
734 {
735 double x, y;
736
737 // levitate particles
738 int u = std::min<unsigned int>(std::max<int>(0, my->x / 16), map.width - 1);
739 int v = std::min<unsigned int>(std::max<int>(0, my->y / 16), map.height - 1);
740 if ( !map.tiles[v * MAPLAYERS + u * MAPLAYERS * map.height] )
741 {
742 int c;
743 for ( c = 0; c < 2; c++ )
744 {
745 Entity* entity = newEntity(171, 1, map.entities, nullptr); //Particle entity.
746 entity->x = my->x - 8 + rand() % 17;
747 entity->y = my->y - 8 + rand() % 17;
748 entity->z = 10 + rand() % 3;
749 entity->scalex = 0.7;
750 entity->scaley = 0.7;
751 entity->scalez = 0.7;
752 entity->sizex = 1;
753 entity->sizey = 1;
754 entity->yaw = (rand() % 360) * PI / 180.f;
755 entity->flags[PASSABLE] = true;
756 entity->flags[BRIGHT] = true;
757 entity->flags[NOUPDATE] = true;
758 entity->flags[UNCLICKABLE] = true;
759 entity->behavior = &actMagicParticle;
760 if ( multiplayer != CLIENT )
761 {
762 entity_uids--;
763 }
764 entity->setUID(-3);
765 }
766 }
767
768 // bust ceilings
769 for ( x = my->x - my->sizex - 1; x <= my->x + my->sizex + 1; x += 1 )
770 {
771 for ( y = my->y - my->sizey - 1; y <= my->y + my->sizey + 1; y += 1 )
772 {
773 if ( x >= 0 && y >= 0 && x < map.width << 4 && y < map.height << 4 )
774 {
775 int index = (MAPLAYERS - 1) + ((int)floor(y / 16)) * MAPLAYERS + ((int)floor(x / 16)) * MAPLAYERS * map.height;
776 if ( map.tiles[index] )
777 {
778 if ( my->monsterAttack == 0 )
779 {
780 if ( multiplayer != CLIENT )
781 {
782 my->attack(MONSTER_POSE_MELEE_WINDUP2, 0, nullptr);
783 }
784 return;
785 }
786 else if ( my->monsterAttack == MONSTER_POSE_MELEE_WINDUP2 )
787 {
788 return;
789 }
790 map.tiles[index] = 0;
791 if ( multiplayer != CLIENT )
792 {
793 playSoundEntity(my, 67, 128);
794 //MONSTER_ATTACK = 1;
795 Stat* myStats = my->getStats();
796 if ( myStats )
797 {
798 // easy hack to stop the minotaur while he breaks stuff
799 myStats->EFFECTS[EFF_PARALYZED] = true;
800 myStats->EFFECTS_TIMERS[EFF_PARALYZED] = 10;
801 }
802 }
803
804 // spawn several rock particles (NOT items)
805 int c, i = 6 + rand() % 4;
806 for ( c = 0; c < i; c++ )
807 {
808 Entity *entity = nullptr;
809 if ( multiplayer == SERVER )
810 {
811 entity = spawnGib(my);
812 }
813 else
814 {
815 entity = spawnGibClient(my->x, my->y, my->z, 5);
816 }
817 if ( entity )
818 {
819 entity->x = ((int)(my->x / 16)) * 16 + rand() % 16;
820 entity->y = ((int)(my->y / 16)) * 16 + rand() % 16;
821 entity->z = -8;
822 entity->flags[PASSABLE] = true;
823 entity->flags[INVISIBLE] = false;
824 entity->flags[NOUPDATE] = true;
825 entity->flags[UPDATENEEDED] = false;
826 entity->sprite = items[GEM_ROCK].index;
827 entity->yaw = rand() % 360 * PI / 180;
828 entity->pitch = rand() % 360 * PI / 180;
829 entity->roll = rand() % 360 * PI / 180;
830 entity->vel_x = (rand() % 20 - 10) / 10.0;
831 entity->vel_y = (rand() % 20 - 10) / 10.0;
832 entity->vel_z = -.25;
833 entity->fskill[3] = 0.03;
834 }
835 }
836 }
837 node_t* node, *nextnode;
838 std::vector<list_t*> entLists = TileEntityList.getEntitiesWithinRadiusAroundEntity(my, 2);
839 for ( std::vector<list_t*>::iterator it = entLists.begin(); it != entLists.end(); ++it )
840 {
841 list_t* currentList = *it;
842 for ( node = currentList->first; node != nullptr; node = nextnode )
843 {
844 nextnode = node->next;
845 Entity* entity = (Entity*)node->element;
846 if ( (int)(x / 16) == (int)(entity->x / 16) && (int)(y / 16) == (int)(entity->y / 16) )
847 {
848 if ( entity->behavior == &actDoorFrame )
849 {
850 // spawn several rock items
851 int c, i = 8 + rand() % 4;
852 for ( c = 0; c < i; c++ )
853 {
854 Entity *entity = nullptr;
855 if ( multiplayer == SERVER )
856 {
857 entity = spawnGib(my);
858 }
859 else
860 {
861 entity = spawnGibClient(my->x, my->y, my->z, 5);
862 }
863 if ( entity )
864 {
865 entity->x = ((int)(my->x / 16)) * 16 + rand() % 16;
866 entity->y = ((int)(my->y / 16)) * 16 + rand() % 16;
867 entity->z = -8;
868 entity->flags[PASSABLE] = true;
869 entity->flags[INVISIBLE] = false;
870 entity->flags[NOUPDATE] = true;
871 entity->flags[UPDATENEEDED] = false;
872 entity->sprite = items[GEM_ROCK].index;
873 entity->yaw = rand() % 360 * PI / 180;
874 entity->pitch = rand() % 360 * PI / 180;
875 entity->roll = rand() % 360 * PI / 180;
876 entity->vel_x = (rand() % 20 - 10) / 10.0;
877 entity->vel_y = (rand() % 20 - 10) / 10.0;
878 entity->vel_z = -.25;
879 entity->fskill[3] = 0.03;
880 }
881 }
882 list_RemoveNode(entity->mynode);
883 }
884 else if ( entity->behavior == &actDoor )
885 {
886 if ( multiplayer != CLIENT )
887 {
888 entity->skill[4] = 0; // destroy the door
889 }
890 }
891 else if ( entity->behavior == &actGate )
892 {
893 if ( multiplayer != CLIENT )
894 {
895 playSoundEntity(entity, 76, 64);
896 list_RemoveNode(entity->mynode);
897 }
898 }
899 else if ( entity->behavior == &actStalagCeiling ||
900 entity->behavior == &actStalagFloor ||
901 entity->behavior == &actStalagColumn
902 )
903 {
904 // spawn several rock items
905 int c, i = rand() % 4;
906 for ( c = 0; c < i; ++c )
907 {
908 //Entity* childEntity = spawnGib(my);
909 Entity *childEntity = nullptr;
910 if ( multiplayer == SERVER )
911 {
912 childEntity = spawnGib(my);
913 }
914 else
915 {
916 childEntity = spawnGibClient(my->x, my->y, my->z, 5);
917 }
918 if ( entity )
919 {
920 childEntity->x = ((int)(my->x / 16)) * 16 + rand() % 16;
921 childEntity->y = ((int)(my->y / 16)) * 16 + rand() % 16;
922 childEntity->z = -8;
923 childEntity->flags[PASSABLE] = true;
924 childEntity->flags[INVISIBLE] = false;
925 childEntity->flags[NOUPDATE] = true;
926 childEntity->flags[UPDATENEEDED] = false;
927 childEntity->sprite = items[GEM_ROCK].index;
928 childEntity->yaw = rand() % 360 * PI / 180;
929 childEntity->pitch = rand() % 360 * PI / 180;
930 childEntity->roll = rand() % 360 * PI / 180;
931 childEntity->vel_x = (rand() % 20 - 10) / 10.0;
932 childEntity->vel_y = (rand() % 20 - 10) / 10.0;
933 childEntity->vel_z = -.25;
934 childEntity->fskill[3] = 0.03;
935 }
936 }
937 list_RemoveNode(entity->mynode);
938 }
939 }
940 }
941 }
942 }
943 }
944 }
945 }
946
createMinotaurTimer(Entity * entity,map_t * map)947 void createMinotaurTimer(Entity* entity, map_t* map)
948 {
949 Entity* childEntity = newEntity(37, 0, map->entities, nullptr); //Timer entity.
950 childEntity->sizex = 2;
951 childEntity->sizey = 2;
952 childEntity->x = entity->x;
953 childEntity->y = entity->y;
954 childEntity->behavior = &actMinotaurTimer;
955 childEntity->flags[SPRITE] = true;
956 childEntity->flags[INVISIBLE] = true;
957 childEntity->flags[PASSABLE] = true;
958 childEntity->flags[NOUPDATE] = true;
959 childEntity->setUID(-3);
960 entity_uids--;
961 }
962