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