1 //----------------------------------------------------------------------------
2 //  EDGE Creature Action Code
3 //----------------------------------------------------------------------------
4 //
5 //  Copyright (c) 1999-2009  The EDGE Team.
6 //
7 //  This program is free software; you can redistribute it and/or
8 //  modify it under the terms of the GNU General Public License
9 //  as published by the Free Software Foundation; either version 2
10 //  of the License, or (at your option) any later version.
11 //
12 //  This program is distributed in the hope that it will be useful,
13 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
14 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 //  GNU General Public License for more details.
16 //
17 //----------------------------------------------------------------------------
18 //
19 //  Based on the DOOM source code, released by Id Software under the
20 //  following copyright:
21 //
22 //    Copyright (C) 1993-1996 by id Software, Inc.
23 //
24 //----------------------------------------------------------------------------
25 //
26 // -KM- 1998/09/27 Sounds.ddf stuff
27 //
28 // -AJA- 1999/07/21: Replaced some non-critical P_Randoms with M_Random,
29 //       and removed some X_Random()-X_Random() things.
30 //
31 
32 #include "i_defs.h"
33 
34 #include "dm_data.h"
35 #include "dm_state.h"
36 #include "g_game.h"
37 #include "m_random.h"
38 #include "p_action.h"
39 #include "p_local.h"
40 #include "s_sound.h"
41 #include "w_wad.h"
42 #include "z_zone.h"
43 
44 #include <float.h>
45 
46 dirtype_e opposite[] =
47 {
48 	DI_WEST,
49 	DI_SOUTHWEST,
50 	DI_SOUTH,
51 	DI_SOUTHEAST,
52 	DI_EAST,
53 	DI_NORTHEAST,
54 	DI_NORTH,
55 	DI_NORTHWEST,
56 	DI_NODIR
57 };
58 
59 dirtype_e diags[] =
60 {
61 	DI_NORTHWEST,
62 	DI_NORTHEAST,
63 	DI_SOUTHWEST,
64 	DI_SOUTHEAST
65 };
66 
67 // sqrt(2) / 2: The diagonal speed of creatures
68 #define SQ2 0.7071067812f
69 
70 float xspeed[8] = {1.0f, SQ2, 0.0f, -SQ2, -1.0f, -SQ2, 0.0f, SQ2};
71 float yspeed[8] = {0.0f, SQ2, 1.0f, SQ2, 0.0f, -SQ2, -1.0f, -SQ2};
72 
73 #undef SQ2
74 
75 //
76 //  ENEMY THINKING
77 //
78 // Enemies are allways spawned
79 // with targetplayer = -1, threshold = 0
80 // Most monsters are spawned unaware of all players,
81 // but some can be made preaware
82 //
83 
84 //
85 // Called by P_NoiseAlert.
86 // Recursively traverse adjacent sectors,
87 // sound blocking lines cut off traversal.
88 //
RecursiveSound(sector_t * sec,int soundblocks,int player)89 static void RecursiveSound(sector_t * sec, int soundblocks, int player)
90 {
91 	int i;
92 	line_t *check;
93 	sector_t *other;
94 
95 	// has the sound flooded this sector
96 	if (sec->validcount == validcount && sec->soundtraversed <= soundblocks + 1)
97 		return;
98 
99 	// wake up all monsters in this sector
100 	sec->validcount = validcount;
101 	sec->soundtraversed = soundblocks + 1;
102 	sec->sound_player = player;
103 
104 	for (i = 0; i < sec->linecount; i++)
105 	{
106 		check = sec->lines[i];
107 
108 		if (!(check->flags & MLF_TwoSided))
109 			continue;
110 
111 		// -AJA- 1999/07/19: Gaps are now stored in line_t.
112 		if (check->gap_num == 0)
113 			continue;  // closed door
114 
115 		// -AJA- 2001/11/11: handle closed Sliding doors
116 		if (check->slide_door && ! check->slide_door->s.see_through &&
117 			! check->slider_move)
118 		{
119 			continue;
120 		}
121 
122 		if (check->frontsector == sec)
123 			other = check->backsector;
124 		else
125 			other = check->frontsector;
126 
127 		if (check->flags & MLF_SoundBlock)
128 		{
129 			if (!soundblocks)
130 				RecursiveSound(other, 1, player);
131 		}
132 		else
133 		{
134 			RecursiveSound(other, soundblocks, player);
135 		}
136 	}
137 }
138 
P_NoiseAlert(player_t * p)139 void P_NoiseAlert(player_t *p)
140 {
141 	validcount++;
142 
143 	RecursiveSound(p->mo->subsector->sector, 0, p->pnum);
144 }
145 
146 //
147 // Move in the current direction,
148 // returns false if the move is blocked.
149 //
P_Move(mobj_t * actor,bool path)150 bool P_Move(mobj_t * actor, bool path)
151 {
152 	vec3_t orig_pos;
153 
154 	orig_pos.Set(actor->x, actor->y, actor->z);
155 
156 	float tryx;
157 	float tryy;
158 
159 	if (path)
160 	{
161 		tryx = actor->x + actor->speed * M_Cos(actor->angle);
162 		tryy = actor->y + actor->speed * M_Sin(actor->angle);
163 	}
164 	else
165 	{
166 		if (actor->movedir == DI_NODIR)
167 			return false;
168 
169 		if ((unsigned)actor->movedir >= 8)
170 			I_Error("Weird actor->movedir!");
171 
172 		tryx = actor->x + actor->speed * xspeed[actor->movedir];
173 		tryy = actor->y + actor->speed * yspeed[actor->movedir];
174 	}
175 
176 	if (! P_TryMove(actor, tryx, tryy))
177 	{
178 		epi::array_iterator_c it;
179 		line_t* ld;
180 
181 		// open any specials
182 		if (actor->flags & MF_FLOAT && floatok)
183 		{
184 			// must adjust height
185 			if (actor->z < float_destz)
186 				actor->z += actor->info->float_speed;
187 			else
188 				actor->z -= actor->info->float_speed;
189 
190 			actor->flags |= MF_INFLOAT;
191 			// FIXME: position interpolation
192 			return true;
193 		}
194 
195 		if (spechit.GetSize() == 0)
196 			return false;
197 
198 		actor->movedir = DI_NODIR;
199 
200 		// -AJA- 1999/09/10: As Lee Killough points out, this is where
201 		//       monsters can get stuck in doortracks.  We follow Lee's
202 		//       method: return true 90% of the time if the blocking line
203 		//       was the one activated, or false 90% of the time if there
204 		//       was some other line activated.
205 
206 		bool any_used = false;
207 		bool block_used = false;
208 
209 		for (it=spechit.GetTailIterator(); it.IsValid(); it--)
210 		{
211 			ld = ITERATOR_TO_TYPE(it, line_t*);
212 			if (P_UseSpecialLine(actor, ld, 0, -FLT_MAX, FLT_MAX))
213 			{
214 				any_used = true;
215 
216 				if (ld == blockline)
217 					block_used = true;
218 			}
219 		}
220 
221 		return any_used && (P_Random() < 230 ? block_used : !block_used);
222 	}
223 
224 	actor->flags &= ~MF_INFLOAT;
225 
226 	if (!(actor->flags & MF_FLOAT) &&
227 		!(actor->extendedflags & EF_GRAVFALL))
228 		actor->z = actor->floorz;
229 
230 	// -AJA- 2008/01/16: position interpolation
231 	if ((actor->state->flags & SFF_Model) ||
232 		(actor->flags & MF_FLOAT))
233 	{
234 		actor->lerp_num = CLAMP(2, actor->state->tics, 10);
235 		actor->lerp_pos = 1;
236 
237 		actor->lerp_from = orig_pos;
238 	}
239 
240 	return true;
241 }
242 
243 //
244 // Attempts to move actor on
245 // in its current (ob->moveangle) direction.
246 // If blocked by either a wall or an actor
247 // returns FALSE
248 // If move is either clear or blocked only by a door,
249 // returns TRUE and sets...
250 // If a door is in the way,
251 // an OpenDoor call is made to start it opening.
252 //
TryWalk(mobj_t * actor)253 static bool TryWalk(mobj_t * actor)
254 {
255 	if (!P_Move(actor, false))
256 		return false;
257 
258 	actor->movecount = P_Random() & 15;
259 	return true;
260 }
261 
262 // -ACB- 1998/09/06 actor is now an object; different movement choices.
P_NewChaseDir(mobj_t * object)263 void P_NewChaseDir(mobj_t * object)
264 {
265 	float deltax;
266 	float deltay;
267 	dirtype_e tdir;
268 
269 	dirtype_e d[3];
270 	dirtype_e olddir;
271 	dirtype_e turnaround;
272 
273 	olddir = object->movedir;
274 	turnaround = opposite[olddir];
275 
276 	//
277 	// Movement choice: Previously this was calculation to find
278 	// the distance between object and target: if the object had
279 	// no target, a fatal error was returned. However it is now
280 	// possible to have movement without a target. if the object
281 	// has a target, go for that; else if it has a supporting
282 	// object aim to go within supporting distance of that; the
283 	// remaining option is to walk aimlessly: the target destination
284 	// is always 128 in the old movement direction, think
285 	// of it like the donkey and the carrot sketch: the donkey will
286 	// move towards the carrot, but since the carrot is always a
287 	// set distance away from the donkey, the rather stupid mammal
288 	// will spend eternity trying to get the carrot and will walk
289 	// forever.
290 	//
291 	// -ACB- 1998/09/06
292 
293 	if (object->target)
294 	{
295 		deltax = object->target->x - object->x;
296 		deltay = object->target->y - object->y;
297 	}
298 	else if (object->supportobj)
299 	{
300 		// not too close
301 		deltax = (object->supportobj->x - object->x) - (object->supportobj->radius * 4);
302 		deltay = (object->supportobj->y - object->y) - (object->supportobj->radius * 4);
303 	}
304 	else
305 	{
306 		deltax = 128 * xspeed[olddir];
307 		deltay = 128 * yspeed[olddir];
308 	}
309 
310 	if (deltax > 10)
311 		d[1] = DI_EAST;
312 	else if (deltax < -10)
313 		d[1] = DI_WEST;
314 	else
315 		d[1] = DI_NODIR;
316 
317 	if (deltay < -10)
318 		d[2] = DI_SOUTH;
319 	else if (deltay > 10)
320 		d[2] = DI_NORTH;
321 	else
322 		d[2] = DI_NODIR;
323 
324 	// try direct route
325 	if (d[1] != DI_NODIR && d[2] != DI_NODIR)
326 	{
327 		object->movedir = diags[((deltay < 0) << 1) + (deltax > 0)];
328 		if (object->movedir != turnaround && TryWalk(object))
329 			return;
330 	}
331 
332 	// try other directions
333 	if (P_Random() > 200 || fabs(deltay) > fabs(deltax))
334 	{
335 		tdir = d[1];
336 		d[1] = d[2];
337 		d[2] = tdir;
338 	}
339 
340 	if (d[1] == turnaround)
341 		d[1] = DI_NODIR;
342 
343 	if (d[2] == turnaround)
344 		d[2] = DI_NODIR;
345 
346 	if (d[1] != DI_NODIR)
347 	{
348 		object->movedir = d[1];
349 		if (TryWalk(object))
350 		{
351 			// either moved forward or attacked
352 			return;
353 		}
354 	}
355 
356 	if (d[2] != DI_NODIR)
357 	{
358 		object->movedir = d[2];
359 
360 		if (TryWalk(object))
361 			return;
362 	}
363 
364 	// there is no direct path to the player,
365 	// so pick another direction.
366 	if (olddir != DI_NODIR)
367 	{
368 		object->movedir = olddir;
369 
370 		if (TryWalk(object))
371 			return;
372 	}
373 
374 	// randomly determine direction of search
375 	if (P_Random() & 1)
376 	{
377 		for (tdir = DI_EAST; tdir <= DI_SOUTHEAST; tdir = (dirtype_e)((int)tdir+1))
378 		{
379 			if (tdir != turnaround)
380 			{
381 				object->movedir = tdir;
382 
383 				if (TryWalk(object))
384 					return;
385 			}
386 		}
387 	}
388 	else
389 	{
390 		for (tdir = DI_SOUTHEAST; tdir != (dirtype_e)(DI_EAST - 1); tdir = (dirtype_e)((int)tdir-1))
391 		{
392 			if (tdir != turnaround)
393 			{
394 				object->movedir = tdir;
395 
396 				if (TryWalk(object))
397 					return;
398 			}
399 		}
400 	}
401 
402 	if (turnaround != DI_NODIR)
403 	{
404 		object->movedir = turnaround;
405 		if (TryWalk(object))
406 			return;
407 	}
408 
409 	// cannot move
410 	object->movedir = DI_NODIR;
411 }
412 
413 //
414 // Range is angle range on either side of eyes, 90 degrees for normal
415 // view, 180 degrees for total sight in all dirs.
416 //
417 // Returns true if a player is targeted.
418 //
P_LookForPlayers(mobj_t * actor,angle_t range)419 bool P_LookForPlayers(mobj_t * actor, angle_t range)
420 {
421 	int c;
422 	int stop;
423 	player_t *player;
424 	angle_t an;
425 	float dist;
426 
427 	c = 0;
428 	stop = (actor->lastlook - 1 + MAXPLAYERS) % MAXPLAYERS;
429 
430 	for (; actor->lastlook != stop; actor->lastlook = (actor->lastlook + 1) % MAXPLAYERS)
431 	{
432 		player = players[actor->lastlook];
433 
434 		if (!player)
435 			continue;
436 
437 		SYS_ASSERT(player->mo);
438 
439 		// done looking ?
440 		if (c++ >= 2)
441 			break;
442 
443 		// dead ?
444 		if (player->health <= 0)
445 			continue;
446 
447 		// on the same team ?
448 		if ((actor->side & player->mo->side) != 0)
449 			continue;
450 
451 		if (range < ANG180)
452 		{
453 			an = R_PointToAngle(actor->x, actor->y, player->mo->x,
454 				player->mo->y) - actor->angle;
455 
456 			if (range <= an && an <= (range * -1))
457 			{
458 				// behind back.
459 				// if real close, react anyway
460 				dist = P_ApproxDistance(player->mo->x - actor->x,
461 					player->mo->y - actor->y);
462 
463 				if (dist > MELEERANGE)
464 					continue;
465 			}
466 		}
467 
468 		// out of sight ?
469 		if (!P_CheckSight(actor, player->mo))
470 			continue;
471 
472 		actor->SetTarget(player->mo);
473 		return true;
474 	}
475 
476 	return false;
477 }
478 
479 //
480 //   BOSS-BRAIN HANDLING
481 //
482 
483 //
484 // -AJA- Savegames: we assume that the spit-spot objects never
485 //       disappear, or new ones appear.  After all, they have to be
486 //       there to be the target of the cubes.  This means we don't
487 //       need to save anything: the set of shoot-spots will be
488 //       regenerated after the loadgame when the BrainShooter next
489 //       tries to shoot a cube.
490 
491 shoot_spot_info_t brain_spots = { -1, NULL };
492 
P_LookForShootSpots(const mobjtype_c * spot_type)493 void P_LookForShootSpots(const mobjtype_c *spot_type)
494 {
495 	int i;
496 	mobj_t *cur;
497 
498 	brain_spots.number = 0;
499 
500 	// count them
501 	for (cur=mobjlisthead; cur != NULL; cur=cur->next)
502 	{
503 		if (cur->info == spot_type)
504 			brain_spots.number++;
505 	}
506 
507 	if (brain_spots.number == 0)
508 	{
509 		I_Warning("No [%s] objects found for BossBrain shooter.\n",
510 			spot_type->name.c_str());
511 		return;
512 	}
513 
514 	// create the spots
515 	brain_spots.targets = new mobj_t* [brain_spots.number];
516 
517 	for (cur=mobjlisthead, i=0; cur != NULL; cur=cur->next)
518 	{
519 		if (cur->info == spot_type)
520 			brain_spots.targets[i++] = cur;
521 	}
522 
523 	SYS_ASSERT(i == brain_spots.number);
524 }
525 
P_FreeShootSpots(void)526 void P_FreeShootSpots(void)
527 {
528 	if (brain_spots.number < 0)
529 		return;
530 
531 	if (brain_spots.targets)
532 	{
533 		SYS_ASSERT(brain_spots.targets);
534 
535 		delete[] brain_spots.targets;
536 	}
537 
538 	brain_spots.number = -1;
539 	brain_spots.targets = NULL;
540 }
541 
SpawnDeathMissile(mobj_t * source,float x,float y,float z)542 static void SpawnDeathMissile(mobj_t *source, float x, float y, float z)
543 {
544 	const mobjtype_c *info;
545 	mobj_t *th;
546 
547 	info = mobjtypes.Lookup("BRAIN_DEATH_MISSILE");
548 
549 	th = P_MobjCreateObject(x, y, z, info);
550 	if (th->info->seesound)
551 		S_StartFX(th->info->seesound, P_MobjGetSfxCategory(th), th);
552 
553 	th->SetRealSource(source);
554 
555 	th->mom.x = (x - source->x) / 50.0f;
556 	th->mom.y = -0.25f;
557 	th->mom.z = (z - source->z) / 50.0f;
558 
559 	th->tics -= M_Random() & 7;
560 
561 	if (th->tics < 1)
562 		th->tics = 1;
563 }
564 
P_ActBrainScream(mobj_t * bossbrain)565 void P_ActBrainScream(mobj_t * bossbrain)
566 {
567 	// The brain and his pain...
568 
569 	float x, y, z;
570 	float min_x, max_x;
571 
572 	min_x = bossbrain->x - 280.0f;
573 	max_x = bossbrain->x + 280.0f;
574 
575 	for (x = min_x; x < max_x; x += 4)
576 	{
577 		y = bossbrain->y - 320.0f;
578 		z = bossbrain->z + (P_Random() - 180.0f) * 2.0f;
579 
580 		SpawnDeathMissile(bossbrain, x, y, z);
581 	}
582 
583 	if (bossbrain->info->deathsound)
584 		S_StartFX(bossbrain->info->deathsound, P_MobjGetSfxCategory(bossbrain), bossbrain);
585 }
586 
P_ActBrainMissileExplode(mobj_t * mo)587 void P_ActBrainMissileExplode(mobj_t * mo)
588 {
589 	float x, y, z;
590 
591 	if (! mo->source)
592 		return;
593 
594 	x = mo->source->x + (P_Random() - 128.0f) * 4.0f;
595 	y = mo->source->y - 320.0f;
596 	z = mo->source->z + (P_Random() - 180.0f) * 2.0f;
597 
598 	SpawnDeathMissile(mo->source, x, y, z);
599 }
600 
P_ActBrainDie(mobj_t * bossbrain)601 void P_ActBrainDie(mobj_t * bossbrain)
602 {
603 	G_ExitLevel(TICRATE);
604 }
605 
P_ActBrainSpit(mobj_t * shooter)606 void P_ActBrainSpit(mobj_t * shooter)
607 {
608 	static int easy = 0;
609 
610 	// when skill is easy, only fire every second cube.
611 
612 	easy ^= 1;
613 
614 	if (gameskill <= sk_easy && (!easy))
615 		return;
616 
617 	// shoot out a cube
618 	P_ActRangeAttack(shooter);
619 }
620 
621 
P_ActCubeSpawn(mobj_t * cube)622 void P_ActCubeSpawn(mobj_t * cube)
623 {
624 	mobj_t *targ;
625 	mobj_t *newmobj;
626 	const mobjtype_c *type;
627 	int r;
628 
629 	targ = cube->target;
630 
631 	// -AJA- 2007/07/28: workaround for DeHackEd patches using S_SPAWNFIRE
632 	if (!targ || !cube->currentattack ||
633 		cube->currentattack->attackstyle != ATK_SHOOTTOSPOT)
634 		return;
635 
636 	// Randomly select monster to spawn.
637 	r = P_Random();
638 
639 	// Probability distribution (kind of :)),
640 	// decreasing likelihood.
641 	if (r < 50)
642 		type = mobjtypes.Lookup("IMP");
643 	else if (r < 90)
644 		type = mobjtypes.Lookup("DEMON");
645 	else if (r < 120)
646 		type = mobjtypes.Lookup("SPECTRE");
647 	else if (r < 130)
648 		type = mobjtypes.Lookup("PAIN_ELEMENTAL");
649 	else if (r < 160)
650 		type = mobjtypes.Lookup("CACODEMON");
651 	else if (r < 162)
652 		type = mobjtypes.Lookup("ARCHVILE");
653 	else if (r < 172)
654 		type = mobjtypes.Lookup("REVENANT");
655 	else if (r < 192)
656 		type = mobjtypes.Lookup("ARACHNOTRON");
657 	else if (r < 222)
658 		type = mobjtypes.Lookup("MANCUBUS");
659 	else if (r < 246)
660 		type = mobjtypes.Lookup("HELL_KNIGHT");
661 	else
662 		type = mobjtypes.Lookup("BARON_OF_HELL");
663 
664 	newmobj = P_MobjCreateObject(targ->x, targ->y, targ->z, type);
665 
666 	if (P_LookForPlayers(newmobj, ANG180))
667 	{
668 		if (newmobj->info->chase_state)
669 			P_SetMobjState(newmobj, newmobj->info->chase_state);
670 		else
671 			P_SetMobjState(newmobj, newmobj->info->spawn_state);
672 	}
673 
674 	// telefrag anything in this spot
675 	P_TeleportMove(newmobj, newmobj->x, newmobj->y, newmobj->z);
676 }
677 
P_ActPlayerScream(mobj_t * mo)678 void P_ActPlayerScream(mobj_t * mo)
679 {
680 	sfx_t *sound;
681 
682 	sound = mo->info->deathsound;
683 
684 	if ((mo->health < -50) && (W_CheckNumForName("DSPDIEHI") >= 0))
685 	{
686 		// if the player dies and unclipped health is < -50%...
687 
688 		sound = sfxdefs.GetEffect("PDIEHI");
689 	}
690 
691 	S_StartFX(sound, P_MobjGetSfxCategory(mo), mo);
692 }
693 
694 
695 //--- editor settings ---
696 // vi:ts=4:sw=4:noexpandtab
697