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