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
52 static mobj_t *current_actor;
53
54 typedef enum
55 {
56 DI_EAST,
57 DI_NORTHEAST,
58 DI_NORTH,
59 DI_NORTHWEST,
60 DI_WEST,
61 DI_SOUTHWEST,
62 DI_SOUTH,
63 DI_SOUTHEAST,
64 DI_NODIR,
65 NUMDIRS
66 } dirtype_t;
67
68 static void P_NewChaseDir(mobj_t *actor);
69 void P_ZBumpCheck(mobj_t *); // phares
70
71 extern void retro_set_rumble_damage(int damage, float duration);
72
73 //
74 // ENEMY THINKING
75 // Enemies are allways spawned
76 // with targetplayer = -1, threshold = 0
77 // Most monsters are spawned unaware of all players,
78 // but some can be made preaware
79 //
80
81 //
82 // Called by P_NoiseAlert.
83 // Recursively traverse adjacent sectors,
84 // sound blocking lines cut off traversal.
85 //
86 // killough 5/5/98: reformatted, cleaned up
87
P_RecursiveSound(sector_t * sec,int soundblocks,mobj_t * soundtarget)88 static void P_RecursiveSound(sector_t *sec, int soundblocks,
89 mobj_t *soundtarget)
90 {
91 int i;
92
93 // wake up all monsters in this sector
94 if (sec->validcount == validcount && sec->soundtraversed <= soundblocks+1)
95 return; // already flooded
96
97 sec->validcount = validcount;
98 sec->soundtraversed = soundblocks+1;
99 P_SetTarget(&sec->soundtarget, soundtarget);
100
101 for (i=0; i<sec->linecount; i++)
102 {
103 sector_t *other;
104 line_t *check = sec->lines[i];
105
106 if (!(check->flags & ML_TWOSIDED))
107 continue;
108
109 P_LineOpening(check);
110
111 if (openrange <= 0)
112 continue; // closed door
113
114 other=sides[check->sidenum[sides[check->sidenum[0]].sector==sec]].sector;
115
116 if (!(check->flags & ML_SOUNDBLOCK))
117 P_RecursiveSound(other, soundblocks, soundtarget);
118 else
119 if (!soundblocks)
120 P_RecursiveSound(other, 1, soundtarget);
121 }
122 }
123
124 //
125 // P_NoiseAlert
126 // If a monster yells at a player,
127 // it will alert other monsters to the player.
128 //
P_NoiseAlert(mobj_t * target,mobj_t * emitter)129 void P_NoiseAlert(mobj_t *target, mobj_t *emitter)
130 {
131 validcount++;
132 P_RecursiveSound(emitter->subsector->sector, 0, target);
133 }
134
135 //
136 // P_CheckMeleeRange
137 //
138
P_CheckMeleeRange(mobj_t * actor)139 static dbool P_CheckMeleeRange(mobj_t *actor)
140 {
141 mobj_t *pl = actor->target;
142
143 return // killough 7/18/98: friendly monsters don't attack other friends
144 pl && !(actor->flags & pl->flags & MF_FRIEND) &&
145 (P_AproxDistance(pl->x-actor->x, pl->y-actor->y) <
146 MELEERANGE - 20*FRACUNIT + pl->info->radius) &&
147 P_CheckSight(actor, actor->target);
148 }
149
150 //
151 // P_HitFriend()
152 //
153 // killough 12/98
154 // This function tries to prevent shooting at friends
155
P_HitFriend(mobj_t * actor)156 static dbool P_HitFriend(mobj_t *actor)
157 {
158 return actor->flags & MF_FRIEND && actor->target &&
159 (P_AimLineAttack(actor,
160 R_PointToAngle2(actor->x, actor->y,
161 actor->target->x, actor->target->y),
162 P_AproxDistance(actor->x-actor->target->x,
163 actor->y-actor->target->y), 0),
164 linetarget) && linetarget != actor->target &&
165 !((linetarget->flags ^ actor->flags) & MF_FRIEND);
166 }
167
168 //
169 // P_CheckMissileRange
170 //
P_CheckMissileRange(mobj_t * actor)171 static dbool P_CheckMissileRange(mobj_t *actor)
172 {
173 fixed_t dist;
174
175 if (!P_CheckSight(actor, actor->target))
176 return FALSE;
177
178 if (actor->flags & MF_JUSTHIT)
179 { // the target just hit the enemy, so fight back!
180 actor->flags &= ~MF_JUSTHIT;
181
182 /* killough 7/18/98: no friendly fire at corpses
183 * killough 11/98: prevent too much infighting among friends
184 * cph - yikes, talk about fitting everything on one line... */
185
186 return
187 !(actor->flags & MF_FRIEND) ||
188 (actor->target->health > 0 &&
189 (!(actor->target->flags & MF_FRIEND) ||
190 (actor->target->player ?
191 monster_infighting || P_Random(pr_defect) >128 :
192 !(actor->target->flags & MF_JUSTHIT) && P_Random(pr_defect) >128)));
193 }
194
195 /* killough 7/18/98: friendly monsters don't attack other friendly
196 * monsters or players (except when attacked, and then only once)
197 */
198 if (actor->flags & actor->target->flags & MF_FRIEND)
199 return FALSE;
200
201 if (actor->reactiontime)
202 return FALSE; // do not attack yet
203
204 // OPTIMIZE: get this from a global checksight
205 dist = P_AproxDistance ( actor->x-actor->target->x,
206 actor->y-actor->target->y) - 64*FRACUNIT;
207
208 if (!actor->info->meleestate)
209 dist -= 128*FRACUNIT; // no melee attack, so fire more
210
211 dist >>= FRACBITS;
212
213 if (actor->info->maxattackrange > 0 && dist > actor->info->maxattackrange)
214 return FALSE; // too far away
215
216 if (actor->info->meleestate != S_NULL && dist < actor->info->meleethreshold)
217 return false; // close enough for melee attack, don't fire
218
219 // higher attack probability like Cyberdemon, Spiderboss, Revenant and Lost Soul
220 if (actor->flags & MF_MISSILEMORE)
221 dist >>= 1;
222
223 // Some mobs (eg. Cyberdemon) have a minimum attack chance
224 if (dist > actor->info->minmissilechance)
225 dist = actor->info->minmissilechance;
226
227 if (P_Random(pr_missrange) < dist)
228 return FALSE;
229
230 if (P_HitFriend(actor))
231 return FALSE;
232
233 return TRUE;
234 }
235
236 /*
237 * P_IsOnLift
238 *
239 * killough 9/9/98:
240 *
241 * Returns TRUE if the object is on a lift. Used for AI,
242 * since it may indicate the need for crowded conditions,
243 * or that a monster should stay on the lift for a while
244 * while it goes up or down.
245 */
246
P_IsOnLift(const mobj_t * actor)247 static dbool P_IsOnLift(const mobj_t *actor)
248 {
249 const sector_t *sec = actor->subsector->sector;
250 line_t line;
251 int l;
252
253 memset(&line, 0, sizeof(line));
254
255 // Short-circuit: it's on a lift which is active.
256 if (sec->floordata && ((thinker_t *) sec->floordata)->function==T_PlatRaise)
257 return TRUE;
258
259 // Check to see if it's in a sector which can be activated as a lift.
260 if ((line.tag = sec->tag))
261 for (l = -1; (l = P_FindLineFromLineTag(&line, l)) >= 0;)
262 switch (lines[l].special)
263 {
264 case 10: case 14: case 15: case 20: case 21: case 22:
265 case 47: case 53: case 62: case 66: case 67: case 68:
266 case 87: case 88: case 95: case 120: case 121: case 122:
267 case 123: case 143: case 162: case 163: case 181: case 182:
268 case 144: case 148: case 149: case 211: case 227: case 228:
269 case 231: case 232: case 235: case 236:
270 return TRUE;
271 }
272
273 return FALSE;
274 }
275
276 /*
277 * P_IsUnderDamage
278 *
279 * killough 9/9/98:
280 *
281 * Returns nonzero if the object is under damage based on
282 * their current position. Returns 1 if the damage is moderate,
283 * -1 if it is serious. Used for AI.
284 */
285
P_IsUnderDamage(mobj_t * actor)286 static int P_IsUnderDamage(mobj_t *actor)
287 {
288 const struct msecnode_s *seclist;
289 const ceiling_t *cl; // Crushing ceiling
290 int dir = 0;
291 for (seclist=actor->touching_sectorlist; seclist; seclist=seclist->m_tnext)
292 if ((cl = seclist->m_sector->ceilingdata) &&
293 cl->thinker.function == T_MoveCeiling)
294 dir |= cl->direction;
295 return dir;
296 }
297
298 //
299 // P_Move
300 // Move in the current direction,
301 // returns FALSE if the move is blocked.
302 //
303
304 static fixed_t xspeed[8] = {FRACUNIT,47000,0,-47000,-FRACUNIT,-47000,0,47000};
305 static fixed_t yspeed[8] = {0,47000,FRACUNIT,47000,0,-47000,-FRACUNIT,-47000};
306
307 // 1/11/98 killough: Limit removed on special lines crossed
308 extern line_t **spechit; // New code -- killough
309 extern int numspechit;
310
P_Move(mobj_t * actor,dbool dropoff)311 static dbool P_Move(mobj_t *actor, dbool dropoff) /* killough 9/12/98 */
312 {
313 fixed_t tryx, tryy, deltax, deltay, origx, origy;
314 dbool try_ok;
315 int movefactor = ORIG_FRICTION_FACTOR; // killough 10/98
316 int friction = ORIG_FRICTION;
317 int speed;
318
319 if (actor->movedir == DI_NODIR)
320 return FALSE;
321
322 // killough 10/98: make monsters get affected by ice and sludge too:
323
324 if (monster_friction)
325 movefactor = P_GetMoveFactor(actor, &friction);
326
327 speed = actor->info->speed;
328
329 if (friction < ORIG_FRICTION && // sludge
330 !(speed = ((ORIG_FRICTION_FACTOR - (ORIG_FRICTION_FACTOR-movefactor)/2)
331 * speed) / ORIG_FRICTION_FACTOR))
332 speed = 1; // always give the monster a little bit of speed
333
334 tryx = (origx = actor->x) + (deltax = speed * xspeed[actor->movedir]);
335 tryy = (origy = actor->y) + (deltay = speed * yspeed[actor->movedir]);
336
337 try_ok = P_TryMove(actor, tryx, tryy, dropoff);
338
339 // killough 10/98:
340 // Let normal momentum carry them, instead of steptoeing them across ice.
341
342 if (try_ok && friction > ORIG_FRICTION)
343 {
344 actor->x = origx;
345 actor->y = origy;
346 movefactor *= FRACUNIT / ORIG_FRICTION_FACTOR / 4;
347 actor->momx += FixedMul(deltax, movefactor);
348 actor->momy += FixedMul(deltay, movefactor);
349 }
350
351 if (!try_ok)
352 { // open any specials
353 int good;
354
355 if (actor->flags & MF_FLOAT && floatok)
356 {
357 if (actor->z < tmfloorz) // must adjust height
358 actor->z += FLOATSPEED;
359 else
360 actor->z -= FLOATSPEED;
361
362 actor->flags |= MF_INFLOAT;
363
364 return TRUE;
365 }
366
367 if (!numspechit)
368 return FALSE;
369
370 actor->movedir = DI_NODIR;
371
372 /* if the special is not a door that can be opened, return FALSE
373 *
374 * killough 8/9/98: this is what caused monsters to get stuck in
375 * doortracks, because it thought that the monster freed itself
376 * by opening a door, even if it was moving towards the doortrack,
377 * and not the door itself.
378 *
379 * killough 9/9/98: If a line blocking the monster is activated,
380 * return TRUE 90% of the time. If a line blocking the monster is
381 * not activated, but some other line is, return FALSE 90% of the
382 * time. A bit of randomness is needed to ensure it's free from
383 * lockups, but for most cases, it returns the correct result.
384 *
385 * Do NOT simply return FALSE 1/4th of the time (causes monsters to
386 * back out when they shouldn't, and creates secondary stickiness).
387 */
388
389 for (good = FALSE; numspechit--; )
390 if (P_UseSpecialLine(actor, spechit[numspechit], 0))
391 good |= spechit[numspechit] == blockline ? 1 : 2;
392
393 /* cph - compatibility maze here
394 * Boom v2.01 and orig. Doom return "good"
395 * Boom v2.02 and LxDoom return good && (P_Random(pr_trywalk)&3)
396 * MBF plays even more games
397 */
398 if (!good || comp[comp_doorstuck]) return good;
399 if (!mbf_features)
400 return (P_Random(pr_trywalk)&3); /* jff 8/13/98 */
401 else /* finally, MBF code */
402 return ((P_Random(pr_opendoor) >= 230) ^ (good & 1));
403 }
404 else
405 actor->flags &= ~MF_INFLOAT;
406
407 /* killough 11/98: fall more slowly, under gravity, if felldown==TRUE */
408 if (!(actor->flags & MF_FLOAT) &&
409 (!felldown || !mbf_features))
410 actor->z = actor->floorz;
411
412 return TRUE;
413 }
414
415 /*
416 * P_SmartMove
417 *
418 * killough 9/12/98: Same as P_Move, except smarter
419 */
420
P_SmartMove(mobj_t * actor)421 static dbool P_SmartMove(mobj_t *actor)
422 {
423 mobj_t *target = actor->target;
424 int on_lift, dropoff = FALSE, under_damage;
425
426 /* killough 9/12/98: Stay on a lift if target is on one */
427 on_lift = !comp[comp_staylift]
428 && target && target->health > 0
429 && target->subsector->sector->tag==actor->subsector->sector->tag &&
430 P_IsOnLift(actor);
431
432 under_damage = monster_avoid_hazards && P_IsUnderDamage(actor);
433
434 // killough 10/98: allow dogs to drop off of taller ledges sometimes.
435 // dropoff==1 means always allow it, dropoff==2 means only up to 128 high,
436 // and only if the target is immediately on the other side of the line.
437
438 if (!P_Move(actor, dropoff))
439 return FALSE;
440
441 // killough 9/9/98: avoid crushing ceilings or other damaging areas
442 if (
443 (on_lift && P_Random(pr_stayonlift) < 230 && // Stay on lift
444 !P_IsOnLift(actor))
445 ||
446 (monster_avoid_hazards && !under_damage && // Get away from damage
447 (under_damage = P_IsUnderDamage(actor)) &&
448 (under_damage < 0 || P_Random(pr_avoidcrush) < 200))
449 )
450 actor->movedir = DI_NODIR; // avoid the area (most of the time anyway)
451
452 return TRUE;
453 }
454
455 //
456 // TryWalk
457 // Attempts to move actor on
458 // in its current (ob->moveangle) direction.
459 // If blocked by either a wall or an actor
460 // returns FALSE
461 // If move is either clear or blocked only by a door,
462 // returns TRUE and sets...
463 // If a door is in the way,
464 // an OpenDoor call is made to start it opening.
465 //
466
P_TryWalk(mobj_t * actor)467 static dbool P_TryWalk(mobj_t *actor)
468 {
469 if (!P_SmartMove(actor))
470 return FALSE;
471 actor->movecount = P_Random(pr_trywalk)&15;
472 return TRUE;
473 }
474
475 //
476 // P_DoNewChaseDir
477 //
478 // killough 9/8/98:
479 //
480 // Most of P_NewChaseDir(), except for what
481 // determines the new direction to take
482 //
483
P_DoNewChaseDir(mobj_t * actor,fixed_t deltax,fixed_t deltay)484 static void P_DoNewChaseDir(mobj_t *actor, fixed_t deltax, fixed_t deltay)
485 {
486 dirtype_t xdir, ydir, tdir;
487 dirtype_t olddir = actor->movedir;
488 dirtype_t turnaround = olddir;
489
490 if (turnaround != DI_NODIR) // find reverse direction
491 turnaround ^= 4;
492
493 xdir =
494 deltax > 10*FRACUNIT ? DI_EAST :
495 deltax < -10*FRACUNIT ? DI_WEST : DI_NODIR;
496
497 ydir =
498 deltay < -10*FRACUNIT ? DI_SOUTH :
499 deltay > 10*FRACUNIT ? DI_NORTH : DI_NODIR;
500
501 // try direct route
502 if (xdir != DI_NODIR && ydir != DI_NODIR && turnaround !=
503 (actor->movedir = deltay < 0 ? deltax > 0 ? DI_SOUTHEAST : DI_SOUTHWEST :
504 deltax > 0 ? DI_NORTHEAST : DI_NORTHWEST) && P_TryWalk(actor))
505 return;
506
507 // try other directions
508 if (P_Random(pr_newchase) > 200 || D_abs(deltay)>D_abs(deltax))
509 tdir = xdir, xdir = ydir, ydir = tdir;
510
511 if ((xdir == turnaround ? xdir = DI_NODIR : xdir) != DI_NODIR &&
512 (actor->movedir = xdir, P_TryWalk(actor)))
513 return; // either moved forward or attacked
514
515 if ((ydir == turnaround ? ydir = DI_NODIR : ydir) != DI_NODIR &&
516 (actor->movedir = ydir, P_TryWalk(actor)))
517 return;
518
519 // there is no direct path to the player, so pick another direction.
520 if (olddir != DI_NODIR && (actor->movedir = olddir, P_TryWalk(actor)))
521 return;
522
523 // randomly determine direction of search
524 if (P_Random(pr_newchasedir) & 1)
525 {
526 for (tdir = DI_EAST; tdir <= DI_SOUTHEAST; tdir++)
527 if (tdir != turnaround && (actor->movedir = tdir, P_TryWalk(actor)))
528 return;
529 }
530 else
531 for (tdir = DI_SOUTHEAST; tdir != (dirtype_t)(DI_EAST-1); tdir--)
532 if (tdir != turnaround && (actor->movedir = tdir, P_TryWalk(actor)))
533 return;
534
535 if ((actor->movedir = turnaround) != DI_NODIR && !P_TryWalk(actor))
536 actor->movedir = DI_NODIR;
537 }
538
539 //
540 // killough 11/98:
541 //
542 // Monsters try to move away from tall dropoffs.
543 //
544 // In Doom, they were never allowed to hang over dropoffs,
545 // and would remain stuck if involuntarily forced over one.
546 // This logic, combined with p_map.c (P_TryMove), allows
547 // monsters to free themselves without making them tend to
548 // hang over dropoffs.
549
550 static fixed_t dropoff_deltax, dropoff_deltay, floorz;
551
PIT_AvoidDropoff(line_t * line)552 static dbool PIT_AvoidDropoff(line_t *line)
553 {
554 if (line->backsector && // Ignore one-sided linedefs
555 tmbbox[BOXRIGHT] > line->bbox[BOXLEFT] &&
556 tmbbox[BOXLEFT] < line->bbox[BOXRIGHT] &&
557 tmbbox[BOXTOP] > line->bbox[BOXBOTTOM] && // Linedef must be contacted
558 tmbbox[BOXBOTTOM] < line->bbox[BOXTOP] &&
559 P_BoxOnLineSide(tmbbox, line) == -1)
560 {
561 fixed_t front = line->frontsector->floorheight;
562 fixed_t back = line->backsector->floorheight;
563 angle_t angle;
564
565 // The monster must contact one of the two floors,
566 // and the other must be a tall dropoff (more than 24).
567
568 if (back == floorz && front < floorz - STEPSIZE)
569 angle = R_PointToAngle2(0,0,line->dx,line->dy); // front side dropoff
570 else
571 if (front == floorz && back < floorz - STEPSIZE)
572 angle = R_PointToAngle2(line->dx,line->dy,0,0); // back side dropoff
573 else
574 return TRUE;
575
576 // Move away from dropoff at a standard speed.
577 // Multiple contacted linedefs are cumulative (e.g. hanging over corner)
578 dropoff_deltax -= finesine[angle >> ANGLETOFINESHIFT]*32;
579 dropoff_deltay += finecosine[angle >> ANGLETOFINESHIFT]*32;
580 }
581 return TRUE;
582 }
583
584 //
585 // Driver for above
586 //
587
P_AvoidDropoff(mobj_t * actor)588 static fixed_t P_AvoidDropoff(mobj_t *actor)
589 {
590 int yh=((tmbbox[BOXTOP] = actor->y+actor->radius)-bmaporgy)>>MAPBLOCKSHIFT;
591 int yl=((tmbbox[BOXBOTTOM]= actor->y-actor->radius)-bmaporgy)>>MAPBLOCKSHIFT;
592 int xh=((tmbbox[BOXRIGHT] = actor->x+actor->radius)-bmaporgx)>>MAPBLOCKSHIFT;
593 int xl=((tmbbox[BOXLEFT] = actor->x-actor->radius)-bmaporgx)>>MAPBLOCKSHIFT;
594 int bx, by;
595
596 floorz = actor->z; // remember floor height
597
598 dropoff_deltax = dropoff_deltay = 0;
599
600 // check lines
601
602 validcount++;
603 for (bx=xl ; bx<=xh ; bx++)
604 for (by=yl ; by<=yh ; by++)
605 P_BlockLinesIterator(bx, by, PIT_AvoidDropoff); // all contacted lines
606
607 return dropoff_deltax | dropoff_deltay; // Non-zero if movement prescribed
608 }
609
610 //
611 // P_NewChaseDir
612 //
613 // killough 9/8/98: Split into two functions
614 //
615
P_NewChaseDir(mobj_t * actor)616 static void P_NewChaseDir(mobj_t *actor)
617 {
618 mobj_t *target = actor->target;
619 fixed_t deltax = target->x - actor->x;
620 fixed_t deltay = target->y - actor->y;
621
622 // killough 8/8/98: sometimes move away from target, keeping distance
623 //
624 // 1) Stay a certain distance away from a friend, to avoid being in their way
625 // 2) Take advantage over an enemy without missiles, by keeping distance
626
627 actor->strafecount = 0;
628
629 if (mbf_features) {
630 if (actor->floorz - actor->dropoffz > STEPSIZE &&
631 actor->z <= actor->floorz &&
632 !(actor->flags & (MF_DROPOFF|MF_FLOAT)) &&
633 !comp[comp_dropoff] &&
634 P_AvoidDropoff(actor)) /* Move away from dropoff */
635 {
636 P_DoNewChaseDir(actor, dropoff_deltax, dropoff_deltay);
637
638 // If moving away from dropoff, set movecount to 1 so that
639 // small steps are taken to get monster away from dropoff.
640
641 actor->movecount = 1;
642 return;
643 }
644 else
645 {
646 fixed_t dist = P_AproxDistance(deltax, deltay);
647
648 // Move away from friends when too close, except
649 // in certain situations (e.g. a crowded lift)
650
651 if (actor->flags & target->flags & MF_FRIEND &&
652 distfriend << FRACBITS > dist &&
653 !P_IsOnLift(target) && !P_IsUnderDamage(actor))
654 {
655 deltax = -deltax, deltay = -deltay;
656 } else
657 if (target->health > 0 && (actor->flags ^ target->flags) & MF_FRIEND)
658 { // Live enemy target
659 if (monster_backing &&
660 actor->info->missilestate && actor->type != MT_SKULL &&
661 ((!target->info->missilestate && dist < MELEERANGE*2) ||
662 (target->player && dist < MELEERANGE*3 &&
663 (target->player->readyweapon == WP_FIST ||
664 target->player->readyweapon == WP_CHAINSAW))))
665 { // Back away from melee attacker
666 actor->strafecount = P_Random(pr_enemystrafe) & 15;
667 deltax = -deltax, deltay = -deltay;
668 }
669 }
670 }
671 }
672
673 P_DoNewChaseDir(actor, deltax, deltay);
674
675 // If strafing, set movecount to strafecount so that old Doom
676 // logic still works the same, except in the strafing part
677
678 if (actor->strafecount)
679 actor->movecount = actor->strafecount;
680 }
681
682 //
683 // P_IsVisible
684 //
685 // killough 9/9/98: whether a target is visible to a monster
686 //
687
P_IsVisible(mobj_t * actor,mobj_t * mo,dbool allaround)688 static dbool P_IsVisible(mobj_t *actor, mobj_t *mo, dbool allaround)
689 {
690 if (!allaround)
691 {
692 angle_t an = R_PointToAngle2(actor->x, actor->y,
693 mo->x, mo->y) - actor->angle;
694 if (an > ANG90 && an < ANG270 &&
695 P_AproxDistance(mo->x-actor->x, mo->y-actor->y) > MELEERANGE)
696 return FALSE;
697 }
698 return P_CheckSight(actor, mo);
699 }
700
701 //
702 // PIT_FindTarget
703 //
704 // killough 9/5/98
705 //
706 // Finds monster targets for other monsters
707 //
708
709 static int current_allaround;
710
PIT_FindTarget(mobj_t * mo)711 static dbool PIT_FindTarget(mobj_t *mo)
712 {
713 mobj_t *actor = current_actor;
714
715 if (!((mo->flags ^ actor->flags) & MF_FRIEND && // Invalid target
716 mo->health > 0 && (mo->flags & MF_ISMONSTER)))
717 return TRUE;
718
719 // If the monster is already engaged in a one-on-one attack
720 // with a healthy friend, don't attack around 60% the time
721 {
722 const mobj_t *targ = mo->target;
723 if (targ && targ->target == mo &&
724 P_Random(pr_skiptarget) > 100 &&
725 (targ->flags ^ mo->flags) & MF_FRIEND &&
726 targ->health*2 >= targ->info->spawnhealth)
727 return TRUE;
728 }
729
730 if (!P_IsVisible(actor, mo, current_allaround))
731 return TRUE;
732
733 P_SetTarget(&actor->lastenemy, actor->target); // Remember previous target
734 P_SetTarget(&actor->target, mo); // Found target
735
736 // Move the selected monster to the end of its associated
737 // list, so that it gets searched last next time.
738
739 {
740 thinker_t *cap = &thinkerclasscap[mo->flags & MF_FRIEND ?
741 th_friends : th_enemies];
742 (mo->thinker.cprev->cnext = mo->thinker.cnext)->cprev = mo->thinker.cprev;
743 (mo->thinker.cprev = cap->cprev)->cnext = &mo->thinker;
744 (mo->thinker.cnext = cap)->cprev = &mo->thinker;
745 }
746
747 return FALSE;
748 }
749
750 //
751 // P_LookForPlayers
752 // If allaround is FALSE, only look 180 degrees in front.
753 // Returns TRUE if a player is targeted.
754 //
755
P_LookForPlayers(mobj_t * actor,dbool allaround)756 static dbool P_LookForPlayers(mobj_t *actor, dbool allaround)
757 {
758 player_t *player;
759 int stop, stopc, c;
760
761 if (actor->flags & MF_FRIEND)
762 { // killough 9/9/98: friendly monsters go about players differently
763 int anyone;
764
765 #if 0
766 if (!allaround) // If you want friendly monsters not to awaken unprovoked
767 return FALSE;
768 #endif
769
770 // Go back to a player, no matter whether it's visible or not
771 for (anyone=0; anyone<=1; anyone++)
772 for (c=0; c<MAXPLAYERS; c++)
773 if (playeringame[c] && players[c].playerstate==PST_LIVE &&
774 (anyone || P_IsVisible(actor, players[c].mo, allaround)))
775 {
776 P_SetTarget(&actor->target, players[c].mo);
777
778 // killough 12/98:
779 // get out of refiring loop, to avoid hitting player accidentally
780
781 if (actor->info->missilestate)
782 {
783 P_SetMobjState(actor, actor->info->seestate);
784 actor->flags &= ~MF_JUSTHIT;
785 }
786
787 return TRUE;
788 }
789
790 return FALSE;
791 }
792
793 // Change mask of 3 to (MAXPLAYERS-1) -- killough 2/15/98:
794 stop = (actor->lastlook-1)&(MAXPLAYERS-1);
795
796 c = 0;
797
798 stopc = !mbf_features &&
799 !demo_compatibility && monsters_remember ?
800 MAXPLAYERS : 2; // killough 9/9/98
801
802 for (;; actor->lastlook = (actor->lastlook+1)&(MAXPLAYERS-1))
803 {
804 if (!playeringame[actor->lastlook])
805 continue;
806
807 // killough 2/15/98, 9/9/98:
808 if (c++ == stopc || actor->lastlook == stop) // done looking
809 {
810 // e6y
811 // Fixed Boom incompatibilities. The following code was missed.
812 // There are no more desyncs on Donce's demos on horror.wad
813
814 // Use last known enemy if no players sighted -- killough 2/15/98:
815 if (!mbf_features && !demo_compatibility && monsters_remember)
816 {
817 if (actor->lastenemy && actor->lastenemy->health > 0)
818 {
819 actor->target = actor->lastenemy;
820 actor->lastenemy = NULL;
821 return TRUE;
822 }
823 }
824
825 return FALSE;
826 }
827
828 player = &players[actor->lastlook];
829
830 if (player->health <= 0)
831 continue; // dead
832
833 if (!P_IsVisible(actor, player->mo, allaround))
834 continue;
835
836 P_SetTarget(&actor->target, player->mo);
837
838 /* killough 9/9/98: give monsters a threshold towards getting players
839 * (we don't want it to be too easy for a player with dogs :)
840 */
841 if (!comp[comp_pursuit])
842 actor->threshold = 60;
843
844 return TRUE;
845 }
846 }
847
848 //
849 // Friendly monsters, by Lee Killough 7/18/98
850 //
851 // Friendly monsters go after other monsters first, but
852 // also return to owner if they cannot find any targets.
853 // A marine's best friend :) killough 7/18/98, 9/98
854 //
855
P_LookForMonsters(mobj_t * actor,dbool allaround)856 static dbool P_LookForMonsters(mobj_t *actor, dbool allaround)
857 {
858 thinker_t *cap, *th;
859
860 if (demo_compatibility)
861 return FALSE;
862
863 if (actor->lastenemy && actor->lastenemy->health > 0 && monsters_remember &&
864 !(actor->lastenemy->flags & actor->flags & MF_FRIEND)) // not friends
865 {
866 P_SetTarget(&actor->target, actor->lastenemy);
867 P_SetTarget(&actor->lastenemy, NULL);
868 return TRUE;
869 }
870
871 /* Old demos do not support monster-seeking bots */
872 if (!mbf_features)
873 return FALSE;
874
875 // Search the threaded list corresponding to this object's potential targets
876 cap = &thinkerclasscap[actor->flags & MF_FRIEND ? th_enemies : th_friends];
877
878 // Search for new enemy
879
880 if (cap->cnext != cap) // Empty list? bail out early
881 {
882 int x = (actor->x - bmaporgx)>>MAPBLOCKSHIFT;
883 int y = (actor->y - bmaporgy)>>MAPBLOCKSHIFT;
884 int d;
885
886 current_actor = actor;
887 current_allaround = allaround;
888
889 // Search first in the immediate vicinity.
890
891 if (!P_BlockThingsIterator(x, y, PIT_FindTarget))
892 return TRUE;
893
894 for (d=1; d<5; d++)
895 {
896 int i = 1 - d;
897 do
898 if (!P_BlockThingsIterator(x+i, y-d, PIT_FindTarget) ||
899 !P_BlockThingsIterator(x+i, y+d, PIT_FindTarget))
900 return TRUE;
901 while (++i < d);
902 do
903 if (!P_BlockThingsIterator(x-d, y+i, PIT_FindTarget) ||
904 !P_BlockThingsIterator(x+d, y+i, PIT_FindTarget))
905 return TRUE;
906 while (--i + d >= 0);
907 }
908
909 { // Random number of monsters, to prevent patterns from forming
910 int n = (P_Random(pr_friends) & 31) + 15;
911
912 for (th = cap->cnext; th != cap; th = th->cnext)
913 if (--n < 0)
914 {
915 // Only a subset of the monsters were searched. Move all of
916 // the ones which were searched so far, to the end of the list.
917
918 (cap->cnext->cprev = cap->cprev)->cnext = cap->cnext;
919 (cap->cprev = th->cprev)->cnext = cap;
920 (th->cprev = cap)->cnext = th;
921 break;
922 }
923 else
924 if (!PIT_FindTarget((mobj_t *) th)) // If target sighted
925 return TRUE;
926 }
927 }
928
929 return FALSE; // No monster found
930 }
931
932 //
933 // P_LookForTargets
934 //
935 // killough 9/5/98: look for targets to go after, depending on kind of monster
936 //
937
P_LookForTargets(mobj_t * actor,int allaround)938 static dbool P_LookForTargets(mobj_t *actor, int allaround)
939 {
940 return actor->flags & MF_FRIEND ?
941 P_LookForMonsters(actor, allaround) || P_LookForPlayers (actor, allaround):
942 P_LookForPlayers (actor, allaround) || P_LookForMonsters(actor, allaround);
943 }
944
945 //
946 // P_HelpFriend
947 //
948 // killough 9/8/98: Help friends in danger of dying
949 //
950
P_HelpFriend(mobj_t * actor)951 static dbool P_HelpFriend(mobj_t *actor)
952 {
953 thinker_t *cap, *th;
954
955 // If less than 33% health, self-preservation rules
956 if (actor->health*3 < actor->info->spawnhealth)
957 return FALSE;
958
959 current_actor = actor;
960 current_allaround = TRUE;
961
962 // Possibly help a friend under 50% health
963 cap = &thinkerclasscap[actor->flags & MF_FRIEND ? th_friends : th_enemies];
964
965 for (th = cap->cnext; th != cap; th = th->cnext)
966 if (((mobj_t *) th)->health*2 >= ((mobj_t *) th)->info->spawnhealth)
967 {
968 if (P_Random(pr_helpfriend) < 180)
969 break;
970 }
971 else
972 if (((mobj_t *) th)->flags & MF_JUSTHIT &&
973 ((mobj_t *) th)->target &&
974 ((mobj_t *) th)->target != actor->target &&
975 !PIT_FindTarget(((mobj_t *) th)->target))
976 {
977 // Ignore any attacking monsters, while searching for friend
978 actor->threshold = BASETHRESHOLD;
979 return TRUE;
980 }
981
982 return FALSE;
983 }
984
985 //
986 // A_KeenDie
987 // DOOM II special, map 32.
988 // Uses special tag 666.
989 //
A_KeenDie(mobj_t * mo)990 void A_KeenDie(mobj_t* mo)
991 {
992 thinker_t *th;
993 line_t junk;
994
995 A_Fall(mo);
996
997 // scan the remaining thinkers to see if all Keens are dead
998
999 for (th = thinkercap.next ; th != &thinkercap ; th=th->next)
1000 if (th->function == P_MobjThinker)
1001 {
1002 mobj_t *mo2 = (mobj_t *) th;
1003 if (mo2 != mo && mo2->type == mo->type && mo2->health > 0)
1004 return; // other Keen not dead
1005 }
1006
1007 junk.tag = 666;
1008 EV_DoDoor(&junk,open);
1009 }
1010
1011
1012 //
1013 // ACTION ROUTINES
1014 //
1015
1016 //
1017 // A_Look
1018 // Stay in state until a player is sighted.
1019 //
1020
A_Look(mobj_t * actor)1021 void A_Look(mobj_t *actor)
1022 {
1023 mobj_t *targ = actor->subsector->sector->soundtarget;
1024 actor->threshold = 0; // any shot will wake up
1025
1026 /* killough 7/18/98:
1027 * Friendly monsters go after other monsters first, but
1028 * also return to player, without attacking them, if they
1029 * cannot find any targets. A marine's best friend :)
1030 */
1031 actor->pursuecount = 0;
1032
1033 if (!(actor->flags & MF_FRIEND && P_LookForTargets(actor, FALSE)) &&
1034 !((targ = actor->subsector->sector->soundtarget) &&
1035 targ->flags & MF_SHOOTABLE &&
1036 (P_SetTarget(&actor->target, targ),
1037 !(actor->flags & MF_AMBUSH) || P_CheckSight(actor, targ))) &&
1038 (actor->flags & MF_FRIEND || !P_LookForTargets(actor, FALSE)))
1039 return;
1040
1041 // go into chase state
1042
1043 if (actor->info->seesound)
1044 {
1045 int sound;
1046 switch (actor->info->seesound)
1047 {
1048 case sfx_posit1:
1049 case sfx_posit2:
1050 case sfx_posit3:
1051 sound = sfx_posit1+P_Random(pr_see)%3;
1052 break;
1053
1054 case sfx_bgsit1:
1055 case sfx_bgsit2:
1056 sound = sfx_bgsit1+P_Random(pr_see)%2;
1057 break;
1058
1059 default:
1060 sound = actor->info->seesound;
1061 break;
1062 }
1063 if (actor->flags & MF_FULLVOLSIGHT)
1064 S_StartSound(NULL, sound); // full volume
1065 else
1066 S_StartSound(actor, sound);
1067 }
1068 P_SetMobjState(actor, actor->info->seestate);
1069 }
1070
1071 //
1072 // A_KeepChasing
1073 //
1074 // killough 10/98:
1075 // Allows monsters to continue movement while attacking
1076 //
1077
1078 #if 0
1079 static void A_KeepChasing(mobj_t *actor)
1080 {
1081 if (actor->movecount)
1082 {
1083 actor->movecount--;
1084 if (actor->strafecount)
1085 actor->strafecount--;
1086 P_SmartMove(actor);
1087 }
1088 }
1089 #endif
1090
1091 //
1092 // A_Chase
1093 // Actor has a melee attack,
1094 // so it tries to close as fast as possible
1095 //
1096
A_Chase(mobj_t * actor)1097 void 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 //
A_FaceTarget(mobj_t * actor)1225 void 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
A_PosAttack(mobj_t * actor)1243 void 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
A_SPosAttack(mobj_t * actor)1261 void 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
A_CPosAttack(mobj_t * actor)1280 void 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
A_CPosRefire(mobj_t * actor)1298 void 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))
1317 stop: P_SetMobjState(actor, actor->info->seestate);
1318 }
1319
A_SpidRefire(mobj_t * actor)1320 void 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))
1336 stop: P_SetMobjState(actor, actor->info->seestate);
1337 }
1338
A_BspiAttack(mobj_t * actor)1339 void 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
A_TroopAttack(mobj_t * actor)1351 void 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
A_SargAttack(mobj_t * actor)1367 void 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
A_HeadAttack(mobj_t * actor)1379 void 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
A_CyberAttack(mobj_t * actor)1393 void 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
A_BruisAttack(mobj_t * actor)1401 void 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
A_SkelMissile(mobj_t * actor)1420 void 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
1437 int TRACEANGLE = 0xc000000;
1438
A_Tracer(mobj_t * actor)1439 void 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
A_SkelWhoosh(mobj_t * actor)1521 void A_SkelWhoosh(mobj_t *actor)
1522 {
1523 if (!actor->target)
1524 return;
1525 A_FaceTarget(actor);
1526 S_StartSound(actor,sfx_skeswg);
1527 }
1528
A_SkelFist(mobj_t * actor)1529 void 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
1547 mobj_t* corpsehit;
1548 mobj_t* vileobj;
1549 fixed_t viletryx;
1550 fixed_t viletryy;
1551
PIT_VileCheck(mobj_t * thing)1552 static dbool PIT_VileCheck(mobj_t *thing)
1553 {
1554 int maxdist;
1555 dbool 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
A_VileChase(mobj_t * actor)1616 void 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 corpsehit->intflags = corpsehit->intflags | MIF_RESURRECTED;//mark as resurrected
1674
1675 if (!((corpsehit->flags ^ MF_COUNTKILL) & (MF_FRIEND | MF_COUNTKILL)))
1676 totallive++;
1677
1678 corpsehit->health = info->spawnhealth;
1679 P_SetTarget(&corpsehit->target, NULL); // killough 11/98
1680
1681 if (mbf_features)
1682 { /* kilough 9/9/98 */
1683 P_SetTarget(&corpsehit->lastenemy, NULL);
1684 corpsehit->flags &= ~MF_JUSTHIT;
1685 }
1686
1687 /* killough 8/29/98: add to appropriate thread */
1688 P_UpdateThinker(&corpsehit->thinker);
1689
1690 return;
1691 }
1692 }
1693 }
1694 }
1695 A_Chase(actor); // Return to normal attack.
1696 }
1697
1698 //
1699 // A_VileStart
1700 //
1701
A_VileStart(mobj_t * actor)1702 void A_VileStart(mobj_t *actor)
1703 {
1704 S_StartSound(actor, sfx_vilatk);
1705 }
1706
1707 //
1708 // A_Fire
1709 // Keep fire in front of player unless out of sight
1710 //
1711
A_StartFire(mobj_t * actor)1712 void A_StartFire(mobj_t *actor)
1713 {
1714 S_StartSound(actor,sfx_flamst);
1715 A_Fire(actor);
1716 }
1717
A_FireCrackle(mobj_t * actor)1718 void A_FireCrackle(mobj_t* actor)
1719 {
1720 S_StartSound(actor,sfx_flame);
1721 A_Fire(actor);
1722 }
1723
A_Fire(mobj_t * actor)1724 void 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(STEPSIZE, finecosine[an]);
1740 actor->y = dest->y + FixedMul(STEPSIZE, finesine[an]);
1741 actor->z = dest->z;
1742 P_SetThingPosition(actor);
1743 }
1744
1745 //
1746 // A_VileTarget
1747 // Spawn the hellfire
1748 //
1749
A_VileTarget(mobj_t * actor)1750 void 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
A_VileAttack(mobj_t * actor)1774 void 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 (STEPSIZE, finecosine[an]);
1800 fire->y = actor->target->y - FixedMul (STEPSIZE, 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
A_FatRaise(mobj_t * actor)1813 void A_FatRaise(mobj_t *actor)
1814 {
1815 A_FaceTarget(actor);
1816 S_StartSound(actor, sfx_manatk);
1817 }
1818
A_FatAttack1(mobj_t * actor)1819 void A_FatAttack1(mobj_t *actor)
1820 {
1821 mobj_t *mo;
1822 int an;
1823
1824 if (!actor->target)
1825 return;
1826
1827 A_FaceTarget(actor);
1828
1829 // Change direction to ...
1830 actor->angle += FATSPREAD;
1831
1832 P_SpawnMissile(actor, actor->target, MT_FATSHOT);
1833
1834 mo = P_SpawnMissile (actor, actor->target, MT_FATSHOT);
1835 mo->angle += FATSPREAD;
1836 an = mo->angle >> ANGLETOFINESHIFT;
1837 mo->momx = FixedMul(mo->info->speed, finecosine[an]);
1838 mo->momy = FixedMul(mo->info->speed, finesine[an]);
1839 }
1840
A_FatAttack2(mobj_t * actor)1841 void A_FatAttack2(mobj_t *actor)
1842 {
1843 mobj_t *mo;
1844 int an;
1845
1846 if (!actor->target)
1847 return;
1848
1849 A_FaceTarget(actor);
1850 // Now here choose opposite deviation.
1851 actor->angle -= FATSPREAD;
1852 P_SpawnMissile(actor, actor->target, MT_FATSHOT);
1853
1854 mo = P_SpawnMissile(actor, actor->target, MT_FATSHOT);
1855 mo->angle -= FATSPREAD*2;
1856 an = mo->angle >> ANGLETOFINESHIFT;
1857 mo->momx = FixedMul(mo->info->speed, finecosine[an]);
1858 mo->momy = FixedMul(mo->info->speed, finesine[an]);
1859 }
1860
A_FatAttack3(mobj_t * actor)1861 void A_FatAttack3(mobj_t *actor)
1862 {
1863 mobj_t *mo;
1864 int an;
1865
1866 if (!actor->target)
1867 return;
1868
1869 A_FaceTarget(actor);
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 mo = P_SpawnMissile(actor, actor->target, MT_FATSHOT);
1878 mo->angle += FATSPREAD/2;
1879 an = mo->angle >> ANGLETOFINESHIFT;
1880 mo->momx = FixedMul(mo->info->speed, finecosine[an]);
1881 mo->momy = FixedMul(mo->info->speed, finesine[an]);
1882 }
1883
1884
1885 //
1886 // SkullAttack
1887 // Fly at the player like a missile.
1888 //
1889 #define SKULLSPEED (20*FRACUNIT)
1890
A_SkullAttack(mobj_t * actor)1891 void A_SkullAttack(mobj_t *actor)
1892 {
1893 mobj_t *dest;
1894 angle_t an;
1895 int dist;
1896
1897 if (!actor->target)
1898 return;
1899
1900 dest = actor->target;
1901 actor->flags |= MF_SKULLFLY;
1902
1903 S_StartSound(actor, actor->info->attacksound);
1904 A_FaceTarget(actor);
1905 an = actor->angle >> ANGLETOFINESHIFT;
1906 actor->momx = FixedMul(SKULLSPEED, finecosine[an]);
1907 actor->momy = FixedMul(SKULLSPEED, finesine[an]);
1908 dist = P_AproxDistance(dest->x - actor->x, dest->y - actor->y);
1909 dist = dist / SKULLSPEED;
1910
1911 if (dist < 1)
1912 dist = 1;
1913 actor->momz = (dest->z+(dest->height>>1) - actor->z) / dist;
1914 }
1915
1916 //
1917 // A_BetaSkullAttack
1918 // The flying skull had different behavior on Beta Doom
1919 //
1920
A_BetaSkullAttack(mobj_t * actor)1921 void A_BetaSkullAttack(mobj_t *actor)
1922 {
1923 int damage;
1924
1925 if (compatibility_level < mbf_compatibility)
1926 return;
1927
1928 if (!actor->target || actor->target->type == MT_SKULL)
1929 return;
1930
1931 S_StartSound(actor, actor->info->attacksound);
1932 A_FaceTarget(actor);
1933 damage = (P_Random(pr_skullfly)%8+1)*actor->info->damage;
1934 P_DamageMobj(actor->target, actor, actor, damage);
1935 }
1936
1937 //
1938 // A_Stop
1939 // The flying skull had different behavior on Beta Doom
1940 //
1941
A_Stop(mobj_t * actor)1942 void A_Stop(mobj_t *actor)
1943 {
1944 if (compatibility_level < mbf_compatibility)
1945 return;
1946
1947 actor->momx = actor->momy = actor->momz = 0;
1948 }
1949
1950 //
1951 // A_PainShootSkull
1952 // Spawn a lost soul and launch it at the target
1953 //
1954
A_PainShootSkull(mobj_t * actor,angle_t angle)1955 static void A_PainShootSkull(mobj_t *actor, angle_t angle)
1956 {
1957 fixed_t x,y,z;
1958 mobj_t *newmobj;
1959 angle_t an;
1960 int prestep;
1961
1962 // The original code checked for 20 skulls on the level, // phares
1963 // and wouldn't spit another one if there were. If not in // phares
1964 // compatibility mode, we remove the limit. // phares
1965 // phares
1966 if (comp[comp_pain]) /* killough 10/98: compatibility-optioned */
1967 {
1968 // count total number of skulls currently on the level
1969 int count = 0;
1970 thinker_t *currentthinker = NULL;
1971 while ((currentthinker = P_NextThinker(currentthinker,th_all)) != NULL)
1972 if ((currentthinker->function == P_MobjThinker)
1973 && ((mobj_t *)currentthinker)->type == MT_SKULL)
1974 count++;
1975 if (count > 20) // phares
1976 return; // phares
1977 }
1978
1979 // okay, there's room for another one
1980
1981 an = angle >> ANGLETOFINESHIFT;
1982
1983 prestep = 4*FRACUNIT + 3*(actor->info->radius + mobjinfo[MT_SKULL].radius)/2;
1984
1985 x = actor->x + FixedMul(prestep, finecosine[an]);
1986 y = actor->y + FixedMul(prestep, finesine[an]);
1987 z = actor->z + 8*FRACUNIT;
1988
1989 if (comp[comp_skull]) /* killough 10/98: compatibility-optioned */
1990 newmobj = P_SpawnMobj(x, y, z, MT_SKULL); // phares
1991 else // V
1992 {
1993 // Check whether the Lost Soul is being fired through a 1-sided
1994 // wall or an impassible line, or a "monsters can't cross" line.
1995 // If it is, then we don't allow the spawn. This is a bug fix, but
1996 // it should be considered an enhancement, since it may disturb
1997 // existing demos, so don't do it in compatibility mode.
1998
1999 if (Check_Sides(actor,x,y))
2000 return;
2001
2002 newmobj = P_SpawnMobj(x, y, z, MT_SKULL);
2003
2004 // Check to see if the new Lost Soul's z value is above the
2005 // ceiling of its new sector, or below the floor. If so, kill it.
2006
2007 if ((newmobj->z >
2008 (newmobj->subsector->sector->ceilingheight - newmobj->height)) ||
2009 (newmobj->z < newmobj->subsector->sector->floorheight))
2010 {
2011 // kill it immediately
2012 P_DamageMobj(newmobj,actor,actor,10000);
2013 return; // ^
2014 } // |
2015 } // phares
2016
2017 /* killough 7/20/98: PEs shoot lost souls with the same friendliness */
2018 newmobj->flags = (newmobj->flags & ~MF_FRIEND) | (actor->flags & MF_FRIEND);
2019
2020 /* killough 8/29/98: add to appropriate thread */
2021 P_UpdateThinker(&newmobj->thinker);
2022
2023 // Check for movements.
2024 // killough 3/15/98: don't jump over dropoffs:
2025
2026 if (!P_TryMove(newmobj, newmobj->x, newmobj->y, FALSE))
2027 {
2028 // kill it immediately
2029 P_DamageMobj(newmobj, actor, actor, 10000);
2030 return;
2031 }
2032
2033 P_SetTarget(&newmobj->target, actor->target);
2034 A_SkullAttack(newmobj);
2035 }
2036
2037 //
2038 // A_PainAttack
2039 // Spawn a lost soul and launch it at the target
2040 //
2041
A_PainAttack(mobj_t * actor)2042 void A_PainAttack(mobj_t *actor)
2043 {
2044 if (!actor->target)
2045 return;
2046 A_FaceTarget(actor);
2047 A_PainShootSkull(actor, actor->angle);
2048 }
2049
A_PainDie(mobj_t * actor)2050 void A_PainDie(mobj_t *actor)
2051 {
2052 A_Fall(actor);
2053 A_PainShootSkull(actor, actor->angle+ANG90);
2054 A_PainShootSkull(actor, actor->angle+ANG180);
2055 A_PainShootSkull(actor, actor->angle+ANG270);
2056 }
2057
A_Scream(mobj_t * actor)2058 void A_Scream(mobj_t *actor)
2059 {
2060 int sound;
2061
2062 switch (actor->info->deathsound)
2063 {
2064 case 0:
2065 return;
2066
2067 case sfx_podth1:
2068 case sfx_podth2:
2069 case sfx_podth3:
2070 sound = sfx_podth1 + P_Random(pr_scream)%3;
2071 break;
2072
2073 case sfx_bgdth1:
2074 case sfx_bgdth2:
2075 sound = sfx_bgdth1 + P_Random(pr_scream)%2;
2076 break;
2077
2078 default:
2079 sound = actor->info->deathsound;
2080 break;
2081 }
2082
2083 // Check for bosses.
2084 if (actor->flags & MF_FULLVOLDEATH)
2085 S_StartSound(NULL, sound); // full volume
2086 else
2087 S_StartSound(actor, sound);
2088 }
2089
A_XScream(mobj_t * actor)2090 void A_XScream(mobj_t *actor)
2091 {
2092 S_StartSound(actor, sfx_slop);
2093 }
2094
A_Pain(mobj_t * actor)2095 void A_Pain(mobj_t *actor)
2096 {
2097 if (actor->info->painsound)
2098 S_StartSound(actor, actor->info->painsound);
2099 }
2100
A_Fall(mobj_t * actor)2101 void A_Fall(mobj_t *actor)
2102 {
2103 // actor is on ground, it can be walked over
2104 actor->flags &= ~MF_SOLID;
2105 }
2106
2107 //
2108 // A_Explode
2109 //
A_Explode(mobj_t * thingy)2110 void A_Explode(mobj_t *thingy)
2111 {
2112 P_RadiusAttack( thingy, thingy->target, 128 );
2113 retro_set_rumble_damage(60, 500.f);
2114 }
2115
2116 //
2117 // A_BossDeath
2118 // Possibly trigger special effects
2119 // if on first boss level
2120 //
2121
A_BossDeath(mobj_t * mo)2122 void A_BossDeath(mobj_t *mo)
2123 {
2124 thinker_t *th;
2125 line_t junk;
2126 int i;
2127
2128 // numbossactions == 0 means to use the defaults.
2129 // numbossactions == -1 means to do nothing.
2130 // positive values mean to check the list of boss actions and run all that apply.
2131 if (gamemapinfo && gamemapinfo->numbossactions != 0)
2132 {
2133 if (gamemapinfo->numbossactions < 0) return; // -1 clears all bossaction
2134
2135 for (i = 0; i < gamemapinfo->numbossactions; i++)
2136 {
2137 if (gamemapinfo->bossactions[i].type == mo->type)
2138 break;
2139 }
2140 if (i >= gamemapinfo->numbossactions)
2141 return; // no matches found
2142 }
2143 else if (gamemode == commercial)
2144 {
2145 if (gamemap != 7)
2146 return;
2147
2148 if ((mo->type != MT_FATSO)
2149 && (mo->type != MT_BABY))
2150 return;
2151 }
2152 else
2153 {
2154 // e6y
2155 // Additional check of gameepisode is necessary, because
2156 // there is no right or wrong solution for E4M6 in original EXEs,
2157 // there's nothing to emulate.
2158 if (comp[comp_666] && gameepisode < 4)
2159 {
2160 // e6y
2161 // Only following checks are present in doom2.exe ver. 1.666 and 1.9
2162 // instead of separate checks for each episode in doomult.exe, plutonia.exe and tnt.exe
2163 // There is no more desync on doom.wad\episode3.lmp
2164 // http://www.doomworld.com/idgames/index.php?id=6909
2165 if (gamemap != 8)
2166 return;
2167 if (mo->type == MT_BRUISER && gameepisode != 1)
2168 return;
2169 }
2170 else
2171 {
2172 switch(gameepisode)
2173 {
2174 case 1:
2175 if (gamemap != 8)
2176 return;
2177
2178 if (mo->type != MT_BRUISER)
2179 return;
2180 break;
2181
2182 case 2:
2183 if (gamemap != 8)
2184 return;
2185
2186 if (mo->type != MT_CYBORG)
2187 return;
2188 break;
2189
2190 case 3:
2191 if (gamemap != 8)
2192 return;
2193
2194 if (mo->type != MT_SPIDER)
2195 return;
2196
2197 break;
2198
2199 case 4:
2200 switch(gamemap)
2201 {
2202 case 6:
2203 if (mo->type != MT_CYBORG)
2204 return;
2205 break;
2206
2207 case 8:
2208 if (mo->type != MT_SPIDER)
2209 return;
2210 break;
2211
2212 default:
2213 return;
2214 break;
2215 }
2216 break;
2217
2218 case 5: // sigil
2219 return;
2220
2221 default:
2222 if (gamemap != 8)
2223 return;
2224 break;
2225 }
2226 }
2227
2228 }
2229
2230 // make sure there is a player alive for victory
2231 for (i=0; i<MAXPLAYERS; i++)
2232 if (playeringame[i] && players[i].health > 0)
2233 break;
2234
2235 if (i==MAXPLAYERS)
2236 return; // no one left alive, so do not end game
2237
2238 // scan the remaining thinkers to see
2239 // if all bosses are dead
2240 for (th = thinkercap.next ; th != &thinkercap ; th=th->next)
2241 if (th->function == P_MobjThinker)
2242 {
2243 mobj_t *mo2 = (mobj_t *) th;
2244 if (mo2 != mo && mo2->type == mo->type && mo2->health > 0)
2245 return; // other boss not dead
2246 }
2247
2248 // victory! apply BossDeath effect
2249 if (gamemapinfo && gamemapinfo->numbossactions != 0)
2250 {
2251 for (i = 0; i < gamemapinfo->numbossactions; i++)
2252 if (gamemapinfo->bossactions[i].type == mo->type)
2253 {
2254 player_t fakeplayer = {0};
2255 fakeplayer.health = 1; // non-zombie fake player
2256 fakeplayer.mo = NULL; // don't play sounds, teleport, etc
2257 junk = *lines;
2258 junk.special = (short)gamemapinfo->bossactions[i].special;
2259 junk.tag = (short)gamemapinfo->bossactions[i].tag;
2260 // Treat the boss as a temporary fake player to activate the special
2261 // it'll be excluded from teleportation since fakeplayer->mo == NULL
2262 mo->player = &fakeplayer;
2263 if (!P_UseSpecialLine(mo, &junk, 0))
2264 P_CrossSpecialLine(&junk, 0, mo);
2265 mo->player = NULL;
2266 }
2267 }
2268 else if ( gamemode == commercial)
2269 {
2270 if (gamemap == 7)
2271 {
2272 if (mo->type == MT_FATSO)
2273 {
2274 junk.tag = 666;
2275 EV_DoFloor(&junk,FLEV_LOWERFLOORTOLOWEST);
2276 return;
2277 }
2278
2279 if (mo->type == MT_BABY)
2280 {
2281 junk.tag = 667;
2282 EV_DoFloor(&junk, FLEV_RAISETOTEXTURE);
2283 return;
2284 }
2285 }
2286 }
2287 else
2288 {
2289 switch(gameepisode)
2290 {
2291 case 1:
2292 junk.tag = 666;
2293 EV_DoFloor(&junk, FLEV_LOWERFLOORTOLOWEST);
2294 return;
2295 case 4:
2296 switch(gamemap)
2297 {
2298 case 6:
2299 junk.tag = 666;
2300 EV_DoDoor(&junk, blazeOpen);
2301 return;
2302 case 8:
2303 junk.tag = 666;
2304 EV_DoFloor(&junk, FLEV_LOWERFLOORTOLOWEST);
2305 return;
2306 }
2307 }
2308 }
2309 G_ExitLevel();
2310 }
2311
2312
A_Hoof(mobj_t * mo)2313 void A_Hoof (mobj_t* mo)
2314 {
2315 S_StartSound(mo, sfx_hoof);
2316 A_Chase(mo);
2317 }
2318
A_Metal(mobj_t * mo)2319 void A_Metal(mobj_t *mo)
2320 {
2321 S_StartSound(mo, sfx_metal);
2322 A_Chase(mo);
2323 }
2324
A_BabyMetal(mobj_t * mo)2325 void A_BabyMetal(mobj_t *mo)
2326 {
2327 S_StartSound(mo, sfx_bspwlk);
2328 A_Chase(mo);
2329 }
2330
A_OpenShotgun2(player_t * player,pspdef_t * psp)2331 void A_OpenShotgun2(player_t *player, pspdef_t *psp)
2332 {
2333 S_StartSound(player->mo, sfx_dbopn);
2334 }
2335
A_LoadShotgun2(player_t * player,pspdef_t * psp)2336 void A_LoadShotgun2(player_t *player, pspdef_t *psp)
2337 {
2338 S_StartSound(player->mo, sfx_dbload);
2339 }
2340
A_CloseShotgun2(player_t * player,pspdef_t * psp)2341 void A_CloseShotgun2(player_t *player, pspdef_t *psp)
2342 {
2343 S_StartSound(player->mo, sfx_dbcls);
2344 A_ReFire(player,psp);
2345 }
2346
2347 // killough 2/7/98: Remove limit on icon landings:
2348 mobj_t **braintargets;
2349 int numbraintargets_alloc;
2350 int numbraintargets;
2351
2352 struct brain_s brain; // killough 3/26/98: global state of boss brain
2353
2354 // killough 3/26/98: initialize icon landings at level startup,
2355 // rather than at boss wakeup, to prevent savegame-related crashes
2356
P_SpawnBrainTargets(void)2357 void P_SpawnBrainTargets(void) // killough 3/26/98: renamed old function
2358 {
2359 thinker_t *thinker;
2360
2361 // find all the target spots
2362 numbraintargets = 0;
2363 brain.targeton = 0;
2364 brain.easy = 0; // killough 3/26/98: always init easy to 0
2365
2366 for (thinker = thinkercap.next ;
2367 thinker != &thinkercap ;
2368 thinker = thinker->next)
2369 if (thinker->function == P_MobjThinker)
2370 {
2371 mobj_t *m = (mobj_t *) thinker;
2372
2373 if (m->type == MT_BOSSTARGET )
2374 { // killough 2/7/98: remove limit on icon landings:
2375 if (numbraintargets >= numbraintargets_alloc)
2376 braintargets = realloc(braintargets,
2377 (numbraintargets_alloc = numbraintargets_alloc ?
2378 numbraintargets_alloc*2 : 32) *sizeof *braintargets);
2379 braintargets[numbraintargets++] = m;
2380 }
2381 }
2382 }
2383
A_BrainAwake(mobj_t * mo)2384 void A_BrainAwake(mobj_t *mo)
2385 {
2386 S_StartSound(NULL,sfx_bossit); // killough 3/26/98: only generates sound now
2387 }
2388
A_BrainPain(mobj_t * mo)2389 void A_BrainPain(mobj_t *mo)
2390 {
2391 S_StartSound(NULL,sfx_bospn);
2392 }
2393
A_BrainScream(mobj_t * mo)2394 void A_BrainScream(mobj_t *mo)
2395 {
2396 int x;
2397 for (x=mo->x - 196*FRACUNIT ; x< mo->x + 320*FRACUNIT ; x+= FRACUNIT*8)
2398 {
2399 int y = mo->y - 320*FRACUNIT;
2400 int z = 128 + P_Random(pr_brainscream)*2*FRACUNIT;
2401 mobj_t *th = P_SpawnMobj (x,y,z, MT_ROCKET);
2402 th->momz = P_Random(pr_brainscream)*512;
2403 P_SetMobjState(th, S_BRAINEXPLODE1);
2404 th->tics -= P_Random(pr_brainscream)&7;
2405 if (th->tics < 1)
2406 th->tics = 1;
2407 }
2408 S_StartSound(NULL,sfx_bosdth);
2409 }
2410
A_BrainExplode(mobj_t * mo)2411 void A_BrainExplode(mobj_t *mo)
2412 { // killough 5/5/98: remove dependence on order of evaluation:
2413 int t = P_Random(pr_brainexp);
2414 int x = mo->x + (t - P_Random(pr_brainexp))*2048;
2415 int y = mo->y;
2416 int z = 128 + P_Random(pr_brainexp)*2*FRACUNIT;
2417 mobj_t *th = P_SpawnMobj(x,y,z, MT_ROCKET);
2418 th->momz = P_Random(pr_brainexp)*512;
2419 P_SetMobjState(th, S_BRAINEXPLODE1);
2420 th->tics -= P_Random(pr_brainexp)&7;
2421 if (th->tics < 1)
2422 th->tics = 1;
2423 }
2424
A_BrainDie(mobj_t * mo)2425 void A_BrainDie(mobj_t *mo)
2426 {
2427 G_ExitLevel();
2428 }
2429
A_BrainSpit(mobj_t * mo)2430 void A_BrainSpit(mobj_t *mo)
2431 {
2432 mobj_t *targ, *newmobj;
2433
2434 if (!numbraintargets) // killough 4/1/98: ignore if no targets
2435 return;
2436
2437 brain.easy ^= 1; // killough 3/26/98: use brain struct
2438 if (gameskill <= sk_easy && !brain.easy)
2439 return;
2440
2441 // shoot a cube at current target
2442 targ = braintargets[brain.targeton++]; // killough 3/26/98:
2443 brain.targeton %= numbraintargets; // Use brain struct for targets
2444
2445 // spawn brain missile
2446 newmobj = P_SpawnMissile(mo, targ, MT_SPAWNSHOT);
2447 P_SetTarget(&newmobj->target, targ);
2448 newmobj->reactiontime = (short)(((targ->y-mo->y)/newmobj->momy)/newmobj->state->tics);
2449
2450 // killough 7/18/98: brain friendliness is transferred
2451 newmobj->flags = (newmobj->flags & ~MF_FRIEND) | (mo->flags & MF_FRIEND);
2452
2453 // killough 8/29/98: add to appropriate thread
2454 P_UpdateThinker(&newmobj->thinker);
2455
2456 S_StartSound(NULL, sfx_bospit);
2457 }
2458
2459 // travelling cube sound
A_SpawnSound(mobj_t * mo)2460 void A_SpawnSound(mobj_t *mo)
2461 {
2462 S_StartSound(mo,sfx_boscub);
2463 A_SpawnFly(mo);
2464 }
2465
A_SpawnFly(mobj_t * mo)2466 void A_SpawnFly(mobj_t *mo)
2467 {
2468 mobj_t *newmobj;
2469 mobj_t *fog;
2470 mobj_t *targ;
2471 int r;
2472 mobjtype_t type;
2473
2474 if (--mo->reactiontime)
2475 return; // still flying
2476
2477 targ = mo->target;
2478
2479 // First spawn teleport fog.
2480 fog = P_SpawnMobj(targ->x, targ->y, targ->z, MT_SPAWNFIRE);
2481 S_StartSound(fog, sfx_telept);
2482
2483 // Randomly select monster to spawn.
2484 r = P_Random(pr_spawnfly);
2485
2486 // Probability distribution (kind of :), decreasing likelihood.
2487 if ( r<50 )
2488 type = MT_TROOP;
2489 else if (r<90)
2490 type = MT_SERGEANT;
2491 else if (r<120)
2492 type = MT_SHADOWS;
2493 else if (r<130)
2494 type = MT_PAIN;
2495 else if (r<160)
2496 type = MT_HEAD;
2497 else if (r<162)
2498 type = MT_VILE;
2499 else if (r<172)
2500 type = MT_UNDEAD;
2501 else if (r<192)
2502 type = MT_BABY;
2503 else if (r<222)
2504 type = MT_FATSO;
2505 else if (r<246)
2506 type = MT_KNIGHT;
2507 else
2508 type = MT_BRUISER;
2509
2510 newmobj = P_SpawnMobj(targ->x, targ->y, targ->z, type);
2511
2512 /* killough 7/18/98: brain friendliness is transferred */
2513 newmobj->flags = (newmobj->flags & ~MF_FRIEND) | (mo->flags & MF_FRIEND);
2514
2515 /* killough 8/29/98: add to appropriate thread */
2516 P_UpdateThinker(&newmobj->thinker);
2517
2518 if (P_LookForTargets(newmobj,TRUE)) /* killough 9/4/98 */
2519 P_SetMobjState(newmobj, newmobj->info->seestate);
2520
2521 // telefrag anything in this spot
2522 P_TeleportMove(newmobj, newmobj->x, newmobj->y, TRUE); /* killough 8/9/98 */
2523
2524 // remove self (i.e., cube).
2525 P_RemoveMobj(mo);
2526 }
2527
A_PlayerScream(mobj_t * mo)2528 void A_PlayerScream(mobj_t *mo)
2529 {
2530 int sound = sfx_pldeth; // Default death sound.
2531 if (gamemode != shareware && mo->health < -50)
2532 sound = sfx_pdiehi; // IF THE PLAYER DIES LESS THAN -50% WITHOUT GIBBING
2533 S_StartSound(mo, sound);
2534 }
2535
2536 /* cph - MBF-added codepointer functions */
2537
2538 // killough 11/98: kill an object
A_Die(mobj_t * actor)2539 void A_Die(mobj_t *actor)
2540 {
2541 P_DamageMobj(actor, NULL, NULL, actor->health);
2542 }
2543
2544 //
2545 // A_Detonate
2546 // killough 8/9/98: same as A_Explode, except that the damage is variable
2547 //
2548
A_Detonate(mobj_t * mo)2549 void A_Detonate(mobj_t *mo)
2550 {
2551 P_RadiusAttack(mo, mo->target, mo->info->damage);
2552 retro_set_rumble_damage(60, 500.f);
2553 }
2554
2555 //
2556 // killough 9/98: a mushroom explosion effect, sorta :)
2557 // Original idea: Linguica
2558 //
2559
A_Mushroom(mobj_t * actor)2560 void A_Mushroom(mobj_t *actor)
2561 {
2562 int i, j, n = actor->info->damage;
2563
2564 A_Explode(actor); // First make normal explosion
2565
2566 // Now launch mushroom cloud
2567 for (i = -n; i <= n; i += 8)
2568 for (j = -n; j <= n; j += 8)
2569 {
2570 mobj_t target = *actor, *mo;
2571 target.x += i << FRACBITS; // Aim in many directions from source
2572 target.y += j << FRACBITS;
2573 target.z += P_AproxDistance(i,j) << (FRACBITS+2); // Aim up fairly high
2574 mo = P_SpawnMissile(actor, &target, MT_FATSHOT); // Launch fireball
2575 mo->momx >>= 1;
2576 mo->momy >>= 1; // Slow it down a bit
2577 mo->momz >>= 1;
2578 mo->flags &= ~MF_NOGRAVITY; // Make debris fall under gravity
2579 }
2580 }
2581
2582 //
2583 // killough 11/98
2584 //
2585 // The following were inspired by Len Pitre
2586 //
2587 // A small set of highly-sought-after code pointers
2588 //
2589
A_Spawn(mobj_t * mo)2590 void A_Spawn(mobj_t *mo)
2591 {
2592 if (mo->state->misc1)
2593 {
2594 /* mobj_t *newmobj = */
2595 P_SpawnMobj(mo->x, mo->y, (mo->state->misc2 << FRACBITS) + mo->z,
2596 mo->state->misc1 - 1);
2597 /* CPhipps - no friendlyness (yet)
2598 newmobj->flags = (newmobj->flags & ~MF_FRIEND) | (mo->flags & MF_FRIEND);
2599 */
2600 }
2601 }
2602
A_Turn(mobj_t * mo)2603 void A_Turn(mobj_t *mo)
2604 {
2605 mo->angle += (unsigned int)(((uint64_t) mo->state->misc1 << 32) / 360);
2606 }
2607
A_Face(mobj_t * mo)2608 void A_Face(mobj_t *mo)
2609 {
2610 mo->angle = (unsigned int)(((uint64_t) mo->state->misc1 << 32) / 360);
2611 }
2612
A_Scratch(mobj_t * mo)2613 void A_Scratch(mobj_t *mo)
2614 {
2615 mo->target && (A_FaceTarget(mo), P_CheckMeleeRange(mo)) ?
2616 mo->state->misc2 ? S_StartSound(mo, mo->state->misc2) : (void) 0,
2617 P_DamageMobj(mo->target, mo, mo, mo->state->misc1) : (void) 0;
2618 }
2619
A_PlaySound(mobj_t * mo)2620 void A_PlaySound(mobj_t *mo)
2621 {
2622 S_StartSound(mo->state->misc2 ? NULL : mo, mo->state->misc1);
2623 }
2624
A_RandomJump(mobj_t * mo)2625 void A_RandomJump(mobj_t *mo)
2626 {
2627 if (P_Random(pr_randomjump) < mo->state->misc2)
2628 P_SetMobjState(mo, mo->state->misc1);
2629 }
2630
2631 //
2632 // This allows linedef effects to be activated inside deh frames.
2633 //
2634
A_LineEffect(mobj_t * mo)2635 void A_LineEffect(mobj_t *mo)
2636 {
2637 static line_t junk;
2638 player_t player;
2639 player_t *oldplayer;
2640 junk = *lines;
2641 oldplayer = mo->player;
2642 mo->player = &player;
2643 player.health = 100;
2644 junk.special = (short)mo->state->misc1;
2645 if (!junk.special)
2646 return;
2647 junk.tag = (short)mo->state->misc2;
2648 if (!P_UseSpecialLine(mo, &junk, 0))
2649 P_CrossSpecialLine(&junk, 0, mo);
2650 mo->state->misc1 = junk.special;
2651 mo->player = oldplayer;
2652 }
2653