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 }