1 /*-------------------------------------------------------------------------------
2
3 BARONY
4 File: monster_devil.cpp
5 Desc: implements all of the devil 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 "scores.hpp"
23 #include "magic/magic.hpp"
24
initDevil(Entity * my,Stat * myStats)25 void initDevil(Entity* my, Stat* myStats)
26 {
27 int c;
28 node_t* node;
29
30 my->initMonster(304);
31
32 if ( multiplayer != CLIENT )
33 {
34 MONSTER_SPOTSND = -1;
35 MONSTER_SPOTVAR = 3;
36 MONSTER_IDLESND = -1;
37 MONSTER_IDLEVAR = 3;
38 }
39 if ( multiplayer != CLIENT && !MONSTER_INIT )
40 {
41 if ( myStats->HP == 1250 )
42 {
43 for ( c = 0; c < MAXPLAYERS; ++c )
44 {
45 if ( !client_disconnected[c] )
46 {
47 myStats->MAXHP += 250;
48 }
49 }
50 myStats->HP = myStats->MAXHP;
51 myStats->OLDHP = myStats->HP;
52 }
53
54 if (players[0] && players[0]->entity)
55 {
56 my->monsterTarget = players[0]->entity->getUID();
57 my->monsterTargetX = players[0]->entity->x;
58 my->monsterTargetY = players[0]->entity->y;
59 }
60
61 my->setHardcoreStats(*myStats);
62 }
63
64 // head
65 Entity* entity = newEntity(303, 0, map.entities, nullptr); //Limb entity.
66 entity->sizex = 4;
67 entity->sizey = 4;
68 entity->skill[2] = my->getUID();
69 entity->flags[PASSABLE] = true;
70 entity->flags[NOUPDATE] = true;
71 entity->flags[USERFLAG2] = my->flags[USERFLAG2];
72 entity->focalx = limbs[DEVIL][1][0]; // 2.5
73 entity->focaly = limbs[DEVIL][1][1]; // 0
74 entity->focalz = limbs[DEVIL][1][2]; // -4
75 entity->behavior = &actDevilLimb;
76 entity->parent = my->getUID();
77 node = list_AddNodeLast(&my->children);
78 node->element = entity;
79 node->deconstructor = &emptyDeconstructor;
80 node->size = sizeof(Entity*);
81 my->bodyparts.push_back(entity);
82
83 // right bicep
84 entity = newEntity(305, 0, map.entities, nullptr); //Limb entity.
85 entity->sizex = 4;
86 entity->sizey = 4;
87 entity->skill[2] = my->getUID();
88 entity->flags[PASSABLE] = true;
89 entity->flags[NOUPDATE] = true;
90 entity->flags[USERFLAG2] = my->flags[USERFLAG2];
91 entity->focalx = limbs[DEVIL][2][0]; // 0
92 entity->focaly = limbs[DEVIL][2][1]; // 18
93 entity->focalz = limbs[DEVIL][2][2]; // 6
94 entity->behavior = &actDevilLimb;
95 entity->parent = my->getUID();
96 node = list_AddNodeLast(&my->children);
97 node->element = entity;
98 node->deconstructor = &emptyDeconstructor;
99 node->size = sizeof(Entity*);
100 my->bodyparts.push_back(entity);
101
102 // right forearm
103 entity = newEntity(306, 0, map.entities, nullptr); //Limb entity.
104 entity->sizex = 4;
105 entity->sizey = 4;
106 entity->skill[2] = my->getUID();
107 entity->flags[PASSABLE] = true;
108 entity->flags[NOUPDATE] = true;
109 entity->flags[USERFLAG2] = my->flags[USERFLAG2];
110 entity->focalx = limbs[DEVIL][3][0]; // 0
111 entity->focaly = limbs[DEVIL][3][1]; // 17
112 entity->focalz = limbs[DEVIL][3][2]; // 26
113 entity->behavior = &actDevilLimb;
114 entity->parent = my->getUID();
115 node = list_AddNodeLast(&my->children);
116 node->element = entity;
117 node->deconstructor = &emptyDeconstructor;
118 node->size = sizeof(Entity*);
119 my->bodyparts.push_back(entity);
120
121 // left bicep
122 entity = newEntity(307, 0, map.entities, nullptr); //Limb entity.
123 entity->sizex = 4;
124 entity->sizey = 4;
125 entity->skill[2] = my->getUID();
126 entity->flags[PASSABLE] = true;
127 entity->flags[NOUPDATE] = true;
128 entity->flags[USERFLAG2] = my->flags[USERFLAG2];
129 entity->focalx = limbs[DEVIL][4][0]; // 0
130 entity->focaly = limbs[DEVIL][4][1]; // -18
131 entity->focalz = limbs[DEVIL][4][2]; // 6
132 entity->behavior = &actDevilLimb;
133 entity->parent = my->getUID();
134 node = list_AddNodeLast(&my->children);
135 node->element = entity;
136 node->deconstructor = &emptyDeconstructor;
137 node->size = sizeof(Entity*);
138 my->bodyparts.push_back(entity);
139
140 // left forearm
141 entity = newEntity(308, 0, map.entities, nullptr); //Limb entity.
142 entity->sizex = 4;
143 entity->sizey = 4;
144 entity->skill[2] = my->getUID();
145 entity->flags[PASSABLE] = true;
146 entity->flags[NOUPDATE] = true;
147 entity->flags[USERFLAG2] = my->flags[USERFLAG2];
148 entity->focalx = limbs[DEVIL][5][0]; // 0
149 entity->focaly = limbs[DEVIL][5][1]; // -17
150 entity->focalz = limbs[DEVIL][5][2]; // 26
151 entity->behavior = &actDevilLimb;
152 entity->parent = my->getUID();
153 node = list_AddNodeLast(&my->children);
154 node->element = entity;
155 node->deconstructor = &emptyDeconstructor;
156 node->size = sizeof(Entity*);
157 my->bodyparts.push_back(entity);
158 }
159
actDevilLimb(Entity * my)160 void actDevilLimb(Entity* my)
161 {
162 my->actMonsterLimb();
163 }
164
devilDie(Entity * my)165 void devilDie(Entity* my)
166 {
167 node_t* node;
168
169 int c;
170 for ( c = 0; c < 5; c++ )
171 {
172 Entity* gib = spawnGib(my);
173 serverSpawnGibForClient(gib);
174 }
175 //playSoundEntity(my, 28, 128);
176
177 my->removeMonsterDeathNodes();
178
179 if ( multiplayer == SERVER )
180 {
181 for ( c = 1; c < MAXPLAYERS; c++ )
182 {
183 if ( client_disconnected[c] )
184 {
185 continue;
186 }
187 strcpy((char*)net_packet->data, "BDTH");
188 net_packet->address.host = net_clients[c - 1].host;
189 net_packet->address.port = net_clients[c - 1].port;
190 net_packet->len = 4;
191 sendPacketSafe(net_sock, -1, net_packet, c - 1);
192 }
193 }
194 unsigned int x = 0;
195 unsigned int y = 0;
196 for ( y = map.height / 2 - 1; y < map.height / 2 + 2; y++ )
197 {
198 for ( x = 3; x < map.width / 2; x++ )
199 {
200 if ( !map.tiles[y * MAPLAYERS + x * MAPLAYERS * map.height] )
201 {
202 map.tiles[y * MAPLAYERS + x * MAPLAYERS * map.height] = 72;
203 }
204 }
205 }
206 for ( node = map.entities->first; node != NULL; node = node->next )
207 {
208 Entity* entity = (Entity*)node->element;
209 if ( entity->skill[28] )
210 {
211 entity->skill[28] = 2;
212 }
213 }
214 list_RemoveNode(my->mynode);
215 for ( c = 0; c < MAXPLAYERS; c++ )
216 {
217 steamAchievementClient(c, "BARONY_ACH_EVIL_INCARNATE");
218 if ( completionTime < 20 * 60 * TICKS_PER_SECOND
219 && currentlevel >= 24 )
220 {
221 //messagePlayer(c, "completion time: %d", completionTime);
222 steamAchievementClient(c, "BARONY_ACH_BOOTS_OF_SPEED");
223 }
224 //messagePlayer(c, language[1112]);
225 //playSoundPlayer(c, 97, 128);
226 //stats[c]->STR += 20;
227 //stats[c]->DEX += 5;
228 //stats[c]->CON += 20;
229 //stats[c]->INT += 5;
230 //if ( multiplayer == SERVER && c > 0 )
231 //{
232 // strcpy((char*)net_packet->data, "ATTR");
233 // net_packet->data[4] = clientnum;
234 // net_packet->data[5] = (Sint8)stats[c]->STR;
235 // net_packet->data[6] = (Sint8)stats[c]->DEX;
236 // net_packet->data[7] = (Sint8)stats[c]->CON;
237 // net_packet->data[8] = (Sint8)stats[c]->INT;
238 // net_packet->data[9] = (Sint8)stats[c]->PER;
239 // net_packet->data[10] = (Sint8)stats[c]->CHR;
240 // net_packet->data[11] = (Sint8)stats[c]->EXP;
241 // net_packet->data[12] = (Sint8)stats[c]->LVL;
242 // SDLNet_Write16((Sint16)stats[c]->HP, &net_packet->data[13]);
243 // SDLNet_Write16((Sint16)stats[c]->MAXHP, &net_packet->data[15]);
244 // SDLNet_Write16((Sint16)stats[c]->MP, &net_packet->data[17]);
245 // SDLNet_Write16((Sint16)stats[c]->MAXMP, &net_packet->data[19]);
246 // net_packet->address.host = net_clients[c - 1].host;
247 // net_packet->address.port = net_clients[c - 1].port;
248 // net_packet->len = 21;
249 // sendPacketSafe(net_sock, -1, net_packet, c - 1);
250 //}
251 }
252 return;
253 }
254
devilMoveBodyparts(Entity * my,Stat * myStats,double dist)255 void devilMoveBodyparts(Entity* my, Stat* myStats, double dist)
256 {
257 node_t* node;
258 Entity* entity = NULL;
259 Entity* rightbody = NULL;
260 Entity* leftbody = NULL;
261 int bodypart;
262
263 // set invisibility //TODO: isInvisible()?
264 if ( multiplayer != CLIENT )
265 {
266 if ( myStats->EFFECTS[EFF_INVISIBLE] == true )
267 {
268 my->flags[INVISIBLE] = true;
269 my->flags[BLOCKSIGHT] = false;
270 bodypart = 0;
271 for (node = my->children.first; node != NULL; node = node->next)
272 {
273 if ( bodypart < 2 )
274 {
275 bodypart++;
276 continue;
277 }
278 if ( bodypart >= 7 )
279 {
280 break;
281 }
282 entity = (Entity*)node->element;
283 if ( !entity->flags[INVISIBLE] )
284 {
285 entity->flags[INVISIBLE] = true;
286 serverUpdateEntityBodypart(my, bodypart);
287 }
288 bodypart++;
289 }
290 }
291 else
292 {
293 my->flags[INVISIBLE] = false;
294 my->flags[BLOCKSIGHT] = true;
295 bodypart = 0;
296 for (node = my->children.first; node != NULL; node = node->next)
297 {
298 if ( bodypart < 2 )
299 {
300 bodypart++;
301 continue;
302 }
303 if ( bodypart >= 7 )
304 {
305 break;
306 }
307 entity = (Entity*)node->element;
308 if ( entity->flags[INVISIBLE] )
309 {
310 entity->flags[INVISIBLE] = false;
311 serverUpdateEntityBodypart(my, bodypart);
312 serverUpdateEntityFlag(my, INVISIBLE);
313 }
314 bodypart++;
315 }
316 }
317 }
318
319 /*if( ticks%120-ticks%60 )
320 MONSTER_ARMBENDED = 1;
321 else
322 MONSTER_ARMBENDED = 0;*/
323
324 //Move bodyparts
325 for (bodypart = 0, node = my->children.first; node != NULL; node = node->next, bodypart++)
326 {
327 entity = (Entity*)node->element;
328 if ( bodypart < 2 )
329 {
330 continue;
331 }
332 else if ( bodypart > 2 )
333 {
334 //entity->roll += .1;
335 //entity->pitch += .1;
336 }
337 if ( bodypart == 3 )
338 {
339 rightbody = entity;
340 }
341 if ( bodypart == 5 )
342 {
343 leftbody = entity;
344 }
345 entity->x = my->x;
346 entity->y = my->y;
347 entity->z = my->z;
348 if ( bodypart != 2 || my->ticks < 60 || MONSTER_ATTACK == 4 )
349 {
350 entity->yaw = my->yaw;
351 }
352
353 if ( MONSTER_ATTACK == 0 && bodypart != 2 )
354 {
355 entity->pitch = 0;
356 }
357 if ( bodypart == 3 )
358 {
359 if ( MONSTER_ATTACK == 1 || MONSTER_ATTACK == 3 )
360 {
361 if ( MONSTER_ATTACKTIME < 30 )
362 {
363 entity->pitch = std::max<real_t>(entity->pitch - .1, -PI / 2);
364 }
365 else if ( MONSTER_ATTACKTIME > 40 )
366 {
367 if ( entity->pitch < 0 )
368 {
369 entity->pitch = std::min<real_t>(entity->pitch + .4, 0.0);
370 if ( entity->pitch >= 0 )
371 {
372 playSound(181, 64);
373 }
374 }
375 }
376 }
377 else if ( MONSTER_ATTACK == 4 )
378 {
379 entity->pitch = std::max<real_t>(entity->pitch - .1, -4 * PI / 5);
380 }
381 else if ( MONSTER_ATTACK == 5 )
382 {
383 entity->pitch = -2 * PI / 5;
384 }
385 else
386 {
387 entity->pitch = 0;
388 }
389 }
390 else if ( bodypart == 5 )
391 {
392 if ( MONSTER_ATTACK == 2 || MONSTER_ATTACK == 3 )
393 {
394 if ( MONSTER_ATTACKTIME < 30 )
395 {
396 entity->pitch = std::max<real_t>(entity->pitch - .1, -PI / 2);
397 }
398 else if ( MONSTER_ATTACKTIME > 40 )
399 {
400 if ( entity->pitch < 0 )
401 {
402 entity->pitch = std::min<real_t>(entity->pitch + .4, 0.0);
403 if ( entity->pitch >= 0 )
404 {
405 playSound(181, 64);
406 }
407 }
408 }
409 }
410 else if ( MONSTER_ATTACK == 4 )
411 {
412 entity->pitch = std::max<real_t>(entity->pitch - .1, -4 * PI / 5);
413 }
414 else if ( MONSTER_ATTACK == 6 )
415 {
416 entity->pitch = -2 * PI / 5;
417 }
418 else
419 {
420 entity->pitch = 0;
421 }
422 }
423 if ( MONSTER_ATTACKTIME > 90 && MONSTER_ATTACK != 4 )
424 {
425 MONSTER_ATTACK = 0;
426 }
427
428 if ( MONSTER_WEAPONYAW > PI / 4 )
429 {
430 MONSTER_WEAPONYAW = 0;
431 MONSTER_ATTACK = 0;
432 MONSTER_ATTACKTIME = 0;
433 }
434
435 if ( MONSTER_ATTACK >= 5 )
436 {
437 if ( MONSTER_ATTACKTIME == 0 )
438 {
439 MONSTER_WEAPONYAW = -PI / 3;
440 }
441 else
442 {
443 MONSTER_WEAPONYAW += .02;
444 }
445 }
446
447 switch ( bodypart )
448 {
449 // head
450 case 2:
451 {
452 entity->z -= 16;
453 node_t* tempNode;
454 Entity* playertotrack = nullptr;
455 for ( tempNode = map.creatures->first; tempNode != nullptr; tempNode = tempNode->next ) //Searching for players only? Don't search full map.entities then.
456 {
457 Entity* tempEntity = (Entity*)tempNode->element;
458 double lowestdist = 5000;
459 if ( tempEntity->behavior == &actPlayer )
460 {
461 double disttoplayer = entityDist(my, tempEntity);
462 if ( disttoplayer < lowestdist )
463 {
464 playertotrack = tempEntity;
465 }
466 }
467 }
468 if ( playertotrack && !MONSTER_ATTACK )
469 {
470 double tangent = atan2( playertotrack->y - entity->y, playertotrack->x - entity->x );
471 double dir = entity->yaw - tangent;
472 while ( dir >= PI )
473 {
474 dir -= PI * 2;
475 }
476 while ( dir < -PI )
477 {
478 dir += PI * 2;
479 }
480 entity->yaw -= dir / 8;
481
482 double dir2 = my->yaw - tangent;
483 while ( dir2 >= PI )
484 {
485 dir2 -= PI * 2;
486 }
487 while ( dir2 < -PI )
488 {
489 dir2 += PI * 2;
490 }
491 if ( dir2 > PI / 2 )
492 {
493 entity->yaw = my->yaw - PI / 2;
494 }
495 else if ( dir2 < -PI / 2 )
496 {
497 entity->yaw = my->yaw + PI / 2;
498 }
499 }
500 else
501 {
502 if ( MONSTER_ATTACKTIME == 0 )
503 {
504 entity->yaw = my->yaw;
505 }
506 else
507 {
508 if ( MONSTER_ATTACK == 1 )
509 {
510 entity->yaw = std::min<real_t>(entity->yaw + .1, my->yaw + PI / 6);
511 }
512 else if ( MONSTER_ATTACK == 2 )
513 {
514 entity->yaw = std::max<real_t>(entity->yaw - .1, my->yaw - PI / 6);
515 }
516 }
517 }
518 if ( MONSTER_ATTACK == 4 )
519 {
520 entity->pitch = std::max<real_t>(entity->pitch - .1, -PI / 6);
521 }
522 else
523 {
524 entity->pitch = std::min<real_t>(entity->pitch + .1, 0.0);
525 }
526 break;
527 }
528 // right bicep
529 case 3:
530 entity->z -= 8;
531 if ( MONSTER_ATTACK == 1 || MONSTER_ATTACK == 3 || MONSTER_ATTACK == 4 )
532 {
533 entity->yaw += PI / 4;
534 }
535 if ( MONSTER_ATTACK == 5 )
536 {
537 entity->yaw += MONSTER_WEAPONYAW;
538 }
539 break;
540 // right forearm
541 case 4:
542 if ( !MONSTER_ARMBENDED && MONSTER_ATTACK != 1 && MONSTER_ATTACK != 3 )
543 {
544 entity->focalx = limbs[DEVIL][3][0]; // 0
545 entity->focaly = limbs[DEVIL][3][1]; // 17
546 entity->focalz = limbs[DEVIL][3][2]; // 26
547 entity->pitch = rightbody->pitch;
548 }
549 else
550 {
551 entity->focalx = limbs[DEVIL][3][0] - 18; // -18
552 entity->focaly = limbs[DEVIL][3][1]; // 17
553 entity->focalz = limbs[DEVIL][3][2] - 16; // 10
554 entity->pitch = rightbody->pitch - PI / 2;
555 }
556 entity->z -= 8;
557 if ( MONSTER_ATTACK == 1 || MONSTER_ATTACK == 3 || MONSTER_ATTACK == 4 )
558 {
559 entity->yaw += PI / 4;
560 }
561 if ( MONSTER_ATTACK == 5 )
562 {
563 entity->yaw += MONSTER_WEAPONYAW;
564 }
565 break;
566 // left bicep
567 case 5:
568 entity->z -= 8;
569 if ( MONSTER_ATTACK == 2 || MONSTER_ATTACK == 3 || MONSTER_ATTACK == 4 )
570 {
571 entity->yaw -= PI / 4;
572 }
573 if ( MONSTER_ATTACK == 6 )
574 {
575 entity->yaw -= MONSTER_WEAPONYAW;
576 }
577 break;
578 // left forearm
579 case 6:
580 if ( !MONSTER_ARMBENDED && MONSTER_ATTACK != 2 && MONSTER_ATTACK != 3 )
581 {
582 entity->focalx = limbs[DEVIL][5][0]; // 0
583 entity->focaly = limbs[DEVIL][5][1]; // -17
584 entity->focalz = limbs[DEVIL][5][2]; // 26
585 entity->pitch = leftbody->pitch;
586 }
587 else
588 {
589 entity->focalx = limbs[DEVIL][5][0] - 18; // -18
590 entity->focaly = limbs[DEVIL][5][1]; // -17
591 entity->focalz = limbs[DEVIL][5][2] - 16; // 10
592 entity->pitch = leftbody->pitch - PI / 2;
593 }
594 entity->z -= 8;
595 if ( MONSTER_ATTACK == 2 || MONSTER_ATTACK == 3 || MONSTER_ATTACK == 4 )
596 {
597 entity->yaw -= PI / 4;
598 }
599 if ( MONSTER_ATTACK == 6 )
600 {
601 entity->yaw -= MONSTER_WEAPONYAW;
602 }
603 break;
604 }
605 }
606 if ( MONSTER_ATTACK != 0 )
607 {
608 MONSTER_ATTACKTIME++;
609 }
610 else
611 {
612 MONSTER_ATTACKTIME = 0;
613 }
614 }
615
actDevilTeleport(Entity * my)616 void actDevilTeleport(Entity* my)
617 {
618 // dummy function
619 my->flags[PASSABLE] = true;
620 }
devilSummonMonster(Entity * summonOnEntity,Monster creature,int radiusFromCenter,int playerToTarget)621 bool Entity::devilSummonMonster(Entity* summonOnEntity, Monster creature, int radiusFromCenter, int playerToTarget)
622 {
623 Entity* target = nullptr;
624 if ( summonOnEntity )
625 {
626 target = summonOnEntity;
627 }
628 else
629 {
630 for ( node_t* searchNode = map.entities->first; searchNode != nullptr; searchNode = searchNode->next )
631 {
632 target = (Entity*)searchNode->element;
633 if ( target->behavior == &actDevilTeleport
634 && target->sprite == 72 )
635 {
636 break; // found specified center of map
637 }
638 target = nullptr;
639 }
640 }
641 if ( target )
642 {
643 int hellArena_x0 = 17;
644 int hellArena_x1 = 47;
645 int hellArena_y0 = 17;
646 int hellArena_y1 = 47;
647 int spawn_x = static_cast<int>(target->x / 16);
648 int spawn_y = static_cast<int>(target->y / 16);
649 std::vector<std::pair<int, int>> goodspots;
650 for ( int j = std::max(hellArena_y0, spawn_y - radiusFromCenter); j <= std::min(hellArena_y1, spawn_y + radiusFromCenter); ++j )
651 {
652 for ( int i = std::max(hellArena_x0, spawn_x - radiusFromCenter); i <= std::min(hellArena_x1, spawn_x + radiusFromCenter); ++i )
653 {
654 int index = (j)* MAPLAYERS + (i)* MAPLAYERS * map.height;
655 if ( !map.tiles[OBSTACLELAYER + index] &&
656 ((target->behavior == &actPlayer && !map.tiles[index])
657 || (target->behavior != &actPlayer
658 && (map.tiles[index] || creature != DEMON) && !swimmingtiles[map.tiles[index]] && !lavatiles[map.tiles[index]] )
659 )
660 )
661 {
662 // spawn on no floor, or lava if the target is a player.
663
664 // otherwise, spawn on solid ground.
665 real_t oldx = this->x;
666 real_t oldy = this->y;
667 this->x = i * 16 + 8;
668 this->y = j * 16 + 8;
669 goodspots.push_back(std::make_pair(i, j));
670 this->x = oldx;
671 this->y = oldy;
672 }
673 }
674 }
675 if ( goodspots.empty() )
676 {
677 return false;
678 }
679 std::pair<int,int> chosen = goodspots.at(rand() % goodspots.size());
680 Entity* timer = createParticleTimer(this, 70, 174);
681 timer->x = chosen.first * 16.0 + 8;
682 timer->y = chosen.second * 16.0 + 8;
683 timer->z = 0;
684 timer->particleTimerCountdownAction = PARTICLE_TIMER_ACTION_DEVIL_SUMMON_MONSTER;
685 timer->particleTimerCountdownSprite = 174;
686 timer->particleTimerEndAction = PARTICLE_EFFECT_DEVIL_SUMMON_MONSTER;
687 timer->particleTimerVariable1 = creature;
688 timer->particleTimerVariable2 = playerToTarget;
689 serverSpawnMiscParticlesAtLocation(static_cast<Sint16>(chosen.first), static_cast<Sint16>(chosen.second), 0, PARTICLE_EFFECT_DEVIL_SUMMON_MONSTER, 174);
690 return true;
691 }
692 return false;
693 }
694
devilGetNumMonstersInArena(Monster creature)695 int Entity::devilGetNumMonstersInArena(Monster creature)
696 {
697 int hellArena_x0 = 15;
698 int hellArena_x1 = 49;
699 int hellArena_y0 = 15;
700 int hellArena_y1 = 49;
701 int numMonstersActiveInArena = 0;
702 node_t* tempNode;
703 for ( tempNode = map.creatures->first; tempNode != nullptr; tempNode = tempNode->next )
704 {
705 Entity* monster = (Entity*)tempNode->element;
706 if ( monster && monster->getMonsterTypeFromSprite() == creature )
707 {
708 if ( static_cast<int>(monster->x / 16) >= hellArena_x0 && static_cast<int>(monster->x / 16) <= hellArena_x1 )
709 {
710 if ( static_cast<int>(monster->y / 16) >= hellArena_y0 && static_cast<int>(monster->y / 16) <= hellArena_y1 )
711 {
712 ++numMonstersActiveInArena;
713 }
714 }
715 }
716 }
717 return numMonstersActiveInArena;
718 }
719
devilBoulderSummonIfPlayerIsHiding(int player)720 bool Entity::devilBoulderSummonIfPlayerIsHiding(int player)
721 {
722 if ( players[player] && players[player]->entity )
723 {
724 int player_x = static_cast<int>(players[player]->entity->x / 16);
725 int player_y = static_cast<int>(players[player]->entity->y / 16);
726 int doSummon = 0;
727 if ( entityDist(this, players[player]->entity) > 16 * 16 /*16 tiles*/ )
728 {
729 doSummon = 1;
730 }
731 else if ( !map.tiles[player_y * MAPLAYERS + player_x * MAPLAYERS * map.height] )
732 {
733 if ( entityDist(this, players[player]->entity) > 16 * 10 /*10 tiles*/ )
734 {
735 doSummon = 2;
736 }
737 else
738 {
739 // standing on no floor.
740 real_t tangent = atan2(players[player]->entity->y - this->y, players[player]->entity->x - this->x);
741 Entity* ohitentity = hit.entity;
742 lineTraceTarget(this, this->x, this->y, tangent, 1024, 0, false, players[player]->entity);
743 if ( hit.entity != players[player]->entity )
744 {
745 // can't see the player
746 doSummon = 2;
747 }
748 hit.entity = ohitentity;
749 }
750 }
751 //messagePlayer(0, "dosummon: %d, distance: %f", doSummon, entityDist(this, players[player]->entity));
752 if ( doSummon )
753 {
754 int numPlayers = 0;
755 for ( int c = 0; c < MAXPLAYERS; ++c )
756 {
757 if ( !client_disconnected[c] )
758 {
759 ++numPlayers;
760 }
761 }
762 if ( devilGetNumMonstersInArena(SHADOW) <= numPlayers && (rand() % 4 == 0) )
763 {
764 if ( !devilSummonMonster(players[player]->entity, SHADOW, 5, player) )
765 {
766 devilSummonMonster(players[player]->entity, SHADOW, 21, player);
767 }
768 return true;
769 }
770 else
771 {
772 int numImps = devilGetNumMonstersInArena(CREATURE_IMP);
773 if ( numImps <= (4 + numPlayers) && !devilSummonMonster(players[player]->entity, CREATURE_IMP, 5, player) )
774 {
775 devilSummonMonster(players[player]->entity, CREATURE_IMP, 21, player);
776 }
777 ++numImps;
778 if ( doSummon == 1 && numImps <= (4 + numPlayers) )
779 {
780 // extra imp.
781 if ( !devilSummonMonster(players[player]->entity, CREATURE_IMP, 5, player) )
782 {
783 devilSummonMonster(players[player]->entity, CREATURE_IMP, 21, player);
784 }
785 }
786 return true;
787 }
788 }
789 }
790 return false;
791 }