1 // Emacs style mode select   -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id: p_enemy.c 1563 2020-11-29 11:52:09Z wesleyjohnson $
5 //
6 // Copyright (C) 1993-1996 by id Software, Inc.
7 // Copyright (C) 1998-2016 by DooM Legacy Team.
8 //
9 // This program is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU General Public License
11 // as published by the Free Software Foundation; either version 2
12 // of the License, or (at your option) any later version.
13 //
14 // This program is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 // GNU General Public License for more details.
18 //
19 //
20 // $Log: p_enemy.c,v $
21 // Revision 1.20  2004/07/27 08:19:36  exl
22 // New fmod, fs functions, bugfix or 2, patrol nodes
23 //
24 // Revision 1.19  2002/11/30 18:41:20  judgecutor
25 //
26 // Revision 1.18  2002/09/27 16:40:09  tonyd
27 // First commit of acbot
28 //
29 // Revision 1.17  2002/09/17 21:20:03  hurdler
30 // Quick hack for hacx freeze
31 //
32 // Revision 1.16  2002/08/13 01:14:20  ssntails
33 // A_BossDeath fix (I hope!)
34 //
35 // Revision 1.15  2002/07/24 22:37:31  ssntails
36 // Fix for Keen deaths in custom maps.
37 //
38 // Revision 1.14  2001/07/28 16:18:37  bpereira
39 // Revision 1.13  2001/05/27 13:42:47  bpereira
40 //
41 // Revision 1.12  2001/04/04 20:24:21  judgecutor
42 // Added support for the 3D Sound
43 //
44 // Revision 1.11  2001/03/30 17:12:50  bpereira
45 //
46 // Revision 1.10  2001/03/19 21:18:48  metzgermeister
47 //   * missing textures in HW mode are replaced by default texture
48 //   * fixed crash bug with P_SpawnMissile(.) returning NULL
49 //   * deep water trick and other nasty thing work now in HW mode (tested with tnt/map02 eternal/map02)
50 //   * added cvar gr_correcttricks
51 //
52 // Revision 1.9  2001/01/25 22:15:43  bpereira
53 // added heretic support
54 //
55 // Revision 1.8  2000/10/21 08:43:30  bpereira
56 // Revision 1.7  2000/10/08 13:30:01  bpereira
57 // Revision 1.6  2000/10/01 10:18:17  bpereira
58 // Revision 1.5  2000/04/30 10:30:10  bpereira
59 //
60 // Revision 1.4  2000/04/11 19:07:24  stroggonmeth
61 // Finished my logs, fixed a crashing bug.
62 //
63 // Revision 1.3  2000/04/04 00:32:46  stroggonmeth
64 // Initial Boom compatability plus few misc changes all around.
65 //
66 // Revision 1.2  2000/02/27 00:42:10  hurdler
67 // Revision 1.1.1.1  2000/02/22 20:32:32  hurdler
68 // Initial import into CVS (v1.29 pr3)
69 //
70 //
71 // DESCRIPTION:
72 //      Enemy thinking, AI.
73 //      Action Pointer Functions
74 //      that are associated with states/frames.
75 //
76 //-----------------------------------------------------------------------------
77 
78 #include "doomincl.h"
79 #include "p_local.h"
80 #include "p_tick.h"
81   // think
82 #include "p_inter.h"
83   // P_KillMobj
84 #include "g_game.h"
85 #include "r_main.h"
86 #include "r_state.h"
87 #include "s_sound.h"
88 #include "m_random.h"
89 #include "t_script.h"
90 
91 
92 #include "hardware/hw3sound.h"
93 
94 void A_Fall(mobj_t *actor);
95 static void FastMonster_OnChange(void);
96 void A_FaceTarget(mobj_t* actor);
97 void CV_monster_OnChange(void);
98 
99 // enable the solid corpses option : still not finished
100 consvar_t cv_solidcorpse =
101   {"solidcorpse","0", CV_NETVAR | CV_SAVE, CV_OnOff};
102 consvar_t cv_fastmonsters =
103   {"fastmonsters","0", CV_NETVAR | CV_CALL, CV_OnOff, FastMonster_OnChange};
104 consvar_t cv_predictingmonsters =
105   {"predictingmonsters","0", CV_NETVAR | CV_SAVE, CV_OnOff};	//added by AC for predmonsters
106 
107 CV_PossibleValue_t mongravity_cons_t[]={
108    {0,"Off"},
109    {1,"MBF"},
110    {2,"On"},
111    {0,NULL}};
112 consvar_t cv_monstergravity =
113   {"monstergravity","2", CV_NETVAR | CV_SAVE, mongravity_cons_t };
114 
115 // DarkWolf95: Monster Behavior
116 // Values saved in demo, so do not change existing values.
117 CV_PossibleValue_t monbehavior_cons_t[]={
118    {0,"Normal"},
119    {1,"Coop"},
120    {8,"No Infight"},
121    {2,"Infight"},
122    {6,"Full Infight"},
123    {3,"Force Coop"},
124    {5,"Force No Infight"},
125    {4,"Force Infight"},
126    {7,"Force Full Infight"},
127    {0,NULL}};
128 consvar_t cv_monbehavior =
129   { "monsterbehavior", "0", CV_NETVAR | CV_SAVE | CV_CALL, monbehavior_cons_t, CV_monster_OnChange };
130 
131 // Doom normal is infight, no coop (see Boom).
132 // Indexed by monbehavior_cons_t values.
133 static byte monbehav_to_infight[] =
134 {
135   INFT_infight,      // 0  Normal  (infight)
136   INFT_coop,         // 1  Coop default
137   INFT_infight,      // 2  Infight default
138   INFT_coop | INFT_force,         // 3  Coop forced
139   INFT_infight | INFT_force,      // 4  Infight forced
140   INFT_none | INFT_force,         // 5  No Infight forced
141   INFT_full_infight, // 6  Full Infight default
142   INFT_full_infight | INFT_force, // 7  Full Infight forced
143   INFT_none,         // 8  No Infight
144 };
145 
146 CV_PossibleValue_t monsterfriction_t[] = {
147    {0,"None"},
148    {1,"MBF"},
149    {2,"Momentum"},
150    {3,"Heretic"},
151    {20,"Normal"},
152    {0,NULL} };
153 consvar_t cv_monsterfriction =
154   {"monsterfriction","2", CV_NETVAR | CV_SAVE | CV_CALL, monsterfriction_t, CV_monster_OnChange};
155 
156 // Monster stuck on door edge
157 CV_PossibleValue_t doorstuck_t[] = {
158    {0,"None"},
159    {1,"MBF"},
160    {2,"Boom"},
161    {0,NULL} };
162 consvar_t cv_doorstuck =
163   {"doorstuck","2", CV_NETVAR | CV_SAVE | CV_CALL, doorstuck_t, CV_monster_OnChange};
164 
165 
166 
167 typedef enum
168 {
169     DI_EAST,
170     DI_NORTHEAST,
171     DI_NORTH,
172     DI_NORTHWEST,
173     DI_WEST,
174     DI_SOUTHWEST,
175     DI_SOUTH,
176     DI_SOUTHEAST,
177     DI_NODIR,
178     NUMDIRS
179 } dirtype_t;
180 
181 
182 //
183 // P_NewChaseDir related LUT.
184 //
185 
186 // [WDJ] Speed comparison of Opposite.
187 //  Table: 35 sec,  Calculated: 33 sec.
188 #if 0
189 static dirtype_t opposite[] =
190 {
191   DI_WEST, DI_SOUTHWEST, DI_SOUTH, DI_SOUTHEAST,
192   DI_EAST, DI_NORTHEAST, DI_NORTH, DI_NORTHWEST, DI_NODIR
193 };
194 #define  DI_OPPOSITE( di )     (opposite[di])
195 #else
196 // Add 180 degrees, same as  (di+4) & 0x0F
197 #define  DI_OPPOSITE( di )     ((di < DI_NODIR)? ((di) ^ 4) : DI_NODIR)
198 #endif
199 
200 static dirtype_t diags[] =
201 {
202     DI_NORTHWEST, DI_NORTHEAST, DI_SOUTHWEST, DI_SOUTHEAST
203 };
204 
205 
206 
207 
208 static
FastMonster_OnChange(void)209 void FastMonster_OnChange(void)
210 {
211 static boolean fast=false;
212 static const struct
213     {
214         mobjtype_t type;
215         int speed[2];
216     } MonsterMissileInfo[] =
217     {
218         // doom
219         { MT_BRUISERSHOT, {15, 20}},
220         { MT_HEADSHOT,    {10, 20}},
221         { MT_TROOPSHOT,   {10, 20}},
222 
223         // heretic
224         { MT_IMPBALL,     {10, 20}},
225         { MT_MUMMYFX1,    { 9, 18}},
226         { MT_KNIGHTAXE,   { 9, 18}},
227         { MT_REDAXE,      { 9, 18}},
228         { MT_BEASTBALL,   {12, 20}},
229         { MT_WIZFX1,      {18, 24}},
230         { MT_SNAKEPRO_A,  {14, 20}},
231         { MT_SNAKEPRO_B,  {14, 20}},
232         { MT_HEADFX1,     {13, 20}},
233         { MT_HEADFX3,     {10, 18}},
234         { MT_MNTRFX1,     {20, 26}},
235         { MT_MNTRFX2,     {14, 20}},
236         { MT_SRCRFX1,     {20, 28}},
237         { MT_SOR2FX1,     {20, 28}},
238 
239         { -1, {-1, -1} } // Terminator
240     };
241 
242     int i;
243     if( cv_fastmonsters.EV && !fast )
244     {
245         for (i=S_SARG_RUN1 ; i<=S_SARG_PAIN2 ; i++)
246             states[i].tics >>= 1;
247         fast=true;
248     }
249     else
250     if( !cv_fastmonsters.EV && fast )
251     {
252         for (i=S_SARG_RUN1 ; i<=S_SARG_PAIN2 ; i++)
253             states[i].tics <<= 1;
254         fast=false;
255     }
256 
257     for(i = 0; MonsterMissileInfo[i].type != -1; i++)
258     {
259         mobjinfo[MonsterMissileInfo[i].type].speed
260             = MonsterMissileInfo[i].speed[cv_fastmonsters.EV]<<FRACBITS;
261     }
262 }
263 
264 
265 // MBF controls
266 static void mbf_OnChange( void );
267 
268 
269 consvar_t cv_monster_remember =
270   {"mon_remember","1", CV_NETVAR | CV_SAVE, CV_OnOff};
271   // Boom monsters_remember
272 
273 consvar_t cv_mbf_dropoff = {"dropoff","1", CV_NETVAR | CV_SAVE, CV_OnOff};
274   // !comp[comp_dropoff]
275 consvar_t cv_mbf_falloff = {"falloff","1", CV_NETVAR | CV_SAVE, CV_OnOff};
276   // !comp[comp_falloff]
277 
278 consvar_t cv_mbf_monster_avoid_hazard =
279   {"mon_avoidhazard","1", CV_NETVAR | CV_SAVE, CV_OnOff};
280   // MBF monster_avoid_hazards
281 consvar_t cv_mbf_monster_backing =
282   {"mon_backing","0", CV_NETVAR | CV_SAVE, CV_OnOff};
283   // MBF monster_backing
284 consvar_t cv_mbf_pursuit = {"pursuit","0", CV_NETVAR | CV_SAVE, CV_OnOff};
285   // !comp[comp_pursuit]
286 consvar_t cv_mbf_distfriend =
287   {"distfriend","128", CV_NETVAR | CV_VALUE | CV_SAVE | CV_CALL, CV_uint16, mbf_OnChange};
288   // MBF distfriend (0..999)
289 consvar_t cv_mbf_staylift = {"staylift","1", CV_NETVAR | CV_SAVE, CV_OnOff};
290   // !comp[comp_staylift]
291 consvar_t cv_mbf_help_friend = {"helpfriend","1", CV_NETVAR | CV_SAVE, CV_OnOff};
292   // MBF help_friends
293 consvar_t cv_mbf_monkeys = {"monkeys","0", CV_NETVAR | CV_SAVE, CV_OnOff};
294   // MBF monkeys (climb steep stairs)
295 #ifdef DOGS
296 CV_PossibleValue_t dogs_cons_t[] = {{0,"MIN"}, {9,"MAX"}, {0,NULL} };
297 consvar_t cv_mbf_dogs = {"dogs_cnt","0", CV_NETVAR | CV_SAVE, dogs_cons_t};
298   // MBF single player (and coop) dogs (0..9)
299 consvar_t cv_mbf_dog_jumping = {"dogjump","1", CV_NETVAR | CV_SAVE, CV_OnOff};
300   // MBF dog_jumping
301 #endif
302 
mbf_OnChange(void)303 static void mbf_OnChange( void )
304 {
305     // Demo sets EV_mbf_distfriend.
306     EV_mbf_distfriend = cv_mbf_distfriend.value << FRACBITS;  // to fixed_t
307 }
308 
309 
310 
311 // Infight settings translated to INFT_ values.  MBF demo sets infight.
312 byte  monster_infight; //DarkWolf95:November 21, 2003: Monsters Infight!
313 byte  monster_infight_deh; // DEH input.
314 
315        byte  EN_monster_friction;
316 static byte  EN_mbf_enemyfactor;
317 static byte  EN_monster_momentum;
318        byte  EN_skull_limit;  // comp[comp_pain], enable pain skull limits
319        byte  EN_old_pain_spawn;  // comp[comp_skull], can spit skull through walls
320 static byte  EN_doorstuck;  // !comp[comp_doorstuck], Boom doorstuck or MBF doorstuck
321 static byte  EN_mbf_doorstuck;  // MBF doorstuck
322        byte  EN_mbf_speed;  // p_spec
323 
324 
325 // [WDJ] Friction got too complicated, use lookup table.
326 typedef enum {
327   FRE_monster_friction = 0x01,
328   FRE_monster_momentum = 0x02,
329   FRE_mbf_enemyfactor = 0x04,
330 } friction_en_e;
331 
332 static const byte friction_table[] =
333 {
334   0,   // None
335   FRE_monster_friction | FRE_mbf_enemyfactor,  // MBF
336   FRE_monster_friction | FRE_monster_momentum,  // Momentum
337   // Heretic demos have FR_orig friction, with special ice sector handling
338   // in P_Thrust (so monsters slip only on ice sector)
339   0,   // Heretic
340 };
341 
342 
343 // [WDJ] Monster friction, doorstuck, infight
CV_monster_OnChange(void)344 void CV_monster_OnChange(void)
345 {
346     // Set monster friction for Boom, MBF, prboom demo, by cv_monsterfriction.EV = 1.
347     if( (demoplayback && (friction_model != FR_legacy))
348         || ( cv_monsterfriction.EV > 3 ) )
349     {
350         EN_mbf_enemyfactor = (friction_model >= FR_mbf) && (friction_model <= FR_prboom);
351         // Where monster friction is determined by friction model,
352         // demo settings.
353         // If cv_monsterfriction == 0x80, then EN_monster_friction has
354         // already been set from the Boom demo compatiblity flag.
355         if( cv_monsterfriction.EV < 0x80 )
356             EN_monster_friction = EN_mbf_enemyfactor;  // MBF, PrBoom default
357         EN_monster_momentum = 0;  // 2=momentum
358     }
359     else
360     {
361         // Legacy Demo, and User settings.
362         // Legacy Demo sets through cv_monsterfriction.
363         // default: 2= momentum
364         register byte ft = friction_table[cv_monsterfriction.EV];
365         EN_monster_friction = (ft & FRE_monster_friction);
366         EN_mbf_enemyfactor = (ft & FRE_mbf_enemyfactor);
367         EN_monster_momentum = (ft & FRE_monster_momentum);
368     }
369 #ifdef FRICTIONTHINKER
370     EN_boom_friction_thinker = (friction_model == FR_boom)
371            && EN_variable_friction;
372 #endif
373 
374     // Demo sets through cv_doorstuck.
375     EN_mbf_doorstuck = (cv_doorstuck.EV == 1);  // 1=MBF
376     EN_doorstuck = (cv_doorstuck.EV > 0 );  // 0=none, 2=Boom
377 
378     // Demo sets infight through cv_monbehavior.
379     // Monster Infight enables, can be changed during game.
380     byte mbif = monbehav_to_infight[ cv_monbehavior.EV ];
381     monster_infight = ((mbif & INFT_force) || (monster_infight_deh == INFT_none))?
382         mbif // cv_monbehavior has precedence
383       : monster_infight_deh;  // from DEH
384     monster_infight &= ~INFT_force;  // clean up transient bit
385 }
386 
387 
388 // local version control
DemoAdapt_p_enemy(void)389 void DemoAdapt_p_enemy( void )
390 {
391     // Demo sets cv_monsterfriction.EV, and other cv_xxx.EV.
392     CV_monster_OnChange();
393 
394     if( demoversion < 200 )
395     {
396         EN_skull_limit = ( demoversion <= 132 );  // doom demos
397         EN_old_pain_spawn = ( demoversion < 143 );
398     }
399 
400     EN_mbf_speed = EN_mbf || (EV_legacy >= 145);  // Legacy 1.45, 1.46
401 
402 #if 1
403     if( demoplayback && verbose > 1 )
404     {
405         GenPrintf(EMSG_ver, "friction_model=%i, EN_monster_friction=%i\n",
406                 friction_model, EN_monster_friction );
407         GenPrintf(EMSG_ver, "EN_mbf_enemyfactor=%i, EN_monster_momentum=%i\n",
408                 EN_mbf_enemyfactor,  EN_monster_momentum );
409         GenPrintf(EMSG_ver, "EN_skull_limit=%i, EN_old_pain_spawn=%i, EN_doorstuck=%i, EN_mbf_doorstuck=%i\n",
410                 EN_skull_limit, EN_old_pain_spawn, EN_doorstuck, EN_mbf_doorstuck );
411     }
412 #endif
413 }
414 
415 
416 //
417 // ENEMY THINKING
418 // Enemies are always spawned
419 // with targetplayer = -1, threshold = 0
420 // Most monsters are spawned unaware of all players,
421 // but some can be made preaware
422 //
423 
424 
425 //
426 // Called by P_NoiseAlert.
427 // Recursively traverse adjacent sectors,
428 // sound blocking lines cut off traversal.
429 //
430 
431 static mobj_t*         soundtarget;
432 
433 //  soundblocks : 0..1 number of sound blocking linedefs
P_RecursiveSound(sector_t * sec,byte soundblocks)434 static void P_RecursiveSound ( sector_t*  sec, byte soundblocks )
435 {
436     int         i;
437     line_t*     check;
438     sector_t*   other;
439 
440     // wake up all monsters in this sector
441     if (sec->validcount == validcount
442         && sec->soundtraversed <= soundblocks+1)
443     {
444         return;         // already flooded
445     }
446 
447     sec->validcount = validcount;
448     sec->soundtraversed = soundblocks+1;
449     P_SetReference(sec->soundtarget, soundtarget);
450     sec->soundtarget = soundtarget;
451 
452     for (i=0 ; i<sec->linecount ; i++)
453     {
454         // for each line of the sector linelist
455         check = sec->linelist[i];
456         if (! (check->flags & ML_TWOSIDED) )
457             continue;  // nothing on other side
458 
459         P_LineOpening (check);
460 
461         if (openrange <= 0)
462             continue;   // closed door
463 
464         if ( sides[ check->sidenum[0] ].sector == sec)
465             other = sides[ check->sidenum[1] ].sector;
466         else
467             other = sides[ check->sidenum[0] ].sector;
468 
469         if (check->flags & ML_SOUNDBLOCK)
470         {
471             // Sound blocking linedef
472             // If 0, then continue recursion with 1 soundblock.
473             // If 1, then this is second, which blocks the sound.
474             if (soundblocks == 0)
475                 P_RecursiveSound (other, 1);
476         }
477         else
478             P_RecursiveSound (other, soundblocks);
479     }
480 }
481 
482 
483 
484 //
485 // P_NoiseAlert
486 // If a monster yells at a player,
487 // it will alert other monsters to the player.
488 //
P_NoiseAlert(mobj_t * target,mobj_t * emmiter)489 void P_NoiseAlert ( mobj_t* target, mobj_t* emmiter )
490 {
491     soundtarget = target;
492     validcount++;
493     P_RecursiveSound (emmiter->subsector->sector, 0);
494 }
495 
496 
497 
498 
499 //
500 // P_CheckMeleeRange
501 //
P_CheckMeleeRange(mobj_t * actor)502 static boolean P_CheckMeleeRange (mobj_t* actor)
503 {
504     mobj_t *    pl = actor->target;
505     fixed_t     dist;
506 
507     if(!pl)
508         goto ret_false;
509 
510     // [WDJ] prboom, killough, friend monsters do not attack other friend
511     if( BOTH_FRIEND(actor, pl) )
512         goto ret_false;
513 
514     dist = P_AproxDistance (pl->x-actor->x, pl->y-actor->y);
515 
516     // [WDJ] FIXME pl->info may be NULL (seen in phobiata.wad)
517     if (pl->info == NULL )
518         goto ret_false;
519     if (dist >= (MELEERANGE - 20*FRACUNIT + pl->info->radius) )
520         goto ret_false;
521 
522     //added:19-03-98: check height now, so that damn imps cant attack
523     //                you if you stand on a higher ledge.
524     if( (EV_legacy > 111)
525          && ((pl->z > actor->z + actor->height)
526              || (actor->z > pl->z + pl->height) )
527       )
528         goto ret_false;
529 
530     if (! P_CheckSight (actor, actor->target) )
531         goto ret_false;
532 
533     return true;
534 
535 ret_false:
536     return false;
537 }
538 
539 
540 // [WDJ] MBF, from MBF, PrBoom, EternityEngine
541 //
542 // P_HitFriend()
543 //
544 // killough 12/98
545 // This function tries to prevent shooting at friends.
546 // Known that (actor->flags & MF_FRIEND), checked by caller.
547 // Return true if aiming would hit a friend.
P_HitFriend(mobj_t * actor)548 static boolean P_HitFriend(mobj_t *actor)
549 {
550     mobj_t * target = actor->target;
551 
552     if( target )
553     {
554         P_AimLineAttack(actor,
555            R_PointToAngle2(actor->x, actor->y, target->x, target->y),
556            P_AproxDistance(actor->x - target->x, actor->y - target->y),
557            0 );
558         // Because actor is already known to have MF_FRIEND (tested by caller),
559         // replace SAME_FRIEND with simpler test of MF_FRIEND.
560         if( lar_linetarget
561             && lar_linetarget != target
562             && lar_linetarget->flags & MF_FRIEND
563 // MBF:	    && SAME_FRIEND(lar_linetarget, actor)
564           )
565             return true;
566     }
567     return false;
568 }
569 
570 
571 //
572 // P_CheckMissileRange
573 //
574 // Return true if good to fire missile.
P_CheckMissileRange(mobj_t * actor)575 static boolean P_CheckMissileRange (mobj_t* actor)
576 {
577     mobj_t * target = actor->target;
578     fixed_t  dist;
579 
580     if (! P_CheckSight (actor, actor->target) )
581         goto ret_false;
582 
583     if ( actor->flags & MF_JUSTHIT )
584     {
585         // the target just hit the enemy,
586         // so fight back!
587         actor->flags &= ~MF_JUSTHIT;
588 
589         if( EN_heretic || (demoversion < 147) )
590             goto ret_true;  // Old Legacy, Old Doom
591 
592         // [WDJ] MBF, from MBF, PrBoom
593         // Boom has two calls of P_Random, which affect demos
594         // killough 7/18/98: no friendly fire at corpses
595         // killough 11/98: prevent too much infighting among friends
596         return  !(actor->flags & MF_FRIEND)
597          ||( (target->health > 0)
598              &&( !(target->flags & MF_FRIEND)
599                  || (target->player ?
600                      // PrBoom, MBF, EternityEngine use P_Random(pr_defect)
601                      (MONSTER_INFIGHTING || (PP_Random(pr_defect)>128))
602                      : !(target->flags & MF_JUSTHIT) && PP_Random(pr_defect)>128  )
603                 )
604            );
605     }
606 
607     // [WDJ] MBF, from MBF, PrBoom
608     // killough 7/18/98: friendly monsters don't attack other friendly
609     // monsters or players (except when attacked, and then only once)
610     if( BOTH_FRIEND(actor, target) )
611         goto ret_false;
612 
613     if (actor->reactiontime)
614         goto ret_false;  // do not attack yet
615 
616     // OPTIMIZE: get this from a global checksight
617     dist = P_AproxDistance ( actor->x - actor->target->x,
618                              actor->y - actor->target->y) - 64*FRACUNIT;
619 
620     if (!actor->info->meleestate)
621         dist -= 128*FRACUNIT;   // no melee attack, so fire more
622 
623     dist >>= FRACBITS;
624 
625     if (actor->type == MT_VILE)
626     {
627         if (dist > 14*64)
628             goto ret_false;  // too far away
629     }
630 
631     if (actor->type == MT_UNDEAD)
632     {
633         if (dist < 196)
634             goto ret_false;  // close for fist attack
635         dist >>= 1;
636     }
637 
638 
639     if (actor->type == MT_CYBORG
640         || actor->type == MT_SPIDER
641         || actor->type == MT_SKULL
642         || actor->type == MT_IMP) // heretic monster
643     {
644         dist >>= 1;
645     }
646 
647     if (dist > 200)
648         dist = 200;
649 
650     if (actor->type == MT_CYBORG && dist > 160)
651         dist = 160;
652 
653     if (PP_Random(pr_missrange) < dist)
654         goto ret_false;
655 
656     if( actor->flags & MF_FRIEND )  // call is only useful if MBF FRIEND
657     {
658         if( P_HitFriend(actor) )
659             goto ret_false;
660     }
661 
662 ret_true:
663     return true;
664 
665 ret_false:
666     return false;
667 }
668 
669 
670 // [WDJ] MBF, from MBF, PrBoom, EternityEngine
671 // P_IsOnLift
672 //
673 // killough 9/9/98:
674 //
675 // Returns true if the object is on a lift. Used for AI, since it may indicate
676 // the need for crowded conditions, or that a monster should stay on the lift
677 // for a while while it goes up or down.
678 
P_IsOnLift(const mobj_t * actor)679 boolean P_IsOnLift( const mobj_t *actor )
680 {
681     const sector_t *sec = actor->subsector->sector;
682     line_t line;
683     int l;
684 
685     // Short-circuit: it's on a lift which is active.
686     if( sec->floordata
687         && ((thinker_t *) sec->floordata)->function.acp1 == (actionf_p1) T_PlatRaise )
688         return true;
689 
690     // Check to see if it is in a sector which can be activated as a lift.
691     line.tag = sec->tag;
692     if( line.tag == 0 )
693         return false;
694 
695     for( l = -1; (l = P_FindLineFromLineTag(&line, l)) >= 0; )
696     {
697 // [WDJ] This is only MBF. Does not include Heretic or expansions.
698         switch( lines[l].special )
699         {
700             case  10: case  14: case  15: case  20: case  21: case  22:
701             case  47: case  53: case  62: case  66: case  67: case  68:
702             case  87: case  88: case  95: case 120: case 121: case 122:
703             case 123: case 143: case 162: case 163: case 181: case 182:
704             case 144: case 148: case 149: case 211: case 227: case 228:
705             case 231: case 232: case 235: case 236:
706             return true;
707         }
708     }
709     return false;
710 }
711 
712 
713 // [WDJ] MBF, from MBF, PrBoom, EternityEngine
714 // P_IsUnderDamage
715 // killough 9/9/98:
716 // Used for AI.
717 // Returns nonzero if the object is under damage based on their current position.
718 // Returns 1 if the damage is moderate (ceiling crusher up),
719 //        -1 if it is serious (ceiling crusher down).
P_IsUnderDamage(mobj_t * actor)720 int P_IsUnderDamage(mobj_t *actor)
721 {
722     const msecnode_t * ts;  // touching sector
723     const ceiling_t * cl;   // Crushing ceiling
724     int dir = 0;
725 
726     for( ts = actor->touching_sectorlist; ts; ts = ts->m_tnext )
727     {
728         cl = ts->m_sector->ceilingdata;
729         if( cl && (cl->thinker.function.acp1 == (actionf_p1) T_MoveCeiling) )
730             dir |= cl->direction;
731     }
732     return dir;
733 }
734 
735 
736 //
737 // P_MoveActor
738 // Move in the current direction,
739 // returns false if the move is blocked.
740 //
741 static const fixed_t xspeed[8] = {FRACUNIT,47000,0,-47000,-FRACUNIT,-47000,0,47000};
742 static const fixed_t yspeed[8] = {0,47000,FRACUNIT,47000,0,-47000,-FRACUNIT,-47000};
743 
744 // Called multiple times in one step, by P_TryWalk, while trying to find
745 // valid path for an actor.
746 // Called by P_SmartMove, indirectly P_TryWalk, A_Chase.
747 // Only called for actor things, not players, nor missiles.
748 //  dropoff : 0, 1, 2 dog jump
749 // Formerly P_Move.
750 // Return false when move is blocked.
P_MoveActor(mobj_t * actor,byte dropoff)751 static boolean P_MoveActor (mobj_t* actor, byte dropoff)
752 {
753     fixed_t  tryx, tryy;
754     fixed_t  old_momx, old_momy;
755     line_t*  ld;
756     boolean  good;
757     int      hit_block = 0;
758     int      speed = actor->info->speed;
759 
760     if (actor->movedir == DI_NODIR)
761         return false;
762 
763 #ifdef PARANOIA
764     if ((unsigned)actor->movedir >= 8)
765         I_Error ("Weird actor->movedir!");
766 #endif
767 
768     old_momx = actor->momx;
769     old_momy = actor->momy;
770 
771     // [WDJ] Monsters too, killough 10/98
772     // Otherwise they move too easily on mud and ice
773     if(EN_monster_friction
774        && (actor->z <= actor->floorz)  // must be on floor
775        && !(actor->flags2&MF2_FLY)  // not heretic flying monsters
776        )
777     {
778         // MBF, prboom: all into speed (except ice)
779         // DoomLegacy: fixed proportion of momentum and speed
780         fixed_t  dx = speed * xspeed[actor->movedir];
781         fixed_t  dy = speed * yspeed[actor->movedir];
782         // [WDJ] Math: Part of speed goes into momentum, R=0.5.
783         // fr = FRICTION_NORM/FRACUNIT = 0.90625
784         // movement due to momentum (for i=0..)
785         //         = Sum( momf * speed * (fr**i)) = momf * speed / (1-fr)
786         // Thus: momf = (1- (FRICTION_NORM/FRACUNIT)) * R
787         // For R=0.5, momf = 0.046875 = 3/64
788         float  momf = ( EN_mbf_enemyfactor )? 0.0 : 0.046875f;  // default
789         float  mdiffm = 1.0f;  // movefactor diff mult (to reduce effect)
790 
791         P_GetMoveFactor(actor);  // sets got_movefactor, got_friction
792         if( got_friction < FRICTION_NORM )
793         {   // mud
794             if( EN_mbf_enemyfactor )
795             {   // MBF, prboom: modify speed
796                 mdiffm = 0.5f;  // 1/2 by MBF
797             }
798             else
799             {   // DoomLegacy:
800                 // movefactor is for cmd=25, but speed=8 for player size actor
801                 mdiffm = 0.64f;  // by experiment, larger is slower
802             }
803         }
804         else if (got_friction > FRICTION_NORM )
805         {   // ice
806             // Avoid adding momentum into walls
807             tryx = actor->x + dx;
808             tryy = actor->y + dy;
809             // [WDJ] Do not use TryMove to check position, as it has
810             // too many side effects and is not reversible.
811             // Cannot just reset actor x,y.
812             if (P_CheckPosition (actor, tryx, tryy))
813             { // success, give it momentum too
814                 if( EN_mbf_enemyfactor )
815                 {   // MBF, prboom:
816                     speed = 0;  // put it all into momf
817                     momf = 0.25f;  // becomes MBF equation
818                 }
819                 else
820                 {   // DoomLegacy:
821                     // monsters a little better in slime than humans
822                     mdiffm = 0.62f; // by experiment, larger is slower
823 #if 0
824                     // [WDJ] proportional as movefactor decreases
825                     // This worked better than MBF, but still showed friction transistion accel and decel.
826                     fixed_t pro = FRACUNIT * (ORIG_FRICTION_FACTOR - got_movefactor) / (ORIG_FRICTION_FACTOR*69/100);
827                     if( pro > FRACUNIT )  pro = FRACUNIT; // limit to 1
828                     fixed_t anti_pro = (FRACUNIT - pro);
829                     momf = FixedMul( momf, pro ); // pro to momentum
830                     anti_pro = FixedMul(anti_pro, anti_pro); // (1-pro)**2 to speed
831                     got_movefactor = FixedMul( got_movefactor, anti_pro);
832 #endif
833                 }
834             }
835             else
836             {
837                 momf = 0.0f;  // otherwise they get stuck at walls
838             }
839         }
840         // [WDJ] Apply mdiffm to difference between movefactor and normal.
841         // movefactor has ORIG_FRICTION_FACTOR
842         // movediff = (got_movefactor - ORIG_FRICTION_FACTOR) * mdiffm
843         // ratio = (ORIG_FRICTION_FACTOR + movediff) / ORIG_FRICTION_FACTOR
844         float mf_ratio = (
845           (((float)(got_movefactor - ORIG_FRICTION_FACTOR)) * mdiffm)
846             + ((float)ORIG_FRICTION_FACTOR)
847           ) / ((float)ORIG_FRICTION_FACTOR);
848 
849         // modify speed by movefactor
850         speed = (int) ( speed * mf_ratio ); // MBF, prboom: no momentum
851 
852         // [WDJ] Trying to use momentum for some sectors and not for others
853         // results in stall when entering an icy momentum sector,
854         // and speed surge when leaving an icy momentum sector.
855         // Use it for all sectors to the same degree (except in demo compatibility).
856         if( momf > 0.0 )  // not disabled
857         {
858             // It is necessary that there be full speed at dead stop to get
859             // monsters unstuck from walls and each other.
860             // Some wads like TNT have overlapping monster starting positions.
861             if( actor->momx || actor->momy )  // if not at standstill
862                 speed /= 2;  // half of speed goes to momentum
863             // apply momentum
864             momf *= mf_ratio;
865             actor->momx += (int) ( dx * momf );
866             actor->momy += (int) ( dy * momf );
867         }
868         else
869         {
870             // if momf disabled, then minimum speed
871             // Minimum speed with momf causes them to walk off ledges.
872             if( speed == 0 )  speed = 1;
873         }
874     }
875 
876     tryx = actor->x + speed * xspeed[actor->movedir];
877     tryy = actor->y + speed * yspeed[actor->movedir];
878 
879     if( !P_TryMove (actor, tryx, tryy, dropoff) )  // caller dropoff
880     {
881         // blocked move
882         // Monsters will be here multiple times in each step while
883         // trying to find sucessful path.
884         actor->momx = old_momx;  // cancel any momentum changes for next try
885         actor->momy = old_momy;
886 
887         // open any specials
888         // tmr_floatok, tmr_floorz returned by P_TryMove
889         if( (actor->flags & MF_FLOAT) && tmr_floatok )
890         {
891             // must adjust height
892             if (actor->z < tmr_floorz)
893                 actor->z += FLOATSPEED;
894             else
895                 actor->z -= FLOATSPEED;
896 
897             actor->flags |= MF_INFLOAT;
898             return true;
899         }
900 
901         if (!numspechit)
902             return false;
903 
904         actor->movedir = DI_NODIR;
905         good = false;
906         while (numspechit--)
907         {
908             ld = &lines[ spechit[numspechit] ];
909             // [WDJ] FIXME: Monsters get stuck in the door track when
910             // they see the door activation and nothing else.
911             // If the special is not a door that can be opened,
912             // return false
913             if( P_UseSpecialLine (actor, ld, 0) )
914             {
915                 if( EN_mbf_doorstuck && (ld == tmr_blockingline))
916                    hit_block = 1;
917                 good = true;
918             }
919         }
920 
921         if( good && EN_doorstuck )
922         {
923             // [WDJ] Calls of P_Random here in Boom, affects Demo sync.
924             // A line blocking the monster got activated, a little randomness
925             // to get unstuck from door frame.
926             if (EN_mbf_doorstuck)
927                 good = (PP_Random(pr_opendoor) >= 230) ^ (hit_block);  // MBF
928             else
929                 good = PP_Random(pr_trywalk) & 3;  // Boom jff, 25% fail
930                 // Causes monsters to back out when they should not,
931                 // and causes secondary stickiness.
932         }
933         return good;
934     }
935     else  // TryMove
936     {
937         // successful move
938 
939 #if 0
940         // Boom ice, from PrBoom
941         if( got_friction > ORIG_FRICTION )
942         {
943             // Let normal momentum carry them across ice.
944             actor->x = origx;
945             actor->y = origy;
946             movefactor *= FRACUNIT / ORIG_FRICTION_FACTOR / 4;
947             actor->momx += FixedMul( dx, movefactor );
948             actor->momy += FixedMul( dy, movefactor );
949         }
950 #endif
951 
952         if( EN_monster_momentum && tmr_dropoffline )
953         {
954             // [WDJ] last move sensed dropoff
955             // Reduce momentum near dropoffs, friction 0x4000 to 0xE000
956 #define  FRICTION_DROPOFF   0x6000
957             actor->momx = FixedMul(actor->momx, FRICTION_DROPOFF);
958             actor->momy = FixedMul(actor->momx, FRICTION_DROPOFF);
959         }
960         actor->flags &= ~MF_INFLOAT;
961     }
962 
963 
964     if(! (actor->flags & MF_FLOAT) )
965     {
966         // Monster gravity, or MBF felldown, blocks this vanilla instant fall.
967         if( ! ( (cv_monstergravity.EV == 2)
968                || ((cv_monstergravity.EV == 1) && tmr_felldown) ) )
969         {
970             if(actor->z > actor->floorz)
971                P_HitFloor(actor);
972             actor->z = actor->floorz;
973         }
974     }
975     return true;
976 }
977 
978 
979 // [WDJ] From MBF, PrBoom
980 // P_SmartMove
981 // killough 9/12/98: Same as P_Move, except smarter
P_SmartMove(mobj_t * actor)982 static boolean P_SmartMove(mobj_t *actor)
983 {
984     mobj_t * target = actor->target;
985     byte  on_lift;
986     byte  dropoff = 0;  // 0,1,2
987     int   under_damage = 0;  // -1,0,1
988 
989     // killough 9/12/98: Stay on a lift if target is on one
990     on_lift = cv_mbf_staylift.EV
991       && target
992       && target->health > 0
993       && target->subsector->sector->tag == actor->subsector->sector->tag
994       && P_IsOnLift(actor);
995 
996     if( cv_mbf_monster_avoid_hazard.EV )
997         under_damage = P_IsUnderDamage(actor);  // 1, 0, -1
998 
999 #ifdef DOGS
1000     // killough 10/98: allow dogs to drop off of taller ledges sometimes.
1001     // dropoff==1 means always allow it,
1002     // dropoff==2 means only up to 128 high,
1003     // and only if the target is immediately on the other side of the line.
1004 
1005     // haleyjd: allow all friends of Helper_MT Type to also jump down
1006 
1007     if( cv_mbf_dog_jumping.EV
1008         && (actor->type == MT_DOG
1009             || ((actor->type == helper_MT) && (actor->flags & MF_FRIEND)) )
1010         && target
1011         && SAME_FRIEND(target, actor)
1012         && P_AproxDistance(actor->x - target->x, actor->y - target->y)
1013              < FRACUNIT*144
1014         && (PP_Random(pr_dropoff) < 235)
1015       )
1016         dropoff = 2;
1017 #endif
1018 
1019     if( !P_MoveActor(actor, dropoff) )
1020         return false;
1021 
1022     // killough 9/9/98: avoid crushing ceilings or other damaging areas
1023     if( on_lift
1024         && (PP_Random(pr_stayonlift) < 230) // Stay on lift
1025         && !P_IsOnLift(actor)
1026       )
1027         goto avoid_damage;
1028 
1029     if( cv_mbf_monster_avoid_hazard.EV
1030         && (under_damage == 0) )
1031     {
1032         // Get away from damage
1033         under_damage = P_IsUnderDamage(actor);
1034         if( under_damage
1035             && ((under_damage < 0) || (PP_Random(pr_avoidcrush) < 200)) )
1036             goto avoid_damage;
1037     }
1038 
1039     return true;
1040 
1041 avoid_damage:
1042     actor->movedir = DI_NODIR;  // avoid the area (most of the time anyway)
1043     return true;
1044 }
1045 
1046 
1047 line_t * trywalk_dropoffline;
1048 
1049 //
1050 // TryWalk
1051 // Attempts to move actor on
1052 // in its current (ob->moveangle) direction.
1053 // If blocked by either a wall or an actor
1054 // returns FALSE
1055 // If move is either clear or blocked only by a door,
1056 // returns TRUE and sets...
1057 // If a door is in the way,
1058 // an OpenDoor call is made to start it opening.
1059 // Called multiple times in one step, while trying to find valid path.
1060 //
P_TryWalk(mobj_t * actor)1061 static boolean P_TryWalk (mobj_t* actor)
1062 {
1063     if( !P_SmartMove(actor) )  // P_MoveActor()
1064     {
1065         // record if failing due to dropoff
1066         if( tmr_dropoffline )
1067             trywalk_dropoffline = tmr_dropoffline;
1068         return false;
1069     }
1070     actor->movecount = PP_Random(pr_trywalk) & 0x0F;
1071     return true;
1072 }
1073 
1074 
1075 
1076 // [WDJ] MBF, From MBF, PrBoom, EternityEngine
1077 // killough 11/98:
1078 //
1079 // Monsters try to move away from tall dropoffs.
1080 //
1081 // In Doom, they were never allowed to hang over dropoffs,
1082 // and would remain stuck if involuntarily forced over one.
1083 // This logic, combined with p_map.c (P_TryMove), allows
1084 // monsters to free themselves without making them tend to
1085 // hang over dropoffs.
1086 
1087 static fixed_t dropoff_r_deltax, dropoff_r_deltay, dropoff_floorz;
1088 
1089 // Always returns true, searches all lines.
PIT_AvoidDropoff(line_t * line)1090 static boolean PIT_AvoidDropoff(line_t *line)
1091 {
1092     if( line->backsector // Ignore one-sided linedefs
1093         && tm_bbox[BOXRIGHT]  > line->bbox[BOXLEFT]
1094         && tm_bbox[BOXLEFT]   < line->bbox[BOXRIGHT]
1095         && tm_bbox[BOXTOP]    > line->bbox[BOXBOTTOM] // Linedef must be contacted
1096         && tm_bbox[BOXBOTTOM] < line->bbox[BOXTOP]
1097         && P_BoxOnLineSide(tm_bbox, line) == -1
1098       )
1099     {
1100         // Touching the line.
1101         fixed_t front = line->frontsector->floorheight;
1102         fixed_t back  = line->backsector->floorheight;
1103         angle_t angle;
1104 
1105         // The monster must contact one of the two floors,
1106         // and the other must be a tall dropoff (more than 24).
1107         if( back == dropoff_floorz
1108             && front < dropoff_floorz - FRACUNIT*24 )
1109         {
1110             // front side dropoff
1111             angle = R_PointToAngle2(0,0,line->dx,line->dy);
1112         }
1113         else if( front == dropoff_floorz
1114              && back < dropoff_floorz - FRACUNIT*24 )
1115         {
1116             // back side dropoff
1117             angle = R_PointToAngle2(line->dx,line->dy,0,0);
1118         }
1119         else
1120             return true;  // not a dropoff
1121 
1122         // Move away from dropoff at a standard speed.
1123         // Multiple contacted linedefs are cumulative
1124         // (e.g. hanging over corner)
1125         dropoff_r_deltax -= finesine[angle >> ANGLETOFINESHIFT]*32;
1126         dropoff_r_deltay += finecosine[angle >> ANGLETOFINESHIFT]*32;
1127     }
1128     return true;
1129 }
1130 
1131 //
1132 // Driver for above
1133 //
P_AvoidDropoff(mobj_t * actor)1134 static fixed_t P_AvoidDropoff(mobj_t *actor)
1135 {
1136     int yh, yl, xh, xl;
1137     int bx, by;
1138 
1139     tm_bbox[BOXTOP] = actor->y + actor->radius;
1140     tm_bbox[BOXBOTTOM] = actor->y - actor->radius;
1141     tm_bbox[BOXRIGHT] = actor->x + actor->radius;
1142     tm_bbox[BOXLEFT] = actor->x - actor->radius;
1143     yh = (tm_bbox[BOXTOP] - bmaporgy) >> MAPBLOCKSHIFT;
1144     yl = (tm_bbox[BOXBOTTOM] - bmaporgy) >> MAPBLOCKSHIFT;
1145     xh = (tm_bbox[BOXRIGHT] - bmaporgx) >> MAPBLOCKSHIFT;
1146     xl = (tm_bbox[BOXLEFT] - bmaporgx) >> MAPBLOCKSHIFT;
1147 
1148     // PIT_AvoidDropoff parameters
1149     dropoff_floorz = actor->z;  // remember floor height
1150     dropoff_r_deltax = dropoff_r_deltay = 0;  // outputs
1151 
1152     // check lines
1153 
1154     validcount++;
1155     for( bx=xl ; bx<=xh ; bx++ )
1156     {
1157         for( by=yl ; by<=yh ; by++ )
1158             P_BlockLinesIterator(bx, by, PIT_AvoidDropoff);  // all contacted lines
1159     }
1160 
1161     // Non-zero if movement prescribed, caller uses dropoff vars.
1162     return dropoff_r_deltax | dropoff_r_deltay;
1163 }
1164 
1165 
1166 static
P_NewChaseDir_P2(mobj_t * actor,fixed_t deltax,fixed_t deltay)1167 void P_NewChaseDir_P2 (mobj_t * actor, fixed_t deltax, fixed_t deltay )
1168 {
1169     int8_t      xdir, ydir, tdir;  // dirtype_t  0..9
1170     dirtype_t   olddir = actor->movedir;
1171     dirtype_t   turnaround = DI_OPPOSITE( olddir );
1172 
1173     trywalk_dropoffline = NULL;  // clear dropoff record
1174 
1175     xdir = ( deltax > 10*FRACUNIT )?   DI_EAST
1176          : ( deltax < -10*FRACUNIT )?  DI_WEST
1177          : DI_NODIR ;
1178 
1179     ydir = ( deltay < -10*FRACUNIT )? DI_SOUTH
1180          : ( deltay > 10*FRACUNIT )?  DI_NORTH
1181          : DI_NODIR ;
1182 
1183     // try direct route
1184     if (   xdir != DI_NODIR
1185         && ydir != DI_NODIR)
1186     {
1187         actor->movedir = diags[((deltay<0)<<1)+(deltax>0)];
1188         if( actor->movedir != turnaround
1189             && P_TryWalk(actor) )
1190             goto accept_move;
1191     }
1192 
1193     // try other directions
1194     if( PP_Random(pr_newchase) > 200
1195         ||  abs(deltay) > abs(deltax))
1196     {
1197         tdir=xdir;
1198         xdir=ydir;
1199         ydir=tdir;
1200     }
1201 
1202     if( xdir == turnaround)
1203         xdir = DI_NODIR;
1204     if( ydir == turnaround)
1205         ydir = DI_NODIR;
1206 
1207     if( xdir != DI_NODIR )
1208     {
1209         actor->movedir = xdir;
1210         if( P_TryWalk(actor) )
1211         {
1212             // either moved forward or attacked
1213             goto accept_move;
1214         }
1215     }
1216 
1217     if( ydir != DI_NODIR )
1218     {
1219         actor->movedir = ydir;
1220         if( P_TryWalk(actor) )
1221             goto accept_move;
1222     }
1223 
1224     // there is no direct path to the player, so pick another direction.
1225     if( olddir != DI_NODIR )
1226     {
1227         actor->movedir = olddir;
1228         if( P_TryWalk(actor) )
1229             goto accept_move;
1230     }
1231 
1232     // randomly determine direction of search
1233     if( PP_Random(pr_newchasedir) & 0x01 )
1234     {
1235         for( tdir = DI_EAST; tdir <= DI_SOUTHEAST; tdir++ )
1236         {
1237             if( tdir != turnaround )
1238             {
1239                 actor->movedir = tdir;
1240                 if( P_TryWalk(actor) )
1241                     goto accept_move;
1242             }
1243         }
1244     }
1245     else
1246     {
1247         for( tdir = DI_SOUTHEAST; tdir >= DI_EAST; tdir-- )  // signed tdir
1248         {
1249             if( tdir != turnaround )
1250             {
1251                 actor->movedir = tdir;
1252                 if( P_TryWalk(actor) )
1253                     goto accept_move;
1254             }
1255         }
1256     }
1257 
1258 #define PRBOOM2_TURNAROUND
1259 #ifdef PRBOOM2_TURNAROUND
1260     // As in PrBoom
1261     // Rearranged to work with monster_momentum test below.
1262     actor->movedir = turnaround;
1263     if( turnaround != DI_NODIR )
1264     {
1265         if( P_TryWalk(actor) )
1266 	    goto accept_move;
1267             // accept move, movdir != DI_NODIR
1268 
1269         actor->movedir = DI_NODIR;
1270     }
1271     // assert actor->movedir == DI_NODIR
1272 #else
1273     // [WDJ] This seems to be functionally the same as above
1274     // due to failure setting NODIR anyway.
1275     if( turnaround != DI_NODIR )
1276     {
1277         actor->movedir = turnaround;
1278         if( P_TryWalk(actor) )
1279             goto accept_move;
1280     }
1281 #endif
1282 
1283     // [WDJ] to not glide off ledges, unless conveyor
1284     if( EN_monster_momentum && trywalk_dropoffline )
1285     {
1286         // [WDJ] Momentum got actor stuck on edge,
1287         // but just reversing momentum is too much for conveyor.
1288         // Move perpendicular to dropoff line to get unstuck.
1289         fixed_t  dax, day;
1290         if( actor->subsector->sector == trywalk_dropoffline->frontsector )
1291         {
1292             // vector to frontsector
1293             dax = trywalk_dropoffline->dy;
1294             day = -trywalk_dropoffline->dx;
1295         }
1296         else if( actor->subsector->sector == trywalk_dropoffline->backsector )
1297         {
1298             // vector to backsector
1299             dax = -trywalk_dropoffline->dy;
1300             day = trywalk_dropoffline->dx;
1301         }
1302         else
1303         {
1304             actor->momx = actor->momy = 0;
1305             goto no_move;  // don't move across the dropoff
1306         }
1307         // Observe monsters on ledge, how long they stay stuck,
1308         // and on conveyor, how long they take to fall off.
1309         // Tuned backpedal speed constant.
1310         register int  backpedal = (actor->info->speed * 0x87BB);
1311 //        debug_Printf( "backpedal 0x%X ", backpedal );
1312         // Vector away from line
1313         fixed_t  dal = P_AproxDistance(dax,day);
1314         dal = FixedDiv( dal, backpedal );
1315         if( dal > 1 )
1316         {
1317             dax = FixedDiv( dax, dal );  // shorten vector
1318             day = FixedDiv( day, dal );
1319         }
1320 
1321 //        debug_Printf( "stuck delta (0x%X,0x%X)\n", dax, day );
1322         if (P_TryMove (actor, actor->x + dax, actor->y + day, true))  // allow cross dropoff
1323             goto accept_move;
1324 
1325     no_move:
1326         // must fall off conveyor end, but not off high ledges
1327         actor->momx = FixedMul(actor->momx, 0xA000);
1328         actor->momy = FixedMul(actor->momx, 0xA000);
1329     }
1330 
1331     actor->movedir = DI_NODIR;  // can not move
1332 
1333 accept_move:
1334     return;
1335 }
1336 
1337 
1338 // [WDJ] MBF, from MBF, PrBoom, EternityEngine
1339 // Most of the original P_NewChaseDir is now in P_NewChaseDir_P2.
P_NewChaseDir(mobj_t * actor)1340 static void P_NewChaseDir(mobj_t *actor)
1341 {
1342     mobj_t *  target = actor->target;
1343     fixed_t deltax, deltay;
1344 
1345     if( !target )
1346     {
1347         I_SoftError ("P_NewChaseDir: called with no target");
1348         return;
1349     }
1350 
1351     deltax = target->x - actor->x;
1352     deltay = target->y - actor->y;
1353 
1354     // killough 8/8/98: sometimes move away from target, keeping distance
1355     //
1356     // 1) Stay a certain distance away from a friend, to avoid being in their way
1357     // 2) Take advantage over an enemy without missiles, by keeping distance
1358 
1359     actor->strafecount = 0;  // MBF
1360 
1361     if( EN_mbf )
1362     {
1363         // CheckPosition has not been called, so tmr_dropoffz is not valid.
1364         // MBF has mobj keep dropoffz field.
1365         if( cv_mbf_dropoff.EV
1366             && actor->floorz - actor->dropoffz > FRACUNIT*24
1367             && actor->z <= actor->floorz
1368             && !(actor->flags & (MF_DROPOFF|MF_FLOAT))
1369             && P_AvoidDropoff(actor) // Move away from dropoff
1370           )
1371         {
1372             P_NewChaseDir_P2(actor, dropoff_r_deltax, dropoff_r_deltay);
1373 
1374             // If moving away from dropoff, set movecount to 1 so that
1375             // small steps are taken to get monster away from dropoff.
1376 
1377             actor->movecount = 1;
1378             return;
1379         }
1380         else
1381         {
1382             fixed_t dist = P_AproxDistance(deltax, deltay);
1383 
1384             // Move away from friends when too close, except
1385             // in certain situations (e.g. a crowded lift)
1386 
1387             if( BOTH_FRIEND(actor, target)
1388                 && dist < EV_mbf_distfriend
1389                 && !P_IsOnLift(target)
1390                 && !P_IsUnderDamage(actor)
1391               )
1392             {
1393                 deltax = -deltax;
1394                 deltay = -deltay;
1395             }
1396             else if( target->health > 0
1397                      && ! SAME_FRIEND(actor, target) )
1398             {   // Live enemy target
1399                 if( cv_mbf_monster_backing.EV
1400                     && actor->info->missilestate
1401                     && actor->type != MT_SKULL
1402                     && ( (!target->info->missilestate
1403                           && dist < MELEERANGE*2 )
1404                         || (target->player
1405                             && dist < MELEERANGE*3
1406                             && (target->player->readyweapon == wp_fist
1407                                 || target->player->readyweapon == wp_chainsaw) ))
1408                   )
1409                 {   // Back away from melee attacker
1410                     actor->strafecount = PP_Random(pr_enemystrafe) & 0x0F;
1411                     deltax = -deltax;
1412                     deltay = -deltay;
1413                 }
1414             }
1415         }
1416     }
1417 
1418     P_NewChaseDir_P2(actor, deltax, deltay);
1419 
1420     // If strafing, set movecount to strafecount so that old Doom
1421     // logic still works the same, except in the strafing part
1422 
1423     if (actor->strafecount)
1424         actor->movecount = actor->strafecount;
1425 }
1426 
1427 
1428 // [WDJ] MBF, From MBF, PrBoom, EternityEngine
1429 //
1430 // P_IsVisible
1431 //
1432 // killough 9/9/98: whether a target is visible to a monster
1433 //
1434 
P_IsVisible(mobj_t * actor,mobj_t * mo,boolean allaround)1435 static boolean P_IsVisible(mobj_t *actor, mobj_t *mo, boolean allaround)
1436 {
1437     if( !allaround )
1438     {
1439         // Not visible if not within 90 degree of facing, and outside MELEE range.
1440         angle_t an = R_PointToAngle2(actor->x, actor->y, mo->x, mo->y)
1441                      - actor->angle;
1442 
1443         if( ( an > ANG90 && an < ANG270 )
1444             && ( P_AproxDistance((mo->x - actor->x), (mo->y - actor->y))
1445                  > MELEERANGE )
1446           )
1447             return false;
1448         // possibly visible, check sight
1449     }
1450     return P_CheckSight(actor, mo);
1451 }
1452 
1453 
1454 // [WDJ] MBF, From MBF, PrBoom, EternityEngine
1455 //
1456 // PIT_FindTarget
1457 //
1458 // killough 9/5/98
1459 //
1460 // Finds monster targets for other monsters
1461 //
1462 
1463 static mobj_t * ft_current_actor;
1464 static int ft_current_allaround;
1465 
PIT_FindTarget(mobj_t * mo)1466 static boolean PIT_FindTarget(mobj_t *mo)
1467 {
1468     mobj_t *actor = ft_current_actor;
1469 
1470     if( SAME_FRIEND(mo, actor)  // Invalid target
1471         || ! (mo->health > 0)
1472         || ! (mo->flags & MF_COUNTKILL || mo->type == MT_SKULL)
1473       )
1474         return true;  // reject, keep searching
1475 
1476     // If the monster is already engaged in a one-on-one attack
1477     // with a healthy friend, don't attack around 60% the time
1478     {
1479         const mobj_t * t2 = mo->target;  // target of this monster
1480         if( t2
1481             && t2->target == mo  // both targeting each other
1482             && PP_Random(pr_skiptarget) > 100  // stuck with test order
1483           )
1484         {
1485             // Already known that mo is not a friend.
1486             if( ! SAME_FRIEND(t2, mo)  // is attacking a friend
1487                 && t2->health*2 >= t2->info->spawnhealth ) // friend health ok
1488                 return true;  // find someone else
1489         }
1490     }
1491 
1492     if( !P_IsVisible(actor, mo, ft_current_allaround) )
1493         return true;
1494 
1495     P_SetReference(actor->lastenemy, actor->target);  // Remember previous target
1496     actor->lastenemy = actor->target;
1497     P_SetReference(actor->target, mo);                // Found target
1498     actor->target = mo;
1499 
1500     // Move the selected monster to the end of its class-list,
1501     // so that it gets searched last next time.
1502     P_MoveClassThink(&mo->thinker, 0);  // Last
1503 
1504     return false;  // Found target
1505 }
1506 
1507 
1508 //
1509 // P_LookForPlayers
1510 // If allaround is false, only look 180 degrees in front.
1511 // Modifies actor.
1512 // Returns true if a player is targeted.
1513 //
P_LookForPlayers(mobj_t * actor,boolean allaround)1514 static boolean P_LookForPlayers ( mobj_t*       actor,
1515                                   boolean       allaround )
1516 {
1517     int  c;
1518     int  stop, stopc;
1519     int  lastlook = actor->lastlook;
1520     player_t*   player;
1521 
1522     if( EN_heretic
1523         && !multiplayer && players[0].health <= 0 )
1524     { // Single player game and player is dead, look for monsters
1525         return(PH_LookForMonsters(actor));  // Heretic version
1526     }
1527 
1528     // [WDJ] MBF, From MBF, PrBoom, EternityEngine.
1529     if( actor->flags & MF_FRIEND )
1530     {   // killough 9/9/98: friendly monsters go about players differently
1531         int anyone;
1532 
1533 #if 0
1534         // If you want friendly monsters not to awaken unprovoked
1535         if( !allaround )
1536             return false;
1537 #endif
1538 
1539         // Go back to a player, no matter whether it's visible or not
1540         // First time look for visible player, then look for anyone.
1541         for( anyone=0; anyone<=1; anyone++ )
1542         {
1543             for( c=0; c<MAXPLAYERS; c++ )
1544             {
1545                 if( playeringame[c]
1546                     && players[c].playerstate == PST_LIVE
1547                     && (anyone
1548                         || P_IsVisible(actor, players[c].mo, allaround) )
1549                   )
1550                 {
1551                     P_SetReference(actor->target, players[c].mo);
1552                     actor->target = players[c].mo;
1553 
1554                     // killough 12/98:
1555                     // get out of refiring loop, to avoid hitting player accidentally
1556                     if( actor->info->missilestate )
1557                     {
1558                         P_SetMobjState(actor, actor->info->seestate);
1559                         actor->flags &= ~MF_JUSTHIT;
1560                     }
1561 
1562                     return true;
1563                 }
1564             }
1565         }
1566         return false;
1567     }
1568 
1569     // Monster looking for a player to attack!
1570 
1571     // Don't look for a player if ignoring
1572     if( actor->eflags & MF_IGNOREPLAYER )
1573         return false;
1574 
1575 //        sector = actor->subsector->sector;
1576 
1577     // This is not in PrBoom, EternityEngine, and it uses P_Random !!!
1578     // BP: first time init, this allow minimum lastlook changes
1579     if( (lastlook < 0) && (EV_legacy >= 129) )
1580     {
1581         // Legacy use of P_Random, enabled by EV_legacy >= 129.
1582         // Boom, MBF demo assumes MAXPLAYERS=4, but Legacy MAXPLAYERS=32.
1583         lastlook = PP_Random(pL_initlastlook) % MAXPLAYERS;
1584     }
1585 
1586     actor->lastlook = lastlook;
1587     stop = (lastlook-1)&PLAYERSMASK;
1588     stopc = ( !EN_mbf
1589               && EN_boom  // !demo_compatibility
1590               && cv_monster_remember.EV )? MAXPLAYERS : 2;  // killough 9/9/98
1591 
1592     for ( ; ; lastlook = (lastlook+1)&PLAYERSMASK )
1593     {
1594         actor->lastlook = lastlook;
1595 
1596         // [WDJ] The test for stop is ignored in PrBoom, MBF,
1597         // if that player is not present (broken test).
1598         // This test just gets to done_looking sooner,
1599         // which is not affected by playeringame.
1600         // But it affects SyncDebug.
1601         // done looking
1602         if( (EV_legacy >= 147 )
1603             && (lastlook == stop) )  goto done_looking;
1604 
1605         if( !playeringame[lastlook] )
1606             continue;
1607 
1608         if( --stopc < 0 )  goto done_looking;
1609 
1610         // Where PrBoom has this test, ignored when stop player is not present.
1611         if( lastlook == stop )  goto done_looking;  // broken test as in PrBoom
1612 
1613         player = &players[lastlook];
1614         if (player->health <= 0)
1615             continue;           // dead
1616 
1617         // MBF, PrBoom: IsVisible does same as previous code.
1618         if( !P_IsVisible( actor, player->mo, allaround) )
1619             continue;           // out of sight
1620 
1621         // Player found.
1622 
1623         if( EN_heretic && player->mo->flags&MF_SHADOW)
1624         { // Player is invisible
1625             if((P_AproxDistance(player->mo->x-actor->x,
1626                 player->mo->y-actor->y) > 2*MELEERANGE)
1627                 && P_AproxDistance(player->mo->momx, player->mo->momy)
1628                 < 5*FRACUNIT)
1629             { // Player is sneaking - can't detect
1630                 goto  none_found;
1631             }
1632 
1633             if(PP_Random(ph_notseen) < 225)
1634             { // Player isn't sneaking, but still didn't detect
1635                 goto  none_found;
1636             }
1637         }
1638 
1639         // Remember old target node for later
1640         if (actor->target)
1641         {
1642             if(actor->target->type == MT_NODE)
1643                actor->targetnode = actor->target;
1644         }
1645 
1646         // New target found
1647         P_SetReference(actor->target, player->mo);
1648         actor->target = player->mo;
1649 
1650         // killough 9/9/98: give monsters a threshold towards getting players
1651         // we don't want it to be too easy for a player with dogs :)
1652         if( cv_mbf_pursuit.EV )  // !comp[comp_pursuit]
1653             actor->threshold = 60;
1654 
1655         return true;
1656     }
1657     goto  none_found;
1658 
1659 done_looking:
1660     // [WDJ] MBF, From MBF, PrBoom, EternityEngine.
1661     // e6y
1662     // Fixed Boom incompatibilities. The following code was missed.
1663     // There are no more desyncs on Donce's demos on horror.wad (in PrBoom).
1664 
1665     if( !EN_mbf
1666         && EN_boom  // !demo_compatibility
1667         && cv_monster_remember.EV )
1668     {
1669         // Use last known enemy if no players sighted -- killough 2/15/98:
1670         if( actor->lastenemy && (actor->lastenemy->health > 0) )
1671         {
1672             actor->target = actor->lastenemy;
1673             actor->lastenemy = NULL;
1674             return true;
1675         }
1676     }
1677 
1678 none_found:
1679     return false;
1680 }
1681 
1682 
1683 // [WDJ] MBF, From MBF, PrBoom, EternityEngine.
1684 //
1685 // Friendly monsters, by Lee Killough 7/18/98
1686 //
1687 // Friendly monsters go after other monsters first, but also return to owner
1688 // if they cannot find any targets.
1689 // A marine's best friend :)  killough 7/18/98, 9/98
1690 
P_LookForMonsters(mobj_t * actor,boolean allaround)1691 static boolean P_LookForMonsters(mobj_t *actor, boolean allaround)
1692 {
1693     int x, y, d;
1694     thinker_t *cap, *th;
1695 
1696     if( ! EN_boom )
1697         return false;
1698 
1699     // Boom: This test is at the end of P_LookForPlayers.
1700     if( cv_monster_remember.EV   // Boom smartypants = monsters_remember && EN_boom
1701         && actor->lastenemy
1702         && actor->lastenemy->health > 0
1703         && ! BOTH_FRIEND(actor->lastenemy, actor) // not friends
1704       )
1705     {
1706         // Boom
1707         // Use last known enemy if no players sighted.
1708         P_SetReference(actor->target, actor->lastenemy);
1709         actor->target = actor->lastenemy;
1710         P_SetReference(actor->lastenemy, NULL);
1711         actor->lastenemy = NULL;
1712         return true;
1713     }
1714 
1715     // Old demos do not support monster-seeking bots
1716     if( ! EN_mbf )
1717         return false;
1718 
1719     // Search the class-list corresponding to this actor's potential targets
1720     cap = &thinkerclasscap[actor->flags & MF_FRIEND ? TH_enemies : TH_friends];
1721     if( cap->cnext == cap )  // When empty list, bail out early
1722         return false;
1723 
1724     // Search for new enemy
1725     x = (actor->x - bmaporgx)>>MAPBLOCKSHIFT;
1726     y = (actor->y - bmaporgy)>>MAPBLOCKSHIFT;
1727 
1728     // Parameters PIT_FindTarget
1729     ft_current_actor = actor;
1730     ft_current_allaround = allaround;
1731 
1732     // Search first in the immediate vicinity.
1733     if( !P_BlockThingsIterator(x, y, PIT_FindTarget) )
1734         return true;
1735 
1736     for (d=1; d<5; d++)
1737     {
1738         int i = 1 - d;
1739 
1740         do{
1741             if( !P_BlockThingsIterator(x+i, y-d, PIT_FindTarget )
1742                 || !P_BlockThingsIterator(x+i, y+d, PIT_FindTarget) )
1743                 return true;
1744         }while (++i < d);
1745 
1746         do{
1747             if( !P_BlockThingsIterator(x-d, y+i, PIT_FindTarget )
1748                 || !P_BlockThingsIterator(x+d, y+i, PIT_FindTarget) )
1749                 return true;
1750         }while (--i + d >= 0);
1751     }
1752 
1753     {   // Random number of monsters, to prevent patterns from forming
1754         int n = (PP_Random(pr_friends) & 31) + 15;
1755 
1756         for( th = cap->cnext; th != cap; th = th->cnext )
1757         {
1758             if( --n < 0 )
1759             {
1760                 // Only a subset of the monsters were searched. Move all of
1761                 // the ones which were searched so far, to the end of the list.
1762                 P_MoveClasslistRangeLast( cap, th );
1763                 break;
1764             }
1765 
1766             if( !PIT_FindTarget((mobj_t *) th) )  // If target sighted
1767                 return true;
1768         }
1769     }
1770 
1771     return false;  // No monster found
1772 }
1773 
1774 
1775 // [WDJ] MBF, From MBF, PrBoom, EternityEngine.
1776 //
1777 // P_LookForTargets
1778 //
1779 // killough 9/5/98: look for targets to go after, depending on kind of monster
1780 //
1781 
P_LookForTargets(mobj_t * actor,int allaround)1782 static boolean P_LookForTargets(mobj_t *actor, int allaround)
1783 {
1784     if( actor->flags & MF_FRIEND )
1785     {
1786         // MBF only
1787         return P_LookForMonsters(actor, allaround)  // Boom, MBF
1788                || P_LookForPlayers (actor, allaround);
1789     }
1790 
1791     return P_LookForPlayers (actor, allaround)
1792            || P_LookForMonsters(actor, allaround);  // Boom, MBF
1793 }
1794 
1795 
1796 // [WDJ] MBF, From MBF, PrBoom, EternityEngine.
1797 //
1798 // P_HelpFriend
1799 //
1800 // killough 9/8/98: Help friends in danger of dying
1801 //
1802 
P_HelpFriend(mobj_t * actor)1803 static boolean P_HelpFriend(mobj_t *actor)
1804 {
1805     thinker_t *cap, *th;
1806 
1807     // If less than 33% health, self-preservation rules
1808     if( actor->health*3 < actor->info->spawnhealth )
1809         return false;
1810 
1811     // Parameters PIT_FindTarget
1812     ft_current_actor = actor;
1813     ft_current_allaround = true;
1814 
1815     // Possibly help a friend under 50% health
1816     cap = &thinkerclasscap[actor->flags & MF_FRIEND ? TH_friends : TH_enemies];
1817 
1818     for (th = cap->cnext; th != cap; th = th->cnext)
1819     {
1820         mobj_t * mo = (mobj_t *) th;  // thinker mobj
1821         if( mo->health*2 >= mo->info->spawnhealth )
1822         {
1823             if( PP_Random(pr_helpfriend) < 180 )
1824                 break;
1825         }
1826         else
1827         {
1828             if( mo->flags & MF_JUSTHIT
1829                 && mo->target
1830                 && mo->target != actor->target
1831                 && !PIT_FindTarget( mo->target)
1832               )
1833             {
1834                 // Ignore any attacking monsters, while searching for friend
1835                 actor->threshold = BASETHRESHOLD;
1836                 return true;
1837             }
1838         }
1839     }
1840 
1841     return false;
1842 }
1843 
1844 
1845 //
1846 // ACTION ROUTINES
1847 //
1848 
1849 //
1850 // A_Look
1851 // Stay in state until a player is sighted.
1852 //
A_Look(mobj_t * actor)1853 void A_Look (mobj_t* actor)
1854 {
1855     mobj_t*     targ;
1856 
1857     // Is there a node we must follow?
1858     if( actor->targetnode )
1859     {
1860         actor->target = actor->targetnode;
1861         P_SetMobjState(actor, actor->info->seestate);
1862         return;
1863     }
1864 
1865     actor->threshold = 0;       // any shot will wake up
1866 
1867     // [WDJ] From Prboom, MBF, EternityEngine
1868     // killough 7/18/98:
1869     // Friendly monsters go after other monsters first, but also return to
1870     // player, without attacking them, if they cannot find any targets.
1871     actor->pursuecount = 0;
1872 
1873     if( actor->flags & MF_FRIEND )
1874     {
1875         if( P_LookForTargets(actor, false) )  goto seeyou;
1876     }
1877 
1878     targ = actor->subsector->sector->soundtarget;
1879     if( targ && (targ->flags & MF_SHOOTABLE) )
1880     {
1881         P_SetReference( actor->target, targ );
1882         actor->target = targ;
1883 
1884         if( !(actor->flags & MF_AMBUSH) )   goto seeyou;
1885         if( P_CheckSight(actor, targ) )     goto seeyou;
1886     }
1887 
1888 #if 0
1889     if( !EN_mbf )
1890     {
1891         if( P_LookForPlayers (actor, false) )   goto seeyou;
1892         return;
1893     }
1894 #endif
1895 
1896     if( !(actor->flags & MF_FRIEND) )
1897     {
1898         // Look for Players, then monsters.
1899         if( P_LookForTargets(actor, false) )  goto seeyou;
1900     }
1901     return;
1902 
1903     // go into chase state
1904   seeyou:
1905     if (actor->info->seesound)
1906     {
1907         int  sound;
1908 
1909         switch (actor->info->seesound)
1910         {
1911           case sfx_posit1:
1912           case sfx_posit2:
1913           case sfx_posit3:
1914             sound = sfx_posit1+PP_Random(pr_see)%3;
1915             break;
1916 
1917           case sfx_bgsit1:
1918           case sfx_bgsit2:
1919             sound = sfx_bgsit1+PP_Random(pr_see)%2;
1920             break;
1921 
1922           default:
1923             sound = actor->info->seesound;
1924             break;
1925         }
1926 
1927         if (actor->type==MT_SPIDER
1928          || actor->type == MT_CYBORG
1929          || (actor->flags2&MF2_BOSS))
1930         {
1931             // full volume
1932             S_StartSound(sound);
1933         }
1934         else
1935             S_StartScreamSound(actor, sound);
1936 
1937     }
1938 
1939     P_SetMobjState (actor, actor->info->seestate);
1940 }
1941 
1942 
1943 // [WDJ] Functions from PrBoom, MBF, EternityEngine.
1944 
1945 // killough 10/98:
1946 // Allows monsters to continue movement while attacking
1947 // EternityEngine makes it BEX available.
1948 //
A_KeepChasing(mobj_t * actor)1949 void A_KeepChasing(mobj_t *actor)
1950 {
1951   if (actor->movecount)
1952   {
1953       actor->movecount--;
1954       if (actor->strafecount)
1955         actor->strafecount--;
1956       P_SmartMove(actor);
1957   }
1958 }
1959 
1960 //
1961 // A_Chase
1962 // Actor has a melee attack,
1963 // so it tries to close as fast as possible
1964 //
A_Chase(mobj_t * actor)1965 void A_Chase (mobj_t*   actor)
1966 {
1967     int  delta;
1968 
1969     if (actor->reactiontime)
1970     {
1971         actor->reactiontime--;
1972 
1973         // We are pausing at a node, just look for players
1974         if ( actor->target && actor->target->type == MT_NODE)
1975         {
1976                         P_LookForPlayers(actor, false);
1977                         return;
1978         }
1979     }
1980 
1981 
1982     // modify target threshold
1983     if( actor->threshold )
1984     {
1985         if (EN_doom_etc
1986             && (!actor->target
1987               || actor->target->health <= 0
1988 //              || (actor->target->flags & MF_CORPSE)  // corpse health < 0
1989             ) )
1990         {
1991             actor->threshold = 0;
1992         }
1993         else
1994             actor->threshold--;
1995     }
1996 
1997     if( EN_heretic && cv_fastmonsters.EV)
1998     { // Monsters move faster in nightmare mode
1999         actor->tics -= actor->tics/2;
2000         if(actor->tics < 3)
2001             actor->tics = 3;
2002     }
2003 
2004     // Turn towards movement direction if not there yet.
2005     // killough 9/7/98: keep facing towards target if strafing or backing out
2006     if( actor->strafecount )  // MBF
2007     {
2008         A_FaceTarget(actor);
2009     }
2010     else if( actor->movedir < 8 )
2011     {
2012         actor->angle &= (7<<29);
2013         delta = actor->angle - (actor->movedir << 29);
2014 
2015         if (delta > 0)
2016             actor->angle -= ANG90/2;
2017         else if (delta < 0)
2018             actor->angle += ANG90/2;
2019     }
2020 
2021 // [WDJ] compiler complains, "suggest parenthesis"
2022 #if 0
2023    // Original code was
2024         if (!actor->target
2025         || !(actor->target->flags&MF_SHOOTABLE)
2026                 && actor->target->type != MT_NODE
2027                 && !(actor->eflags & MF_IGNOREPLAYER))
2028 #else
2029 // but, based on other tests, the last two tests were added later.
2030 // [WDJ] I think they meant:
2031     if ( !( actor->target &&
2032            ( actor->target->flags&MF_SHOOTABLE
2033              || actor->target->type == MT_NODE
2034            ))
2035          && !(actor->eflags & MF_IGNOREPLAYER)
2036        )
2037 #endif
2038     {
2039         // [WDJ] Recursion check from EternityEngine.
2040         // haleyjd 07/26/04: Detect and prevent infinite recursion if
2041         // Chase is called from a thing's spawnstate.
2042         static byte  chase_recursion = 0;
2043         static byte  chase_recursion_count = 0;  // [WDJ] too many messages
2044 
2045         // If recursion is true at this point, P_SetMobjState sent
2046         // us back here -- print an error message and return.
2047         if( chase_recursion )
2048         {
2049             if( verbose > 1 && (chase_recursion_count++ == 0) )
2050                 GenPrintf( EMSG_warn, "Chase recursion detected\n");
2051             return;
2052         }
2053 
2054         // set the flag true before calling P_SetMobjState
2055         chase_recursion = 1;
2056 #if 1
2057         // MBF
2058         // look for a new target
2059         if( ! P_LookForTargets(actor,true) )
2060 #else
2061         // look for a new target
2062         if( ! P_LookForPlayers(actor,true) )
2063 #endif
2064         {
2065             // None found
2066             // This monster will start waiting again
2067             P_SetMobjState (actor, actor->info->spawnstate);
2068         }
2069 
2070         chase_recursion = 0; // Must clear this, in either case.
2071         return;
2072     }
2073 
2074     // do not attack twice in a row
2075     if (actor->flags & MF_JUSTATTACKED)
2076     {
2077         actor->flags &= ~MF_JUSTATTACKED;
2078         // MBF: if( gameskill != nightmare && !fastparm )
2079         if( !cv_fastmonsters.EV )
2080             P_NewChaseDir (actor);
2081         return;
2082     }
2083 
2084     // check for melee attack
2085     if (actor->info->meleestate
2086         && P_CheckMeleeRange (actor)
2087         && (actor->target && actor->target->type != MT_NODE)
2088         && !(actor->eflags & MF_IGNOREPLAYER))
2089     {
2090         if (actor->info->attacksound)
2091             S_StartAttackSound(actor, actor->info->attacksound);
2092 
2093         P_SetMobjState (actor, actor->info->meleestate);
2094 
2095         //MBF: killough 8/98: remember an attack
2096         // cph - DEMOSYNC?
2097         if( EN_mbf
2098             && !actor->info->missilestate )
2099             actor->flags |= MF_JUSTHIT;
2100 
2101         return;
2102     }
2103 
2104     // check for missile attack
2105     if (actor->info->missilestate
2106         && (actor->target && actor->target->type != MT_NODE)
2107         && !(actor->eflags & MF_IGNOREPLAYER))
2108     {
2109         if( !cv_fastmonsters.EV && actor->movecount )
2110         {
2111             goto nomissile;
2112         }
2113 
2114         if (!P_CheckMissileRange (actor))
2115             goto nomissile;
2116 
2117         P_SetMobjState (actor, actor->info->missilestate);
2118         actor->flags |= MF_JUSTATTACKED;
2119         return;
2120     }
2121 
2122     // ?
2123   nomissile:
2124     // possibly choose another target
2125 #if 0
2126     // DoomLegacy 1.46.3
2127     if (multiplayer
2128         && !actor->threshold
2129         && !P_CheckSight (actor, actor->target)
2130         && (actor->target && actor->target->type != MT_NODE)
2131         && !(actor->eflags & MF_IGNOREPLAYER))
2132     {
2133         if (P_LookForPlayers(actor,true))
2134             return;     // got a new target
2135 
2136     }
2137 #endif
2138     if( !actor->threshold )
2139     {
2140         // [WDJ] MBF, from MBF, PrBoom, EternityEngine.
2141         if( ! EN_mbf )
2142         {
2143             // Doom, Boom, Legacy
2144             if( multiplayer
2145                 && !P_CheckSight (actor, actor->target)
2146                 && (actor->target && actor->target->type != MT_NODE)
2147                 && !(actor->eflags & MF_IGNOREPLAYER))
2148             {
2149                 if( P_LookForPlayers(actor,true) )
2150                     return;     // got a new target
2151             }
2152         }
2153         // MBF
2154         // killough 7/18/98, 9/9/98: new monster AI
2155         else if( cv_mbf_help_friend.EV && P_HelpFriend(actor) )
2156             return;  // killough 9/8/98: Help friends in need.
2157         // Look for new targets if current one is bad or is out of view.
2158         else if( actor->pursuecount )
2159             actor->pursuecount--;
2160         else
2161         {
2162             // Our pursuit time has expired. We're going to think about
2163             // changing targets.
2164             actor->pursuecount = BASETHRESHOLD;
2165 
2166             // Unless (we have a live target, and it's not friendly
2167             // and we can see it)
2168             // try to find a new one; return if sucessful.
2169             if( !actor->target
2170                 || ! (actor->target->health > 0)
2171                 ||( (cv_mbf_pursuit.EV || multiplayer)
2172                     && ! ( ( ! SAME_FRIEND(actor->target, actor)
2173                              ||( !(actor->flags & MF_FRIEND)
2174                                  && MONSTER_INFIGHTING
2175                                )
2176                             )
2177                             && P_CheckSight(actor, actor->target)
2178                          )
2179                   )
2180               )
2181             {
2182                 if( P_LookForTargets(actor, true) )
2183                     return;
2184             }
2185 
2186             // Current target was good, or no new target was found.
2187             // If monster is a missile-less friend, give up pursuit and
2188             // return to player, if no attacks have occurred recently.
2189 
2190             if( (actor->flags & MF_FRIEND) && !actor->info->missilestate )
2191             {
2192                 // if recent action, keep fighting, else return to player.
2193                 if( actor->flags & MF_JUSTHIT)
2194                     actor->flags &= ~MF_JUSTHIT;
2195                 else if( P_LookForPlayers(actor, true) )
2196                     return;
2197             }
2198         }
2199     }
2200 
2201     if( actor->strafecount )  // MBF
2202         actor->strafecount--;
2203 
2204     // Patrolling nodes
2205     if (actor->target && actor->target->type == MT_NODE)
2206     {
2207         // Check if a player is near
2208         if (P_LookForPlayers(actor, false))
2209         {
2210             // We found one, let him know we saw him!
2211             S_StartScreamSound(actor, actor->info->seesound);
2212             return;
2213         }
2214 
2215         // Did we touch a node as target?
2216         if (R_PointToDist2(actor->x, actor->y, actor->target->x, actor->target->y)
2217             <= actor->target->info->radius )
2218         {
2219 
2220             // Execute possible FS script
2221             if (actor->target->nodescript)
2222             {
2223                 T_RunScript((actor->target->nodescript - 1), actor);
2224             }
2225 
2226             // Do we wait here?
2227             if (actor->target->nodewait)
2228                 actor->reactiontime = actor->target->nodewait;
2229 
2230             // Set next node, if any
2231             if (actor->target->nextnode)
2232             {
2233                 // Also remember it, if we will encounter an enemy
2234                 actor->target = actor->target->nextnode;
2235                 actor->targetnode = actor->target->nextnode;
2236             }
2237             else
2238             {
2239                 actor->target = NULL;
2240                 actor->targetnode = NULL;
2241             }
2242 
2243             return;
2244         }
2245     }
2246 
2247 
2248     // chase towards player
2249     if (--actor->movecount<0
2250         || !P_SmartMove(actor) )
2251     {
2252         P_NewChaseDir (actor);
2253     }
2254 
2255 
2256     // make active sound
2257     if (actor->info->activesound
2258         && PP_Random(pr_see) < 3)
2259     {
2260         if(actor->type == MT_WIZARD && PP_Random(ph_wizscream) < 128)
2261             S_StartScreamSound(actor, actor->info->seesound);
2262         else if(actor->type == MT_SORCERER2)
2263             S_StartSound( actor->info->activesound );
2264         else
2265             S_StartScreamSound(actor, actor->info->activesound);
2266     }
2267 
2268 }
2269 
2270 
2271 //
2272 // A_FaceTarget
2273 //
A_FaceTarget(mobj_t * actor)2274 void A_FaceTarget (mobj_t* actor)
2275 {
2276     if (!actor->target)
2277         return;
2278 
2279     actor->flags &= ~MF_AMBUSH;
2280 
2281     actor->angle = R_PointToAngle2 (actor->x, actor->y,
2282                                     actor->target->x, actor->target->y );
2283 
2284     if (actor->target->flags & MF_SHADOW)
2285         actor->angle += PP_SignedRandom(pr_facetarget)<<21;
2286 }
2287 
2288 
2289 //
2290 // A_PosAttack
2291 //
A_PosAttack(mobj_t * actor)2292 void A_PosAttack (mobj_t* actor)
2293 {
2294     int         angle;
2295     int         damage;
2296     int         slope;
2297 
2298     if (!actor->target)
2299         return;
2300 
2301     A_FaceTarget (actor);
2302     angle = actor->angle;
2303     slope = P_AimLineAttack (actor, angle, MISSILERANGE, 0);
2304 
2305     S_StartAttackSound(actor, sfx_pistol);
2306     angle += PP_SignedRandom(pr_posattack)<<20;
2307     damage = ((PP_Random(pr_posattack)%5)+1)*3;
2308     P_LineAttack (actor, angle, MISSILERANGE, slope, damage);
2309 }
2310 
A_SPosAttack(mobj_t * actor)2311 void A_SPosAttack (mobj_t* actor)
2312 {
2313     int         i;
2314     int         angle;
2315     int         bangle;
2316     int         damage;
2317     int         slope;
2318 
2319     if (!actor->target)
2320         return;
2321     S_StartAttackSound(actor, sfx_shotgn);
2322     A_FaceTarget (actor);
2323     bangle = actor->angle;
2324     slope = P_AimLineAttack (actor, bangle, MISSILERANGE, 0);
2325 
2326     for (i=0 ; i<3 ; i++)
2327     {
2328         angle  = (PP_SignedRandom(pr_sposattack)<<20)+bangle;
2329         damage = ((PP_Random(pr_sposattack)%5)+1)*3;
2330         P_LineAttack (actor, angle, MISSILERANGE, slope, damage);
2331     }
2332 }
2333 
A_CPosAttack(mobj_t * actor)2334 void A_CPosAttack (mobj_t* actor)
2335 {
2336     int         angle;
2337     int         bangle;
2338     int         damage;
2339     int         slope;
2340 
2341     if (!actor->target)
2342         return;
2343     S_StartAttackSound(actor, sfx_shotgn);
2344     A_FaceTarget (actor);
2345     bangle = actor->angle;
2346     slope = P_AimLineAttack (actor, bangle, MISSILERANGE, 0);
2347 
2348     angle  = (PP_SignedRandom(pr_cposattack)<<20)+bangle;
2349 
2350     damage = ((PP_Random(pr_cposattack)%5)+1)*3;
2351     P_LineAttack (actor, angle, MISSILERANGE, slope, damage);
2352 }
2353 
A_CPosRefire(mobj_t * actor)2354 void A_CPosRefire (mobj_t* actor)
2355 {
2356     // keep firing unless target got out of sight
2357     A_FaceTarget (actor);
2358 
2359     // MBF: Simplified, HitFriend only true when friend.
2360     if( actor->flags & MF_FRIEND )
2361     {
2362         // Killough: stop firing if a friend has gotten in the way
2363         if( P_HitFriend(actor) )
2364             goto stop_refire;
2365     }
2366 
2367     mobj_t * target = actor->target;
2368 
2369     if( PP_Random(pr_cposrefire) < 40 )
2370     {
2371         // MBF:
2372         // Killough: Prevent firing on friends continuously.
2373         if( target && BOTH_FRIEND( actor, target ) )
2374             goto stop_refire;
2375 
2376         return;
2377     }
2378 
2379     if( !target
2380         || (target->health <= 0)
2381 //      || target->flags & MF_CORPSE  // corpse health < 0
2382         || ! P_CheckSight( actor, target ) )
2383         goto stop_refire;
2384 
2385     return;
2386 
2387 stop_refire:
2388     P_SetMobjState( actor, actor->info->seestate );
2389 }
2390 
2391 
A_SpidRefire(mobj_t * actor)2392 void A_SpidRefire (mobj_t* actor)
2393 {
2394     // keep firing unless target got out of sight
2395     A_FaceTarget (actor);
2396 
2397     // MBF: Simplified, HitFriend only true when friend.
2398     if( actor->flags & MF_FRIEND )
2399     {
2400         // Killough: stop firing if a friend has gotten in the way
2401         if( P_HitFriend(actor) )
2402             goto stop_refire;
2403     }
2404 
2405     if( PP_Random(pr_spidrefire) < 10 )
2406         return;
2407 
2408     mobj_t * target = actor->target;
2409     if( !target
2410         || (target->health <= 0)
2411 //      || target->flags & MF_CORPSE  // corpse health < 0
2412         || BOTH_FRIEND( actor, target )
2413         || ! P_CheckSight( actor, target ) )
2414         goto stop_refire;
2415 
2416     return;
2417 
2418 stop_refire:
2419     P_SetMobjState( actor, actor->info->seestate );
2420 }
2421 
A_BspiAttack(mobj_t * actor)2422 void A_BspiAttack (mobj_t *actor)
2423 {
2424     if (!actor->target)
2425         return;
2426 
2427     A_FaceTarget (actor);
2428 
2429     // launch a missile
2430     P_SpawnMissile (actor, actor->target, MT_ARACHPLAZ);
2431 }
2432 
2433 
2434 //
2435 // A_TroopAttack
2436 //
A_TroopAttack(mobj_t * actor)2437 void A_TroopAttack (mobj_t* actor)
2438 {
2439     int         damage;
2440 
2441     if (!actor->target)
2442         return;
2443 
2444     A_FaceTarget (actor);
2445     if (P_CheckMeleeRange (actor))
2446     {
2447         S_StartAttackSound(actor, sfx_claw);
2448         damage = (PP_Random(pr_troopattack)%8+1)*3;
2449         P_DamageMobj (actor->target, actor, actor, damage);
2450         return;
2451     }
2452 
2453 
2454     // launch a missile
2455     P_SpawnMissile (actor, actor->target, MT_TROOPSHOT);
2456 }
2457 
2458 
A_SargAttack(mobj_t * actor)2459 void A_SargAttack (mobj_t* actor)
2460 {
2461     int         damage;
2462 
2463     if (!actor->target)
2464         return;
2465 
2466     A_FaceTarget (actor);
2467     if (P_CheckMeleeRange (actor))
2468     {
2469         damage = ((PP_Random(pr_sargattack)%10)+1)*4;
2470         P_DamageMobj (actor->target, actor, actor, damage);
2471     }
2472 }
2473 
A_HeadAttack(mobj_t * actor)2474 void A_HeadAttack (mobj_t* actor)
2475 {
2476     int         damage;
2477 
2478     if (!actor->target)
2479         return;
2480 
2481     A_FaceTarget (actor);
2482     if (P_CheckMeleeRange (actor))
2483     {
2484         damage = (PP_Random(pr_headattack)%6+1)*10;
2485         P_DamageMobj (actor->target, actor, actor, damage);
2486         return;
2487     }
2488 
2489     // launch a missile
2490     P_SpawnMissile (actor, actor->target, MT_HEADSHOT);
2491 }
2492 
A_CyberAttack(mobj_t * actor)2493 void A_CyberAttack (mobj_t* actor)
2494 {
2495     if (!actor->target)
2496         return;
2497 
2498     A_FaceTarget (actor);
2499     P_SpawnMissile (actor, actor->target, MT_ROCKET);
2500 }
2501 
2502 
A_BruisAttack(mobj_t * actor)2503 void A_BruisAttack (mobj_t* actor)
2504 {
2505     int         damage;
2506 
2507     if (!actor->target)
2508         return;
2509 
2510     if (P_CheckMeleeRange (actor))
2511     {
2512         S_StartAttackSound(actor, sfx_claw);
2513         damage = (PP_Random(pr_bruisattack)%8+1)*10;
2514         P_DamageMobj (actor->target, actor, actor, damage);
2515         return;
2516     }
2517 
2518     // launch a missile
2519     P_SpawnMissile (actor, actor->target, MT_BRUISERSHOT);
2520 }
2521 
2522 
2523 //
2524 // A_SkelMissile
2525 //
A_SkelMissile(mobj_t * actor)2526 void A_SkelMissile (mobj_t* actor)
2527 {
2528     mobj_t*     mo;
2529 
2530     if (!actor->target)
2531         return;
2532 
2533     A_FaceTarget (actor);
2534     actor->z += 16*FRACUNIT;    // so missile spawns higher
2535     mo = P_SpawnMissile (actor, actor->target, MT_TRACER);
2536     actor->z -= 16*FRACUNIT;    // back to normal
2537 
2538     if(mo)
2539     {
2540         mo->x += mo->momx;
2541         mo->y += mo->momy;
2542         P_SetReference(mo->tracer, actor->target);
2543         mo->tracer = actor->target;
2544     }
2545 }
2546 
2547 angle_t  TRACEANGLE = 0x0c000000;
2548 
A_Tracer(mobj_t * actor)2549 void A_Tracer (mobj_t* actor)
2550 {
2551     angle_t     exact;
2552     fixed_t     dist;
2553     fixed_t     slope;
2554     mobj_t    * dest;
2555     mobj_t    * th;
2556 
2557     // killough 1/18/98: this is why some missiles do not have smoke and some do.
2558     // Also, internal demos start at random gametics, thus the bug in which
2559     // revenants cause internal demos to go out of sync.
2560     //
2561     // [WDJ] Use a proper tic, demo_comp_tic, that only runs while demo plays,
2562     // instead of the contrived basetic correction.
2563     //
2564     // It would have been better to use leveltime to start with in Doom,
2565     // but since old demos were recorded using gametic, we must stick with it,
2566     // and improvise around it (using leveltime causes desync across levels).
2567 
2568     // [WDJ] As NEWTICRATERATIO = 1, this is same as gametic & 3,
2569     // PrBoom, MBF: use (gametic - basetic), which is same as demo_comp_tick.
2570     if( ((EV_legacy && (EV_legacy < 147))? gametic : game_comp_tic) % (4 * NEWTICRATERATIO) )
2571         return;
2572 
2573     // spawn a puff of smoke behind the rocket
2574     P_SpawnPuff (actor->x, actor->y, actor->z);
2575 
2576     th = P_SpawnMobj ((actor->x - actor->momx), (actor->y - actor->momy),
2577                       actor->z, MT_SMOKE);
2578 
2579     th->momz = FRACUNIT;
2580     th->tics -= PP_Random(pr_tracer)&3;
2581     if (th->tics < 1)
2582         th->tics = 1;
2583 
2584     // adjust direction
2585     dest = actor->tracer;
2586 
2587     if (!dest || dest->health <= 0)
2588         return;
2589 
2590     // change angle
2591     exact = R_PointToAngle2 (actor->x, actor->y,
2592                              dest->x, dest->y);
2593 
2594     if (exact != actor->angle)
2595     {
2596         if (exact - actor->angle > 0x80000000) // (actor->angle > exact)
2597         {
2598             actor->angle -= TRACEANGLE;  // correct towards exact
2599             if (exact - actor->angle < 0x80000000)  // (actor->angle < exact)
2600                 actor->angle = exact;
2601         }
2602         else
2603         {
2604             actor->angle += TRACEANGLE;  // correct towards exact
2605             if (exact - actor->angle > 0x80000000)  // (actor->angle > exact)
2606                 actor->angle = exact;
2607         }
2608     }
2609 
2610     int angf = ANGLE_TO_FINE(actor->angle);
2611     actor->momx = FixedMul (actor->info->speed, finecosine[angf]);
2612     actor->momy = FixedMul (actor->info->speed, finesine[angf]);
2613 
2614     // change slope
2615     dist = P_AproxDistance (dest->x - actor->x,
2616                             dest->y - actor->y);
2617 
2618     dist = dist / actor->info->speed;
2619 
2620     if (dist < 1)
2621         dist = 1;
2622     slope = (dest->z+40*FRACUNIT - actor->z) / dist;
2623 
2624     if (slope < actor->momz)
2625         actor->momz -= FRACUNIT/8;
2626     else
2627         actor->momz += FRACUNIT/8;
2628 }
2629 
2630 
A_SkelWhoosh(mobj_t * actor)2631 void A_SkelWhoosh (mobj_t*      actor)
2632 {
2633     if (!actor->target)
2634         return;
2635     A_FaceTarget (actor);
2636     // judgecutor:
2637     // CHECK ME!
2638     S_StartAttackSound(actor, sfx_skeswg);
2639 }
2640 
A_SkelFist(mobj_t * actor)2641 void A_SkelFist (mobj_t*        actor)
2642 {
2643     int         damage;
2644 
2645     if (!actor->target)
2646         return;
2647 
2648     A_FaceTarget (actor);
2649 
2650     if (P_CheckMeleeRange (actor))
2651     {
2652         damage = ((PP_Random(pr_skelfist)%10)+1)*6;
2653         S_StartAttackSound(actor, sfx_skepch);
2654         P_DamageMobj (actor->target, actor, actor, damage);
2655     }
2656 }
2657 
2658 
2659 
2660 //
2661 // PIT_VileCheck
2662 // Detect a corpse that could be raised.
2663 //
2664 // PIT_VileCheck Global parameter
2665 static mobj_t * vile_r_corpse;  // OUT
2666 static mobj_t * vileobj;
2667 static xyz_t    vile_pos;
2668 
PIT_VileCheck(mobj_t * thing)2669 boolean PIT_VileCheck (mobj_t*  thing)
2670 {
2671     int         maxdist;
2672     boolean     check;
2673 
2674     if (!(thing->flags & MF_CORPSE) )
2675         return true;    // not a monster
2676 
2677     if (thing->tics != -1)
2678         return true;    // not lying still yet
2679 
2680     if (thing->info->raisestate == S_NULL)
2681         return true;    // monster doesn't have a raise state
2682 
2683     maxdist = thing->info->radius + mobjinfo[MT_VILE].radius;
2684 
2685     if ( abs(thing->x - vile_pos.x) > maxdist
2686          || abs(thing->y - vile_pos.y) > maxdist )
2687         return true;            // not actually touching
2688 
2689     // [WDJ] Prevent raise of corpse on another 3d floor.
2690     // Because corpse may have 0 height, use only vile reach.
2691     if( thing->z > (vile_pos.z + mobjinfo[MT_VILE].height)
2692         || thing->z < (vile_pos.z - 20*FRACUNIT) )  // reasonable reach down
2693         return true;
2694 
2695     vile_r_corpse = thing;
2696     vile_r_corpse->momx = vile_r_corpse->momy = 0;
2697 
2698     if( EN_vile_revive_bug )
2699     {
2700         // [WDJ] The original code.  Corpse heights are not this simple.
2701         // Would touch another monster and get stuck.
2702         // In PrBoom this is enabled by comp[comp_vile].
2703         vile_r_corpse->height <<= 2;
2704         check = P_CheckPosition (vile_r_corpse, vile_r_corpse->x, vile_r_corpse->y);
2705         vile_r_corpse->height >>= 2;
2706     }
2707     else
2708     {
2709         // [WDJ] Test with revived sizes from info, to fix monsters stuck together bug.
2710         // Must test as it would be revived, and then restore after the check
2711         // (because a collision could be found).
2712         // From considering the same fix in zdoom and prboom.
2713         fixed_t corpse_height = vile_r_corpse->height;
2714         vile_r_corpse->height = vile_r_corpse->info->height; // revived height
2715         fixed_t corpse_radius = vile_r_corpse->radius;
2716         vile_r_corpse->radius = vile_r_corpse->info->radius; // revived radius
2717         int corpse_flags = vile_r_corpse->flags;
2718         vile_r_corpse->flags |= MF_SOLID; // revived would be SOLID
2719         check = P_CheckPosition (vile_r_corpse, vile_r_corpse->x, vile_r_corpse->y);
2720         vile_r_corpse->height = corpse_height;
2721         vile_r_corpse->radius = corpse_radius;
2722         vile_r_corpse->flags = corpse_flags;
2723     }
2724 
2725     return !check;	// stop searching when no collisions found
2726 }
2727 
2728 
2729 
2730 //
2731 // A_VileChase
2732 // Check for ressurecting a body
2733 //
A_VileChase(mobj_t * actor)2734 void A_VileChase (mobj_t* actor)
2735 {
2736     int    xl, xh;
2737     int    yl, yh;
2738 
2739     int    bx, by;
2740 
2741     mobjinfo_t * info;
2742     mobj_t     * temp;
2743 
2744     if (actor->movedir != DI_NODIR)
2745     {
2746         // check for corpses to raise
2747         // Parameters to VileCheck.
2748         vile_pos.x =
2749             actor->x + actor->info->speed * xspeed[actor->movedir];
2750         vile_pos.y =
2751             actor->y + actor->info->speed * yspeed[actor->movedir];
2752         vile_pos.z = actor->z;
2753 
2754         xl = (vile_pos.x - bmaporgx - MAXRADIUS*2)>>MAPBLOCKSHIFT;
2755         xh = (vile_pos.x - bmaporgx + MAXRADIUS*2)>>MAPBLOCKSHIFT;
2756         yl = (vile_pos.y - bmaporgy - MAXRADIUS*2)>>MAPBLOCKSHIFT;
2757         yh = (vile_pos.y - bmaporgy + MAXRADIUS*2)>>MAPBLOCKSHIFT;
2758 
2759         vileobj = actor;
2760         for (bx=xl ; bx<=xh ; bx++)
2761         {
2762             for (by=yl ; by<=yh ; by++)
2763             {
2764                 // Call PIT_VileCheck to check whether object is a corpse
2765                 // that can be raised.
2766                 if (!P_BlockThingsIterator(bx,by,PIT_VileCheck))
2767                     goto raise_corpse;
2768             }
2769         }
2770     }
2771 
2772     // Return to normal attack.
2773     A_Chase (actor);
2774     return;
2775 
2776 raise_corpse:
2777     // got one!
2778     temp = actor->target;
2779     actor->target = vile_r_corpse;  // do not change reference
2780     A_FaceTarget (actor);
2781     actor->target = temp;  // reference is still correct
2782 
2783     P_SetMobjState (actor, S_VILE_HEAL1);
2784     S_StartObjSound( vile_r_corpse, sfx_slop );
2785     info = vile_r_corpse->info;
2786 
2787     P_SetMobjState (vile_r_corpse,info->raisestate);
2788     if( demoversion<129 )
2789     {
2790         // original code, with ghost bug
2791         // does not work when monster has been crushed
2792         vile_r_corpse->height <<= 2;
2793     }
2794     else
2795     {
2796         // fix vile revives crushed monster as ghost bug
2797         vile_r_corpse->height = info->height;
2798         vile_r_corpse->radius = info->radius;
2799     }
2800     vile_r_corpse->flags = info->flags & ~MF_FRIEND;
2801     vile_r_corpse->health = info->spawnhealth;
2802     P_SetReference(vile_r_corpse->target, NULL);
2803     vile_r_corpse->target = NULL;
2804 
2805     if( EN_mbf )
2806     {
2807         // killough 7/18/98:
2808         // friendliness is transferred from vile to raised corpse
2809         vile_r_corpse->flags =
2810             (info->flags & ~(MF_FRIEND|MF_JUSTHIT)) | (actor->flags & MF_FRIEND);
2811 
2812 #if 0
2813         if( vile_r_corpse->flags & (MF_FRIEND | MF_COUNTKILL) == MF_FRIEND)
2814             totallive++;
2815 #endif
2816 
2817         P_SetReference(vile_r_corpse->lastenemy, NULL);
2818         vile_r_corpse->lastenemy = NULL;
2819 
2820         // killough 8/29/98: add to appropriate thread.
2821         P_UpdateClassThink( &vile_r_corpse->thinker, TH_unknown );
2822     }
2823     return;
2824 }
2825 
2826 
2827 //
2828 // A_VileStart
2829 //
A_VileStart(mobj_t * actor)2830 void A_VileStart (mobj_t* actor)
2831 {
2832     S_StartAttackSound(actor, sfx_vilatk);
2833 }
2834 
2835 
2836 //
2837 // A_Fire
2838 // Keep fire in front of player unless out of sight
2839 //
2840 void A_Fire (mobj_t* actor);
2841 
A_StartFire(mobj_t * actor)2842 void A_StartFire (mobj_t* actor)
2843 {
2844     S_StartObjSound(actor, sfx_flamst);
2845     A_Fire(actor);
2846 }
2847 
A_FireCrackle(mobj_t * actor)2848 void A_FireCrackle (mobj_t* actor)
2849 {
2850     S_StartObjSound(actor, sfx_flame);
2851     A_Fire(actor);
2852 }
2853 
A_Fire(mobj_t * actor)2854 void A_Fire (mobj_t* actor)
2855 {
2856     mobj_t*     dest;
2857 
2858     dest = actor->tracer;
2859     if (!dest)
2860         return;
2861 
2862     // don't move it if the vile lost sight
2863     if (!P_CheckSight (actor->target, dest) )
2864         return;
2865 
2866     int angf = ANGLE_TO_FINE(dest->angle);
2867 
2868     P_UnsetThingPosition (actor);
2869     actor->x = dest->x + FixedMul (24*FRACUNIT, finecosine[angf]);
2870     actor->y = dest->y + FixedMul (24*FRACUNIT, finesine[angf]);
2871     actor->z = dest->z;
2872     P_SetThingPosition (actor);
2873 }
2874 
2875 
2876 
2877 //
2878 // A_VileTarget
2879 // Spawn the hellfire
2880 //
A_VileTarget(mobj_t * actor)2881 void A_VileTarget (mobj_t*      actor)
2882 {
2883     mobj_t*     fog;
2884 
2885     if (!actor->target)
2886         return;
2887 
2888     A_FaceTarget (actor);
2889 
2890 #if 0
2891     // Original bug
2892     fog = P_SpawnMobj (actor->target->x,
2893                        actor->target->x,           // Bp: shoul'nt be y ?
2894                        actor->target->z, MT_FIRE);
2895 #endif
2896 
2897     // [WDJ] Found by Bp: Fix Vile fog bug, similar to prboom (Killough 12/98).
2898     fog = P_SpawnMobj (actor->target->x,
2899                        actor->target->y,
2900                        actor->target->z, MT_FIRE);
2901 
2902     P_SetReference(actor->tracer, fog);
2903     actor->tracer = fog;
2904     P_SetReference(fog->target, actor);
2905     fog->target = actor;
2906     P_SetReference(fog->tracer, actor->target);
2907     fog->tracer = actor->target;
2908 
2909     A_Fire (fog);
2910 }
2911 
2912 
2913 
2914 
2915 //
2916 // A_VileAttack
2917 //
A_VileAttack(mobj_t * actor)2918 void A_VileAttack (mobj_t* actor)
2919 {
2920     mobj_t*     fire;
2921 
2922     if (!actor->target)
2923         return;
2924 
2925     A_FaceTarget (actor);
2926 
2927     if (!P_CheckSight (actor, actor->target) )
2928         return;
2929 
2930     S_StartObjSound(actor, sfx_barexp);
2931     P_DamageMobj (actor->target, actor, actor, 20);
2932     actor->target->momz = 1000*FRACUNIT/actor->target->info->mass;
2933 
2934     int angf = ANGLE_TO_FINE(actor->angle);
2935 
2936     fire = actor->tracer;
2937     if (!fire)
2938         return;
2939 
2940     // move the fire between the vile and the player
2941     fire->x = actor->target->x - FixedMul (24*FRACUNIT, finecosine[angf]);
2942     fire->y = actor->target->y - FixedMul (24*FRACUNIT, finesine[angf]);
2943     P_RadiusAttack (fire, actor, 70 );
2944 }
2945 
2946 
2947 
2948 
2949 //
2950 // Mancubus attack,
2951 // firing three missiles (bruisers)
2952 // in three different directions?
2953 // Doesn't look like it.
2954 //
2955 #define FATSPREAD       (ANG90/8)
2956 
A_FatRaise(mobj_t * actor)2957 void A_FatRaise (mobj_t *actor)
2958 {
2959     A_FaceTarget (actor);
2960     S_StartAttackSound(actor, sfx_manatk);
2961 }
2962 
2963 
A_FatAttack1(mobj_t * actor)2964 void A_FatAttack1 (mobj_t* actor)
2965 {
2966     mobj_t*     mo;
2967 
2968     A_FaceTarget (actor);
2969     // Change direction  to ...
2970     actor->angle += FATSPREAD;
2971     P_SpawnMissile (actor, actor->target, MT_FATSHOT);
2972 
2973     mo = P_SpawnMissile (actor, actor->target, MT_FATSHOT);
2974     if(mo)
2975     {
2976         mo->angle += FATSPREAD;
2977         int angf = ANGLE_TO_FINE(mo->angle);
2978         mo->momx = FixedMul (mo->info->speed, finecosine[angf]);
2979         mo->momy = FixedMul (mo->info->speed, finesine[angf]);
2980     }
2981 }
2982 
A_FatAttack2(mobj_t * actor)2983 void A_FatAttack2 (mobj_t* actor)
2984 {
2985     mobj_t*     mo;
2986 
2987     A_FaceTarget (actor);
2988     // Now here choose opposite deviation.
2989     actor->angle -= FATSPREAD;
2990     P_SpawnMissile (actor, actor->target, MT_FATSHOT);
2991 
2992     mo = P_SpawnMissile (actor, actor->target, MT_FATSHOT);
2993     if(mo)
2994     {
2995         mo->angle -= FATSPREAD*2;
2996         int angf = ANGLE_TO_FINE(mo->angle);
2997         mo->momx = FixedMul (mo->info->speed, finecosine[angf]);
2998         mo->momy = FixedMul (mo->info->speed, finesine[angf]);
2999     }
3000 }
3001 
A_FatAttack3(mobj_t * actor)3002 void A_FatAttack3 (mobj_t* actor)
3003 {
3004     mobj_t*     mo;
3005     int         angf;
3006 
3007     A_FaceTarget (actor);
3008 
3009     mo = P_SpawnMissile (actor, actor->target, MT_FATSHOT);
3010     if(mo)
3011     {
3012         mo->angle -= FATSPREAD/2;
3013         angf = ANGLE_TO_FINE(mo->angle);
3014         mo->momx = FixedMul (mo->info->speed, finecosine[angf]);
3015         mo->momy = FixedMul (mo->info->speed, finesine[angf]);
3016     }
3017 
3018     mo = P_SpawnMissile (actor, actor->target, MT_FATSHOT);
3019     if(mo)
3020     {
3021         mo->angle += FATSPREAD/2;
3022         angf = ANGLE_TO_FINE(mo->angle);
3023         mo->momx = FixedMul (mo->info->speed, finecosine[angf]);
3024         mo->momy = FixedMul (mo->info->speed, finesine[angf]);
3025     }
3026 }
3027 
3028 
3029 //
3030 // SkullAttack
3031 // Fly at the player like a missile.
3032 //
3033 #define SKULLSPEED              (20*FRACUNIT)
3034 
A_SkullAttack(mobj_t * actor)3035 void A_SkullAttack (mobj_t* actor)
3036 {
3037     mobj_t*             dest;
3038     angle_t             ang;
3039     int                 dist;
3040 
3041     if (!actor->target)
3042         return;
3043 
3044     if (actor->target->health <= 0)
3045     {
3046        actor->target = NULL;
3047        return;
3048     }
3049 
3050     dest = actor->target;
3051     actor->flags |= MF_SKULLFLY;
3052     S_StartScreamSound(actor, actor->info->attacksound);
3053     A_FaceTarget (actor);
3054 
3055     if( cv_predictingmonsters.EV || (actor->eflags & MF_PREDICT) ) //added by AC for predmonsters
3056     {
3057 
3058                 boolean canHit;
3059                 fixed_t	px, py, pz;
3060                 int	t, time;
3061                 subsector_t *sec;
3062 
3063                 dist = P_AproxDistance (dest->x - actor->x, dest->y - actor->y);
3064                 time = dist/SKULLSPEED;
3065                 time = P_AproxDistance (dest->x + dest->momx*time - actor->x,
3066                                                                 dest->y + dest->momy*time - actor->y)/SKULLSPEED;
3067 
3068                 canHit = 0;
3069                 t = time + 4;
3070                 do
3071                 {
3072                         t-=4;
3073                         if (t < 1)
3074                                 t = 1;
3075                         px = dest->x + dest->momx*t;
3076                         py = dest->y + dest->momy*t;
3077                         pz = dest->z + dest->momz*t;
3078                         canHit = P_CheckSight2(actor, dest, px, py, pz);
3079                 } while (!canHit && (t > 1));
3080 
3081                 sec = R_PointInSubsector(px, py);
3082                 if (!sec)
3083                         sec = dest->subsector;
3084 
3085                 if (pz < sec->sector->floorheight)
3086                         pz = sec->sector->floorheight;
3087                 else if (pz > sec->sector->ceilingheight)
3088                         pz = sec->sector->ceilingheight - dest->height;
3089 
3090                 ang = R_PointToAngle2 (actor->x, actor->y, px, py);
3091 
3092                 // fuzzy player
3093                 if (dest->flags & MF_SHADOW)
3094                 {
3095                     int aa = PP_SignedRandom(pr_shadow);
3096                     ang += ( EN_heretic )? (aa << 21) : (aa << 20);
3097                 }
3098 
3099                 actor->angle = ang;
3100                 actor->momx = FixedMul (SKULLSPEED, cosine_ANG(ang));
3101                 actor->momy = FixedMul (SKULLSPEED, sine_ANG(ang));
3102 
3103                 actor->momz = (pz+(dest->height>>1) - actor->z) / t;
3104     }
3105     else
3106     {
3107                 ang = actor->angle;
3108                 actor->momx = FixedMul (SKULLSPEED, cosine_ANG(ang));
3109                 actor->momy = FixedMul (SKULLSPEED, sine_ANG(ang));
3110                 dist = P_AproxDistance (dest->x - actor->x, dest->y - actor->y);
3111                 dist = dist / SKULLSPEED;
3112 
3113                 if (dist < 1)
3114                         dist = 1;
3115                 actor->momz = (dest->z+(dest->height>>1) - actor->z) / dist;
3116     }
3117 }
3118 
3119 
3120 //
3121 // A_PainShootSkull
3122 // Spawn a lost soul and launch it at the target
3123 //
3124 void
A_PainShootSkull(mobj_t * actor,angle_t angle)3125 A_PainShootSkull( mobj_t* actor, angle_t angle )
3126 {
3127     fixed_t     x, y, z;
3128     mobj_t*     newmobj;
3129     int         prestep;
3130 
3131 
3132     if( EN_skull_limit )  // Boom comp_pain
3133     {
3134     //  --------------- SKULL LIMIT CODE -----------------
3135 //  Original Doom code that limits the number of skulls to 20
3136     int         count;
3137     thinker_t*  currentthinker;
3138 
3139     // count total number of skull currently on the level
3140     count = 0;
3141 
3142     currentthinker = thinkercap.next;
3143     while (currentthinker != &thinkercap)
3144     {
3145         if (   (currentthinker->function.acp1 == (actionf_p1)P_MobjThinker)
3146             && ((mobj_t *)currentthinker)->type == MT_SKULL)
3147             count++;
3148         currentthinker = currentthinker->next;
3149     }
3150 
3151     // if there are already 20 skulls on the level,
3152     // don't spit another one
3153     if (count > 20)
3154         goto no_skull;
3155 
3156     }
3157 
3158     // okay, there's place for another one
3159     prestep =
3160         4*FRACUNIT
3161         + 3*(actor->info->radius + mobjinfo[MT_SKULL].radius)/2;
3162 
3163     x = actor->x + FixedMul (prestep, cosine_ANG(angle));
3164     y = actor->y + FixedMul (prestep, sine_ANG(angle));
3165     z = actor->z + 8*FRACUNIT;
3166 
3167     if( EN_old_pain_spawn )  // Boom comp_skull
3168     {
3169        newmobj = P_SpawnMobj (x, y, z, MT_SKULL);
3170     }
3171     else
3172     {
3173        // Check before spawning if spawn spot is valid, not in a wall,
3174        // not crossing any lines that monsters could not cross.
3175        if( P_CheckCrossLine( actor, x, y ) )
3176            goto no_skull;
3177 
3178        newmobj = P_SpawnMobj (x, y, z, MT_SKULL);
3179 
3180        // [WDJ] Could not think of better way to check this.
3181        // So modified from prboom (by phares).
3182        {
3183            register sector_t * nmsec = newmobj->subsector->sector;
3184            // check for above ceiling or below floor
3185            // skull z may be modified by SpawnMobj, so check newmobj itself
3186            if( ( (newmobj->z + newmobj->height) > nmsec->ceilingheight )
3187                || ( newmobj->z < nmsec->floorheight ) )
3188                goto remove_skull;
3189        }
3190     }
3191 
3192     // [WDJ] From PrBoom, MBF, EternityEngine.
3193     // killough 7/20/98: PEs shoot lost souls with the same friendliness
3194     newmobj->flags = (newmobj->flags & ~MF_FRIEND) | (actor->flags & MF_FRIEND);
3195     P_UpdateClassThink(&newmobj->thinker, TH_unknown);
3196 
3197     // Check for movements.
3198     if (!P_TryMove (newmobj, newmobj->x, newmobj->y, false))
3199        goto remove_skull;
3200 
3201     if( actor->target && (actor->target->health > 0) )
3202     {
3203         P_SetReference( newmobj->target, actor->target );
3204         newmobj->target = actor->target;
3205     }
3206 
3207     A_SkullAttack (newmobj);
3208     return;
3209 
3210 remove_skull:
3211     // kill it immediately
3212 #define RMSKULL  2
3213 #if RMSKULL == 0
3214     // The skull dives to the floor and dies
3215     P_DamageMobj (newmobj,actor,actor,10000);
3216 #endif
3217 #if RMSKULL == 1
3218     // The skull dies less showy, like prboom
3219     newmobj->health = 0;
3220     P_KillMobj (newmobj,NULL,actor);  // no death messages
3221 #endif
3222 #if RMSKULL == 2
3223     // The skull does not appear, like Edge
3224     P_RemoveMobj (newmobj);
3225 #endif
3226 no_skull:
3227     return;
3228 }
3229 
3230 
3231 //
3232 // A_PainAttack
3233 // Spawn a lost soul and launch it at the target
3234 //
A_PainAttack(mobj_t * actor)3235 void A_PainAttack (mobj_t* actor)
3236 {
3237     if (!actor->target)
3238         return;
3239 
3240     if (actor->target->health <= 0 )
3241     {
3242        P_SetReference(actor->target, NULL);
3243        actor->target = NULL;
3244        return;
3245     }
3246 
3247     A_FaceTarget (actor);
3248     A_PainShootSkull (actor, actor->angle);
3249 }
3250 
3251 
A_PainDie(mobj_t * actor)3252 void A_PainDie (mobj_t* actor)
3253 {
3254     A_Fall (actor);
3255     A_PainShootSkull (actor, actor->angle+ANG90);
3256     A_PainShootSkull (actor, actor->angle+ANG180);
3257     A_PainShootSkull (actor, actor->angle+ANG270);
3258 }
3259 
3260 
3261 
3262 
3263 
3264 
A_Scream(mobj_t * actor)3265 void A_Scream (mobj_t* actor)
3266 {
3267     int         sound;
3268 
3269     switch (actor->info->deathsound)
3270     {
3271       case 0:
3272         return;
3273 
3274       case sfx_podth1:
3275       case sfx_podth2:
3276       case sfx_podth3:
3277         sound = sfx_podth1 + PP_Random(pr_scream)%3;
3278         break;
3279 
3280       case sfx_bgdth1:
3281       case sfx_bgdth2:
3282         sound = sfx_bgdth1 + PP_Random(pr_scream)%2;
3283         break;
3284 
3285       default:
3286         sound = actor->info->deathsound;
3287         break;
3288     }
3289 
3290     // Check for bosses.
3291     if (actor->type==MT_SPIDER
3292         || actor->type == MT_CYBORG)
3293     {
3294         // full volume
3295         S_StartSound(sound);
3296     }
3297     else
3298         S_StartScreamSound(actor, sound);
3299 }
3300 
3301 
A_XScream(mobj_t * actor)3302 void A_XScream (mobj_t* actor)
3303 {
3304     S_StartScreamSound(actor, sfx_slop);
3305 }
3306 
A_Pain(mobj_t * actor)3307 void A_Pain (mobj_t* actor)
3308 {
3309     if (actor->info->painsound)
3310         S_StartScreamSound(actor, actor->info->painsound);
3311 }
3312 
3313 
3314 //
3315 //  A dying thing falls to the ground (monster deaths)
3316 //
3317 // Invoked by state during death sequence, after P_KillMobj.
A_Fall(mobj_t * actor)3318 void A_Fall (mobj_t *actor)
3319 {
3320     // actor is on ground, it can be walked over
3321     if (!cv_solidcorpse.EV)
3322         actor->flags &= ~MF_SOLID;  // not solid (vanilla doom)
3323 
3324     if( EV_legacy >= 131 )
3325     {
3326         // Before version 131 this is done later in P_KillMobj.
3327         actor->flags   |= MF_CORPSE|MF_DROPOFF;
3328         actor->height = actor->info->height>>2;
3329         actor->radius -= (actor->radius>>4);      //for solid corpses
3330         // [WDJ] Corpse health must be < 0.
3331         // Too many health checks all over the place, like BossDeath.
3332         if( actor->health >= 0 )
3333             actor->health = -1;
3334         if( actor->health > -actor->info->spawnhealth )
3335         {
3336             // Not gibbed yet.
3337             // Determine health until gibbed, keep some of the damage.
3338             actor->health = (actor->health - actor->info->spawnhealth)/2;
3339         }
3340     }
3341 
3342     // So change this if corpse objects
3343     // are meant to be obstacles.
3344 }
3345 
3346 
3347 //
3348 // A_Explode
3349 //
A_Explode(mobj_t * actor)3350 void A_Explode (mobj_t* actor)
3351 {
3352     int damage = 128;
3353 
3354     switch(actor->type)
3355     {
3356     case MT_FIREBOMB: // Time Bombs
3357         actor->z += 32*FRACUNIT;
3358         actor->flags &= ~MF_SHADOW;
3359         break;
3360     case MT_MNTRFX2: // Minotaur floor fire
3361         damage = 24;
3362         break;
3363     case MT_SOR2FX1: // D'Sparil missile
3364         damage = 80+(PP_Random(ph_soratkdam)&31);
3365         break;
3366     default:
3367         break;
3368     }
3369 
3370     P_RadiusAttack ( actor, actor->target, damage );
3371     P_HitFloor(actor);
3372 }
3373 
P_FinalState(statenum_t state)3374 static state_t *P_FinalState(statenum_t state)
3375 {
3376     static char final_state[NUMSTATES]; //Hurdler: quick temporary hack to fix hacx freeze
3377 
3378     memset(final_state, 0, NUMSTATES);
3379     while (states[state].tics!=-1)
3380     {
3381         final_state[state] = 1;
3382         state=states[state].nextstate;
3383         if (final_state[state])
3384             return NULL;
3385     }
3386 
3387     return &states[state];
3388 }
3389 
3390 //
3391 // A_BossDeath
3392 // Possibly trigger special effects
3393 // if on first boss level
3394 //
3395 // Triggered by actor state change action, last in death sequence.
3396 // A_Fall usually occurs before this.
3397 // Heretic: see A_HBossDeath() in p_henemy.c
3398 // [WDJ]  Keen death does not have tests for mo->type and thus allows
3399 // Dehacked monsters to trigger Keen death and BossDeath effects.
3400 // Should duplicate that ability in Doom maps.
A_Bosstype_Death(mobj_t * mo,int boss_type)3401 void A_Bosstype_Death (mobj_t* mo, int boss_type)
3402 {
3403     thinker_t*  th;
3404     mobj_t*     mo2;
3405     line_t      lineop;  // operation performed when all bosses are dead.
3406     int         i;
3407 
3408     if ( gamemode == doom2_commercial)
3409     {
3410         // Doom2 MAP07: When last Mancubus is dead,
3411         //   execute lowerFloortoLowest(sectors tagged 666).
3412         // Doom2 MAP07: When last Arachnotron is dead,
3413         //   execute raisetoTexture(sectors tagged 667).
3414         // Doom2 MAP32: When last Keen is dead,
3415         //   execute doorOpen(doors tagged 666).
3416           if((boss_type != MT_FATSO)
3417             && (boss_type != MT_BABY)
3418             && (boss_type != MT_KEEN))
3419                 goto no_action;
3420     }
3421     // [WDJ] Untested
3422     // This could be done with compatibility switch (comp_666), as in prboom.
3423     else if( (gamemode == doom_shareware || gamemode == doom_registered)
3424              && gameepisode < 4 )
3425     {
3426         // [WDJ] Revert to behavior before UltimateDoom,
3427         // to fix "Doomsday of UAC" bug.
3428         if (gamemap != 8)
3429             goto no_action;
3430         // Allow all boss types in each episode, (for PWAD)
3431         // E1: all, such as Baron and Cyberdemon
3432         // E2,E3,E4: all except Baron
3433         // [WDJ] Logic from prboom
3434         if (gameepisode != 1)
3435             if (boss_type == MT_BRUISER)
3436                 goto no_action;
3437     }
3438     else
3439     {
3440         switch(gameepisode)
3441         {
3442           case 1:
3443             // Doom E1M8: When all Baron are dead,
3444             //   execute lowerFloortoLowest(sectors tagged 666).
3445             if (gamemap != 8)
3446                 goto no_action;
3447 
3448             // This test was added in UltimateDoom,
3449             // some PWAD from before then, such as "Doomsday of UAC" which
3450             // requires death of last Baron and last Cyberdemon, will fail.
3451             if (boss_type != MT_BRUISER)
3452                 goto no_action;
3453             break;
3454 
3455           case 2:
3456             // Doom E2M8: When last Cyberdemon is dead, level ends.
3457             if (gamemap != 8)
3458                 goto no_action;
3459 
3460             if (boss_type != MT_CYBORG)
3461                 goto no_action;
3462             break;
3463 
3464           case 3:
3465             // Doom E3M8: When last Spidermastermind is dead, level ends.
3466             if (gamemap != 8)
3467                 goto no_action;
3468 
3469             if (boss_type != MT_SPIDER)
3470                 goto no_action;
3471 
3472             break;
3473 
3474           case 4:
3475             switch(gamemap)
3476             {
3477               case 6:
3478                 // Doom E4M6: When last Cyberdemon is dead,
3479                 //   execute blazeOpen(doors tagged 666).
3480                 if (boss_type != MT_CYBORG)
3481                     goto no_action;
3482                 break;
3483 
3484               case 8:
3485                 // Doom E4M8: When last Spidermastermind is dead,
3486                 //   execute lowerFloortoLowest(sectors tagged 666).
3487                 if (boss_type != MT_SPIDER)
3488                     goto no_action;
3489                 break;
3490 
3491               default:
3492                 goto no_action;
3493             }
3494             break;
3495 
3496           default:
3497             if (gamemap != 8)
3498                 goto no_action;
3499             break;
3500         }
3501 
3502     }
3503 
3504 
3505     // make sure there is a player alive for victory
3506     for (i=0 ; i<MAXPLAYERS ; i++)
3507         if (playeringame[i] && players[i].health > 0)
3508             break;
3509 
3510     if (i==MAXPLAYERS)
3511         return; // no one left alive, so do not end game
3512 
3513     // scan the remaining thinkers to see
3514     // if all bosses are dead
3515     for (th = thinkercap.next ; th != &thinkercap ; th=th->next)
3516     {
3517         if (th->function.acp1 != (actionf_p1)P_MobjThinker)
3518             continue;
3519 
3520         // Fixes MAP07 bug where if last arachnotron is killed while first
3521         // still in death sequence, then both would trigger this code
3522         // and the floor would be raised twice (bad).
3523         mo2 = (mobj_t *)th;
3524         // Check all boss of the same type
3525         if ( mo2 != mo
3526             && mo2->type == boss_type )
3527         {
3528             // Check if really dead and finished the death sequence.
3529             // [WDJ] Corpse has health < 0.
3530             // If two monsters are killed at the same time, this test may occur
3531             // while first is corpse and second is not.  But the simple health
3532             // test may trigger twice because second monster already has
3533             // health < 0 during the first death test.
3534             if( mo2->health > 0  // the old test (doom original 1.9)
3535                 || !(mo2->flags & MF_CORPSE)
3536                 || mo2->state != P_FinalState(mo2->info->deathstate) )
3537             {
3538                 // other boss not dead
3539                 goto no_action;
3540             }
3541         }
3542     }
3543 
3544     // victory!
3545     if ( gamemode == doom2_commercial)
3546     {
3547         if (boss_type == MT_FATSO)
3548         {
3549             if(gamemap == 7)
3550             {
3551                 // Doom2 MAP07: When last Mancubus is dead, execute lowerFloortoLowest.
3552                 //   execute lowerFloortoLowest(sectors tagged 666).
3553                 lineop.tag = 666;
3554                 EV_DoFloor( &lineop, FT_lowerFloorToLowest);
3555             }
3556             goto done;
3557         }
3558         if (boss_type == MT_BABY)
3559         {
3560             if(gamemap == 7)
3561             {
3562                 // Doom2 MAP07: When last Arachnotron is dead,
3563                 //   execute raisetoTexture(sectors tagged 667).
3564                 lineop.tag = 667;
3565                 EV_DoFloor( &lineop, FT_raiseToTexture);
3566             }
3567             goto done;
3568         }
3569         else if(boss_type == MT_KEEN)
3570         {
3571             // Doom2 MAP32: When last Keen is dead,
3572             //   execute doorOpen(doors tagged 666).
3573             lineop.tag = 666;
3574             EV_DoDoor( &lineop, VD_dooropen, VDOORSPEED);
3575             goto done;
3576         }
3577     }
3578     else
3579     {
3580         switch(gameepisode)
3581         {
3582           case 1:
3583             // Doom E1M8: When all Baron are dead, execute lowerFloortoLowest
3584             //   on all sectors tagged 666.
3585             lineop.tag = 666;
3586             EV_DoFloor( &lineop, FT_lowerFloorToLowest);
3587             goto done;
3588 
3589           case 4:
3590             switch(gamemap)
3591             {
3592               case 6:
3593                 // Doom E4M6: When last Cyberdemon is dead, execute blazeOpen.
3594                 //   on all doors tagged 666.
3595                 lineop.tag = 666;
3596                 EV_DoDoor( &lineop, VD_blazeOpen, 4*VDOORSPEED);
3597                 goto done;
3598 
3599               case 8:
3600                 // Doom E4M8: When last Spidermastermind is dead, execute lowerFloortoLowest.
3601                 //   on all sectors tagged 666.
3602                 lineop.tag = 666;
3603                 EV_DoFloor( &lineop, FT_lowerFloorToLowest);
3604                 goto done;
3605             }
3606         }
3607     }
3608     if( cv_allowexitlevel.EV )
3609         G_ExitLevel ();
3610 done:
3611     return;
3612 
3613 no_action:
3614     return;
3615 }
3616 
3617 // Info table call, as in Heretic.
A_BossDeath(mobj_t * mo)3618 void A_BossDeath (mobj_t* mo)
3619 {
3620     A_Bosstype_Death( mo, mo->type );
3621 }
3622 
3623 //
3624 // A_KeenDie
3625 // DOOM II special, map 32.
3626 // Uses special tag 666.
3627 //
A_KeenDie(mobj_t * mo)3628 void A_KeenDie (mobj_t* mo)
3629 {
3630     A_Fall (mo);
3631 
3632     // Doom2 MAP32: When last Keen is dead,
3633     //   execute doorOpen(doors tagged 666).
3634     // Some Dehacked mods use Keen death to trigger 666 tagged door.
3635     // Cannot use mo->type when dehacked may have only changed death frame.
3636     A_Bosstype_Death(mo, MT_KEEN);
3637 }
3638 
A_Hoof(mobj_t * mo)3639 void A_Hoof (mobj_t* mo)
3640 {
3641     S_StartObjSound(mo, sfx_hoof);
3642     A_Chase (mo);
3643 }
3644 
A_Metal(mobj_t * mo)3645 void A_Metal (mobj_t* mo)
3646 {
3647     S_StartObjSound(mo, sfx_metal);
3648     A_Chase (mo);
3649 }
3650 
A_BabyMetal(mobj_t * mo)3651 void A_BabyMetal (mobj_t* mo)
3652 {
3653     S_StartObjSound(mo, sfx_bspwlk);
3654     A_Chase (mo);
3655 }
3656 
A_OpenShotgun2(player_t * player,pspdef_t * psp)3657 void A_OpenShotgun2 ( player_t*     player,
3658                       pspdef_t*     psp )
3659 {
3660     S_StartAttackSound(player->mo, sfx_dbopn);
3661 }
3662 
A_LoadShotgun2(player_t * player,pspdef_t * psp)3663 void A_LoadShotgun2 ( player_t*     player,
3664                       pspdef_t*     psp )
3665 {
3666     S_StartAttackSound(player->mo, sfx_dbload);
3667 }
3668 
3669 void A_ReFire ( player_t*     player,
3670                 pspdef_t*     psp );
3671 
A_CloseShotgun2(player_t * player,pspdef_t * psp)3672 void A_CloseShotgun2 ( player_t*     player,
3673                        pspdef_t*     psp )
3674 {
3675     S_StartAttackSound(player->mo, sfx_dbcls);
3676     A_ReFire(player,psp);
3677 }
3678 
3679 // [WDJ] Remove hard limits. Similar to prboom, killough 3/26/98.
3680 // Dynamic allocation.
3681 static mobj_t* * braintargets; // dynamic array of ptrs
3682 static int     braintargets_max = 0; // allocated
3683 static int     numbraintargets;
3684 static int     braintargeton;
3685 
3686 // Original was 32 max.
3687 // Non-fatal - limited for useability, otherwise can have too many thinkers.
3688 // This affects compatibility in netplay.
3689 #ifdef MACHINE_MHZ
3690 #define MAX_BRAINTARGETS  (32*MACHINE_MHZ/100)
3691 #endif
3692 
3693 // return 1 on success
expand_braintargets(void)3694 boolean  expand_braintargets( void )
3695 {
3696     int needed = braintargets_max += 32;
3697 #ifdef MAX_BRAINTARGETS
3698     if( needed > MAX_BRAINTARGETS )
3699     {
3700         I_SoftError( "Expand braintargets exceeds MAX_BRAINTARGETS %d.\n", MAX_BRAINTARGETS );
3701         return 0;
3702     }
3703 #endif
3704     // realloc to new size, copying contents
3705     mobj_t ** new_braintargets =
3706      realloc( braintargets, sizeof(mobj_t *) * needed );
3707     if( new_braintargets )
3708     {
3709         braintargets = new_braintargets;
3710         braintargets_max = needed;
3711     }
3712     else
3713     {
3714         // non-fatal protection, allow savegame or continue play
3715         // realloc fail does not disturb existing allocation
3716         numbraintargets = braintargets_max;
3717         I_SoftError( "Expand braintargets realloc failed at $d.\n", needed );
3718         return 0;  // fail to expand
3719     }
3720     return 1;
3721 }
3722 
P_Init_BrainTarget()3723 void P_Init_BrainTarget()
3724 {
3725     thinker_t*  thinker;
3726 
3727     // find all the target spots
3728     numbraintargets = 0;
3729     braintargeton = 0;
3730 
3731     thinker = thinkercap.next;
3732     for (thinker = thinkercap.next ;
3733          thinker != &thinkercap ;
3734          thinker = thinker->next)
3735     {
3736         if (thinker->function.acp1 != (actionf_p1)P_MobjThinker)
3737             continue;   // not a mobj
3738 
3739         register mobj_t* m = (mobj_t *)thinker;
3740 
3741         if (m->type == MT_BOSSTARGET )
3742         {
3743             if( numbraintargets >= braintargets_max )
3744             {
3745                 if( ! expand_braintargets() )  break;
3746             }
3747             braintargets[numbraintargets] = m;
3748             numbraintargets++;
3749         }
3750     }
3751 }
3752 
3753 
A_BrainAwake(mobj_t * mo)3754 void A_BrainAwake (mobj_t* mo)
3755 {
3756     S_StartSound(sfx_bossit);
3757 }
3758 
3759 
A_BrainPain(mobj_t * mo)3760 void A_BrainPain (mobj_t*       mo)
3761 {
3762     S_StartSound(sfx_bospn);
3763 }
3764 
3765 
A_BrainScream(mobj_t * mo)3766 void A_BrainScream (mobj_t*     mo)
3767 {
3768     int         x;
3769     int         y;
3770     int         z;
3771     mobj_t*     th;
3772 
3773     for (x=mo->x - 196*FRACUNIT ; x< mo->x + 320*FRACUNIT ; x+= FRACUNIT*8)
3774     {
3775         y = mo->y - 320*FRACUNIT;
3776         z = 128 + PP_Random(pr_brainscream)*2*FRACUNIT;
3777         th = P_SpawnMobj (x,y,z, MT_ROCKET);
3778         th->momz = PP_Random(pr_brainscream)*512;
3779 
3780         P_SetMobjState (th, S_BRAINEXPLODE1);
3781 
3782         th->tics -= PP_Random(pr_brainscream)&7;
3783         if (th->tics < 1)
3784             th->tics = 1;
3785     }
3786 
3787     S_StartSound(sfx_bosdth);
3788 }
3789 
3790 
3791 
A_BrainExplode(mobj_t * mo)3792 void A_BrainExplode (mobj_t* mo)
3793 {
3794     int         x;
3795     int         y;
3796     int         z;
3797     mobj_t*     th;
3798 
3799     x = (PP_SignedRandom(pr_brainexp)<<11)+mo->x;
3800     y = mo->y;
3801     z = 128 + PP_Random(pr_brainexp)*2*FRACUNIT;
3802     th = P_SpawnMobj (x,y,z, MT_ROCKET);
3803     th->momz = PP_Random(pr_brainexp)*512;
3804 
3805     P_SetMobjState (th, S_BRAINEXPLODE1);
3806 
3807     th->tics -= PP_Random(pr_brainexp)&7;
3808     if (th->tics < 1)
3809         th->tics = 1;
3810 }
3811 
3812 
A_BrainDie(mobj_t * mo)3813 void A_BrainDie (mobj_t*        mo)
3814 {
3815     if( cv_allowexitlevel.EV )
3816        G_ExitLevel ();
3817 }
3818 
A_BrainSpit(mobj_t * mo)3819 void A_BrainSpit (mobj_t*       mo)
3820 {
3821     mobj_t*     targ;
3822     mobj_t*     newmobj;
3823 
3824     static int  easy = 0;
3825 
3826     easy ^= 1;
3827     if (gameskill <= sk_easy && (!easy))
3828         return;
3829 
3830     if( numbraintargets>0 )
3831     {
3832         // shoot a cube at current target
3833         targ = braintargets[braintargeton];
3834         braintargeton = (braintargeton+1)%numbraintargets;
3835 
3836         // spawn brain missile
3837         newmobj = P_SpawnMissile (mo, targ, MT_SPAWNSHOT);
3838         if(newmobj)
3839         {
3840             P_SetReference( newmobj->target, targ);
3841             newmobj->target = targ;
3842             newmobj->reactiontime =
3843                 ((targ->y - mo->y)/newmobj->momy) / newmobj->state->tics;
3844 
3845             // [WDJ] MBF: From PrBoom, MBF, EternityEngine.
3846             // killough 7/18/98: brain friendliness is transferred
3847             newmobj->flags = (newmobj->flags & ~MF_FRIEND) | (mo->flags & MF_FRIEND);
3848             P_UpdateClassThink(&newmobj->thinker, TH_unknown);
3849         }
3850 
3851         S_StartSound(sfx_bospit);
3852     }
3853 }
3854 
3855 
3856 
3857 void A_SpawnFly (mobj_t* mo);
3858 
3859 // travelling cube sound
A_SpawnSound(mobj_t * mo)3860 void A_SpawnSound (mobj_t* mo)
3861 {
3862     S_StartObjSound(mo,sfx_boscub);
3863     A_SpawnFly(mo);
3864 }
3865 
A_SpawnFly(mobj_t * mo)3866 void A_SpawnFly (mobj_t* mo)
3867 {
3868     mobj_t*     newmobj;
3869     mobj_t*     fog;
3870     mobj_t*     targ;
3871     int         r;
3872     mobjtype_t  type;
3873 
3874     if (--mo->reactiontime)
3875         return; // still flying
3876 
3877     targ = mo->target;
3878     if( targ == NULL )
3879     {
3880         // Happens if save game with cube flying.
3881         // targ should be the previous braintarget.
3882         int bt = ((braintargeton == 0)? numbraintargets : braintargeton) - 1;
3883         targ = braintargets[bt];
3884     }
3885 
3886     // First spawn teleport fog.
3887     fog = P_SpawnMobj (targ->x, targ->y, targ->z, MT_SPAWNFIRE);
3888     S_StartObjSound(fog, sfx_telept);
3889 
3890     // Randomly select monster to spawn.
3891     r = PP_Random(pr_spawnfly);
3892 
3893     // Probability distribution (kind of :),
3894     // decreasing likelihood.
3895     if ( r<50 )
3896         type = MT_TROOP;
3897     else if (r<90)
3898         type = MT_SERGEANT;
3899     else if (r<120)
3900         type = MT_SHADOWS;
3901     else if (r<130)
3902         type = MT_PAIN;
3903     else if (r<160)
3904         type = MT_HEAD;
3905     else if (r<162)
3906         type = MT_VILE;
3907     else if (r<172)
3908         type = MT_UNDEAD;
3909     else if (r<192)
3910         type = MT_BABY;
3911     else if (r<222)
3912         type = MT_FATSO;
3913     else if (r<246)
3914         type = MT_KNIGHT;
3915     else
3916         type = MT_BRUISER;
3917 
3918     newmobj     = P_SpawnMobj (targ->x, targ->y, targ->z, type);
3919 
3920     // MBF: killough 7/18/98: brain friendliness is transferred
3921     newmobj->flags = (newmobj->flags & ~MF_FRIEND) | (mo->flags & MF_FRIEND);
3922     P_UpdateClassThink(&newmobj->thinker, TH_unknown);
3923 
3924     if( P_LookForTargets(newmobj, true) )
3925         P_SetMobjState (newmobj, newmobj->info->seestate);
3926     // cube monsters have no mapthing (spawnpoint=NULL), do not respawn
3927 
3928     // telefrag anything in this spot
3929     P_TeleportMove (newmobj, newmobj->x, newmobj->y, true);
3930 
3931     // remove self (i.e., cube).
3932     P_RemoveMobj (mo);
3933 }
3934 
3935 
3936 
A_PlayerScream(mobj_t * mo)3937 void A_PlayerScream (mobj_t* mo)
3938 {
3939     // Default death sound.
3940     int         sound = sfx_pldeth;
3941 
3942     if ( (gamemode == doom2_commercial)
3943         &&      (mo->health < -50))
3944     {
3945         // IF THE PLAYER DIES
3946         // LESS THAN -50% WITHOUT GIBBING
3947         sound = sfx_pdiehi;
3948     }
3949     S_StartScreamSound(mo, sound);
3950 }
3951 
3952 
3953 // Exl: More Toxness :)
3954 // Running scripts from states (both mobj and weapon)
A_StartFS(mobj_t * actor)3955 void A_StartFS(mobj_t *actor)
3956 {
3957    // load script number from misc1
3958    int misc1 = actor->tics;
3959    actor->tics = 0;
3960    T_RunScript(misc1, actor);
3961 }
3962 
A_StartWeaponFS(player_t * player,pspdef_t * psp)3963 void A_StartWeaponFS(player_t *player, pspdef_t *psp)
3964 {
3965    int misc1;
3966 
3967    // check all pointers for validacy
3968    if(player && psp && psp->state)
3969    {
3970                 misc1 = psp->tics;
3971                 psp->tics = 0;
3972                 T_RunScript(misc1, player->mo);
3973    }
3974 }
3975 
3976 
3977 // [WDJ] From MBF, PrBoom, EternityEngine.
3978 // cph - MBF-added codepointer functions.
3979 
3980 // killough 11/98: kill an object
A_Die(mobj_t * actor)3981 void A_Die(mobj_t *actor)
3982 {
3983     P_DamageMobj(actor, NULL, NULL, actor->health);
3984 }
3985 
3986 //
3987 // A_Detonate
3988 // killough 8/9/98: same as A_Explode, except that the damage is variable
3989 //
A_Detonate(mobj_t * mo)3990 void A_Detonate(mobj_t *mo)
3991 {
3992     P_RadiusAttack(mo, mo->target, mo->info->damage);
3993 }
3994 
3995 //
3996 // killough 9/98: a mushroom explosion effect, sorta :)
3997 // Original idea: Linguica
3998 //
A_Mushroom(mobj_t * actor)3999 void A_Mushroom(mobj_t *actor)
4000 {
4001     int i, j, n = actor->info->damage;
4002 
4003     A_Explode(actor);  // First make normal explosion
4004 
4005     // Now launch mushroom cloud
4006     for (i = -n; i <= n; i += 8)
4007     {
4008         for (j = -n; j <= n; j += 8)
4009         {
4010             mobj_t target = *actor, *mo;
4011             // Aim in many directions from source, up fairly high.
4012             target.x += i << FRACBITS;
4013             target.y += j << FRACBITS;
4014             target.z += P_AproxDistance(i,j) << (FRACBITS+2);
4015             // Launch fireball
4016             mo = P_SpawnMissile(actor, &target, MT_FATSHOT);
4017             mo->momx >>= 1;
4018             mo->momy >>= 1;  // Slow it down a bit
4019             mo->momz >>= 1;
4020             mo->flags &= ~MF_NOGRAVITY;  // Make debris fall under gravity
4021         }
4022     }
4023 }
4024 
4025 //
4026 // killough 11/98
4027 //
4028 // The following were inspired by Len Pitre
4029 // A small set of highly-sought-after code pointers
4030 
4031 
A_Spawn(mobj_t * mo)4032 void A_Spawn(mobj_t *mo)
4033 {
4034     state_ext_t * sep = P_state_ext( mo->state );
4035     if( sep->parm1 )
4036     {
4037 //        mobj_t *newmobj =
4038         P_SpawnMobj(mo->x, mo->y, (sep->parm2 << FRACBITS) + mo->z,
4039                     sep->parm1 - 1);
4040 // CPhipps - no friendlyness (yet)
4041 //      newmobj->flags = (newmobj->flags & ~MF_FRIEND) | (mo->flags & MF_FRIEND);
4042     }
4043 }
4044 
A_Turn(mobj_t * mo)4045 void A_Turn(mobj_t *mo)
4046 {
4047     state_ext_t * sep = P_state_ext( mo->state );
4048     mo->angle += (uint32_t)((((uint64_t) sep->parm1) << 32) / 360);
4049 }
4050 
A_Face(mobj_t * mo)4051 void A_Face(mobj_t *mo)
4052 {
4053     state_ext_t * sep = P_state_ext( mo->state );
4054     mo->angle = (uint32_t)((((uint64_t) sep->parm1) << 32) / 360);
4055 }
4056 
4057 // Melee attack
A_Scratch(mobj_t * mo)4058 void A_Scratch(mobj_t *mo)
4059 {
4060     state_ext_t * sep = P_state_ext( mo->state );
4061 
4062     // [WDJ] Unraveled from horrible one-liner.
4063     A_FaceTarget(mo);
4064     if( ! P_CheckMeleeRange(mo) )  return;
4065 
4066     if( sep->parm2 )
4067     {
4068        S_StartObjSound(mo, sep->parm2);
4069     }
4070 
4071     P_DamageMobj(mo->target, mo, mo, sep->parm1);
4072 }
4073 
A_PlaySound(mobj_t * mo)4074 void A_PlaySound(mobj_t *mo)
4075 {
4076     state_ext_t * sep = P_state_ext( mo->state );
4077     if( sep->parm2 )
4078         S_StartSound( sep->parm1 );
4079     else
4080         S_StartObjSound( mo, sep->parm1 );
4081 }
4082 
A_RandomJump(mobj_t * mo)4083 void A_RandomJump(mobj_t *mo)
4084 {
4085     state_ext_t * sep = P_state_ext( mo->state );
4086 
4087     // As in EternityEngine, test first, then use Random Number.
4088     statenum_t si = deh_frame_to_state( sep->parm1 );
4089     if( si == S_NULL )  return;
4090 
4091     if( PP_Random(pr_randomjump) < sep->parm2 )
4092         P_SetMobjState(mo, si);
4093 }
4094 
4095 //
4096 // This allows linedef effects to be activated inside deh frames.
4097 //
A_LineEffect(mobj_t * mo)4098 void A_LineEffect(mobj_t *mo)
4099 {
4100     // [WDJ] From PrBoom, EternityEngine.
4101     // haleyjd 05/02/04: bug fix:
4102     // This line can end up being referred to long after this function returns,
4103     // thus it must be made static or memory corruption is possible.
4104     static line_t  fake_line;
4105 
4106     player_t fake_player;
4107     player_t * oldplayer;
4108     state_ext_t * sep = P_state_ext( mo->state );
4109 
4110 #if 0
4111     // From EternityEngine
4112     if( mo->eflags & MIF_LINEDONE )  return; // Unless already used up
4113 #endif
4114 
4115     fake_line = *lines;
4116 
4117     fake_line.special = (short)sep->parm1;  // param linedef type
4118     if( !fake_line.special )
4119         return;
4120 
4121     fake_line.tag = (short)sep->parm2;  // param sector tag
4122 
4123     // Substitute a fake player to absorb effects.
4124     // Do this after tests that return.  Do not leave fake player in mo.
4125     fake_player.health = 100;  // make em alive
4126     oldplayer = mo->player;
4127     mo->player = &fake_player;  // fake player
4128 
4129     // Invoke the fake line, first by invoke, then by crossing.
4130     if( !P_UseSpecialLine(mo, &fake_line, 0) )
4131         P_CrossSpecialLine(&fake_line, 0, mo);
4132 
4133     mo->player = oldplayer;  // restore player
4134 
4135 #if 0
4136     // From EternityEngine
4137     // If type cleared, no more for this thing.
4138     if( !fake_line.special )
4139         mo->eflags |= MIF_LINEDONE;
4140 #else
4141     // If type cleared, no more for this state line type.
4142     sep->parm1 = fake_line.special;
4143 #endif
4144 }
4145 
4146 #include "p_henemy.c"
4147