1 // Emacs style mode select -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id: p_enemy.cpp 4542 2014-02-09 17:39:42Z dr_sean $
5 //
6 // Copyright (C) 1993-1996 by id Software, Inc.
7 // Copyright (C) 2006-2014 by The Odamex 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 // DESCRIPTION:
20 // Enemy thinking, AI.
21 // Action Pointer Functions
22 // that are associated with states/frames.
23 //
24 //-----------------------------------------------------------------------------
25
26 #include <math.h>
27 #include "m_random.h"
28 #include "m_alloc.h"
29 #include "i_system.h"
30 #include "doomdef.h"
31 #include "p_local.h"
32 #include "p_lnspec.h"
33 #include "s_sound.h"
34 #include "g_game.h"
35 #include "doomstat.h"
36 #include "r_state.h"
37 #include "c_cvars.h"
38 #include "gi.h"
39 #include "p_mobj.h"
40
41 #include "d_player.h"
42
43 extern bool HasBehavior;
44
45 EXTERN_CVAR (sv_allowexit)
46 EXTERN_CVAR (sv_fastmonsters)
47 EXTERN_CVAR (co_realactorheight)
48 EXTERN_CVAR (co_zdoomphys)
49
50 enum dirtype_t
51 {
52 DI_EAST,
53 DI_NORTHEAST,
54 DI_NORTH,
55 DI_NORTHWEST,
56 DI_WEST,
57 DI_SOUTHWEST,
58 DI_SOUTH,
59 DI_SOUTHEAST,
60 DI_NODIR,
61 NUMDIRS
62 };
63
64 //
65 // P_NewChaseDir related LUT.
66 //
67
68 dirtype_t opposite[9] =
69 {
70 DI_WEST, DI_SOUTHWEST, DI_SOUTH, DI_SOUTHEAST,
71 DI_EAST, DI_NORTHEAST, DI_NORTH, DI_NORTHWEST, DI_NODIR
72 };
73
74 dirtype_t diags[4] =
75 {
76 DI_NORTHWEST, DI_NORTHEAST, DI_SOUTHWEST, DI_SOUTHEAST
77 };
78
79 fixed_t xspeed[8] = {FRACUNIT,47000,0,-47000,-FRACUNIT,-47000,0,47000};
80 fixed_t yspeed[8] = {0,47000,FRACUNIT,47000,0,-47000,-FRACUNIT,-47000};
81
82
83 void A_Die (AActor *actor);
84 void A_Detonate (AActor *mo);
85 void A_Explode (AActor *thing);
86 void A_Mushroom (AActor *actor);
87 void A_Fall (AActor *actor);
88
89
90 //
91 // ENEMY THINKING
92 // Enemies are always spawned
93 // with targetplayer = -1, threshold = 0
94 // Most monsters are spawned unaware of all players,
95 // but some can be made preaware
96 //
97
98
99 //
100 // Called by P_NoiseAlert.
101 // Recursively traverse adjacent sectors,
102 // sound blocking lines cut off traversal.
103 //
104
P_RecursiveSound(sector_t * sec,int soundblocks,AActor * soundtarget)105 void P_RecursiveSound (sector_t *sec, int soundblocks, AActor *soundtarget)
106 {
107 int i;
108 line_t* check;
109 sector_t* other;
110
111 // wake up all monsters in this sector
112 if (sec->validcount == validcount
113 && sec->soundtraversed <= soundblocks+1)
114 {
115 return; // already flooded
116 }
117
118 sec->validcount = validcount;
119 sec->soundtraversed = soundblocks+1;
120 sec->soundtarget = soundtarget->ptr();
121
122 for (i=0 ;i<sec->linecount ; i++)
123 {
124 check = sec->lines[i];
125 if (! (check->flags & ML_TWOSIDED) )
126 continue;
127
128 if ( sides[ check->sidenum[0] ].sector == sec)
129 other = sides[ check->sidenum[1] ] .sector;
130 else
131 other = sides[ check->sidenum[0] ].sector;
132
133 // [SL] 2012-02-08 - FIXME: Currently only checks for a line opening at
134 // midpoint of a sloped linedef. P_RecursiveSound() in ZDoom 1.23 causes
135 // demo desyncs.
136 P_LineOpening(check, (check->v1->x >> 1) + (check->v2->x >> 1),
137 (check->v1->y >> 1) + (check->v2->y >> 1));
138
139 if (openrange <= 0)
140 continue; // closed door
141
142 if (check->flags & ML_SOUNDBLOCK)
143 {
144 if (!soundblocks)
145 P_RecursiveSound (other, 1, soundtarget);
146 }
147 else
148 P_RecursiveSound (other, soundblocks, soundtarget);
149 }
150 }
151
152
153
154 //
155 // P_NoiseAlert
156 // If a monster yells at a player,
157 // it will alert other monsters to the player.
158 //
P_NoiseAlert(AActor * target,AActor * emmiter)159 void P_NoiseAlert (AActor *target, AActor *emmiter)
160 {
161 if (target->player && (!multiplayer && (target->player->cheats & CF_NOTARGET)))
162 return;
163
164 validcount++;
165 P_RecursiveSound (emmiter->subsector->sector, 0, target);
166 }
167
168
169
170
171 //
172 // P_CheckMeleeRange
173 //
P_CheckMeleeRange(AActor * actor)174 BOOL P_CheckMeleeRange (AActor *actor)
175 {
176 AActor *pl;
177 fixed_t dist;
178
179 if (!actor->target)
180 return false;
181
182 pl = actor->target;
183 dist = P_AproxDistance (pl->x-actor->x, pl->y-actor->y);
184
185 if (dist >= MELEERANGE-20*FRACUNIT+pl->info->radius)
186 return false;
187
188 // [RH] If moving toward goal, then we've reached it.
189 if (actor->target == actor->goal)
190 return true;
191
192 // [RH] Don't melee things too far above or below actor.
193 if (co_realactorheight)
194 {
195 if (pl->z > actor->z + actor->height)
196 return false;
197 if (pl->z + pl->height < actor->z)
198 return false;
199 }
200
201 if (!P_CheckSight(actor, pl))
202 return false;
203
204 return true;
205 }
206
207 //
208 // P_CheckMissileRange
209 //
P_CheckMissileRange(AActor * actor)210 BOOL P_CheckMissileRange (AActor *actor)
211 {
212 fixed_t dist;
213
214 if (!P_CheckSight (actor, actor->target))
215 return false;
216
217 if (actor->flags & MF_JUSTHIT)
218 {
219 // the target just hit the enemy,
220 // so fight back!
221 actor->flags &= ~MF_JUSTHIT;
222 return true;
223 }
224
225 if (actor->reactiontime)
226 return false; // do not attack yet
227
228 // OPTIMIZE: get this from a global checksight
229 dist = P_AproxDistance ( actor->x-actor->target->x,
230 actor->y-actor->target->y) - 64*FRACUNIT;
231
232 if (!actor->info->meleestate)
233 dist -= 128*FRACUNIT; // no melee attack, so fire more
234
235 dist >>= 16;
236
237 if (actor->type == MT_VILE)
238 {
239 if (dist > 14*64)
240 return false; // too far away
241 }
242
243
244 if (actor->type == MT_UNDEAD)
245 {
246 if (dist < 196)
247 return false; // close for fist attack
248 dist >>= 1;
249 }
250
251
252 if (actor->type == MT_CYBORG
253 || actor->type == MT_SPIDER
254 || actor->type == MT_SKULL)
255 {
256 dist >>= 1;
257 }
258
259 if (dist > 200)
260 dist = 200;
261
262 if (actor->type == MT_CYBORG && dist > 160)
263 dist = 160;
264
265 if (P_Random (actor) < dist)
266 return false;
267
268 return true;
269 }
270
271
272 //
273 // P_Move
274 // Move in the current direction,
275 // returns false if the move is blocked.
276 //
277 extern std::vector<line_t*> spechit;
278
P_Move(AActor * actor)279 BOOL P_Move (AActor *actor)
280 {
281 fixed_t tryx, tryy, deltax, deltay, origx, origy;
282 BOOL try_ok;
283 int good;
284 int speed;
285 int movefactor = ORIG_FRICTION_FACTOR;
286 int friction = ORIG_FRICTION;
287
288 if (!actor->subsector)
289 return false;
290
291 if (actor->flags2 & MF2_BLASTED)
292 return true;
293
294 if (actor->movedir == DI_NODIR)
295 return false;
296
297 // [RH] Instead of yanking non-floating monsters to the ground,
298 // let gravity drop them down, unless they're moving down a step.
299 if (co_zdoomphys && !(actor->flags & MF_NOGRAVITY) && actor->z > actor->floorz
300 && !(actor->flags2 & MF2_ONMOBJ))
301 {
302 if (actor->z > actor->floorz + 24*FRACUNIT)
303 {
304 return false;
305 }
306 else
307 {
308 actor->z = actor->floorz;
309 }
310 }
311
312 if ((unsigned)actor->movedir >= 8)
313 I_Error ("Weird actor->movedir!");
314
315 speed = actor->info->speed;
316
317 #if 0 // [RH] I'm not so sure this is such a good idea
318 // killough 10/98: make monsters get affected by ice and sludge too:
319 movefactor = P_GetMoveFactor (actor, &friction);
320
321 if (friction < ORIG_FRICTION && // sludge
322 !(speed = ((ORIG_FRICTION_FACTOR - (ORIG_FRICTION_FACTOR-movefactor)/2)
323 * speed) / ORIG_FRICTION_FACTOR))
324 speed = 1; // always give the monster a little bit of speed
325 #endif
326
327 tryx = (origx = actor->x) + (deltax = speed * xspeed[actor->movedir]);
328 tryy = (origy = actor->y) + (deltay = speed * yspeed[actor->movedir]);
329
330 // killough 3/15/98: don't jump over dropoffs:
331 try_ok = P_TryMove (actor, tryx, tryy, false);
332
333 if (try_ok && friction > ORIG_FRICTION)
334 {
335 actor->x = origx;
336 actor->y = origy;
337 movefactor *= FRACUNIT / ORIG_FRICTION_FACTOR / 4;
338 actor->momx += FixedMul (deltax, movefactor);
339 actor->momy += FixedMul (deltay, movefactor);
340 }
341
342 if (!try_ok)
343 {
344 // open any specials
345 if (actor->flags & MF_FLOAT && floatok)
346 {
347 // must adjust height
348 if (actor->z < tmfloorz)
349 actor->z += FLOATSPEED;
350 else
351 actor->z -= FLOATSPEED;
352
353 actor->flags |= MF_INFLOAT;
354 return true;
355 }
356
357 if (spechit.empty())
358 return false;
359
360 actor->movedir = DI_NODIR;
361 good = false;
362 while (!spechit.empty())
363 {
364 line_t *ld = spechit.back();
365 spechit.pop_back();
366
367 // if the special is not a door
368 // that can be opened,
369 // return false
370 if (P_UseSpecialLine (actor, ld, 0) ||
371 P_PushSpecialLine (actor, ld, 0))
372 good = true;
373 }
374 return good;
375 }
376 else
377 {
378 actor->flags &= ~MF_INFLOAT;
379 }
380
381 if (!co_zdoomphys && !(actor->flags & MF_FLOAT))
382 actor->z = actor->floorz;
383
384 return true;
385 }
386
387
388 //
389 // TryWalk
390 // Attempts to move actor on
391 // in its current (ob->moveangle) direction.
392 // If blocked by either a wall or an actor
393 // returns FALSE
394 // If move is either clear or blocked only by a door,
395 // returns TRUE and sets...
396 // If a door is in the way,
397 // an OpenDoor call is made to start it opening.
398 //
P_TryWalk(AActor * actor)399 BOOL P_TryWalk (AActor *actor)
400 {
401 if (!P_Move (actor))
402 {
403 return false;
404 }
405
406 actor->movecount = P_Random (actor) & 15;
407 return true;
408 }
409
410
411
412
P_NewChaseDir(AActor * actor)413 void P_NewChaseDir (AActor *actor)
414 {
415 fixed_t deltax;
416 fixed_t deltay;
417
418 dirtype_t d[3];
419
420 int tdir;
421 dirtype_t olddir;
422
423 dirtype_t turnaround;
424
425 if (!actor->target)
426 I_Error ("P_NewChaseDir: called with no target");
427
428 olddir = (dirtype_t)actor->movedir;
429 turnaround = opposite[olddir];
430
431 deltax = actor->target->x - actor->x;
432 deltay = actor->target->y - actor->y;
433
434 if (deltax>10*FRACUNIT)
435 d[1]= DI_EAST;
436 else if (deltax<-10*FRACUNIT)
437 d[1]= DI_WEST;
438 else
439 d[1]=DI_NODIR;
440
441 if (deltay<-10*FRACUNIT)
442 d[2]= DI_SOUTH;
443 else if (deltay>10*FRACUNIT)
444 d[2] = DI_NORTH;
445 else
446 d[2] = DI_NODIR;
447
448 // try direct route
449 if (d[1] != DI_NODIR && d[2] != DI_NODIR)
450 {
451 actor->movedir = diags[((deltay<0)<<1) + (deltax>0)];
452 if (actor->movedir != turnaround && P_TryWalk(actor))
453 return;
454 }
455
456 // try other directions
457 if (P_Random (actor) > 200 || abs(deltay) > abs(deltax))
458 {
459 tdir = d[1];
460 d[1] = d[2];
461 d[2] = (dirtype_t)tdir;
462 }
463
464 if (d[1] == turnaround)
465 d[1] = DI_NODIR;
466 if (d[2] == turnaround)
467 d[2] = DI_NODIR;
468
469 if (d[1] != DI_NODIR)
470 {
471 actor->movedir = d[1];
472 if (P_TryWalk (actor))
473 {
474 // either moved forward or attacked
475 return;
476 }
477 }
478
479 if (d[2] != DI_NODIR)
480 {
481 actor->movedir = d[2];
482
483 if (P_TryWalk (actor))
484 return;
485 }
486
487 // there is no direct path to the player,
488 // so pick another direction.
489 if (olddir != DI_NODIR)
490 {
491 actor->movedir = olddir;
492
493 if (P_TryWalk (actor))
494 return;
495 }
496
497 // randomly determine direction of search
498 if (P_Random (actor) & 1)
499 {
500 for (tdir = DI_EAST; tdir <= DI_SOUTHEAST; tdir++)
501 {
502 if (tdir != turnaround)
503 {
504 actor->movedir = tdir;
505
506 if ( P_TryWalk(actor) )
507 return;
508 }
509 }
510 }
511 else
512 {
513 for (tdir = DI_SOUTHEAST; tdir != (DI_EAST-1); tdir--)
514 {
515 if (tdir != turnaround)
516 {
517 actor->movedir = tdir;
518
519 if ( P_TryWalk(actor) )
520 return;
521 }
522 }
523 }
524
525 if (turnaround != DI_NODIR)
526 {
527 actor->movedir =turnaround;
528 if ( P_TryWalk(actor) )
529 return;
530 }
531
532 actor->movedir = DI_NODIR; // can not move
533 }
534
535
536
537 //
538 // P_LookForPlayers
539 // If allaround is false, only look 180 degrees in front.
540 // Returns true if a player is targeted.
541 //
542 // This function can trip you up if you're not careful. The piece of
543 // functionality that is critical to vanilla compatibility is actor->lastlook.
544 // In vanilla, it was randomly set to a number between 0 and 3 on mobj
545 // creation, and this function is SUPPOSED to loop through all player numbers,
546 // set the mobj target and leave lastlook at the last-looked at player, in
547 // the hopes that not all monsters will immediately target the same player.
548 //
549 // However, the looping logic is _very_ tricky to get your head around, as
550 // the function was written with a fixed array of players in mind and makes
551 // some bad assumptions. The ending sentinal (stop) can refer to a player
552 // index that doesn't exist. The hard-limit counter (c) is post-incremented,
553 // so the loop will do at most two sight-checks, but lastlook is actually
554 // incremented one more time than that.
555 //
P_LookForPlayers(AActor * actor,bool allaround)556 bool P_LookForPlayers(AActor *actor, bool allaround)
557 {
558 sector_t* sector = actor->subsector->sector;
559
560 if (!sector)
561 return false;
562
563 // Construct our table of ingame players
564 // [AM] TODO: Have the Players container handle this instead of having to
565 // check every single tic.
566 static player_t* playeringame[MAXPLAYERS];
567 memset(playeringame, 0, sizeof(player_t*) * MAXPLAYERS);
568
569 short maxid = 0;
570 for (Players::iterator it = players.begin();it != players.end();++it)
571 {
572 if (it->ingame() && !(it->spectator))
573 {
574 playeringame[(it->id) - 1] = &*it;
575 maxid = it->id;
576 }
577 }
578
579 // If there are no ingame players, we need to bug out now because
580 // otherwise we're going to cause an infinite loop.
581 if (maxid == 0)
582 return false;
583
584 // denis - vanilla sync, original code always looped over size-4 array.
585 if (maxid < MAXPLAYERS_VANILLA)
586 maxid = MAXPLAYERS_VANILLA;
587
588 // denis - prevents calling P_CheckSight twice on the same player
589 static bool sightcheckfailed[MAXPLAYERS];
590 memset(sightcheckfailed, 0, sizeof(bool) * maxid);
591
592 int counter = 0;
593
594 // [AM] Vanilla braindamage, "fixing" will lead to vanilla desyncs
595 unsigned int stop;
596 if (actor->lastlook > 0)
597 stop = actor->lastlook - 1;
598 else
599 stop = maxid - 1;
600
601 for ( ; ; actor->lastlook = (actor->lastlook + 1) % maxid)
602 {
603 if (playeringame[actor->lastlook] == NULL)
604 continue;
605
606 if (++counter == 3 || actor->lastlook == stop)
607 {
608 // done looking
609 // [RH] Use goal as a last resort
610 if (!actor->target && actor->goal)
611 {
612 actor->target = actor->goal;
613 return true;
614 }
615 return false;
616 }
617
618 if (sightcheckfailed[actor->lastlook])
619 continue;
620
621 player_t* player = playeringame[actor->lastlook];
622
623 if (player->cheats & CF_NOTARGET)
624 continue; // no target
625
626 if (player->health <= 0)
627 continue; // dead
628
629 if (!player->mo)
630 continue; // out of game
631
632 if (!P_CheckSight(actor, player->mo))
633 {
634 sightcheckfailed[actor->lastlook] = true;
635 continue; // out of sight
636 }
637
638 if (!allaround)
639 {
640 angle_t an = P_PointToAngle(actor->x, actor->y,
641 player->mo->x, player->mo->y) - actor->angle;
642 if (an > ANG90 && an < ANG270)
643 {
644 fixed_t dist = P_AproxDistance(player->mo->x - actor->x,
645 player->mo->y - actor->y);
646 // if real close, react anyway
647 if (dist > MELEERANGE)
648 continue; // behind back
649 }
650 }
651
652 // [RH] Need to be sure the reactiontime is 0 if the monster is
653 // leaving its goal to go after a player.
654 if (actor->goal && actor->target == actor->goal)
655 actor->reactiontime = 0;
656
657 actor->target = player->mo->ptr();
658 return true;
659 }
660
661 return false;
662 }
663
664
665 //
666 // A_KeenDie
667 // DOOM II special, map 32.
668 // Uses special tag 666.
669 //
A_KeenDie(AActor * actor)670 void A_KeenDie (AActor *actor)
671 {
672 A_Fall (actor);
673
674 // scan the remaining thinkers
675 // to see if all Keens are dead
676 AActor *other;
677 TThinkerIterator<AActor> iterator;
678
679 while ( (other = iterator.Next ()) )
680 {
681 if (other != actor && other->type == actor->type && other->health > 0)
682 {
683 // other Keen not dead
684 return;
685 }
686 }
687
688 EV_DoDoor (DDoor::doorOpen, NULL, NULL, 666, 2*FRACUNIT, 0, NoKey);
689 }
690
691
692 //
693 // ACTION ROUTINES
694 //
695
696 //
697 // A_Look
698 // Stay in state until a player is sighted.
699 // [RH] Will also leave state to move to goal.
700 //
A_Look(AActor * actor)701 void A_Look (AActor *actor)
702 {
703 AActor *targ;
704 AActor *newgoal;
705
706 if(!actor->subsector)
707 return;
708
709 // [RH] Set goal now if appropriate
710 if (actor->special == Thing_SetGoal && actor->args[0] == 0)
711 {
712 actor->special = 0;
713 newgoal = AActor::FindGoal (NULL, actor->args[1], MT_PATHNODE);
714 actor->goal = newgoal->ptr();
715 actor->reactiontime = actor->args[2] * TICRATE + level.time;
716 }
717
718 actor->threshold = 0; // any shot will wake up
719 targ = actor->subsector->sector->soundtarget;
720
721 if (targ && targ->player && (targ->player->cheats & CF_NOTARGET))
722 return;
723
724 // GhostlyDeath -- can't hear spectators
725 if (targ && targ->player && targ->player->spectator)
726 return;
727
728 if (targ && (targ->flags & MF_SHOOTABLE))
729 {
730 actor->target = targ->ptr();
731
732 if (actor->flags & MF_AMBUSH)
733 {
734 if (P_CheckSight(actor, actor->target))
735 goto seeyou;
736 }
737 else
738 goto seeyou;
739 }
740
741
742 if (!P_LookForPlayers (actor, false))
743 return;
744
745 // go into chase state
746 seeyou:
747
748 // GhostlyDeath -- Can't see spectators
749 if (actor->target->player && actor->target->player->spectator)
750 return;
751
752 // [RH] Don't start chasing after a goal if it isn't time yet.
753 if (actor->target == actor->goal)
754 {
755 if (actor->reactiontime > level.time)
756 actor->target = AActor::AActorPtr();
757 }
758 else if (actor->info->seesound)
759 {
760 char sound[MAX_SNDNAME];
761
762 strcpy (sound, actor->info->seesound);
763
764 if (sound[strlen(sound)-1] == '1')
765 {
766 sound[strlen(sound)-1] = P_Random(actor)%3 + '1';
767 if (S_FindSound (sound) == -1)
768 sound[strlen(sound)-1] = '1';
769 }
770
771 S_Sound (actor, CHAN_VOICE, sound, 1, ATTN_NORM);
772 }
773
774 if (actor->target)
775 P_SetMobjState (actor, actor->info->seestate, true);
776 }
777 #include "m_vectors.h"
778
779 //
780 // A_Chase
781 // Actor has a melee attack,
782 // so it tries to close as fast as possible
783 //
A_Chase(AActor * actor)784 void A_Chase (AActor *actor)
785 {
786 int delta;
787 AActor *ngoal;
788
789 // GhostlyDeath -- Don't chase spectators at all
790 if (actor->target && actor->target->player && actor->target->player->spectator)
791 return;
792
793 if (actor->reactiontime)
794 actor->reactiontime--;
795
796 // modify target threshold
797 if (actor->threshold)
798 {
799 if (!actor->target || actor->target->health <= 0)
800 {
801 actor->threshold = 0;
802 }
803 else
804 actor->threshold--;
805 }
806
807 // turn towards movement direction if not there yet
808 if (actor->movedir < 8)
809 {
810 actor->angle &= (angle_t)(7<<29);
811 delta = actor->angle - (actor->movedir << 29);
812
813 if (delta > 0)
814 actor->angle -= ANG90/2;
815 else if (delta < 0)
816 actor->angle += ANG90/2;
817 }
818
819 // [RH] If the target is dead (and not a goal), stop chasing it.
820 if (actor->target && actor->target != actor->goal && actor->target->health <= 0)
821 actor->target = AActor::AActorPtr();
822
823 if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
824 {
825 // look for a new target
826 if (P_LookForPlayers (actor, true) && actor->target != actor->goal)
827 return; // got a new target
828
829 if (!actor->target)
830 {
831 P_SetMobjState (actor, actor->info->spawnstate, true); // denis - todo - this sometimes leads to a stack overflow due to infinite recursion: A_Chase->SetMobjState->A_Look->SetMobjState
832 return;
833 }
834 }
835
836 // do not attack twice in a row
837 if (actor->flags & MF_JUSTATTACKED)
838 {
839 actor->flags &= ~MF_JUSTATTACKED;
840 if ((sv_skill != sk_nightmare) && !sv_fastmonsters)
841 P_NewChaseDir (actor);
842 return;
843 }
844
845 // [RH] Don't attack if just moving toward goal
846 if (actor->target == actor->goal)
847 {
848 if (P_CheckMeleeRange (actor))
849 {
850 // reached the goal
851 actor->reactiontime = actor->goal->args[1] * TICRATE + level.time;
852 ngoal = AActor::FindGoal (NULL, actor->goal->args[0], MT_PATHNODE);
853 if (ngoal)
854 actor->goal = ngoal->ptr();
855 else
856 actor->goal = AActor::AActorPtr();
857
858 actor->target = AActor::AActorPtr();
859 P_SetMobjState (actor, actor->info->spawnstate, true);
860 return;
861 }
862 goto nomissile;
863 }
864
865 // check for melee attack
866 if (actor->info->meleestate && P_CheckMeleeRange (actor))
867 {
868 if (actor->info->attacksound)
869 S_Sound (actor, CHAN_WEAPON, actor->info->attacksound, 1, ATTN_NORM);
870
871 P_SetMobjState (actor, actor->info->meleestate, true);
872 return;
873 }
874
875 // check for missile attack
876 if (actor->info->missilestate)
877 {
878 if (sv_skill < sk_nightmare
879 && actor->movecount && !sv_fastmonsters)
880 {
881 goto nomissile;
882 }
883
884 if (!P_CheckMissileRange (actor))
885 goto nomissile;
886
887 P_SetMobjState (actor, actor->info->missilestate, true);
888 actor->flags |= MF_JUSTATTACKED;
889 return;
890 }
891
892 // ?
893 nomissile:
894 // possibly choose another target
895 if (multiplayer
896 && !actor->threshold
897 && !P_CheckSight(actor, actor->target))
898 {
899 if (P_LookForPlayers(actor,true))
900 return; // got a new target
901 }
902
903 // chase towards player
904 if (--actor->movecount < 0 || !P_Move (actor))
905 {
906 P_NewChaseDir (actor);
907 }
908
909 // make active sound
910 if (actor->info->activesound && P_Random (actor) < 3)
911 {
912 S_Sound (actor, CHAN_VOICE, actor->info->activesound, 1, ATTN_IDLE);
913 }
914 }
915
916
917 //
918 // A_FaceTarget
919 //
A_FaceTarget(AActor * actor)920 void A_FaceTarget (AActor *actor)
921 {
922 if (!actor->target)
923 return;
924
925 actor->flags &= ~MF_AMBUSH;
926
927 actor->angle = P_PointToAngle (actor->x,
928 actor->y,
929 actor->target->x,
930 actor->target->y);
931
932 if (actor->target->flags & MF_SHADOW)
933 actor->angle += P_RandomDiff(actor)<<21;
934 }
935
936 //
937 // [RH] A_MonsterRail
938 //
939 // New function to let monsters shoot a railgun
940 //
A_MonsterRail(AActor * actor)941 void A_MonsterRail (AActor *actor)
942 {
943 if (!actor->target)
944 return;
945
946 actor->flags &= ~MF_AMBUSH;
947
948 actor->angle = R_PointToAngle2 (actor->x,
949 actor->y,
950 actor->target->x,
951 actor->target->y);
952
953 //actor->pitch = tantoangle[P_AimLineAttack (actor, actor->angle, MISSILERANGE) >> DBITS];
954 actor->pitch = -(int)(tan ((float)P_AimLineAttack (actor, actor->angle, MISSILERANGE)/65536.0f)*ANG180/PI);
955
956 // Let the aim trail behind the player
957 actor->angle = R_PointToAngle2 (actor->x,
958 actor->y,
959 actor->target->x - actor->target->momx * 3,
960 actor->target->y - actor->target->momy * 3);
961
962 if (actor->target->flags & MF_SHADOW)
963 {
964 int t = P_Random(actor);
965 actor->angle += (t-P_Random(actor))<<21;
966 }
967
968 P_RailAttack (actor, actor->info->damage, 0);
969 }
970
971 //
972 //
973 // A_PosAttack
974 //
A_PosAttack(AActor * actor)975 void A_PosAttack (AActor *actor)
976 {
977 int angle;
978 int damage;
979 int slope;
980
981 if (!actor->target)
982 return;
983
984 A_FaceTarget (actor);
985 angle = actor->angle;
986 slope = P_AimLineAttack (actor, angle, MISSILERANGE);
987
988 S_Sound (actor, CHAN_WEAPON, "grunt/attack", 1, ATTN_NORM);
989 angle += P_RandomDiff (actor)<<20;
990 damage = ((P_Random (actor)%5)+1)*3;
991 P_LineAttack (actor, angle, MISSILERANGE, slope, damage);
992 }
993
A_SPosAttack(AActor * actor)994 void A_SPosAttack (AActor *actor)
995 {
996 int i;
997 int bangle;
998 int slope;
999
1000 if (!actor->target)
1001 return;
1002
1003 S_Sound (actor, CHAN_WEAPON, "shotguy/attack", 1, ATTN_NORM);
1004 A_FaceTarget (actor);
1005 bangle = actor->angle;
1006 slope = P_AimLineAttack (actor, bangle, MISSILERANGE);
1007
1008 for (i=0 ; i<3 ; i++)
1009 {
1010 int angle = bangle + (P_RandomDiff (actor)<<20);
1011 int damage = ((P_Random (actor)%5)+1)*3;
1012 P_LineAttack(actor, angle, MISSILERANGE, slope, damage);
1013 }
1014 }
1015
A_CPosAttack(AActor * actor)1016 void A_CPosAttack (AActor *actor)
1017 {
1018 int angle;
1019 int bangle;
1020 int damage;
1021 int slope;
1022
1023 if (!actor->target)
1024 return;
1025
1026 S_Sound (actor, CHAN_WEAPON, "chainguy/attack", 1, ATTN_NORM);
1027 A_FaceTarget (actor);
1028 bangle = actor->angle;
1029 slope = P_AimLineAttack (actor, bangle, MISSILERANGE);
1030
1031 angle = bangle + (P_RandomDiff (actor)<<20);
1032 damage = ((P_Random (actor)%5)+1)*3;
1033 P_LineAttack (actor, angle, MISSILERANGE, slope, damage);
1034 }
1035
A_CPosRefire(AActor * actor)1036 void A_CPosRefire (AActor *actor)
1037 {
1038 // keep firing unless target got out of sight
1039 A_FaceTarget (actor);
1040
1041 if (P_Random (actor) < 40)
1042 return;
1043
1044 if (!actor->target
1045 || actor->target->health <= 0
1046 || !P_CheckSight(actor, actor->target)
1047 )
1048 {
1049 P_SetMobjState (actor, actor->info->seestate, true);
1050 }
1051 }
1052
1053
A_SpidRefire(AActor * actor)1054 void A_SpidRefire (AActor *actor)
1055 {
1056 // keep firing unless target got out of sight
1057 A_FaceTarget (actor);
1058
1059 if (P_Random (actor) < 10)
1060 return;
1061
1062 if (!actor->target
1063 || actor->target->health <= 0
1064 || !P_CheckSight(actor, actor->target)
1065 )
1066 {
1067 P_SetMobjState (actor, actor->info->seestate, true);
1068 }
1069 }
1070
A_BspiAttack(AActor * actor)1071 void A_BspiAttack (AActor *actor)
1072 {
1073 if (!actor->target)
1074 return;
1075
1076 A_FaceTarget (actor);
1077
1078 // launch a missile
1079 if(serverside)
1080 P_SpawnMissile (actor, actor->target, MT_ARACHPLAZ);
1081 }
1082
1083
1084 //
1085 // A_TroopAttack
1086 //
A_TroopAttack(AActor * actor)1087 void A_TroopAttack (AActor *actor)
1088 {
1089 if (!actor->target)
1090 return;
1091
1092 A_FaceTarget (actor);
1093 if (P_CheckMeleeRange (actor))
1094 {
1095 S_Sound (actor, CHAN_WEAPON, "imp/melee", 1, ATTN_NORM);
1096 int damage = (P_Random (actor)%8+1)*3;
1097 P_DamageMobj (actor->target, actor, actor, damage, MOD_HIT);
1098 return;
1099 }
1100
1101 // launch a missile
1102 if(serverside)
1103 P_SpawnMissile (actor, actor->target, MT_TROOPSHOT);
1104 }
1105
1106
A_SargAttack(AActor * actor)1107 void A_SargAttack (AActor *actor)
1108 {
1109 if (!actor->target)
1110 return;
1111
1112 A_FaceTarget (actor);
1113 if (P_CheckMeleeRange (actor))
1114 {
1115 int damage = ((P_Random (actor)%10)+1)*4;
1116 P_DamageMobj (actor->target, actor, actor, damage, MOD_HIT);
1117 }
1118 }
1119
A_HeadAttack(AActor * actor)1120 void A_HeadAttack (AActor *actor)
1121 {
1122 if (!actor->target)
1123 return;
1124
1125 A_FaceTarget (actor);
1126 if (P_CheckMeleeRange (actor))
1127 {
1128 int damage = (P_Random (actor)%6+1)*10;
1129 P_DamageMobj (actor->target, actor, actor, damage, MOD_HIT);
1130 return;
1131 }
1132
1133 // launch a missile
1134 if(serverside)
1135 P_SpawnMissile (actor, actor->target, MT_HEADSHOT);
1136 }
1137
A_CyberAttack(AActor * actor)1138 void A_CyberAttack (AActor *actor)
1139 {
1140 if (!actor->target)
1141 return;
1142
1143 A_FaceTarget (actor);
1144
1145 if(serverside)
1146 {
1147 P_SpawnMissile (actor, actor->target, MT_ROCKET);
1148 }
1149 }
1150
1151
A_BruisAttack(AActor * actor)1152 void A_BruisAttack (AActor *actor)
1153 {
1154 if (!actor->target)
1155 return;
1156
1157 if (P_CheckMeleeRange (actor))
1158 {
1159 int damage = (P_Random (actor)%8+1)*10;
1160 S_Sound (actor, CHAN_WEAPON, "baron/melee", 1, ATTN_NORM);
1161 P_DamageMobj (actor->target, actor, actor, damage, MOD_HIT);
1162 return;
1163 }
1164
1165 // launch a missile
1166 if(serverside)
1167 P_SpawnMissile (actor, actor->target, MT_BRUISERSHOT);
1168 }
1169
1170
1171 //
1172 // A_SkelMissile
1173 //
A_SkelMissile(AActor * actor)1174 void A_SkelMissile (AActor *actor)
1175 {
1176 if (!actor->target)
1177 return;
1178
1179 A_FaceTarget (actor);
1180
1181 if(serverside)
1182 {
1183 actor->z += 16*FRACUNIT; // so missile spawns higher
1184 AActor *mo = P_SpawnMissile (actor, actor->target, MT_TRACER);
1185 actor->z -= 16*FRACUNIT; // back to normal
1186
1187 mo->x += mo->momx;
1188 mo->y += mo->momy;
1189 mo->tracer = actor->target;
1190 }
1191 }
1192
1193 #define TRACEANGLE (0xc000000)
1194
A_Tracer(AActor * actor)1195 void A_Tracer (AActor *actor)
1196 {
1197 // killough 1/18/98: this is why some missiles do not have smoke
1198 // and some do. Also, internal demos start at random gametics, thus
1199 // the bug in which revenants cause internal demos to go out of sync.
1200 //
1201 // killough 3/6/98: fix revenant internal demo bug by subtracting
1202 // levelstarttic from gametic:
1203 //
1204 // [RH] level.time is always 0-based, so nothing special to do here.
1205
1206 // denis - demogametic must be 0-based, but from start of entire demo,
1207 // not just this level!
1208 extern int demostartgametic;
1209 int demogametic = gametic - demostartgametic;
1210 if (demogametic & 3)
1211 return;
1212
1213 // spawn a puff of smoke behind the rocket
1214 if(serverside)
1215 {
1216 P_SpawnPuff(actor->x, actor->y, actor->z);
1217
1218 AActor* th = new AActor (actor->x - actor->momx,
1219 actor->y - actor->momy,
1220 actor->z, MT_SMOKE);
1221
1222 th->momz = FRACUNIT;
1223 th->tics -= P_Random (th)&3;
1224 if (th->tics < 1)
1225 th->tics = 1;
1226 }
1227
1228 // adjust direction
1229 AActor *dest = actor->tracer;
1230
1231 if (!dest || dest->health <= 0)
1232 return;
1233
1234 // change angle
1235 angle_t exact = P_PointToAngle (actor->x,
1236 actor->y,
1237 dest->x,
1238 dest->y);
1239
1240 if (exact != actor->angle)
1241 {
1242 if (exact - actor->angle > ANG180)
1243 {
1244 actor->angle -= TRACEANGLE;
1245 if (exact - actor->angle < ANG180)
1246 actor->angle = exact;
1247 }
1248 else
1249 {
1250 actor->angle += TRACEANGLE;
1251 if (exact - actor->angle > ANG180)
1252 actor->angle = exact;
1253 }
1254 }
1255
1256 exact = actor->angle>>ANGLETOFINESHIFT;
1257 actor->momx = FixedMul (actor->info->speed, finecosine[exact]);
1258 actor->momy = FixedMul (actor->info->speed, finesine[exact]);
1259
1260 // change slope
1261 fixed_t dist = P_AproxDistance (dest->x - actor->x,
1262 dest->y - actor->y);
1263
1264 dist = dist / actor->info->speed;
1265
1266 if (dist < 1)
1267 dist = 1;
1268 fixed_t slope = (dest->z+40*FRACUNIT - actor->z) / dist;
1269
1270 if (slope < actor->momz)
1271 actor->momz -= FRACUNIT/8;
1272 else
1273 actor->momz += FRACUNIT/8;
1274 }
1275
1276
A_SkelWhoosh(AActor * actor)1277 void A_SkelWhoosh (AActor *actor)
1278 {
1279 if (!actor->target)
1280 return;
1281 A_FaceTarget (actor);
1282 S_Sound (actor, CHAN_WEAPON, "skeleton/swing", 1, ATTN_NORM);
1283 }
1284
A_SkelFist(AActor * actor)1285 void A_SkelFist (AActor *actor)
1286 {
1287 if (!actor->target)
1288 return;
1289
1290 A_FaceTarget (actor);
1291
1292 if (P_CheckMeleeRange (actor))
1293 {
1294 int damage = ((P_Random (actor)%10)+1)*6;
1295 S_Sound (actor, CHAN_WEAPON, "skeleton/melee", 1, ATTN_NORM);
1296 P_DamageMobj (actor->target, actor, actor, damage, MOD_HIT);
1297 }
1298 }
1299
1300
1301
1302 //
1303 // PIT_VileCheck
1304 // Detect a corpse that could be raised.
1305 //
1306 AActor* corpsehit;
1307 AActor* vileobj;
1308 fixed_t viletryx;
1309 fixed_t viletryy;
1310
PIT_VileCheck(AActor * thing)1311 BOOL PIT_VileCheck (AActor *thing)
1312 {
1313 int maxdist;
1314 BOOL check;
1315
1316 if (!(thing->flags & MF_CORPSE) )
1317 return true; // not a monster
1318
1319 if (thing->tics != -1)
1320 return true; // not lying still yet
1321
1322 if (thing->info->raisestate == S_NULL)
1323 return true; // monster doesn't have a raise state
1324
1325 maxdist = thing->info->radius + mobjinfo[MT_VILE].radius;
1326
1327 if ( abs(thing->x - viletryx) > maxdist
1328 || abs(thing->y - viletryy) > maxdist )
1329 return true; // not actually touching
1330
1331 corpsehit = thing;
1332 corpsehit->momx = corpsehit->momy = 0;
1333 corpsehit->height <<= 2;
1334 check = P_CheckPosition (corpsehit, corpsehit->x, corpsehit->y);
1335 corpsehit->height >>= 2;
1336
1337 return !check;
1338 }
1339
1340
1341
1342 //
1343 // A_VileChase
1344 // Check for ressurecting a body
1345 //
A_VileChase(AActor * actor)1346 void A_VileChase (AActor *actor)
1347 {
1348 if(!serverside)
1349 {
1350 // Return to normal attack.
1351 A_Chase (actor);
1352 return;
1353 }
1354
1355 int xl;
1356 int xh;
1357 int yl;
1358 int yh;
1359
1360 int bx;
1361 int by;
1362
1363 mobjinfo_t* info;
1364 AActor::AActorPtr temp;
1365
1366 if (actor->movedir != DI_NODIR)
1367 {
1368 // check for corpses to raise
1369 viletryx =
1370 actor->x + actor->info->speed*xspeed[actor->movedir];
1371 viletryy =
1372 actor->y + actor->info->speed*yspeed[actor->movedir];
1373
1374 xl = (viletryx - bmaporgx - MAXRADIUS*2)>>MAPBLOCKSHIFT;
1375 xh = (viletryx - bmaporgx + MAXRADIUS*2)>>MAPBLOCKSHIFT;
1376 yl = (viletryy - bmaporgy - MAXRADIUS*2)>>MAPBLOCKSHIFT;
1377 yh = (viletryy - bmaporgy + MAXRADIUS*2)>>MAPBLOCKSHIFT;
1378
1379 vileobj = actor;
1380 for (bx=xl ; bx<=xh ; bx++)
1381 {
1382 for (by=yl ; by<=yh ; by++)
1383 {
1384 // Call PIT_VileCheck to check
1385 // whether object is a corpse
1386 // that canbe raised.
1387 if (!P_BlockThingsIterator(bx,by,PIT_VileCheck))
1388 {
1389 // got one!
1390 temp = actor->target;
1391 actor->target = corpsehit->ptr();
1392 A_FaceTarget (actor);
1393 actor->target = temp;
1394
1395 P_SetMobjState (actor, S_VILE_HEAL1, true);
1396 S_Sound (corpsehit, CHAN_BODY, "vile/raise", 1, ATTN_IDLE);
1397 info = corpsehit->info;
1398
1399 P_SetMobjState (corpsehit,info->raisestate, true);
1400
1401 // [Nes] - Classic demo compatability: Ghost monster bug.
1402 if ((demoplayback || demorecording) && democlassic) {
1403 corpsehit->height <<= 2;
1404 } else {
1405 corpsehit->height = P_ThingInfoHeight(info); // [RH] Use real mobj height
1406 corpsehit->radius = info->radius; // [RH] Use real radius
1407 }
1408
1409 corpsehit->flags = info->flags;
1410 corpsehit->health = info->spawnhealth;
1411 corpsehit->target = AActor::AActorPtr();
1412
1413 return;
1414 }
1415 }
1416 }
1417 }
1418
1419 // Return to normal attack.
1420 A_Chase (actor);
1421 }
1422
1423
1424 //
1425 // A_VileStart
1426 //
A_VileStart(AActor * actor)1427 void A_VileStart (AActor *actor)
1428 {
1429 S_Sound (actor, CHAN_VOICE, "vile/start", 1, ATTN_NORM);
1430 }
1431
1432
1433 //
1434 // A_Fire
1435 // Keep fire in front of player unless out of sight
1436 //
1437 void A_Fire (AActor *actor);
1438
A_StartFire(AActor * actor)1439 void A_StartFire (AActor *actor)
1440 {
1441 S_Sound(actor, CHAN_BODY, "vile/firestrt", 1, ATTN_NORM);
1442 A_Fire(actor);
1443 }
1444
A_FireCrackle(AActor * actor)1445 void A_FireCrackle (AActor *actor)
1446 {
1447 S_Sound(actor, CHAN_BODY, "vile/firecrkl", 1, ATTN_NORM);
1448 A_Fire(actor);
1449 }
1450
A_Fire(AActor * actor)1451 void A_Fire (AActor *actor)
1452 {
1453 AActor* dest;
1454 unsigned an;
1455
1456 dest = actor->tracer;
1457 if (!dest)
1458 return;
1459
1460 // don't move it if the vile lost sight
1461 if (!P_CheckSight(actor->target, dest))
1462 return;
1463
1464 an = dest->angle >> ANGLETOFINESHIFT;
1465
1466 actor->SetOrigin (dest->x + FixedMul (24*FRACUNIT, finecosine[an]),
1467 dest->y + FixedMul (24*FRACUNIT, finesine[an]),
1468 dest->z);
1469 }
1470
1471
1472
1473 //
1474 // A_VileTarget
1475 // Spawn the hellfire
1476 //
A_VileTarget(AActor * actor)1477 void A_VileTarget (AActor *actor)
1478 {
1479 AActor *fog;
1480
1481 if (!actor->target)
1482 return;
1483
1484 A_FaceTarget (actor);
1485
1486 fog = new AActor (actor->target->x,
1487 actor->target->x,
1488 actor->target->z, MT_FIRE);
1489
1490 actor->tracer = fog->ptr();
1491 fog->target = actor->ptr();
1492 fog->tracer = actor->target;
1493 A_Fire (fog);
1494 }
1495
1496
1497
1498
1499 //
1500 // A_VileAttack
1501 //
A_VileAttack(AActor * actor)1502 void A_VileAttack (AActor *actor)
1503 {
1504 AActor *fire;
1505 int an;
1506
1507 if (!actor->target)
1508 return;
1509
1510 A_FaceTarget (actor);
1511
1512 if (!P_CheckSight(actor, actor->target))
1513 return;
1514
1515 S_Sound (actor, CHAN_WEAPON, "vile/stop", 1, ATTN_NORM);
1516 P_DamageMobj (actor->target, actor, actor, 20, MOD_UNKNOWN);
1517 actor->target->momz = 1000*FRACUNIT/actor->target->info->mass;
1518
1519 an = actor->angle >> ANGLETOFINESHIFT;
1520
1521 fire = actor->tracer;
1522
1523 if (!fire)
1524 return;
1525
1526 // move the fire between the vile and the player
1527 fire->x = actor->target->x - FixedMul (24*FRACUNIT, finecosine[an]);
1528 fire->y = actor->target->y - FixedMul (24*FRACUNIT, finesine[an]);
1529 P_RadiusAttack (fire, actor, 70, 70, true, MOD_UNKNOWN);
1530 }
1531
1532
1533
1534
1535 //
1536 // Mancubus attack,
1537 // firing three missiles (bruisers) in three different directions?
1538 // Doesn't look like it.
1539 //
1540 #define FATSPREAD (ANG90/8)
1541
A_FatRaise(AActor * actor)1542 void A_FatRaise (AActor *actor)
1543 {
1544 A_FaceTarget (actor);
1545 S_Sound (actor, CHAN_WEAPON, "fatso/raiseguns", 1, ATTN_NORM);
1546 }
1547
1548
A_FatAttack1(AActor * actor)1549 void A_FatAttack1 (AActor *actor)
1550 {
1551 if(!actor->target)
1552 return;
1553
1554 A_FaceTarget (actor);
1555
1556 if(serverside)
1557 {
1558 // Change direction to ...
1559 actor->angle += FATSPREAD;
1560 P_SpawnMissile (actor, actor->target, MT_FATSHOT);
1561
1562 AActor *mo = P_SpawnMissile (actor, actor->target, MT_FATSHOT);
1563 mo->angle += FATSPREAD;
1564 int an = mo->angle >> ANGLETOFINESHIFT;
1565 mo->momx = FixedMul (mo->info->speed, finecosine[an]);
1566 mo->momy = FixedMul (mo->info->speed, finesine[an]);
1567 }
1568 }
1569
A_FatAttack2(AActor * actor)1570 void A_FatAttack2 (AActor *actor)
1571 {
1572 if(!actor->target)
1573 return;
1574
1575 A_FaceTarget (actor);
1576
1577 if(serverside)
1578 {
1579 // Now here choose opposite deviation.
1580 actor->angle -= FATSPREAD;
1581 P_SpawnMissile (actor, actor->target, MT_FATSHOT);
1582
1583 AActor *mo = P_SpawnMissile (actor, actor->target, MT_FATSHOT);
1584 mo->angle -= FATSPREAD*2;
1585 int an = mo->angle >> ANGLETOFINESHIFT;
1586 mo->momx = FixedMul (mo->info->speed, finecosine[an]);
1587 mo->momy = FixedMul (mo->info->speed, finesine[an]);
1588 }
1589 }
1590
A_FatAttack3(AActor * actor)1591 void A_FatAttack3 (AActor *actor)
1592 {
1593 if(!actor->target)
1594 return;
1595
1596 A_FaceTarget (actor);
1597
1598 if(serverside)
1599 {
1600 AActor *mo = P_SpawnMissile (actor, actor->target, MT_FATSHOT);
1601 mo->angle -= FATSPREAD/2;
1602 int an = mo->angle >> ANGLETOFINESHIFT;
1603 mo->momx = FixedMul (mo->info->speed, finecosine[an]);
1604 mo->momy = FixedMul (mo->info->speed, finesine[an]);
1605
1606 mo = P_SpawnMissile (actor, actor->target, MT_FATSHOT);
1607 mo->angle += FATSPREAD/2;
1608 an = mo->angle >> ANGLETOFINESHIFT;
1609 mo->momx = FixedMul (mo->info->speed, finecosine[an]);
1610 mo->momy = FixedMul (mo->info->speed, finesine[an]);
1611 }
1612 }
1613
1614
1615 //
1616 // killough 9/98: a mushroom explosion effect, sorta :)
1617 // Original idea: Linguica
1618 //
1619
A_Mushroom(AActor * actor)1620 void A_Mushroom (AActor *actor)
1621 {
1622 int i, j, n = actor->damage;
1623
1624 A_Explode (actor); // First make normal explosion
1625
1626 if(serverside)
1627 {
1628 // Now launch mushroom cloud
1629 for (i = -n; i <= n; i += 8)
1630 {
1631 for (j = -n; j <= n; j += 8)
1632 {
1633 AActor target = *actor, *mo;
1634 target.x += i << FRACBITS; // Aim in many directions from source
1635 target.y += j << FRACBITS;
1636 target.z += P_AproxDistance(i,j) << (FRACBITS+2); // Aim up fairly high
1637 mo = P_SpawnMissile (actor, &target, MT_FATSHOT); // Launch fireball
1638 if (mo != NULL)
1639 {
1640 mo->momx >>= 1;
1641 mo->momy >>= 1; // Slow it down a bit
1642 mo->momz >>= 1;
1643 mo->flags &= ~MF_NOGRAVITY; // Make debris fall under gravity
1644 }
1645 }
1646 }
1647 }
1648 }
1649
1650
1651
1652 //
1653 // SkullAttack
1654 // Fly at the player like a missile.
1655 //
1656 #define SKULLSPEED (20*FRACUNIT)
1657
A_SkullAttack(AActor * actor)1658 void A_SkullAttack (AActor *actor)
1659 {
1660 AActor* dest;
1661 angle_t an;
1662 int dist;
1663
1664 if (!actor->target)
1665 return;
1666
1667 dest = actor->target;
1668 actor->flags |= MF_SKULLFLY;
1669
1670 S_Sound (actor, CHAN_VOICE, actor->info->attacksound, 1, ATTN_NORM);
1671 A_FaceTarget (actor);
1672 an = actor->angle >> ANGLETOFINESHIFT;
1673 actor->momx = FixedMul (SKULLSPEED, finecosine[an]);
1674 actor->momy = FixedMul (SKULLSPEED, finesine[an]);
1675 dist = P_AproxDistance (dest->x - actor->x, dest->y - actor->y);
1676 dist = dist / SKULLSPEED;
1677
1678 if (dist < 1)
1679 dist = 1;
1680 actor->momz = (dest->z+(dest->height>>1) - actor->z) / dist;
1681 }
1682
1683
1684 //
1685 // A_PainShootSkull
1686 // Spawn a lost soul and launch it at the target
1687 //
A_PainShootSkull(AActor * actor,angle_t angle)1688 void A_PainShootSkull (AActor *actor, angle_t angle)
1689 {
1690 fixed_t x;
1691 fixed_t y;
1692 fixed_t z;
1693
1694 AActor* other;
1695 angle_t an;
1696 int prestep;
1697 int count;
1698
1699 if(!serverside)
1700 return;
1701
1702 // count total number of skull currently on the level
1703 count = 0;
1704
1705 TThinkerIterator<AActor> iterator;
1706
1707 while ( (other = iterator.Next ()) )
1708 {
1709 if (other->type == MT_SKULL)
1710 count++;
1711 }
1712
1713 // if there are already 20 skulls on the level,
1714 // don't spit another one
1715 if (count > 20)
1716 return;
1717
1718 // okay, there's room for another one
1719 an = angle >> ANGLETOFINESHIFT;
1720
1721 prestep = 4*FRACUNIT + 3*(actor->info->radius + mobjinfo[MT_SKULL].radius)/2;
1722
1723 x = actor->x + FixedMul (prestep, finecosine[an]);
1724 y = actor->y + FixedMul (prestep, finesine[an]);
1725 z = actor->z + 8*FRACUNIT;
1726
1727 // Check whether the Lost Soul is being fired through a 1-sided // phares
1728 // wall or an impassible line, or a "monsters can't cross" line.// |
1729 // If it is, then we don't allow the spawn. // V
1730 // denis - vanilla desync
1731 /*
1732 if (Check_Sides(actor,x,y))
1733 return;*/
1734
1735 other = new AActor (x, y, z, MT_SKULL);
1736
1737 // Check to see if the new Lost Soul's z value is above the
1738 // ceiling of its new sector, or below the floor. If so, kill it.
1739 // denis - vanilla desync
1740 /*
1741 if ((other->z >
1742 (other->subsector->sector->ceilingheight - other->height)) ||
1743 (other->z < other->subsector->sector->floorheight))
1744 {
1745 // kill it immediately
1746 P_DamageMobj (other, actor, actor, 10000, MOD_UNKNOWN); // ^
1747 return; // |
1748 } // phares
1749 */
1750 // Check for movements.
1751 if (!P_TryMove(other, x, y, false))
1752 {
1753 // kill it immediately
1754 P_DamageMobj (other, actor, actor, 10000, MOD_UNKNOWN);
1755 return;
1756 }
1757
1758 other->target = actor->target;
1759 A_SkullAttack (other);
1760 }
1761
1762
1763 //
1764 // A_PainAttack
1765 // Spawn a lost soul and launch it at the target
1766 //
A_PainAttack(AActor * actor)1767 void A_PainAttack (AActor *actor)
1768 {
1769 if (!actor->target)
1770 return;
1771
1772 A_FaceTarget (actor);
1773
1774 if(!serverside)
1775 return;
1776
1777 A_PainShootSkull (actor, actor->angle);
1778 }
1779
A_PainDie(AActor * actor)1780 void A_PainDie (AActor *actor)
1781 {
1782 A_Fall (actor);
1783
1784 if(!serverside)
1785 return;
1786
1787 A_PainShootSkull (actor, actor->angle+ANG90);
1788 A_PainShootSkull (actor, actor->angle+ANG180);
1789 A_PainShootSkull (actor, actor->angle+ANG270);
1790 }
1791
1792
A_Scream(AActor * actor)1793 void A_Scream (AActor *actor)
1794 {
1795 char sound[MAX_SNDNAME];
1796
1797 if (actor->info->deathsound == NULL)
1798 return;
1799
1800
1801 strcpy (sound, actor->info->deathsound);
1802
1803 if (stricmp(sound, "grunt/death1") == 0 ||
1804 stricmp(sound, "shotguy/death1") == 0 ||
1805 stricmp(sound, "chainguy/death1") == 0)
1806 {
1807 sound[strlen(sound)-1] = P_Random(actor) % 3 + '1';
1808 }
1809
1810 if (stricmp(sound, "imp/death1") == 0 ||
1811 stricmp(sound, "imp/death2") == 0)
1812 {
1813 sound[strlen(sound)-1] = P_Random(actor) % 2 + '1';
1814 }
1815
1816
1817 S_Sound (actor, CHAN_VOICE, sound, 1, ATTN_NORM);
1818 }
1819
1820
A_XScream(AActor * actor)1821 void A_XScream (AActor *actor)
1822 {
1823 if (actor->player)
1824 S_Sound (actor, CHAN_VOICE, "*gibbed", 1, ATTN_NORM);
1825 else
1826 S_Sound (actor, CHAN_VOICE, "misc/gibbed", 1, ATTN_NORM);
1827 }
1828
A_Pain(AActor * actor)1829 void A_Pain (AActor *actor)
1830 {
1831 if (actor->info->painsound)
1832 S_Sound (actor, CHAN_VOICE, actor->info->painsound, 1, ATTN_NORM);
1833 }
1834
1835
1836
A_Fall(AActor * actor)1837 void A_Fall (AActor *actor)
1838 {
1839 // actor is on ground, it can be walked over
1840 actor->flags &= ~MF_SOLID;
1841
1842 // So change this if corpse objects
1843 // are meant to be obstacles.
1844 }
1845
1846
1847 // killough 11/98: kill an object
A_Die(AActor * actor)1848 void A_Die (AActor *actor)
1849 {
1850 P_DamageMobj (actor, NULL, NULL, actor->health, MOD_UNKNOWN);
1851 }
1852
1853 //
1854 // A_Detonate
1855 // killough 8/9/98: same as A_Explode, except that the damage is variable
1856 //
1857
A_Detonate(AActor * mo)1858 void A_Detonate (AActor *mo)
1859 {
1860 P_RadiusAttack (mo, mo->target, mo->damage, mo->damage, true, MOD_UNKNOWN);
1861 if (mo->z <= mo->floorz + (mo->damage<<FRACBITS))
1862 {
1863 P_HitFloor (mo);
1864 }
1865 }
1866
1867
1868 //
1869 // A_Explode
1870 //
A_Explode(AActor * thing)1871 void A_Explode (AActor *thing)
1872 {
1873 // [RH] figure out means of death;
1874 int mod;
1875 int damage = 128;
1876 int distance = 128;
1877 bool hurtSource = true;
1878
1879 switch (thing->type) {
1880 case MT_BARREL:
1881 mod = MOD_BARREL;
1882 break;
1883 case MT_ROCKET:
1884 mod = MOD_R_SPLASH;
1885 break;
1886 default:
1887 mod = MOD_UNKNOWN;
1888 break;
1889 }
1890
1891 P_RadiusAttack (thing, thing->target, damage, distance, hurtSource, mod);
1892 }
1893
1894 #define SPEED(a) ((a)*(FRACUNIT/8))
1895
1896 //
1897 // A_BossDeath
1898 // Possibly trigger special effects if on a boss level
1899 //
A_BossDeath(AActor * actor)1900 void A_BossDeath (AActor *actor)
1901 {
1902 // [RH] These all depend on the presence of level flags now
1903 // rather than being hard-coded to specific levels.
1904
1905 if ((level.flags & (LEVEL_MAP07SPECIAL|
1906 LEVEL_BRUISERSPECIAL|
1907 LEVEL_CYBORGSPECIAL|
1908 LEVEL_SPIDERSPECIAL)) == 0)
1909 return;
1910
1911 if (
1912 ((level.flags & LEVEL_MAP07SPECIAL) && (actor->type == MT_FATSO || actor->type == MT_BABY)) ||
1913 ((level.flags & LEVEL_BRUISERSPECIAL) && (actor->type == MT_BRUISER)) ||
1914 ((level.flags & LEVEL_CYBORGSPECIAL) && (actor->type == MT_CYBORG)) ||
1915 ((level.flags & LEVEL_SPIDERSPECIAL) && (actor->type == MT_SPIDER))
1916 )
1917 ;
1918 else return;
1919
1920 // make sure there is a player alive for victory
1921 Players::const_iterator it = players.begin();
1922 for (;it != players.end();++it)
1923 {
1924 if (it->ingame() && it->health > 0)
1925 break;
1926 }
1927
1928 if (it == players.end())
1929 return; // no one left alive, so do not end game
1930
1931 // scan the remaining thinkers to see if all bosses are dead
1932 TThinkerIterator<AActor> iterator;
1933 AActor *other;
1934
1935 while ( (other = iterator.Next ()) )
1936 {
1937 if (other != actor && other->type == actor->type && other->health > 0)
1938 {
1939 // other boss not dead
1940 return;
1941 }
1942 }
1943
1944 // victory!
1945 if (level.flags & LEVEL_MAP07SPECIAL)
1946 {
1947 if (actor->type == MT_FATSO)
1948 {
1949 EV_DoFloor (DFloor::floorLowerToLowest, NULL, 666, FRACUNIT, 0, 0, 0);
1950 return;
1951 }
1952
1953 if (actor->type == MT_BABY)
1954 {
1955 EV_DoFloor (DFloor::floorRaiseByTexture, NULL, 667, FRACUNIT, 0, 0, 0);
1956 return;
1957 }
1958 }
1959 else
1960 {
1961 switch (level.flags & LEVEL_SPECACTIONSMASK)
1962 {
1963 case LEVEL_SPECLOWERFLOOR:
1964 EV_DoFloor (DFloor::floorLowerToLowest, NULL, 666, FRACUNIT, 0, 0, 0);
1965 return;
1966
1967 case LEVEL_SPECOPENDOOR:
1968 EV_DoDoor (DDoor::doorOpen, NULL, NULL, 666, SPEED(64), 0, NoKey);
1969 return;
1970 }
1971 }
1972
1973 // [RH] If noexit, then don't end the level.
1974 if (sv_gametype != GM_COOP && !sv_allowexit)
1975 return;
1976
1977 G_ExitLevel (0, 1);
1978 }
1979
1980
A_Hoof(AActor * mo)1981 void A_Hoof (AActor *mo)
1982 {
1983 S_Sound (mo, CHAN_BODY, "cyber/hoof", 1, ATTN_IDLE);
1984 A_Chase (mo);
1985 }
1986
A_Metal(AActor * mo)1987 void A_Metal (AActor *mo)
1988 {
1989 S_Sound (mo, CHAN_BODY, "spider/walk", 1, ATTN_IDLE);
1990 A_Chase (mo);
1991 }
1992
A_BabyMetal(AActor * mo)1993 void A_BabyMetal (AActor *mo)
1994 {
1995 S_Sound (mo, CHAN_BODY, "baby/walk", 1, ATTN_IDLE);
1996 A_Chase (mo);
1997 }
1998
1999 // killough 2/7/98: Remove limit on icon landings:
2000 AActor **braintargets;
2001 int numbraintargets_alloc;
2002 int numbraintargets;
2003
2004 struct brain_s brain; // killough 3/26/98: global state of boss brain
2005
2006 // killough 3/26/98: initialize icon landings at level startup,
2007 // rather than at boss wakeup, to prevent savegame-related crashes
2008
P_SpawnBrainTargets(void)2009 void P_SpawnBrainTargets (void) // killough 3/26/98: renamed old function
2010 {
2011 AActor *other;
2012 TThinkerIterator<AActor> iterator;
2013
2014 // find all the target spots
2015 numbraintargets = 0;
2016 brain.targeton = 0;
2017 brain.easy = 0; // killough 3/26/98: always init easy to 0
2018
2019 while ( (other = iterator.Next ()) )
2020 {
2021 if (other->type == MT_BOSSTARGET)
2022 { // killough 2/7/98: remove limit on icon landings:
2023 if (numbraintargets >= numbraintargets_alloc)
2024 {
2025 braintargets = (AActor **)Realloc (braintargets,
2026 (numbraintargets_alloc = numbraintargets_alloc ?
2027 numbraintargets_alloc*2 : 32) *sizeof *braintargets);
2028 }
2029 braintargets[numbraintargets++] = other;
2030 }
2031 }
2032 }
2033
A_BrainAwake(AActor * mo)2034 void A_BrainAwake (AActor *mo)
2035 {
2036 // killough 3/26/98: only generates sound now
2037 S_Sound (mo, CHAN_VOICE, "brain/sight", 1, ATTN_NONE);
2038 }
2039
2040
A_BrainPain(AActor * mo)2041 void A_BrainPain (AActor *mo)
2042 {
2043 S_Sound (mo, CHAN_VOICE, "brain/pain", 1, ATTN_NONE);
2044 }
2045
2046
A_BrainScream(AActor * mo)2047 void A_BrainScream (AActor *mo)
2048 {
2049 if(!clientside)
2050 return;
2051
2052 int x;
2053 int y;
2054 int z;
2055 AActor* th;
2056
2057 for (x=mo->x - 196*FRACUNIT ; x< mo->x + 320*FRACUNIT ; x+= FRACUNIT*8)
2058 {
2059 y = mo->y - 320*FRACUNIT;
2060 z = 128 + (P_Random (mo) << (FRACBITS + 1));
2061 th = new AActor (x,y,z, MT_ROCKET);
2062 th->momz = P_Random (mo) << 9;
2063
2064 P_SetMobjState (th, S_BRAINEXPLODE1, true);
2065
2066 th->tics -= P_Random (mo) & 7;
2067 if (th->tics < 1)
2068 th->tics = 1;
2069 }
2070
2071 S_Sound (mo, CHAN_VOICE, "brain/death", 1, ATTN_NONE);
2072 }
2073
2074
2075
A_BrainExplode(AActor * mo)2076 void A_BrainExplode (AActor *mo)
2077 {
2078 if(!clientside)
2079 return;
2080
2081 int x = mo->x + P_RandomDiff (mo)*2048;
2082 int y = mo->y;
2083 int z = 128 + P_Random (mo)*2*FRACUNIT;
2084 AActor *th = new AActor (x,y,z, MT_ROCKET);
2085 th->momz = P_Random (mo) << 9;
2086
2087 P_SetMobjState (th, S_BRAINEXPLODE1, true);
2088
2089 th->tics -= P_Random (mo) & 7;
2090 if (th->tics < 1)
2091 th->tics = 1;
2092 }
2093
2094
A_BrainDie(AActor * mo)2095 void A_BrainDie (AActor *mo)
2096 {
2097 // [RH] If noexit, then don't end the level.
2098 if (sv_gametype != GM_COOP && !sv_allowexit)
2099 return;
2100
2101 G_ExitLevel (0, 1);
2102 }
2103
A_BrainSpit(AActor * mo)2104 void A_BrainSpit (AActor *mo)
2105 {
2106 if(!serverside)
2107 return;
2108
2109 AActor* targ;
2110 AActor* newmobj;
2111
2112 // [RH] Do nothing if there are no brain targets.
2113 if (numbraintargets == 0)
2114 return;
2115
2116 brain.easy ^= 1; // killough 3/26/98: use brain struct
2117 if (sv_skill <= sk_easy && (!brain.easy))
2118 return;
2119
2120 // shoot a cube at current target
2121 targ = braintargets[brain.targeton++]; // killough 3/26/98:
2122 brain.targeton %= numbraintargets; // Use brain struct for targets
2123
2124 // spawn brain missile
2125 newmobj = P_SpawnMissile (mo, targ, MT_SPAWNSHOT);
2126
2127 if(newmobj)
2128 {
2129 newmobj->target = targ->ptr();
2130 newmobj->reactiontime =
2131 ((targ->y - mo->y)/newmobj->momy) / newmobj->state->tics;
2132 }
2133
2134 S_Sound (mo, CHAN_WEAPON, "brain/spit", 1, ATTN_NONE);
2135 }
2136
2137
2138
2139 void A_SpawnFly (AActor *mo);
2140
2141 // travelling cube sound
A_SpawnSound(AActor * mo)2142 void A_SpawnSound (AActor *mo)
2143 {
2144 S_Sound (mo, CHAN_BODY, "brain/cube", 1, ATTN_IDLE);
2145 A_SpawnFly(mo);
2146 }
2147
A_SpawnFly(AActor * mo)2148 void A_SpawnFly (AActor *mo)
2149 {
2150 AActor* newmobj;
2151 AActor* fog;
2152 AActor* targ;
2153 int r;
2154 mobjtype_t type;
2155
2156 if (--mo->reactiontime)
2157 return; // still flying
2158
2159 if(!serverside)
2160 return;
2161
2162 targ = mo->target;
2163
2164 // First spawn teleport fog.
2165 fog = new AActor (targ->x, targ->y, targ->z, MT_SPAWNFIRE);
2166 S_Sound (fog, CHAN_BODY, "misc/teleport", 1, ATTN_NORM);
2167
2168 // Randomly select monster to spawn.
2169 r = P_Random (mo);
2170
2171 // Probability distribution (kind of :),
2172 // decreasing likelihood.
2173 if ( r<50 )
2174 type = MT_TROOP;
2175 else if (r<90)
2176 type = MT_SERGEANT;
2177 else if (r<120)
2178 type = MT_SHADOWS;
2179 else if (r<130)
2180 type = MT_PAIN;
2181 else if (r<160)
2182 type = MT_HEAD;
2183 else if (r<162)
2184 type = MT_VILE;
2185 else if (r<172)
2186 type = MT_UNDEAD;
2187 else if (r<192)
2188 type = MT_BABY;
2189 else if (r<222)
2190 type = MT_FATSO;
2191 else if (r<246)
2192 type = MT_KNIGHT;
2193 else
2194 type = MT_BRUISER;
2195
2196 newmobj = new AActor (targ->x, targ->y, targ->z, type);
2197 if (P_LookForPlayers (newmobj, true))
2198 P_SetMobjState (newmobj, newmobj->info->seestate, true);
2199
2200 // telefrag anything in this spot
2201 P_TeleportMove (newmobj, newmobj->x, newmobj->y, newmobj->z, true);
2202
2203 // remove self (i.e., cube).
2204 mo->Destroy ();
2205
2206 // [SL] 2011-06-19 - Emulate vanilla doom bug where monsters spawned after
2207 // the start of the level (eg, spawned from a cube) are respawned at map
2208 // location (0, 0).
2209 memset(&newmobj->spawnpoint, 0, sizeof(newmobj->spawnpoint));
2210 }
2211
2212
2213
A_PlayerScream(AActor * mo)2214 void A_PlayerScream (AActor *mo)
2215 {
2216 if (!mo)
2217 return;
2218
2219 char nametemp[128];
2220 const char *sound;
2221
2222 // [SL] 2011-06-15 - Spectators shouldn't make any noises
2223 if (mo->player && mo->player->spectator)
2224 return;
2225
2226 // [Fly] added for csDoom
2227 if (mo->health < -mo->info->spawnhealth)
2228 {
2229 A_XScream (mo);
2230 return;
2231 }
2232
2233 if (!(gameinfo.flags & GI_NOCRAZYDEATH) && (mo->health < -50))
2234 {
2235 // IF THE PLAYER DIES LESS THAN -50% WITHOUT GIBBING
2236 sound = "*xdeath1";
2237 } else {
2238 // [RH] More variety in death sounds
2239 //sprintf (nametemp, "*death%d", (M_Random ()&3) + 1); // denis - do not use randomness source!
2240 sprintf (nametemp, "*death1");
2241 sound = nametemp;
2242 }
2243
2244 S_Sound (mo, CHAN_VOICE, sound, 1, ATTN_NORM);
2245 }
2246
A_Gibify(AActor * mo)2247 void A_Gibify(AActor *mo) // denis - squash thing
2248 {
2249 mo->flags &= ~MF_SOLID;
2250 mo->height = 0;
2251 mo->radius = 0;
2252 }
2253
2254 VERSION_CONTROL (p_enemy_cpp, "$Id: p_enemy.cpp 4542 2014-02-09 17:39:42Z dr_sean $")
2255
2256