summaryrefslogtreecommitdiff
path: root/apps/plugins/doom/p_enemy.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/doom/p_enemy.c')
-rw-r--r--apps/plugins/doom/p_enemy.c2623
1 files changed, 2623 insertions, 0 deletions
diff --git a/apps/plugins/doom/p_enemy.c b/apps/plugins/doom/p_enemy.c
new file mode 100644
index 0000000000..b1f40bc5b8
--- /dev/null
+++ b/apps/plugins/doom/p_enemy.c
@@ -0,0 +1,2623 @@
1/* Emacs style mode select -*- C++ -*-
2 *-----------------------------------------------------------------------------
3 *
4 *
5 * PrBoom a Doom port merged with LxDoom and LSDLDoom
6 * based on BOOM, a modified and improved DOOM engine
7 * Copyright (C) 1999 by
8 * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
9 * Copyright (C) 1999-2000 by
10 * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
25 * 02111-1307, USA.
26 *
27 * DESCRIPTION:
28 * Enemy thinking, AI.
29 * Action Pointer Functions
30 * that are associated with states/frames.
31 *
32 *-----------------------------------------------------------------------------*/
33
34#include "doomstat.h"
35#include "m_random.h"
36#include "r_main.h"
37#include "p_maputl.h"
38#include "p_map.h"
39#include "p_setup.h"
40#include "p_spec.h"
41#include "s_sound.h"
42#include "sounds.h"
43#include "p_inter.h"
44#include "g_game.h"
45#include "p_enemy.h"
46#include "p_tick.h"
47#include "m_bbox.h"
48
49#include "rockmacros.h"
50
51static mobj_t *current_actor;
52
53typedef enum {
54 DI_EAST,
55 DI_NORTHEAST,
56 DI_NORTH,
57 DI_NORTHWEST,
58 DI_WEST,
59 DI_SOUTHWEST,
60 DI_SOUTH,
61 DI_SOUTHEAST,
62 DI_NODIR,
63 NUMDIRS
64} dirtype_t;
65
66void A_Fall(mobj_t *actor);
67void A_FaceTarget(mobj_t *actor);
68static void P_NewChaseDir(mobj_t *actor);
69void P_ZBumpCheck(mobj_t *); // phares
70
71//
72// ENEMY THINKING
73// Enemies are allways spawned
74// with targetplayer = -1, threshold = 0
75// Most monsters are spawned unaware of all players,
76// but some can be made preaware
77//
78
79//
80// Called by P_NoiseAlert.
81// Recursively traverse adjacent sectors,
82// sound blocking lines cut off traversal.
83//
84// killough 5/5/98: reformatted, cleaned up
85
86static void P_RecursiveSound(sector_t *sec, int soundblocks,
87 mobj_t *soundtarget)
88{
89 int i;
90
91 // wake up all monsters in this sector
92 if (sec->validcount == validcount && sec->soundtraversed <= soundblocks+1)
93 return; // already flooded
94
95 sec->validcount = validcount;
96 sec->soundtraversed = soundblocks+1;
97 P_SetTarget(&sec->soundtarget, soundtarget);
98
99 for (i=0; i<sec->linecount; i++)
100 {
101 sector_t *other;
102 line_t *check = sec->lines[i];
103
104 if (!(check->flags & ML_TWOSIDED))
105 continue;
106
107 P_LineOpening(check);
108
109 if (openrange <= 0)
110 continue; // closed door
111
112 other=sides[check->sidenum[sides[check->sidenum[0]].sector==sec]].sector;
113
114 if (!(check->flags & ML_SOUNDBLOCK))
115 P_RecursiveSound(other, soundblocks, soundtarget);
116 else
117 if (!soundblocks)
118 P_RecursiveSound(other, 1, soundtarget);
119 }
120}
121
122//
123// P_NoiseAlert
124// If a monster yells at a player,
125// it will alert other monsters to the player.
126//
127void P_NoiseAlert(mobj_t *target, mobj_t *emitter)
128{
129 validcount++;
130 P_RecursiveSound(emitter->subsector->sector, 0, target);
131}
132
133//
134// P_CheckMeleeRange
135//
136
137static boolean P_CheckMeleeRange(mobj_t *actor)
138{
139 mobj_t *pl = actor->target;
140
141 return // killough 7/18/98: friendly monsters don't attack other friends
142 pl && !(actor->flags & pl->flags & MF_FRIEND) &&
143 (P_AproxDistance(pl->x-actor->x, pl->y-actor->y) <
144 MELEERANGE - 20*FRACUNIT + pl->info->radius) &&
145 P_CheckSight(actor, actor->target);
146}
147
148//
149// P_HitFriend()
150//
151// killough 12/98
152// This function tries to prevent shooting at friends
153
154static boolean P_HitFriend(mobj_t *actor)
155{
156 return actor->flags & MF_FRIEND && actor->target &&
157 (P_AimLineAttack(actor,
158 R_PointToAngle2(actor->x, actor->y,
159 actor->target->x, actor->target->y),
160 P_AproxDistance(actor->x-actor->target->x,
161 actor->y-actor->target->y), 0),
162 linetarget) && linetarget != actor->target &&
163 !((linetarget->flags ^ actor->flags) & MF_FRIEND);
164}
165
166//
167// P_CheckMissileRange
168//
169boolean P_CheckMissileRange(mobj_t *actor)
170{
171 fixed_t dist;
172
173 if (!P_CheckSight(actor, actor->target))
174 return false;
175
176 if (actor->flags & MF_JUSTHIT)
177 { // the target just hit the enemy, so fight back!
178 actor->flags &= ~MF_JUSTHIT;
179
180 /* killough 7/18/98: no friendly fire at corpses
181 * killough 11/98: prevent too much infighting among friends
182 * cph - yikes, talk about fitting everything on one line... */
183
184 return
185 !(actor->flags & MF_FRIEND) ||
186 (actor->target->health > 0 &&
187 (!(actor->target->flags & MF_FRIEND) ||
188 (actor->target->player ?
189 monster_infighting || P_Random(pr_defect) >128 :
190 !(actor->target->flags & MF_JUSTHIT) && P_Random(pr_defect) >128)));
191 }
192
193 /* killough 7/18/98: friendly monsters don't attack other friendly
194 * monsters or players (except when attacked, and then only once)
195 */
196 if (actor->flags & actor->target->flags & MF_FRIEND)
197 return false;
198
199 if (actor->reactiontime)
200 return false; // do not attack yet
201
202 // OPTIMIZE: get this from a global checksight
203 dist = P_AproxDistance ( actor->x-actor->target->x,
204 actor->y-actor->target->y) - 64*FRACUNIT;
205
206 if (!actor->info->meleestate)
207 dist -= 128*FRACUNIT; // no melee attack, so fire more
208
209 dist >>= FRACBITS;
210
211 if (actor->type == MT_VILE)
212 if (dist > 14*64)
213 return false; // too far away
214
215
216 if (actor->type == MT_UNDEAD)
217 {
218 if (dist < 196)
219 return false; // close for fist attack
220 dist >>= 1;
221 }
222
223 if (actor->type == MT_CYBORG ||
224 actor->type == MT_SPIDER ||
225 actor->type == MT_SKULL)
226 dist >>= 1;
227
228 if (dist > 200)
229 dist = 200;
230
231 if (actor->type == MT_CYBORG && dist > 160)
232 dist = 160;
233
234 if (P_Random(pr_missrange) < dist)
235 return false;
236
237 if (P_HitFriend(actor))
238 return false;
239
240 return true;
241}
242
243/*
244 * P_IsOnLift
245 *
246 * killough 9/9/98:
247 *
248 * Returns true if the object is on a lift. Used for AI,
249 * since it may indicate the need for crowded conditions,
250 * or that a monster should stay on the lift for a while
251 * while it goes up or down.
252 */
253
254static boolean P_IsOnLift(const mobj_t *actor)
255{
256 const sector_t *sec = actor->subsector->sector;
257 line_t line;
258 int l;
259
260 // Short-circuit: it's on a lift which is active.
261 if (sec->floordata && ((thinker_t *) sec->floordata)->function==T_PlatRaise)
262 return true;
263
264 // Check to see if it's in a sector which can be activated as a lift.
265 if ((line.tag = sec->tag))
266 for (l = -1; (l = P_FindLineFromLineTag(&line, l)) >= 0;)
267 switch (lines[l].special)
268 {
269case 10: case 14: case 15: case 20: case 21: case 22:
270case 47: case 53: case 62: case 66: case 67: case 68:
271case 87: case 88: case 95: case 120: case 121: case 122:
272case 123: case 143: case 162: case 163: case 181: case 182:
273case 144: case 148: case 149: case 211: case 227: case 228:
274case 231: case 232: case 235: case 236:
275 return true;
276 }
277
278 return false;
279}
280
281/*
282 * P_IsUnderDamage
283 *
284 * killough 9/9/98:
285 *
286 * Returns nonzero if the object is under damage based on
287 * their current position. Returns 1 if the damage is moderate,
288 * -1 if it is serious. Used for AI.
289 */
290
291static int P_IsUnderDamage(mobj_t *actor)
292{
293 const struct msecnode_s *seclist;
294 const ceiling_t *cl; // Crushing ceiling
295 int dir = 0;
296 for (seclist=actor->touching_sectorlist; seclist; seclist=seclist->m_tnext)
297 if ((cl = seclist->m_sector->ceilingdata) &&
298 cl->thinker.function == T_MoveCeiling)
299 dir |= cl->direction;
300 return dir;
301}
302
303//
304// P_Move
305// Move in the current direction,
306// returns false if the move is blocked.
307//
308
309static fixed_t xspeed[8] = {FRACUNIT,47000,0,-47000,-FRACUNIT,-47000,0,47000};
310static fixed_t yspeed[8] = {0,47000,FRACUNIT,47000,0,-47000,-FRACUNIT,-47000};
311
312// 1/11/98 killough: Limit removed on special lines crossed
313extern line_t **spechit; // New code -- killough
314extern int numspechit;
315
316static boolean P_Move(mobj_t *actor, boolean dropoff) /* killough 9/12/98 */
317{
318 fixed_t tryx, tryy, deltax, deltay, origx, origy;
319 boolean try_ok;
320 int movefactor = ORIG_FRICTION_FACTOR; // killough 10/98
321 int friction = ORIG_FRICTION;
322 int speed;
323
324 if (actor->movedir == DI_NODIR)
325 return false;
326
327#ifdef RANGECHECK
328 if ((unsigned)actor->movedir >= 8)
329 I_Error ("P_Move: Weird actor->movedir!");
330#endif
331
332 // killough 10/98: make monsters get affected by ice and sludge too:
333
334 if (monster_friction)
335 movefactor = P_GetMoveFactor(actor, &friction);
336
337 speed = actor->info->speed;
338
339 if (friction < ORIG_FRICTION && // sludge
340 !(speed = ((ORIG_FRICTION_FACTOR - (ORIG_FRICTION_FACTOR-movefactor)/2)
341 * speed) / ORIG_FRICTION_FACTOR))
342 speed = 1; // always give the monster a little bit of speed
343
344 tryx = (origx = actor->x) + (deltax = speed * xspeed[actor->movedir]);
345 tryy = (origy = actor->y) + (deltay = speed * yspeed[actor->movedir]);
346
347 try_ok = P_TryMove(actor, tryx, tryy, dropoff);
348
349 // killough 10/98:
350 // Let normal momentum carry them, instead of steptoeing them across ice.
351
352 if (try_ok && friction > ORIG_FRICTION)
353 {
354 actor->x = origx;
355 actor->y = origy;
356 movefactor *= FRACUNIT / ORIG_FRICTION_FACTOR / 4;
357 actor->momx += FixedMul(deltax, movefactor);
358 actor->momy += FixedMul(deltay, movefactor);
359 }
360
361 if (!try_ok)
362 { // open any specials
363 int good;
364
365 if (actor->flags & MF_FLOAT && floatok)
366 {
367 if (actor->z < tmfloorz) // must adjust height
368 actor->z += FLOATSPEED;
369 else
370 actor->z -= FLOATSPEED;
371
372 actor->flags |= MF_INFLOAT;
373
374 return true;
375 }
376
377 if (!numspechit)
378 return false;
379
380 actor->movedir = DI_NODIR;
381
382 /* if the special is not a door that can be opened, return false
383 *
384 * killough 8/9/98: this is what caused monsters to get stuck in
385 * doortracks, because it thought that the monster freed itself
386 * by opening a door, even if it was moving towards the doortrack,
387 * and not the door itself.
388 *
389 * killough 9/9/98: If a line blocking the monster is activated,
390 * return true 90% of the time. If a line blocking the monster is
391 * not activated, but some other line is, return false 90% of the
392 * time. A bit of randomness is needed to ensure it's free from
393 * lockups, but for most cases, it returns the correct result.
394 *
395 * Do NOT simply return false 1/4th of the time (causes monsters to
396 * back out when they shouldn't, and creates secondary stickiness).
397 */
398
399 for (good = false; numspechit--; )
400 if (P_UseSpecialLine(actor, spechit[numspechit], 0))
401 good |= spechit[numspechit] == blockline ? 1 : 2;
402
403 /* cph - compatibility maze here
404 * Boom v2.01 and orig. Doom return "good"
405 * Boom v2.02 and LxDoom return good && (P_Random(pr_trywalk)&3)
406 * MBF plays even more games
407 */
408 if (!good || comp[comp_doorstuck]) return good;
409 if (!mbf_features)
410 return (P_Random(pr_trywalk)&3); /* jff 8/13/98 */
411 else /* finally, MBF code */
412 return ((P_Random(pr_opendoor) >= 230) ^ (good & 1));
413 }
414 else
415 actor->flags &= ~MF_INFLOAT;
416
417 /* killough 11/98: fall more slowly, under gravity, if felldown==true */
418 if (!(actor->flags & MF_FLOAT) &&
419 (!felldown || !mbf_features))
420 actor->z = actor->floorz;
421
422 return true;
423}
424
425/*
426 * P_SmartMove
427 *
428 * killough 9/12/98: Same as P_Move, except smarter
429 */
430
431static boolean P_SmartMove(mobj_t *actor)
432{
433 mobj_t *target = actor->target;
434 int on_lift, dropoff = false, under_damage;
435
436 /* killough 9/12/98: Stay on a lift if target is on one */
437 on_lift = !comp[comp_staylift]
438 && target && target->health > 0
439 && target->subsector->sector->tag==actor->subsector->sector->tag &&
440 P_IsOnLift(actor);
441
442 under_damage = monster_avoid_hazards && P_IsUnderDamage(actor);
443
444 // killough 10/98: allow dogs to drop off of taller ledges sometimes.
445 // dropoff==1 means always allow it, dropoff==2 means only up to 128 high,
446 // and only if the target is immediately on the other side of the line.
447
448#ifdef DOGS
449 if (actor->type == MT_DOGS && target && dog_jumping &&
450 !((target->flags ^ actor->flags) & MF_FRIEND) &&
451 P_AproxDistance(actor->x - target->x,
452 actor->y - target->y) < FRACUNIT*144 &&
453 P_Random(pr_dropoff) < 235)
454 dropoff = 2;
455#endif
456
457 if (!P_Move(actor, dropoff))
458 return false;
459
460 // killough 9/9/98: avoid crushing ceilings or other damaging areas
461 if (
462 (on_lift && P_Random(pr_stayonlift) < 230 && // Stay on lift
463 !P_IsOnLift(actor))
464 ||
465 (monster_avoid_hazards && !under_damage && // Get away from damage
466 (under_damage = P_IsUnderDamage(actor)) &&
467 (under_damage < 0 || P_Random(pr_avoidcrush) < 200))
468 )
469 actor->movedir = DI_NODIR; // avoid the area (most of the time anyway)
470
471 return true;
472}
473
474//
475// TryWalk
476// Attempts to move actor on
477// in its current (ob->moveangle) direction.
478// If blocked by either a wall or an actor
479// returns FALSE
480// If move is either clear or blocked only by a door,
481// returns TRUE and sets...
482// If a door is in the way,
483// an OpenDoor call is made to start it opening.
484//
485
486boolean P_TryWalk(mobj_t *actor)
487{
488 if (!P_SmartMove(actor))
489 return false;
490 actor->movecount = P_Random(pr_trywalk)&15;
491 return true;
492}
493
494//
495// P_DoNewChaseDir
496//
497// killough 9/8/98:
498//
499// Most of P_NewChaseDir(), except for what
500// determines the new direction to take
501//
502
503static void P_DoNewChaseDir(mobj_t *actor, fixed_t deltax, fixed_t deltay)
504{
505 dirtype_t xdir, ydir, tdir;
506 dirtype_t olddir = actor->movedir;
507 dirtype_t turnaround = olddir;
508
509 if (turnaround != DI_NODIR) // find reverse direction
510 turnaround ^= 4;
511
512 xdir =
513 deltax > 10*FRACUNIT ? DI_EAST :
514 deltax < -10*FRACUNIT ? DI_WEST : DI_NODIR;
515
516 ydir =
517 deltay < -10*FRACUNIT ? DI_SOUTH :
518 deltay > 10*FRACUNIT ? DI_NORTH : DI_NODIR;
519
520 // try direct route
521 if (xdir != DI_NODIR && ydir != DI_NODIR && turnaround !=
522 (actor->movedir = deltay < 0 ? deltax > 0 ? DI_SOUTHEAST : DI_SOUTHWEST :
523 deltax > 0 ? DI_NORTHEAST : DI_NORTHWEST) && P_TryWalk(actor))
524 return;
525
526 // try other directions
527 if (P_Random(pr_newchase) > 200 || abs(deltay)>abs(deltax))
528 tdir = xdir, xdir = ydir, ydir = tdir;
529
530 if ((xdir == turnaround ? xdir = DI_NODIR : xdir) != DI_NODIR &&
531 (actor->movedir = xdir, P_TryWalk(actor)))
532 return; // either moved forward or attacked
533
534 if ((ydir == turnaround ? ydir = DI_NODIR : ydir) != DI_NODIR &&
535 (actor->movedir = ydir, P_TryWalk(actor)))
536 return;
537
538 // there is no direct path to the player, so pick another direction.
539 if (olddir != DI_NODIR && (actor->movedir = olddir, P_TryWalk(actor)))
540 return;
541
542 // randomly determine direction of search
543 if (P_Random(pr_newchasedir) & 1)
544 {
545 for (tdir = DI_EAST; tdir <= DI_SOUTHEAST; tdir++)
546 if (tdir != turnaround && (actor->movedir = tdir, P_TryWalk(actor)))
547 return;
548 }
549 else
550 for (tdir = DI_SOUTHEAST; tdir != (unsigned)(DI_EAST-1); tdir--)
551 if (tdir != turnaround && (actor->movedir = tdir, P_TryWalk(actor)))
552 return;
553
554 if ((actor->movedir = turnaround) != DI_NODIR && !P_TryWalk(actor))
555 actor->movedir = DI_NODIR;
556}
557
558//
559// killough 11/98:
560//
561// Monsters try to move away from tall dropoffs.
562//
563// In Doom, they were never allowed to hang over dropoffs,
564// and would remain stuck if involuntarily forced over one.
565// This logic, combined with p_map.c (P_TryMove), allows
566// monsters to free themselves without making them tend to
567// hang over dropoffs.
568
569static fixed_t dropoff_deltax, dropoff_deltay, floorz;
570
571static boolean PIT_AvoidDropoff(line_t *line)
572{
573 if (line->backsector && // Ignore one-sided linedefs
574 tmbbox[BOXRIGHT] > line->bbox[BOXLEFT] &&
575 tmbbox[BOXLEFT] < line->bbox[BOXRIGHT] &&
576 tmbbox[BOXTOP] > line->bbox[BOXBOTTOM] && // Linedef must be contacted
577 tmbbox[BOXBOTTOM] < line->bbox[BOXTOP] &&
578 P_BoxOnLineSide(tmbbox, line) == -1)
579 {
580 fixed_t front = line->frontsector->floorheight;
581 fixed_t back = line->backsector->floorheight;
582 angle_t angle;
583
584 // The monster must contact one of the two floors,
585 // and the other must be a tall dropoff (more than 24).
586
587 if (back == floorz && front < floorz - FRACUNIT*24)
588 angle = R_PointToAngle2(0,0,line->dx,line->dy); // front side dropoff
589 else
590 if (front == floorz && back < floorz - FRACUNIT*24)
591 angle = R_PointToAngle2(line->dx,line->dy,0,0); // back side dropoff
592 else
593 return true;
594
595 // Move away from dropoff at a standard speed.
596 // Multiple contacted linedefs are cumulative (e.g. hanging over corner)
597 dropoff_deltax -= finesine[angle >> ANGLETOFINESHIFT]*32;
598 dropoff_deltay += finecosine[angle >> ANGLETOFINESHIFT]*32;
599 }
600 return true;
601}
602
603//
604// Driver for above
605//
606
607static fixed_t P_AvoidDropoff(mobj_t *actor)
608{
609 int yh=((tmbbox[BOXTOP] = actor->y+actor->radius)-bmaporgy)>>MAPBLOCKSHIFT;
610 int yl=((tmbbox[BOXBOTTOM]= actor->y-actor->radius)-bmaporgy)>>MAPBLOCKSHIFT;
611 int xh=((tmbbox[BOXRIGHT] = actor->x+actor->radius)-bmaporgx)>>MAPBLOCKSHIFT;
612 int xl=((tmbbox[BOXLEFT] = actor->x-actor->radius)-bmaporgx)>>MAPBLOCKSHIFT;
613 int bx, by;
614
615 floorz = actor->z; // remember floor height
616
617 dropoff_deltax = dropoff_deltay = 0;
618
619 // check lines
620
621 validcount++;
622 for (bx=xl ; bx<=xh ; bx++)
623 for (by=yl ; by<=yh ; by++)
624 P_BlockLinesIterator(bx, by, PIT_AvoidDropoff); // all contacted lines
625
626 return dropoff_deltax | dropoff_deltay; // Non-zero if movement prescribed
627}
628
629//
630// P_NewChaseDir
631//
632// killough 9/8/98: Split into two functions
633//
634
635static void P_NewChaseDir(mobj_t *actor)
636{
637 mobj_t *target = actor->target;
638 fixed_t deltax = target->x - actor->x;
639 fixed_t deltay = target->y - actor->y;
640
641 // killough 8/8/98: sometimes move away from target, keeping distance
642 //
643 // 1) Stay a certain distance away from a friend, to avoid being in their way
644 // 2) Take advantage over an enemy without missiles, by keeping distance
645
646 actor->strafecount = 0;
647
648 if (mbf_features) {
649 if (actor->floorz - actor->dropoffz > FRACUNIT*24 &&
650 actor->z <= actor->floorz &&
651 !(actor->flags & (MF_DROPOFF|MF_FLOAT)) &&
652 !comp[comp_dropoff] &&
653 P_AvoidDropoff(actor)) /* Move away from dropoff */
654 {
655 P_DoNewChaseDir(actor, dropoff_deltax, dropoff_deltay);
656
657 // If moving away from dropoff, set movecount to 1 so that
658 // small steps are taken to get monster away from dropoff.
659
660 actor->movecount = 1;
661 return;
662 }
663 else
664 {
665 fixed_t dist = P_AproxDistance(deltax, deltay);
666
667 // Move away from friends when too close, except
668 // in certain situations (e.g. a crowded lift)
669
670 if (actor->flags & target->flags & MF_FRIEND &&
671 distfriend << FRACBITS > dist &&
672 !P_IsOnLift(target) && !P_IsUnderDamage(actor))
673 {
674 deltax = -deltax, deltay = -deltay;
675 } else
676 if (target->health > 0 && (actor->flags ^ target->flags) & MF_FRIEND)
677 { // Live enemy target
678 if (monster_backing &&
679 actor->info->missilestate && actor->type != MT_SKULL &&
680 ((!target->info->missilestate && dist < MELEERANGE*2) ||
681 (target->player && dist < MELEERANGE*3 &&
682 (target->player->readyweapon == wp_fist ||
683 target->player->readyweapon == wp_chainsaw))))
684 { // Back away from melee attacker
685 actor->strafecount = P_Random(pr_enemystrafe) & 15;
686 deltax = -deltax, deltay = -deltay;
687 }
688 }
689 }
690 }
691
692 P_DoNewChaseDir(actor, deltax, deltay);
693
694 // If strafing, set movecount to strafecount so that old Doom
695 // logic still works the same, except in the strafing part
696
697 if (actor->strafecount)
698 actor->movecount = actor->strafecount;
699}
700
701//
702// P_IsVisible
703//
704// killough 9/9/98: whether a target is visible to a monster
705//
706
707static boolean P_IsVisible(mobj_t *actor, mobj_t *mo, boolean allaround)
708{
709 if (!allaround)
710 {
711 angle_t an = R_PointToAngle2(actor->x, actor->y,
712 mo->x, mo->y) - actor->angle;
713 if (an > ANG90 && an < ANG270 &&
714 P_AproxDistance(mo->x-actor->x, mo->y-actor->y) > MELEERANGE)
715 return false;
716 }
717 return P_CheckSight(actor, mo);
718}
719
720//
721// PIT_FindTarget
722//
723// killough 9/5/98
724//
725// Finds monster targets for other monsters
726//
727
728static int current_allaround;
729
730static boolean PIT_FindTarget(mobj_t *mo)
731{
732 mobj_t *actor = current_actor;
733
734 if (!((mo->flags ^ actor->flags) & MF_FRIEND && // Invalid target
735 mo->health > 0 && (mo->flags & MF_COUNTKILL || mo->type == MT_SKULL)))
736 return true;
737
738 // If the monster is already engaged in a one-on-one attack
739 // with a healthy friend, don't attack around 60% the time
740 {
741 const mobj_t *targ = mo->target;
742 if (targ && targ->target == mo &&
743 P_Random(pr_skiptarget) > 100 &&
744 (targ->flags ^ mo->flags) & MF_FRIEND &&
745 targ->health*2 >= targ->info->spawnhealth)
746 return true;
747 }
748
749 if (!P_IsVisible(actor, mo, current_allaround))
750 return true;
751
752 P_SetTarget(&actor->lastenemy, actor->target); // Remember previous target
753 P_SetTarget(&actor->target, mo); // Found target
754
755 // Move the selected monster to the end of its associated
756 // list, so that it gets searched last next time.
757
758 {
759 thinker_t *cap = &thinkerclasscap[mo->flags & MF_FRIEND ?
760 th_friends : th_enemies];
761 (mo->thinker.cprev->cnext = mo->thinker.cnext)->cprev = mo->thinker.cprev;
762 (mo->thinker.cprev = cap->cprev)->cnext = &mo->thinker;
763 (mo->thinker.cnext = cap)->cprev = &mo->thinker;
764 }
765
766 return false;
767}
768
769//
770// P_LookForPlayers
771// If allaround is false, only look 180 degrees in front.
772// Returns true if a player is targeted.
773//
774
775static boolean P_LookForPlayers(mobj_t *actor, boolean allaround)
776{
777 player_t *player;
778 int stop, stopc, c;
779
780 if (actor->flags & MF_FRIEND)
781 { // killough 9/9/98: friendly monsters go about players differently
782 int anyone;
783
784#if 0
785 if (!allaround) // If you want friendly monsters not to awaken unprovoked
786 return false;
787#endif
788
789 // Go back to a player, no matter whether it's visible or not
790 for (anyone=0; anyone<=1; anyone++)
791 for (c=0; c<MAXPLAYERS; c++)
792 if (playeringame[c] && players[c].playerstate==PST_LIVE &&
793 (anyone || P_IsVisible(actor, players[c].mo, allaround)))
794 {
795 P_SetTarget(&actor->target, players[c].mo);
796
797 // killough 12/98:
798 // get out of refiring loop, to avoid hitting player accidentally
799
800 if (actor->info->missilestate)
801 {
802 P_SetMobjState(actor, actor->info->seestate);
803 actor->flags &= ~MF_JUSTHIT;
804 }
805
806 return true;
807 }
808
809 return false;
810 }
811
812 // Change mask of 3 to (MAXPLAYERS-1) -- killough 2/15/98:
813 stop = (actor->lastlook-1)&(MAXPLAYERS-1);
814
815 c = 0;
816
817 stopc = !mbf_features &&
818 !demo_compatibility && monsters_remember ?
819 MAXPLAYERS : 2; // killough 9/9/98
820
821 for (;; actor->lastlook = (actor->lastlook+1)&(MAXPLAYERS-1))
822 {
823 if (!playeringame[actor->lastlook])
824 continue;
825
826 // killough 2/15/98, 9/9/98:
827 if (c++ == stopc || actor->lastlook == stop) // done looking
828 return false;
829
830 player = &players[actor->lastlook];
831
832 if (player->health <= 0)
833 continue; // dead
834
835 if (!P_IsVisible(actor, player->mo, allaround))
836 continue;
837
838 P_SetTarget(&actor->target, player->mo);
839
840 /* killough 9/9/98: give monsters a threshold towards getting players
841 * (we don't want it to be too easy for a player with dogs :)
842 */
843 if (!comp[comp_pursuit])
844 actor->threshold = 60;
845
846 return true;
847 }
848}
849
850//
851// Friendly monsters, by Lee Killough 7/18/98
852//
853// Friendly monsters go after other monsters first, but
854// also return to owner if they cannot find any targets.
855// A marine's best friend :) killough 7/18/98, 9/98
856//
857
858static boolean P_LookForMonsters(mobj_t *actor, boolean allaround)
859{
860 thinker_t *cap, *th;
861
862 if (demo_compatibility)
863 return false;
864
865 if (actor->lastenemy && actor->lastenemy->health > 0 && monsters_remember &&
866 !(actor->lastenemy->flags & actor->flags & MF_FRIEND)) // not friends
867 {
868 P_SetTarget(&actor->target, actor->lastenemy);
869 P_SetTarget(&actor->lastenemy, NULL);
870 return true;
871 }
872
873 /* Old demos do not support monster-seeking bots */
874 if (!mbf_features)
875 return false;
876
877 // Search the threaded list corresponding to this object's potential targets
878 cap = &thinkerclasscap[actor->flags & MF_FRIEND ? th_enemies : th_friends];
879
880 // Search for new enemy
881
882 if (cap->cnext != cap) // Empty list? bail out early
883 {
884 int x = (actor->x - bmaporgx)>>MAPBLOCKSHIFT;
885 int y = (actor->y - bmaporgy)>>MAPBLOCKSHIFT;
886 int d;
887
888 current_actor = actor;
889 current_allaround = allaround;
890
891 // Search first in the immediate vicinity.
892
893 if (!P_BlockThingsIterator(x, y, PIT_FindTarget))
894 return true;
895
896 for (d=1; d<5; d++)
897 {
898 int i = 1 - d;
899 do
900 if (!P_BlockThingsIterator(x+i, y-d, PIT_FindTarget) ||
901 !P_BlockThingsIterator(x+i, y+d, PIT_FindTarget))
902 return true;
903 while (++i < d);
904 do
905 if (!P_BlockThingsIterator(x-d, y+i, PIT_FindTarget) ||
906 !P_BlockThingsIterator(x+d, y+i, PIT_FindTarget))
907 return true;
908 while (--i + d >= 0);
909 }
910
911 { // Random number of monsters, to prevent patterns from forming
912 int n = (P_Random(pr_friends) & 31) + 15;
913
914 for (th = cap->cnext; th != cap; th = th->cnext)
915 if (--n < 0)
916 {
917 // Only a subset of the monsters were searched. Move all of
918 // the ones which were searched so far, to the end of the list.
919
920 (cap->cnext->cprev = cap->cprev)->cnext = cap->cnext;
921 (cap->cprev = th->cprev)->cnext = cap;
922 (th->cprev = cap)->cnext = th;
923 break;
924 }
925 else
926 if (!PIT_FindTarget((mobj_t *) th)) // If target sighted
927 return true;
928 }
929 }
930
931 return false; // No monster found
932}
933
934//
935// P_LookForTargets
936//
937// killough 9/5/98: look for targets to go after, depending on kind of monster
938//
939
940static boolean P_LookForTargets(mobj_t *actor, int allaround)
941{
942 return actor->flags & MF_FRIEND ?
943 P_LookForMonsters(actor, allaround) || P_LookForPlayers (actor, allaround):
944 P_LookForPlayers (actor, allaround) || P_LookForMonsters(actor, allaround);
945}
946
947//
948// P_HelpFriend
949//
950// killough 9/8/98: Help friends in danger of dying
951//
952
953static boolean P_HelpFriend(mobj_t *actor)
954{
955 thinker_t *cap, *th;
956
957 // If less than 33% health, self-preservation rules
958 if (actor->health*3 < actor->info->spawnhealth)
959 return false;
960
961 current_actor = actor;
962 current_allaround = true;
963
964 // Possibly help a friend under 50% health
965 cap = &thinkerclasscap[actor->flags & MF_FRIEND ? th_friends : th_enemies];
966
967 for (th = cap->cnext; th != cap; th = th->cnext)
968 if (((mobj_t *) th)->health*2 >= ((mobj_t *) th)->info->spawnhealth)
969 {
970 if (P_Random(pr_helpfriend) < 180)
971 break;
972 }
973 else
974 if (((mobj_t *) th)->flags & MF_JUSTHIT &&
975 ((mobj_t *) th)->target &&
976 ((mobj_t *) th)->target != actor->target &&
977 !PIT_FindTarget(((mobj_t *) th)->target))
978 {
979 // Ignore any attacking monsters, while searching for friend
980 actor->threshold = BASETHRESHOLD;
981 return true;
982 }
983
984 return false;
985}
986
987//
988// A_KeenDie
989// DOOM II special, map 32.
990// Uses special tag 666.
991//
992void A_KeenDie(mobj_t* mo)
993{
994 thinker_t *th;
995 line_t junk;
996
997 A_Fall(mo);
998
999 // scan the remaining thinkers to see if all Keens are dead
1000
1001 for (th = thinkercap.next ; th != &thinkercap ; th=th->next)
1002 if (th->function == P_MobjThinker)
1003 {
1004 mobj_t *mo2 = (mobj_t *) th;
1005 if (mo2 != mo && mo2->type == mo->type && mo2->health > 0)
1006 return; // other Keen not dead
1007 }
1008
1009 junk.tag = 666;
1010 EV_DoDoor(&junk,p_open);
1011}
1012
1013
1014//
1015// ACTION ROUTINES
1016//
1017
1018//
1019// A_Look
1020// Stay in state until a player is sighted.
1021//
1022
1023void A_Look(mobj_t *actor)
1024{
1025 mobj_t *targ = actor->subsector->sector->soundtarget;
1026 actor->threshold = 0; // any shot will wake up
1027
1028 /* killough 7/18/98:
1029 * Friendly monsters go after other monsters first, but
1030 * also return to player, without attacking them, if they
1031 * cannot find any targets. A marine's best friend :)
1032 */
1033 actor->pursuecount = 0;
1034
1035 if (!(actor->flags & MF_FRIEND && P_LookForTargets(actor, false)) &&
1036 !((targ = actor->subsector->sector->soundtarget) &&
1037 targ->flags & MF_SHOOTABLE &&
1038 (P_SetTarget(&actor->target, targ),
1039 !(actor->flags & MF_AMBUSH) || P_CheckSight(actor, targ))) &&
1040 (actor->flags & MF_FRIEND || !P_LookForTargets(actor, false)))
1041 return;
1042
1043 // go into chase state
1044
1045 if (actor->info->seesound)
1046 {
1047 int sound;
1048 switch (actor->info->seesound)
1049 {
1050 case sfx_posit1:
1051 case sfx_posit2:
1052 case sfx_posit3:
1053 sound = sfx_posit1+P_Random(pr_see)%3;
1054 break;
1055
1056 case sfx_bgsit1:
1057 case sfx_bgsit2:
1058 sound = sfx_bgsit1+P_Random(pr_see)%2;
1059 break;
1060
1061 default:
1062 sound = actor->info->seesound;
1063 break;
1064 }
1065 if (actor->type==MT_SPIDER || actor->type == MT_CYBORG)
1066 S_StartSound(NULL, sound); // full volume
1067 else
1068 S_StartSound(actor, sound);
1069 }
1070 P_SetMobjState(actor, actor->info->seestate);
1071}
1072
1073//
1074// A_KeepChasing
1075//
1076// killough 10/98:
1077// Allows monsters to continue movement while attacking
1078//
1079
1080void A_KeepChasing(mobj_t *actor)
1081{
1082 if (actor->movecount)
1083 {
1084 actor->movecount--;
1085 if (actor->strafecount)
1086 actor->strafecount--;
1087 P_SmartMove(actor);
1088 }
1089}
1090
1091//
1092// A_Chase
1093// Actor has a melee attack,
1094// so it tries to close as fast as possible
1095//
1096
1097void A_Chase(mobj_t *actor)
1098{
1099 if (actor->reactiontime)
1100 actor->reactiontime--;
1101
1102 if (actor->threshold) { /* modify target threshold */
1103 if (!actor->target || actor->target->health <= 0)
1104 actor->threshold = 0;
1105 else
1106 actor->threshold--;
1107 }
1108
1109 /* turn towards movement direction if not there yet
1110 * killough 9/7/98: keep facing towards target if strafing or backing out
1111 */
1112
1113 if (actor->strafecount)
1114 A_FaceTarget(actor);
1115 else if (actor->movedir < 8)
1116 {
1117 int delta = (actor->angle &= (7<<29)) - (actor->movedir << 29);
1118 if (delta > 0)
1119 actor->angle -= ANG90/2;
1120 else
1121 if (delta < 0)
1122 actor->angle += ANG90/2;
1123 }
1124
1125 if (!actor->target || !(actor->target->flags&MF_SHOOTABLE))
1126 {
1127 if (!P_LookForTargets(actor,true)) // look for a new target
1128 P_SetMobjState(actor, actor->info->spawnstate); // no new target
1129 return;
1130 }
1131
1132 // do not attack twice in a row
1133 if (actor->flags & MF_JUSTATTACKED)
1134 {
1135 actor->flags &= ~MF_JUSTATTACKED;
1136 if (gameskill != sk_nightmare && !fastparm)
1137 P_NewChaseDir(actor);
1138 return;
1139 }
1140
1141 // check for melee attack
1142 if (actor->info->meleestate && P_CheckMeleeRange(actor))
1143 {
1144 if (actor->info->attacksound)
1145 S_StartSound(actor, actor->info->attacksound);
1146 P_SetMobjState(actor, actor->info->meleestate);
1147 /* killough 8/98: remember an attack
1148 * cph - DEMOSYNC? */
1149 if (!actor->info->missilestate)
1150 actor->flags |= MF_JUSTHIT;
1151 return;
1152 }
1153
1154 // check for missile attack
1155 if (actor->info->missilestate)
1156 if (!(gameskill < sk_nightmare && !fastparm && actor->movecount))
1157 if (P_CheckMissileRange(actor))
1158 {
1159 P_SetMobjState(actor, actor->info->missilestate);
1160 actor->flags |= MF_JUSTATTACKED;
1161 return;
1162 }
1163
1164 if (!actor->threshold) {
1165 if (!mbf_features)
1166 { /* killough 9/9/98: for backward demo compatibility */
1167 if (netgame && !P_CheckSight(actor, actor->target) &&
1168 P_LookForPlayers(actor, true))
1169 return;
1170 }
1171 /* killough 7/18/98, 9/9/98: new monster AI */
1172 else if (help_friends && P_HelpFriend(actor))
1173 return; /* killough 9/8/98: Help friends in need */
1174 /* Look for new targets if current one is bad or is out of view */
1175 else if (actor->pursuecount)
1176 actor->pursuecount--;
1177 else {
1178 /* Our pursuit time has expired. We're going to think about
1179 * changing targets */
1180 actor->pursuecount = BASETHRESHOLD;
1181
1182 /* Unless (we have a live target
1183 * and it's not friendly
1184 * and we can see it)
1185 * try to find a new one; return if sucessful */
1186
1187 if (!(actor->target && actor->target->health > 0 &&
1188 ((comp[comp_pursuit] && !netgame) ||
1189 (((actor->target->flags ^ actor->flags) & MF_FRIEND ||
1190 (!(actor->flags & MF_FRIEND) && monster_infighting)) &&
1191 P_CheckSight(actor, actor->target))))
1192 && P_LookForTargets(actor, true))
1193 return;
1194
1195 /* (Current target was good, or no new target was found.)
1196 *
1197 * If monster is a missile-less friend, give up pursuit and
1198 * return to player, if no attacks have occurred recently.
1199 */
1200
1201 if (!actor->info->missilestate && actor->flags & MF_FRIEND) {
1202 if (actor->flags & MF_JUSTHIT) /* if recent action, */
1203 actor->flags &= ~MF_JUSTHIT; /* keep fighting */
1204 else if (P_LookForPlayers(actor, true)) /* else return to player */
1205 return;
1206 }
1207 }
1208 }
1209
1210 if (actor->strafecount)
1211 actor->strafecount--;
1212
1213 // chase towards player
1214 if (--actor->movecount<0 || !P_SmartMove(actor))
1215 P_NewChaseDir(actor);
1216
1217 // make active sound
1218 if (actor->info->activesound && P_Random(pr_see)<3)
1219 S_StartSound(actor, actor->info->activesound);
1220}
1221
1222//
1223// A_FaceTarget
1224//
1225void A_FaceTarget(mobj_t *actor)
1226{
1227 if (!actor->target)
1228 return;
1229 actor->flags &= ~MF_AMBUSH;
1230 actor->angle = R_PointToAngle2(actor->x, actor->y,
1231 actor->target->x, actor->target->y);
1232 if (actor->target->flags & MF_SHADOW)
1233 { // killough 5/5/98: remove dependence on order of evaluation:
1234 int t = P_Random(pr_facetarget);
1235 actor->angle += (t-P_Random(pr_facetarget))<<21;
1236 }
1237}
1238
1239//
1240// A_PosAttack
1241//
1242
1243void A_PosAttack(mobj_t *actor)
1244{
1245 int angle, damage, slope, t;
1246
1247 if (!actor->target)
1248 return;
1249 A_FaceTarget(actor);
1250 angle = actor->angle;
1251 slope = P_AimLineAttack(actor, angle, MISSILERANGE, 0); /* killough 8/2/98 */
1252 S_StartSound(actor, sfx_pistol);
1253
1254 // killough 5/5/98: remove dependence on order of evaluation:
1255 t = P_Random(pr_posattack);
1256 angle += (t - P_Random(pr_posattack))<<20;
1257 damage = (P_Random(pr_posattack)%5 + 1)*3;
1258 P_LineAttack(actor, angle, MISSILERANGE, slope, damage);
1259}
1260
1261void A_SPosAttack(mobj_t* actor)
1262{
1263 int i, bangle, slope;
1264
1265 if (!actor->target)
1266 return;
1267 S_StartSound(actor, sfx_shotgn);
1268 A_FaceTarget(actor);
1269 bangle = actor->angle;
1270 slope = P_AimLineAttack(actor, bangle, MISSILERANGE, 0); /* killough 8/2/98 */
1271 for (i=0; i<3; i++)
1272 { // killough 5/5/98: remove dependence on order of evaluation:
1273 int t = P_Random(pr_sposattack);
1274 int angle = bangle + ((t - P_Random(pr_sposattack))<<20);
1275 int damage = ((P_Random(pr_sposattack)%5)+1)*3;
1276 P_LineAttack(actor, angle, MISSILERANGE, slope, damage);
1277 }
1278}
1279
1280void A_CPosAttack(mobj_t *actor)
1281{
1282 int angle, bangle, damage, slope, t;
1283
1284 if (!actor->target)
1285 return;
1286 S_StartSound(actor, sfx_shotgn);
1287 A_FaceTarget(actor);
1288 bangle = actor->angle;
1289 slope = P_AimLineAttack(actor, bangle, MISSILERANGE, 0); /* killough 8/2/98 */
1290
1291 // killough 5/5/98: remove dependence on order of evaluation:
1292 t = P_Random(pr_cposattack);
1293 angle = bangle + ((t - P_Random(pr_cposattack))<<20);
1294 damage = ((P_Random(pr_cposattack)%5)+1)*3;
1295 P_LineAttack(actor, angle, MISSILERANGE, slope, damage);
1296}
1297
1298void A_CPosRefire(mobj_t *actor)
1299{
1300 // keep firing unless target got out of sight
1301 A_FaceTarget(actor);
1302
1303 /* killough 12/98: Stop firing if a friend has gotten in the way */
1304 if (P_HitFriend(actor))
1305 goto stop;
1306
1307 /* killough 11/98: prevent refiring on friends continuously */
1308 if (P_Random(pr_cposrefire) < 40) {
1309 if (actor->target && actor->flags & actor->target->flags & MF_FRIEND)
1310 goto stop;
1311 else
1312 return;
1313 }
1314
1315 if (!actor->target || actor->target->health <= 0
1316 || !P_CheckSight(actor, actor->target))
1317stop: P_SetMobjState(actor, actor->info->seestate);
1318}
1319
1320void A_SpidRefire(mobj_t* actor)
1321{
1322 // keep firing unless target got out of sight
1323 A_FaceTarget(actor);
1324
1325 /* killough 12/98: Stop firing if a friend has gotten in the way */
1326 if (P_HitFriend(actor))
1327 goto stop;
1328
1329 if (P_Random(pr_spidrefire) < 10)
1330 return;
1331
1332 // killough 11/98: prevent refiring on friends continuously
1333 if (!actor->target || actor->target->health <= 0
1334 || actor->flags & actor->target->flags & MF_FRIEND
1335 || !P_CheckSight(actor, actor->target))
1336stop: P_SetMobjState(actor, actor->info->seestate);
1337}
1338
1339void A_BspiAttack(mobj_t *actor)
1340{
1341 if (!actor->target)
1342 return;
1343 A_FaceTarget(actor);
1344 P_SpawnMissile(actor, actor->target, MT_ARACHPLAZ); // launch a missile
1345}
1346
1347//
1348// A_TroopAttack
1349//
1350
1351void A_TroopAttack(mobj_t *actor)
1352{
1353 if (!actor->target)
1354 return;
1355 A_FaceTarget(actor);
1356 if (P_CheckMeleeRange(actor))
1357 {
1358 int damage;
1359 S_StartSound(actor, sfx_claw);
1360 damage = (P_Random(pr_troopattack)%8+1)*3;
1361 P_DamageMobj(actor->target, actor, actor, damage);
1362 return;
1363 }
1364 P_SpawnMissile(actor, actor->target, MT_TROOPSHOT); // launch a missile
1365}
1366
1367void A_SargAttack(mobj_t *actor)
1368{
1369 if (!actor->target)
1370 return;
1371 A_FaceTarget(actor);
1372 if (P_CheckMeleeRange(actor))
1373 {
1374 int damage = ((P_Random(pr_sargattack)%10)+1)*4;
1375 P_DamageMobj(actor->target, actor, actor, damage);
1376 }
1377}
1378
1379void A_HeadAttack(mobj_t *actor)
1380{
1381 if (!actor->target)
1382 return;
1383 A_FaceTarget (actor);
1384 if (P_CheckMeleeRange(actor))
1385 {
1386 int damage = (P_Random(pr_headattack)%6+1)*10;
1387 P_DamageMobj(actor->target, actor, actor, damage);
1388 return;
1389 }
1390 P_SpawnMissile(actor, actor->target, MT_HEADSHOT); // launch a missile
1391}
1392
1393void A_CyberAttack(mobj_t *actor)
1394{
1395 if (!actor->target)
1396 return;
1397 A_FaceTarget(actor);
1398 P_SpawnMissile(actor, actor->target, MT_ROCKET);
1399}
1400
1401void A_BruisAttack(mobj_t *actor)
1402{
1403 if (!actor->target)
1404 return;
1405 if (P_CheckMeleeRange(actor))
1406 {
1407 int damage;
1408 S_StartSound(actor, sfx_claw);
1409 damage = (P_Random(pr_bruisattack)%8+1)*10;
1410 P_DamageMobj(actor->target, actor, actor, damage);
1411 return;
1412 }
1413 P_SpawnMissile(actor, actor->target, MT_BRUISERSHOT); // launch a missile
1414}
1415
1416//
1417// A_SkelMissile
1418//
1419
1420void A_SkelMissile(mobj_t *actor)
1421{
1422 mobj_t *mo;
1423
1424 if (!actor->target)
1425 return;
1426
1427 A_FaceTarget (actor);
1428 actor->z += 16*FRACUNIT; // so missile spawns higher
1429 mo = P_SpawnMissile (actor, actor->target, MT_TRACER);
1430 actor->z -= 16*FRACUNIT; // back to normal
1431
1432 mo->x += mo->momx;
1433 mo->y += mo->momy;
1434 P_SetTarget(&mo->tracer, actor->target);
1435}
1436
1437int TRACEANGLE = 0xc000000;
1438
1439void A_Tracer(mobj_t *actor)
1440{
1441 angle_t exact;
1442 fixed_t dist;
1443 fixed_t slope;
1444 mobj_t *dest;
1445 mobj_t *th;
1446
1447 /* killough 1/18/98: this is why some missiles do not have smoke
1448 * and some do. Also, internal demos start at random gametics, thus
1449 * the bug in which revenants cause internal demos to go out of sync.
1450 *
1451 * killough 3/6/98: fix revenant internal demo bug by subtracting
1452 * levelstarttic from gametic.
1453 *
1454 * killough 9/29/98: use new "basetic" so that demos stay in sync
1455 * during pauses and menu activations, while retaining old demo sync.
1456 *
1457 * leveltime would have been better to use to start with in Doom, but
1458 * since old demos were recorded using gametic, we must stick with it,
1459 * and improvise around it (using leveltime causes desync across levels).
1460 */
1461
1462 if ((gametic-basetic) & 3)
1463 return;
1464
1465 // spawn a puff of smoke behind the rocket
1466 P_SpawnPuff(actor->x, actor->y, actor->z);
1467
1468 th = P_SpawnMobj (actor->x-actor->momx,
1469 actor->y-actor->momy,
1470 actor->z, MT_SMOKE);
1471
1472 th->momz = FRACUNIT;
1473 th->tics -= P_Random(pr_tracer) & 3;
1474 if (th->tics < 1)
1475 th->tics = 1;
1476
1477 // adjust direction
1478 dest = actor->tracer;
1479
1480 if (!dest || dest->health <= 0)
1481 return;
1482
1483 // change angle
1484 exact = R_PointToAngle2(actor->x, actor->y, dest->x, dest->y);
1485
1486 if (exact != actor->angle) {
1487 if (exact - actor->angle > 0x80000000)
1488 {
1489 actor->angle -= TRACEANGLE;
1490 if (exact - actor->angle < 0x80000000)
1491 actor->angle = exact;
1492 }
1493 else
1494 {
1495 actor->angle += TRACEANGLE;
1496 if (exact - actor->angle > 0x80000000)
1497 actor->angle = exact;
1498 }
1499 }
1500
1501 exact = actor->angle>>ANGLETOFINESHIFT;
1502 actor->momx = FixedMul(actor->info->speed, finecosine[exact]);
1503 actor->momy = FixedMul(actor->info->speed, finesine[exact]);
1504
1505 // change slope
1506 dist = P_AproxDistance(dest->x - actor->x, dest->y - actor->y);
1507
1508 dist = dist / actor->info->speed;
1509
1510 if (dist < 1)
1511 dist = 1;
1512
1513 slope = (dest->z+40*FRACUNIT - actor->z) / dist;
1514
1515 if (slope < actor->momz)
1516 actor->momz -= FRACUNIT/8;
1517 else
1518 actor->momz += FRACUNIT/8;
1519}
1520
1521void A_SkelWhoosh(mobj_t *actor)
1522{
1523 if (!actor->target)
1524 return;
1525 A_FaceTarget(actor);
1526 S_StartSound(actor,sfx_skeswg);
1527}
1528
1529void A_SkelFist(mobj_t *actor)
1530{
1531 if (!actor->target)
1532 return;
1533 A_FaceTarget(actor);
1534 if (P_CheckMeleeRange(actor))
1535 {
1536 int damage = ((P_Random(pr_skelfist)%10)+1)*6;
1537 S_StartSound(actor, sfx_skepch);
1538 P_DamageMobj(actor->target, actor, actor, damage);
1539 }
1540}
1541
1542//
1543// PIT_VileCheck
1544// Detect a corpse that could be raised.
1545//
1546
1547mobj_t* corpsehit;
1548mobj_t* vileobj;
1549fixed_t viletryx;
1550fixed_t viletryy;
1551
1552boolean PIT_VileCheck(mobj_t *thing)
1553{
1554 int maxdist;
1555 boolean check;
1556
1557 if (!(thing->flags & MF_CORPSE) )
1558 return true; // not a monster
1559
1560 if (thing->tics != -1)
1561 return true; // not lying still yet
1562
1563 if (thing->info->raisestate == S_NULL)
1564 return true; // monster doesn't have a raise state
1565
1566 maxdist = thing->info->radius + mobjinfo[MT_VILE].radius;
1567
1568 if (D_abs(thing->x-viletryx) > maxdist || D_abs(thing->y-viletryy) > maxdist)
1569 return true; // not actually touching
1570
1571 // Check to see if the radius and height are zero. If they are // phares
1572 // then this is a crushed monster that has been turned into a // |
1573 // gib. One of the options may be to ignore this guy. // V
1574
1575 // Option 1: the original, buggy method, -> ghost (compatibility)
1576 // Option 2: ressurect the monster, but not as a ghost
1577 // Option 3: ignore the gib
1578
1579 // if (Option3) // ^
1580 // if ((thing->height == 0) && (thing->radius == 0)) // |
1581 // return true; // phares
1582
1583 corpsehit = thing;
1584 corpsehit->momx = corpsehit->momy = 0;
1585 if (comp[comp_vile]) // phares
1586 { // |
1587 corpsehit->height <<= 2; // V
1588 check = P_CheckPosition(corpsehit,corpsehit->x,corpsehit->y);
1589 corpsehit->height >>= 2;
1590 }
1591 else
1592 {
1593 int height,radius;
1594
1595 height = corpsehit->height; // save temporarily
1596 radius = corpsehit->radius; // save temporarily
1597 corpsehit->height = corpsehit->info->height;
1598 corpsehit->radius = corpsehit->info->radius;
1599 corpsehit->flags |= MF_SOLID;
1600 check = P_CheckPosition(corpsehit,corpsehit->x,corpsehit->y);
1601 corpsehit->height = height; // restore
1602 corpsehit->radius = radius; // restore // ^
1603 corpsehit->flags &= ~MF_SOLID;
1604 } // |
1605 // phares
1606 if (!check)
1607 return true; // doesn't fit here
1608 return false; // got one, so stop checking
1609}
1610
1611//
1612// A_VileChase
1613// Check for ressurecting a body
1614//
1615
1616void A_VileChase(mobj_t* actor)
1617{
1618 int xl, xh;
1619 int yl, yh;
1620 int bx, by;
1621
1622 if (actor->movedir != DI_NODIR)
1623 {
1624 // check for corpses to raise
1625 viletryx =
1626 actor->x + actor->info->speed*xspeed[actor->movedir];
1627 viletryy =
1628 actor->y + actor->info->speed*yspeed[actor->movedir];
1629
1630 xl = (viletryx - bmaporgx - MAXRADIUS*2)>>MAPBLOCKSHIFT;
1631 xh = (viletryx - bmaporgx + MAXRADIUS*2)>>MAPBLOCKSHIFT;
1632 yl = (viletryy - bmaporgy - MAXRADIUS*2)>>MAPBLOCKSHIFT;
1633 yh = (viletryy - bmaporgy + MAXRADIUS*2)>>MAPBLOCKSHIFT;
1634
1635 vileobj = actor;
1636 for (bx=xl ; bx<=xh ; bx++)
1637 {
1638 for (by=yl ; by<=yh ; by++)
1639 {
1640 // Call PIT_VileCheck to check
1641 // whether object is a corpse
1642 // that canbe raised.
1643 if (!P_BlockThingsIterator(bx,by,PIT_VileCheck))
1644 {
1645 mobjinfo_t *info;
1646
1647 // got one!
1648 mobj_t* temp = actor->target;
1649 actor->target = corpsehit;
1650 A_FaceTarget(actor);
1651 actor->target = temp;
1652
1653 P_SetMobjState(actor, S_VILE_HEAL1);
1654 S_StartSound(corpsehit, sfx_slop);
1655 info = corpsehit->info;
1656
1657 P_SetMobjState(corpsehit,info->raisestate);
1658
1659 if (comp[comp_vile]) // phares
1660 corpsehit->height <<= 2; // |
1661 else // V
1662 {
1663 corpsehit->height = info->height; // fix Ghost bug
1664 corpsehit->radius = info->radius; // fix Ghost bug
1665 } // phares
1666
1667 /* killough 7/18/98:
1668 * friendliness is transferred from AV to raised corpse
1669 */
1670 corpsehit->flags =
1671 (info->flags & ~MF_FRIEND) | (actor->flags & MF_FRIEND);
1672
1673 if (!((corpsehit->flags ^ MF_COUNTKILL) & (MF_FRIEND | MF_COUNTKILL)))
1674 totallive++;
1675
1676 corpsehit->health = info->spawnhealth;
1677 P_SetTarget(&corpsehit->target, NULL); // killough 11/98
1678
1679 if (mbf_features)
1680 { /* kilough 9/9/98 */
1681 P_SetTarget(&corpsehit->lastenemy, NULL);
1682 corpsehit->flags &= ~MF_JUSTHIT;
1683 }
1684
1685 /* killough 8/29/98: add to appropriate thread */
1686 P_UpdateThinker(&corpsehit->thinker);
1687
1688 return;
1689 }
1690 }
1691 }
1692 }
1693 A_Chase(actor); // Return to normal attack.
1694}
1695
1696//
1697// A_VileStart
1698//
1699
1700void A_VileStart(mobj_t *actor)
1701{
1702 S_StartSound(actor, sfx_vilatk);
1703}
1704
1705//
1706// A_Fire
1707// Keep fire in front of player unless out of sight
1708//
1709
1710void A_Fire(mobj_t *actor);
1711
1712void A_StartFire(mobj_t *actor)
1713{
1714 S_StartSound(actor,sfx_flamst);
1715 A_Fire(actor);
1716}
1717
1718void A_FireCrackle(mobj_t* actor)
1719{
1720 S_StartSound(actor,sfx_flame);
1721 A_Fire(actor);
1722}
1723
1724void A_Fire(mobj_t *actor)
1725{
1726 unsigned an;
1727 mobj_t *dest = actor->tracer;
1728
1729 if (!dest)
1730 return;
1731
1732 // don't move it if the vile lost sight
1733 if (!P_CheckSight(actor->target, dest) )
1734 return;
1735
1736 an = dest->angle >> ANGLETOFINESHIFT;
1737
1738 P_UnsetThingPosition(actor);
1739 actor->x = dest->x + FixedMul(24*FRACUNIT, finecosine[an]);
1740 actor->y = dest->y + FixedMul(24*FRACUNIT, finesine[an]);
1741 actor->z = dest->z;
1742 P_SetThingPosition(actor);
1743}
1744
1745//
1746// A_VileTarget
1747// Spawn the hellfire
1748//
1749
1750void A_VileTarget(mobj_t *actor)
1751{
1752 mobj_t *fog;
1753
1754 if (!actor->target)
1755 return;
1756
1757 A_FaceTarget(actor);
1758
1759 // killough 12/98: fix Vile fog coordinates // CPhipps - compatibility optioned
1760 fog = P_SpawnMobj(actor->target->x,
1761 (compatibility_level < lxdoom_1_compatibility) ? actor->target->x : actor->target->y,
1762 actor->target->z,MT_FIRE);
1763
1764 P_SetTarget(&actor->tracer, fog);
1765 P_SetTarget(&fog->target, actor);
1766 P_SetTarget(&fog->tracer, actor->target);
1767 A_Fire(fog);
1768}
1769
1770//
1771// A_VileAttack
1772//
1773
1774void A_VileAttack(mobj_t *actor)
1775{
1776 mobj_t *fire;
1777 int an;
1778
1779 if (!actor->target)
1780 return;
1781
1782 A_FaceTarget(actor);
1783
1784 if (!P_CheckSight(actor, actor->target))
1785 return;
1786
1787 S_StartSound(actor, sfx_barexp);
1788 P_DamageMobj(actor->target, actor, actor, 20);
1789 actor->target->momz = 1000*FRACUNIT/actor->target->info->mass;
1790
1791 an = actor->angle >> ANGLETOFINESHIFT;
1792
1793 fire = actor->tracer;
1794
1795 if (!fire)
1796 return;
1797
1798 // move the fire between the vile and the player
1799 fire->x = actor->target->x - FixedMul (24*FRACUNIT, finecosine[an]);
1800 fire->y = actor->target->y - FixedMul (24*FRACUNIT, finesine[an]);
1801 P_RadiusAttack(fire, actor, 70);
1802}
1803
1804//
1805// Mancubus attack,
1806// firing three missiles (bruisers)
1807// in three different directions?
1808// Doesn't look like it.
1809//
1810
1811#define FATSPREAD (ANG90/8)
1812
1813void A_FatRaise(mobj_t *actor)
1814{
1815 A_FaceTarget(actor);
1816 S_StartSound(actor, sfx_manatk);
1817}
1818
1819void A_FatAttack1(mobj_t *actor)
1820{
1821 mobj_t *mo;
1822 int an;
1823
1824 A_FaceTarget(actor);
1825
1826 // Change direction to ...
1827 actor->angle += FATSPREAD;
1828
1829 P_SpawnMissile(actor, actor->target, MT_FATSHOT);
1830
1831 mo = P_SpawnMissile (actor, actor->target, MT_FATSHOT);
1832 mo->angle += FATSPREAD;
1833 an = mo->angle >> ANGLETOFINESHIFT;
1834 mo->momx = FixedMul(mo->info->speed, finecosine[an]);
1835 mo->momy = FixedMul(mo->info->speed, finesine[an]);
1836}
1837
1838void A_FatAttack2(mobj_t *actor)
1839{
1840 mobj_t *mo;
1841 int an;
1842
1843 A_FaceTarget(actor);
1844 // Now here choose opposite deviation.
1845 actor->angle -= FATSPREAD;
1846 P_SpawnMissile(actor, actor->target, MT_FATSHOT);
1847
1848 mo = P_SpawnMissile(actor, actor->target, MT_FATSHOT);
1849 mo->angle -= FATSPREAD*2;
1850 an = mo->angle >> ANGLETOFINESHIFT;
1851 mo->momx = FixedMul(mo->info->speed, finecosine[an]);
1852 mo->momy = FixedMul(mo->info->speed, finesine[an]);
1853}
1854
1855void A_FatAttack3(mobj_t *actor)
1856{
1857 mobj_t *mo;
1858 int an;
1859
1860 A_FaceTarget(actor);
1861
1862 mo = P_SpawnMissile(actor, actor->target, MT_FATSHOT);
1863 mo->angle -= FATSPREAD/2;
1864 an = mo->angle >> ANGLETOFINESHIFT;
1865 mo->momx = FixedMul(mo->info->speed, finecosine[an]);
1866 mo->momy = FixedMul(mo->info->speed, finesine[an]);
1867
1868 mo = P_SpawnMissile(actor, actor->target, MT_FATSHOT);
1869 mo->angle += FATSPREAD/2;
1870 an = mo->angle >> ANGLETOFINESHIFT;
1871 mo->momx = FixedMul(mo->info->speed, finecosine[an]);
1872 mo->momy = FixedMul(mo->info->speed, finesine[an]);
1873}
1874
1875
1876//
1877// SkullAttack
1878// Fly at the player like a missile.
1879//
1880#define SKULLSPEED (20*FRACUNIT)
1881
1882void A_SkullAttack(mobj_t *actor)
1883{
1884 mobj_t *dest;
1885 angle_t an;
1886 int dist;
1887
1888 if (!actor->target)
1889 return;
1890
1891 dest = actor->target;
1892 actor->flags |= MF_SKULLFLY;
1893
1894 S_StartSound(actor, actor->info->attacksound);
1895 A_FaceTarget(actor);
1896 an = actor->angle >> ANGLETOFINESHIFT;
1897 actor->momx = FixedMul(SKULLSPEED, finecosine[an]);
1898 actor->momy = FixedMul(SKULLSPEED, finesine[an]);
1899 dist = P_AproxDistance(dest->x - actor->x, dest->y - actor->y);
1900 dist = dist / SKULLSPEED;
1901
1902 if (dist < 1)
1903 dist = 1;
1904 actor->momz = (dest->z+(dest->height>>1) - actor->z) / dist;
1905}
1906
1907//
1908// A_PainShootSkull
1909// Spawn a lost soul and launch it at the target
1910//
1911
1912void A_PainShootSkull(mobj_t *actor, angle_t angle)
1913{
1914 fixed_t x,y,z;
1915 mobj_t *newmobj;
1916 angle_t an;
1917 int prestep;
1918
1919 // The original code checked for 20 skulls on the level, // phares
1920 // and wouldn't spit another one if there were. If not in // phares
1921 // compatibility mode, we remove the limit. // phares
1922 // phares
1923 if (comp[comp_pain]) /* killough 10/98: compatibility-optioned */
1924 {
1925 // count total number of skulls currently on the level
1926 int count = 0;
1927 thinker_t *currentthinker;
1928 for (currentthinker = thinkercap.next;
1929 currentthinker != &thinkercap;
1930 currentthinker = currentthinker->next)
1931 if ((currentthinker->function == P_MobjThinker)
1932 && ((mobj_t *)currentthinker)->type == MT_SKULL)
1933 count++;
1934 if (count > 20) // phares
1935 return; // phares
1936 }
1937
1938 // okay, there's room for another one
1939
1940 an = angle >> ANGLETOFINESHIFT;
1941
1942 prestep = 4*FRACUNIT + 3*(actor->info->radius + mobjinfo[MT_SKULL].radius)/2;
1943
1944 x = actor->x + FixedMul(prestep, finecosine[an]);
1945 y = actor->y + FixedMul(prestep, finesine[an]);
1946 z = actor->z + 8*FRACUNIT;
1947
1948 if (comp[comp_skull]) /* killough 10/98: compatibility-optioned */
1949 newmobj = P_SpawnMobj(x, y, z, MT_SKULL); // phares
1950 else // V
1951 {
1952 // Check whether the Lost Soul is being fired through a 1-sided
1953 // wall or an impassible line, or a "monsters can't cross" line.
1954 // If it is, then we don't allow the spawn. This is a bug fix, but
1955 // it should be considered an enhancement, since it may disturb
1956 // existing demos, so don't do it in compatibility mode.
1957
1958 if (Check_Sides(actor,x,y))
1959 return;
1960
1961 newmobj = P_SpawnMobj(x, y, z, MT_SKULL);
1962
1963 // Check to see if the new Lost Soul's z value is above the
1964 // ceiling of its new sector, or below the floor. If so, kill it.
1965
1966 if ((newmobj->z >
1967 (newmobj->subsector->sector->ceilingheight - newmobj->height)) ||
1968 (newmobj->z < newmobj->subsector->sector->floorheight))
1969 {
1970 // kill it immediately
1971 P_DamageMobj(newmobj,actor,actor,10000);
1972 return; // ^
1973 } // |
1974 } // phares
1975
1976 /* killough 7/20/98: PEs shoot lost souls with the same friendliness */
1977 newmobj->flags = (newmobj->flags & ~MF_FRIEND) | (actor->flags & MF_FRIEND);
1978
1979 /* killough 8/29/98: add to appropriate thread */
1980 P_UpdateThinker(&newmobj->thinker);
1981
1982 // Check for movements.
1983 // killough 3/15/98: don't jump over dropoffs:
1984
1985 if (!P_TryMove(newmobj, newmobj->x, newmobj->y, false))
1986 {
1987 // kill it immediately
1988 P_DamageMobj(newmobj, actor, actor, 10000);
1989 return;
1990 }
1991
1992 P_SetTarget(&newmobj->target, actor->target);
1993 A_SkullAttack(newmobj);
1994}
1995
1996//
1997// A_PainAttack
1998// Spawn a lost soul and launch it at the target
1999//
2000
2001void A_PainAttack(mobj_t *actor)
2002{
2003 if (!actor->target)
2004 return;
2005 A_FaceTarget(actor);
2006 A_PainShootSkull(actor, actor->angle);
2007}
2008
2009void A_PainDie(mobj_t *actor)
2010{
2011 A_Fall(actor);
2012 A_PainShootSkull(actor, actor->angle+ANG90);
2013 A_PainShootSkull(actor, actor->angle+ANG180);
2014 A_PainShootSkull(actor, actor->angle+ANG270);
2015}
2016
2017void A_Scream(mobj_t *actor)
2018{
2019 int sound;
2020
2021 switch (actor->info->deathsound)
2022 {
2023 case 0:
2024 return;
2025
2026 case sfx_podth1:
2027 case sfx_podth2:
2028 case sfx_podth3:
2029 sound = sfx_podth1 + P_Random(pr_scream)%3;
2030 break;
2031
2032 case sfx_bgdth1:
2033 case sfx_bgdth2:
2034 sound = sfx_bgdth1 + P_Random(pr_scream)%2;
2035 break;
2036
2037 default:
2038 sound = actor->info->deathsound;
2039 break;
2040 }
2041
2042 // Check for bosses.
2043 if (actor->type==MT_SPIDER || actor->type == MT_CYBORG)
2044 S_StartSound(NULL, sound); // full volume
2045 else
2046 S_StartSound(actor, sound);
2047}
2048
2049void A_XScream(mobj_t *actor)
2050{
2051 S_StartSound(actor, sfx_slop);
2052}
2053
2054void A_Pain(mobj_t *actor)
2055{
2056 if (actor->info->painsound)
2057 S_StartSound(actor, actor->info->painsound);
2058}
2059
2060void A_Fall(mobj_t *actor)
2061{
2062 // actor is on ground, it can be walked over
2063 actor->flags &= ~MF_SOLID;
2064}
2065
2066//
2067// A_Explode
2068//
2069void A_Explode(mobj_t *thingy)
2070{
2071 P_RadiusAttack( thingy, thingy->target, 128 );
2072}
2073
2074//
2075// A_BossDeath
2076// Possibly trigger special effects
2077// if on first boss level
2078//
2079
2080void A_BossDeath(mobj_t *mo)
2081{
2082 thinker_t *th;
2083 line_t junk;
2084 int i;
2085
2086 if (gamemode == commercial)
2087 {
2088 if (gamemap != 7)
2089 return;
2090
2091 if ((mo->type != MT_FATSO)
2092 && (mo->type != MT_BABY))
2093 return;
2094 }
2095 else
2096 {
2097 switch(gameepisode)
2098 {
2099 case 1:
2100 if (gamemap != 8)
2101 return;
2102
2103 if (mo->type != MT_BRUISER)
2104 return;
2105 break;
2106
2107 case 2:
2108 if (gamemap != 8)
2109 return;
2110
2111 if (mo->type != MT_CYBORG)
2112 return;
2113 break;
2114
2115 case 3:
2116 if (gamemap != 8)
2117 return;
2118
2119 if (mo->type != MT_SPIDER)
2120 return;
2121
2122 break;
2123
2124 case 4:
2125 switch(gamemap)
2126 {
2127 case 6:
2128 if (mo->type != MT_CYBORG)
2129 return;
2130 break;
2131
2132 case 8:
2133 if (mo->type != MT_SPIDER)
2134 return;
2135 break;
2136
2137 default:
2138 return;
2139 break;
2140 }
2141 break;
2142
2143 default:
2144 if (gamemap != 8)
2145 return;
2146 break;
2147 }
2148
2149 }
2150
2151 // make sure there is a player alive for victory
2152 for (i=0; i<MAXPLAYERS; i++)
2153 if (playeringame[i] && players[i].health > 0)
2154 break;
2155
2156 if (i==MAXPLAYERS)
2157 return; // no one left alive, so do not end game
2158
2159 // scan the remaining thinkers to see
2160 // if all bosses are dead
2161 for (th = thinkercap.next ; th != &thinkercap ; th=th->next)
2162 if (th->function == P_MobjThinker)
2163 {
2164 mobj_t *mo2 = (mobj_t *) th;
2165 if (mo2 != mo && mo2->type == mo->type && mo2->health > 0)
2166 return; // other boss not dead
2167 }
2168
2169 // victory!
2170 if ( gamemode == commercial)
2171 {
2172 if (gamemap == 7)
2173 {
2174 if (mo->type == MT_FATSO)
2175 {
2176 junk.tag = 666;
2177 EV_DoFloor(&junk,lowerFloorToLowest);
2178 return;
2179 }
2180
2181 if (mo->type == MT_BABY)
2182 {
2183 junk.tag = 667;
2184 EV_DoFloor(&junk,raiseToTexture);
2185 return;
2186 }
2187 }
2188 }
2189 else
2190 {
2191 switch(gameepisode)
2192 {
2193 case 1:
2194 junk.tag = 666;
2195 EV_DoFloor(&junk, lowerFloorToLowest);
2196 return;
2197 break;
2198
2199 case 4:
2200 switch(gamemap)
2201 {
2202 case 6:
2203 junk.tag = 666;
2204 EV_DoDoor(&junk, blazeOpen);
2205 return;
2206 break;
2207
2208 case 8:
2209 junk.tag = 666;
2210 EV_DoFloor(&junk, lowerFloorToLowest);
2211 return;
2212 break;
2213 }
2214 }
2215 }
2216 G_ExitLevel();
2217}
2218
2219
2220void A_Hoof (mobj_t* mo)
2221{
2222 S_StartSound(mo, sfx_hoof);
2223 A_Chase(mo);
2224}
2225
2226void A_Metal(mobj_t *mo)
2227{
2228 S_StartSound(mo, sfx_metal);
2229 A_Chase(mo);
2230}
2231
2232void A_BabyMetal(mobj_t *mo)
2233{
2234 S_StartSound(mo, sfx_bspwlk);
2235 A_Chase(mo);
2236}
2237
2238void A_OpenShotgun2(player_t *player, pspdef_t *psp)
2239{
2240 (void)psp;
2241 S_StartSound(player->mo, sfx_dbopn);
2242}
2243
2244void A_LoadShotgun2(player_t *player, pspdef_t *psp)
2245{
2246 (void)psp;
2247 S_StartSound(player->mo, sfx_dbload);
2248}
2249
2250void A_ReFire(player_t *player, pspdef_t *psp);
2251
2252void A_CloseShotgun2(player_t *player, pspdef_t *psp)
2253{
2254 S_StartSound(player->mo, sfx_dbcls);
2255 A_ReFire(player,psp);
2256}
2257
2258// killough 2/7/98: Remove limit on icon landings:
2259mobj_t **braintargets;
2260int numbraintargets_alloc;
2261int numbraintargets;
2262
2263struct brain_s brain; // killough 3/26/98: global state of boss brain
2264
2265// killough 3/26/98: initialize icon landings at level startup,
2266// rather than at boss wakeup, to prevent savegame-related crashes
2267
2268void P_SpawnBrainTargets(void) // killough 3/26/98: renamed old function
2269{
2270 thinker_t *thinker;
2271
2272 // find all the target spots
2273 numbraintargets = 0;
2274 brain.targeton = 0;
2275 brain.easy = 0; // killough 3/26/98: always init easy to 0
2276
2277 for (thinker = thinkercap.next ;
2278 thinker != &thinkercap ;
2279 thinker = thinker->next)
2280 if (thinker->function == P_MobjThinker)
2281 {
2282 mobj_t *m = (mobj_t *) thinker;
2283
2284 if (m->type == MT_BOSSTARGET )
2285 { // killough 2/7/98: remove limit on icon landings:
2286 if (numbraintargets >= numbraintargets_alloc)
2287 braintargets = realloc(braintargets,
2288 (numbraintargets_alloc = numbraintargets_alloc ?
2289 numbraintargets_alloc*2 : 32) *sizeof *braintargets);
2290 braintargets[numbraintargets++] = m;
2291 }
2292 }
2293}
2294
2295void A_BrainAwake(mobj_t *mo)
2296{
2297 (void)mo;
2298 S_StartSound(NULL,sfx_bossit); // killough 3/26/98: only generates sound now
2299}
2300
2301void A_BrainPain(mobj_t *mo)
2302{
2303 (void)mo;
2304 S_StartSound(NULL,sfx_bospn);
2305}
2306
2307void A_BrainScream(mobj_t *mo)
2308{
2309 int x;
2310 for (x=mo->x - 196*FRACUNIT ; x< mo->x + 320*FRACUNIT ; x+= FRACUNIT*8)
2311 {
2312 int y = mo->y - 320*FRACUNIT;
2313 int z = 128 + P_Random(pr_brainscream)*2*FRACUNIT;
2314 mobj_t *th = P_SpawnMobj (x,y,z, MT_ROCKET);
2315 th->momz = P_Random(pr_brainscream)*512;
2316 P_SetMobjState(th, S_BRAINEXPLODE1);
2317 th->tics -= P_Random(pr_brainscream)&7;
2318 if (th->tics < 1)
2319 th->tics = 1;
2320 }
2321 S_StartSound(NULL,sfx_bosdth);
2322}
2323
2324void A_BrainExplode(mobj_t *mo)
2325{ // killough 5/5/98: remove dependence on order of evaluation:
2326 (void)mo;
2327 int t = P_Random(pr_brainexp);
2328 int x = mo->x + (t - P_Random(pr_brainexp))*2048;
2329 int y = mo->y;
2330 int z = 128 + P_Random(pr_brainexp)*2*FRACUNIT;
2331 mobj_t *th = P_SpawnMobj(x,y,z, MT_ROCKET);
2332 th->momz = P_Random(pr_brainexp)*512;
2333 P_SetMobjState(th, S_BRAINEXPLODE1);
2334 th->tics -= P_Random(pr_brainexp)&7;
2335 if (th->tics < 1)
2336 th->tics = 1;
2337}
2338
2339void A_BrainDie(mobj_t *mo)
2340{
2341 (void)mo;
2342 G_ExitLevel();
2343}
2344
2345void A_BrainSpit(mobj_t *mo)
2346{
2347 mobj_t *targ, *newmobj;
2348
2349 if (!numbraintargets) // killough 4/1/98: ignore if no targets
2350 return;
2351
2352 brain.easy ^= 1; // killough 3/26/98: use brain struct
2353 if (gameskill <= sk_easy && !brain.easy)
2354 return;
2355
2356 // shoot a cube at current target
2357 targ = braintargets[brain.targeton++]; // killough 3/26/98:
2358 brain.targeton %= numbraintargets; // Use brain struct for targets
2359
2360 // spawn brain missile
2361 newmobj = P_SpawnMissile(mo, targ, MT_SPAWNSHOT);
2362 P_SetTarget(&newmobj->target, targ);
2363 newmobj->reactiontime = (short)(((targ->y-mo->y)/newmobj->momy)/newmobj->state->tics);
2364
2365 // killough 7/18/98: brain friendliness is transferred
2366 newmobj->flags = (newmobj->flags & ~MF_FRIEND) | (mo->flags & MF_FRIEND);
2367
2368 // killough 8/29/98: add to appropriate thread
2369 P_UpdateThinker(&newmobj->thinker);
2370
2371 S_StartSound(NULL, sfx_bospit);
2372}
2373
2374void A_SpawnFly(mobj_t *mo);
2375
2376// travelling cube sound
2377void A_SpawnSound(mobj_t *mo)
2378{
2379 S_StartSound(mo,sfx_boscub);
2380 A_SpawnFly(mo);
2381}
2382
2383void A_SpawnFly(mobj_t *mo)
2384{
2385 mobj_t *newmobj;
2386 mobj_t *fog;
2387 mobj_t *targ;
2388 int r;
2389 mobjtype_t type;
2390
2391 if (--mo->reactiontime)
2392 return; // still flying
2393
2394 targ = mo->target;
2395
2396 // First spawn teleport fog.
2397 fog = P_SpawnMobj(targ->x, targ->y, targ->z, MT_SPAWNFIRE);
2398 S_StartSound(fog, sfx_telept);
2399
2400 // Randomly select monster to spawn.
2401 r = P_Random(pr_spawnfly);
2402
2403 // Probability distribution (kind of :), decreasing likelihood.
2404 if ( r<50 )
2405 type = MT_TROOP;
2406 else if (r<90)
2407 type = MT_SERGEANT;
2408 else if (r<120)
2409 type = MT_SHADOWS;
2410 else if (r<130)
2411 type = MT_PAIN;
2412 else if (r<160)
2413 type = MT_HEAD;
2414 else if (r<162)
2415 type = MT_VILE;
2416 else if (r<172)
2417 type = MT_UNDEAD;
2418 else if (r<192)
2419 type = MT_BABY;
2420 else if (r<222)
2421 type = MT_FATSO;
2422 else if (r<246)
2423 type = MT_KNIGHT;
2424 else
2425 type = MT_BRUISER;
2426
2427 newmobj = P_SpawnMobj(targ->x, targ->y, targ->z, type);
2428
2429 /* killough 7/18/98: brain friendliness is transferred */
2430 newmobj->flags = (newmobj->flags & ~MF_FRIEND) | (mo->flags & MF_FRIEND);
2431
2432 /* killough 8/29/98: add to appropriate thread */
2433 P_UpdateThinker(&newmobj->thinker);
2434
2435 if (P_LookForTargets(newmobj,true)) /* killough 9/4/98 */
2436 P_SetMobjState(newmobj, newmobj->info->seestate);
2437
2438 // telefrag anything in this spot
2439 P_TeleportMove(newmobj, newmobj->x, newmobj->y, true); /* killough 8/9/98 */
2440
2441 // remove self (i.e., cube).
2442 P_RemoveMobj(mo);
2443}
2444
2445void A_PlayerScream(mobj_t *mo)
2446{
2447 int sound = sfx_pldeth; // Default death sound.
2448 if (gamemode != shareware && mo->health < -50)
2449 sound = sfx_pdiehi; // IF THE PLAYER DIES LESS THAN -50% WITHOUT GIBBING
2450 S_StartSound(mo, sound);
2451}
2452
2453/* cph - MBF-added codepointer functions */
2454
2455// killough 11/98: kill an object
2456void A_Die(mobj_t *actor)
2457{
2458 P_DamageMobj(actor, NULL, NULL, actor->health);
2459}
2460
2461//
2462// A_Detonate
2463// killough 8/9/98: same as A_Explode, except that the damage is variable
2464//
2465
2466void A_Detonate(mobj_t *mo)
2467{
2468 P_RadiusAttack(mo, mo->target, mo->info->damage);
2469}
2470
2471//
2472// killough 9/98: a mushroom explosion effect, sorta :)
2473// Original idea: Linguica
2474//
2475
2476void A_Mushroom(mobj_t *actor)
2477{
2478 int i, j, n = actor->info->damage;
2479
2480 A_Explode(actor); // First make normal explosion
2481
2482 // Now launch mushroom cloud
2483 for (i = -n; i <= n; i += 8)
2484 for (j = -n; j <= n; j += 8)
2485 {
2486 mobj_t target = *actor, *mo;
2487 target.x += i << FRACBITS; // Aim in many directions from source
2488 target.y += j << FRACBITS;
2489 target.z += P_AproxDistance(i,j) << (FRACBITS+2); // Aim up fairly high
2490 mo = P_SpawnMissile(actor, &target, MT_FATSHOT); // Launch fireball
2491 mo->momx >>= 1;
2492 mo->momy >>= 1; // Slow it down a bit
2493 mo->momz >>= 1;
2494 mo->flags &= ~MF_NOGRAVITY; // Make debris fall under gravity
2495 }
2496}
2497
2498//
2499// killough 11/98
2500//
2501// The following were inspired by Len Pitre
2502//
2503// A small set of highly-sought-after code pointers
2504//
2505
2506void A_Spawn(mobj_t *mo)
2507{
2508 if (mo->state->misc1)
2509 {
2510 /* mobj_t *newmobj = */
2511 P_SpawnMobj(mo->x, mo->y, (mo->state->misc2 << FRACBITS) + mo->z,
2512 mo->state->misc1 - 1);
2513 /* CPhipps - no friendlyness (yet)
2514 newmobj->flags = (newmobj->flags & ~MF_FRIEND) | (mo->flags & MF_FRIEND);
2515 */
2516 }
2517}
2518
2519void A_Turn(mobj_t *mo)
2520{
2521 mo->angle += (unsigned int)(((uint_64_t) mo->state->misc1 << 32) / 360);
2522}
2523
2524void A_Face(mobj_t *mo)
2525{
2526 mo->angle = (unsigned int)(((uint_64_t) mo->state->misc1 << 32) / 360);
2527}
2528
2529void A_Scratch(mobj_t *mo)
2530{
2531 mo->target && (A_FaceTarget(mo), P_CheckMeleeRange(mo)) ?
2532 mo->state->misc2 ? S_StartSound(mo, mo->state->misc2) : (void) 0,
2533 P_DamageMobj(mo->target, mo, mo, mo->state->misc1) : (void) 0;
2534}
2535
2536void A_PlaySound(mobj_t *mo)
2537{
2538 S_StartSound(mo->state->misc2 ? NULL : mo, mo->state->misc1);
2539}
2540
2541void A_RandomJump(mobj_t *mo)
2542{
2543 if (P_Random(pr_randomjump) < mo->state->misc2)
2544 P_SetMobjState(mo, mo->state->misc1);
2545}
2546
2547//
2548// This allows linedef effects to be activated inside deh frames.
2549//
2550
2551void A_LineEffect(mobj_t *mo)
2552{
2553 static line_t junk;
2554 player_t player;
2555 player_t *oldplayer;
2556 junk = *lines;
2557 oldplayer = mo->player;
2558 mo->player = &player;
2559 player.health = 100;
2560 junk.special = (short)mo->state->misc1;
2561 if (!junk.special)
2562 return;
2563 junk.tag = (short)mo->state->misc2;
2564 if (!P_UseSpecialLine(mo, &junk, 0))
2565 P_CrossSpecialLine(&junk, 0, mo);
2566 mo->state->misc1 = junk.special;
2567 mo->player = oldplayer;
2568}
2569
2570/***** Start of new functions for Andy Baker's stealth monsters ******/
2571
2572void P_BecomeVisible(mobj_t* actor)
2573{
2574 actor->invisible = false;
2575 actor->flags &= ~MF_TRANSLUCBITS;
2576};
2577
2578void P_IncreaseVisibility(mobj_t* actor)
2579{
2580 if (actor->invisible) {
2581 actor->invisible = false;
2582 actor->flags |= MF_TRANSLUC25;
2583 } else switch (actor->flags & MF_TRANSLUCBITS) {
2584 case MF_TRANSLUC25:
2585 actor->flags &= ~MF_TRANSLUCBITS;
2586 actor->flags |= MF_TRANSLUC50;
2587 break;
2588 case MF_TRANSLUC50:
2589 actor->flags &= ~MF_TRANSLUCBITS;
2590 actor->flags |= MF_TRANSLUC25;
2591 actor->flags |= MF_TRANSLUC50;
2592 break;
2593 case MF_TRANSLUC75:
2594 actor->flags &= ~MF_TRANSLUCBITS;
2595 break;
2596 }
2597}
2598
2599void P_DecreaseVisibility(mobj_t* actor)
2600{
2601 if (actor->invisible)
2602 return; // already invisible
2603
2604 switch (actor->flags & MF_TRANSLUCBITS) {
2605 case 0:
2606 actor->flags &= ~MF_TRANSLUCBITS;
2607 actor->flags |= MF_TRANSLUC75;
2608 break;
2609 case MF_TRANSLUC75:
2610 actor->flags &= ~MF_TRANSLUCBITS;
2611 actor->flags |= MF_TRANSLUC50;
2612 break;
2613 case MF_TRANSLUC50:
2614 actor->flags &= ~MF_TRANSLUCBITS;
2615 actor->flags |= MF_TRANSLUC25;
2616 break;
2617 case MF_TRANSLUC25:
2618 actor->flags &= ~MF_TRANSLUCBITS;
2619 actor->invisible = true;
2620 }
2621}
2622/***** End of new functions for Andy Baker's stealth monsters ******/
2623