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