1 //
2 // Copyright(C) 1993-1996 Id Software, Inc.
3 // Copyright(C) 1993-2008 Raven Software
4 // Copyright(C) 2005-2014 Simon Howard
5 //
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 // GNU General Public License for more details.
15 //
16 
17 
18 #include "h2def.h"
19 #include "m_random.h"
20 #include "i_system.h"
21 #include "i_swap.h"
22 #include "p_local.h"
23 #include "s_sound.h"
24 
25 // Macros
26 // Types
27 // Private Data
28 // External Data
29 extern fixed_t FloatBobOffsets[64];
30 
31 
32 //----------------------------------------------------------------------------
33 //
34 // PROC P_RecursiveSound
35 //
36 //----------------------------------------------------------------------------
37 
38 mobj_t *soundtarget;
39 
P_RecursiveSound(sector_t * sec,int soundblocks)40 void P_RecursiveSound(sector_t * sec, int soundblocks)
41 {
42     int i;
43     line_t *check;
44     sector_t *other;
45 
46     // Wake up all monsters in this sector
47     if (sec->validcount == validcount
48         && sec->soundtraversed <= soundblocks + 1)
49     {                           // Already flooded
50         return;
51     }
52     sec->validcount = validcount;
53     sec->soundtraversed = soundblocks + 1;
54     sec->soundtarget = soundtarget;
55     for (i = 0; i < sec->linecount; i++)
56     {
57         check = sec->lines[i];
58         if (!(check->flags & ML_TWOSIDED))
59         {
60             continue;
61         }
62         P_LineOpening(check);
63         if (openrange <= 0)
64         {                       // Closed door
65             continue;
66         }
67         if (sides[check->sidenum[0]].sector == sec)
68         {
69             other = sides[check->sidenum[1]].sector;
70         }
71         else
72         {
73             other = sides[check->sidenum[0]].sector;
74         }
75         if (check->flags & ML_SOUNDBLOCK)
76         {
77             if (!soundblocks)
78             {
79                 P_RecursiveSound(other, 1);
80             }
81         }
82         else
83         {
84             P_RecursiveSound(other, soundblocks);
85         }
86     }
87 }
88 
89 //----------------------------------------------------------------------------
90 //
91 // PROC P_NoiseAlert
92 //
93 // If a monster yells at a player, it will alert other monsters to the
94 // player.
95 //
96 //----------------------------------------------------------------------------
97 
P_NoiseAlert(mobj_t * target,mobj_t * emmiter)98 void P_NoiseAlert(mobj_t * target, mobj_t * emmiter)
99 {
100     soundtarget = target;
101     validcount++;
102     P_RecursiveSound(emmiter->subsector->sector, 0);
103 }
104 
105 //----------------------------------------------------------------------------
106 //
107 // FUNC P_CheckMeleeRange
108 //
109 //----------------------------------------------------------------------------
110 
P_CheckMeleeRange(mobj_t * actor)111 boolean P_CheckMeleeRange(mobj_t * actor)
112 {
113     mobj_t *mo;
114     fixed_t dist;
115 
116     if (!actor->target)
117     {
118         return (false);
119     }
120     mo = actor->target;
121     dist = P_AproxDistance(mo->x - actor->x, mo->y - actor->y);
122     if (dist >= MELEERANGE)
123     {
124         return (false);
125     }
126     if (!P_CheckSight(actor, mo))
127     {
128         return (false);
129     }
130     if (mo->z > actor->z + actor->height)
131     {                           // Target is higher than the attacker
132         return (false);
133     }
134     else if (actor->z > mo->z + mo->height)
135     {                           // Attacker is higher
136         return (false);
137     }
138     return (true);
139 }
140 
141 //----------------------------------------------------------------------------
142 //
143 // FUNC P_CheckMeleeRange2
144 //
145 //----------------------------------------------------------------------------
146 
P_CheckMeleeRange2(mobj_t * actor)147 boolean P_CheckMeleeRange2(mobj_t * actor)
148 {
149     mobj_t *mo;
150     fixed_t dist;
151 
152     if (!actor->target)
153     {
154         return (false);
155     }
156     mo = actor->target;
157     dist = P_AproxDistance(mo->x - actor->x, mo->y - actor->y);
158     if (dist >= MELEERANGE * 2 || dist < MELEERANGE)
159     {
160         return (false);
161     }
162     if (!P_CheckSight(actor, mo))
163     {
164         return (false);
165     }
166     if (mo->z > actor->z + actor->height)
167     {                           // Target is higher than the attacker
168         return (false);
169     }
170     else if (actor->z > mo->z + mo->height)
171     {                           // Attacker is higher
172         return (false);
173     }
174     return (true);
175 }
176 
177 //----------------------------------------------------------------------------
178 //
179 // FUNC P_CheckMissileRange
180 //
181 //----------------------------------------------------------------------------
182 
P_CheckMissileRange(mobj_t * actor)183 boolean P_CheckMissileRange(mobj_t * actor)
184 {
185     fixed_t dist;
186 
187     if (!P_CheckSight(actor, actor->target))
188     {
189         return (false);
190     }
191     if (actor->flags & MF_JUSTHIT)
192     {                           // The target just hit the enemy, so fight back!
193         actor->flags &= ~MF_JUSTHIT;
194         return (true);
195     }
196     if (actor->reactiontime)
197     {                           // Don't attack yet
198         return (false);
199     }
200     dist = (P_AproxDistance(actor->x - actor->target->x,
201                             actor->y - actor->target->y) >> FRACBITS) - 64;
202     if (!actor->info->meleestate)
203     {                           // No melee attack, so fire more frequently
204         dist -= 128;
205     }
206     if (dist > 200)
207     {
208         dist = 200;
209     }
210     if (P_Random() < dist)
211     {
212         return (false);
213     }
214     return (true);
215 }
216 
217 /*
218 ================
219 =
220 = P_Move
221 =
222 = Move in the current direction
223 = returns false if the move is blocked
224 ================
225 */
226 
227 fixed_t xspeed[8] =
228     { FRACUNIT, 47000, 0, -47000, -FRACUNIT, -47000, 0, 47000 };
229 fixed_t yspeed[8] =
230     { 0, 47000, FRACUNIT, 47000, 0, -47000, -FRACUNIT, -47000 };
231 
232 #define MAXSPECIALCROSS         8
233 extern line_t *spechit[MAXSPECIALCROSS];
234 extern int numspechit;
235 
P_Move(mobj_t * actor)236 boolean P_Move(mobj_t * actor)
237 {
238     fixed_t tryx, tryy;
239     line_t *ld;
240     boolean good;
241 
242     if (actor->flags2 & MF2_BLASTED)
243         return (true);
244     if (actor->movedir == DI_NODIR)
245     {
246         return (false);
247     }
248     tryx = actor->x + actor->info->speed * xspeed[actor->movedir];
249     tryy = actor->y + actor->info->speed * yspeed[actor->movedir];
250     if (!P_TryMove(actor, tryx, tryy))
251     {                           // open any specials
252         if (actor->flags & MF_FLOAT && floatok)
253         {                       // must adjust height
254             if (actor->z < tmfloorz)
255             {
256                 actor->z += FLOATSPEED;
257             }
258             else
259             {
260                 actor->z -= FLOATSPEED;
261             }
262             actor->flags |= MF_INFLOAT;
263             return (true);
264         }
265         if (!numspechit)
266         {
267             return false;
268         }
269         actor->movedir = DI_NODIR;
270         good = false;
271         while (numspechit--)
272         {
273             ld = spechit[numspechit];
274             // if the special isn't a door that can be opened, return false
275             if (P_ActivateLine(ld, actor, 0, SPAC_USE))
276             {
277                 good = true;
278             }
279 /* Old version before use/cross/impact specials were combined
280 			if(P_UseSpecialLine(actor, ld))
281 			{
282 				good = true;
283 			}
284 */
285         }
286         return (good);
287     }
288     else
289     {
290         actor->flags &= ~MF_INFLOAT;
291     }
292     if (!(actor->flags & MF_FLOAT))
293     {
294         if (actor->z > actor->floorz)
295         {
296             P_HitFloor(actor);
297         }
298         actor->z = actor->floorz;
299     }
300     return (true);
301 }
302 
303 //----------------------------------------------------------------------------
304 //
305 // FUNC P_TryWalk
306 //
307 // Attempts to move actor in its current (ob->moveangle) direction.
308 // If blocked by either a wall or an actor returns FALSE.
309 // If move is either clear of block only by a door, returns TRUE and sets.
310 // If a door is in the way, an OpenDoor call is made to start it opening.
311 //
312 //----------------------------------------------------------------------------
313 
P_TryWalk(mobj_t * actor)314 boolean P_TryWalk(mobj_t * actor)
315 {
316     if (!P_Move(actor))
317     {
318         return (false);
319     }
320     actor->movecount = P_Random() & 15;
321     return (true);
322 }
323 
324 /*
325 ================
326 =
327 = P_NewChaseDir
328 =
329 ================
330 */
331 
332 dirtype_t opposite[] =
333     { DI_WEST, DI_SOUTHWEST, DI_SOUTH, DI_SOUTHEAST, DI_EAST, DI_NORTHEAST,
334     DI_NORTH, DI_NORTHWEST, DI_NODIR
335 };
336 
337 dirtype_t diags[] =
338     { DI_NORTHWEST, DI_NORTHEAST, DI_SOUTHWEST, DI_SOUTHEAST };
339 
P_NewChaseDir(mobj_t * actor)340 void P_NewChaseDir(mobj_t * actor)
341 {
342     fixed_t deltax, deltay;
343     dirtype_t d[3];
344     dirtype_t tdir, olddir, turnaround;
345 
346     if (!actor->target)
347         I_Error("P_NewChaseDir: called with no target");
348 
349     olddir = actor->movedir;
350     turnaround = opposite[olddir];
351 
352     deltax = actor->target->x - actor->x;
353     deltay = actor->target->y - actor->y;
354     if (deltax > 10 * FRACUNIT)
355         d[1] = DI_EAST;
356     else if (deltax < -10 * FRACUNIT)
357         d[1] = DI_WEST;
358     else
359         d[1] = DI_NODIR;
360     if (deltay < -10 * FRACUNIT)
361         d[2] = DI_SOUTH;
362     else if (deltay > 10 * FRACUNIT)
363         d[2] = DI_NORTH;
364     else
365         d[2] = DI_NODIR;
366 
367 // try direct route
368     if (d[1] != DI_NODIR && d[2] != DI_NODIR)
369     {
370         actor->movedir = diags[((deltay < 0) << 1) + (deltax > 0)];
371         if (actor->movedir != turnaround && P_TryWalk(actor))
372             return;
373     }
374 
375 // try other directions
376     if (P_Random() > 200 || abs(deltay) > abs(deltax))
377     {
378         tdir = d[1];
379         d[1] = d[2];
380         d[2] = tdir;
381     }
382 
383     if (d[1] == turnaround)
384         d[1] = DI_NODIR;
385     if (d[2] == turnaround)
386         d[2] = DI_NODIR;
387 
388     if (d[1] != DI_NODIR)
389     {
390         actor->movedir = d[1];
391         if (P_TryWalk(actor))
392             return;             /*either moved forward or attacked */
393     }
394 
395     if (d[2] != DI_NODIR)
396     {
397         actor->movedir = d[2];
398         if (P_TryWalk(actor))
399             return;
400     }
401 
402 /* there is no direct path to the player, so pick another direction */
403 
404     if (olddir != DI_NODIR)
405     {
406         actor->movedir = olddir;
407         if (P_TryWalk(actor))
408             return;
409     }
410 
411     if (P_Random() & 1)         /*randomly determine direction of search */
412     {
413         for (tdir = DI_EAST; tdir <= DI_SOUTHEAST; tdir++)
414         {
415             if (tdir != turnaround)
416             {
417                 actor->movedir = tdir;
418                 if (P_TryWalk(actor))
419                     return;
420             }
421         }
422     }
423     else
424     {
425         tdir = DI_SOUTHEAST;
426 
427         for (;;)
428         {
429             if (tdir != turnaround)
430             {
431                 actor->movedir = tdir;
432                 if (P_TryWalk(actor))
433                     return;
434             }
435 
436             if (tdir == DI_EAST)
437             {
438                 break;
439             }
440 
441             --tdir;
442         }
443     }
444 
445     if (turnaround != DI_NODIR)
446     {
447         actor->movedir = turnaround;
448         if (P_TryWalk(actor))
449             return;
450     }
451 
452     actor->movedir = DI_NODIR;  // can't move
453 }
454 
455 //---------------------------------------------------------------------------
456 //
457 // FUNC P_LookForMonsters
458 //
459 //---------------------------------------------------------------------------
460 
461 #define MONS_LOOK_RANGE (16*64*FRACUNIT)
462 #define MONS_LOOK_LIMIT 64
463 
P_LookForMonsters(mobj_t * actor)464 boolean P_LookForMonsters(mobj_t * actor)
465 {
466     int count;
467     mobj_t *mo;
468     thinker_t *think;
469 
470     if (!P_CheckSight(players[0].mo, actor))
471     {                           // Player can't see monster
472         return (false);
473     }
474     count = 0;
475     for (think = thinkercap.next; think != &thinkercap; think = think->next)
476     {
477         if (think->function != P_MobjThinker)
478         {                       // Not a mobj thinker
479             continue;
480         }
481         mo = (mobj_t *) think;
482         if (!(mo->flags & MF_COUNTKILL) || (mo == actor) || (mo->health <= 0))
483         {                       // Not a valid monster
484             continue;
485         }
486         if (P_AproxDistance(actor->x - mo->x, actor->y - mo->y)
487             > MONS_LOOK_RANGE)
488         {                       // Out of range
489             continue;
490         }
491         if (P_Random() < 16)
492         {                       // Skip
493             continue;
494         }
495         if (count++ > MONS_LOOK_LIMIT)
496         {                       // Stop searching
497             return (false);
498         }
499         if (!P_CheckSight(actor, mo))
500         {                       // Out of sight
501             continue;
502         }
503         if (actor->type == MT_MINOTAUR)
504         {
505             if ((mo->type == MT_MINOTAUR) &&
506                 (mo->target != actor->special1.p->mo))
507             {
508                 continue;
509             }
510         }
511         // Found a target monster
512         actor->target = mo;
513         return (true);
514     }
515     return (false);
516 }
517 
518 /*
519 ================
520 =
521 = P_LookForPlayers
522 =
523 = If allaround is false, only look 180 degrees in front
524 = returns true if a player is targeted
525 ================
526 */
527 
P_LookForPlayers(mobj_t * actor,boolean allaround)528 boolean P_LookForPlayers(mobj_t * actor, boolean allaround)
529 {
530     int c;
531     int stop;
532     player_t *player;
533     angle_t an;
534     fixed_t dist;
535 
536     if (!netgame && players[0].health <= 0)
537     {                           // Single player game and player is dead, look for monsters
538         return (P_LookForMonsters(actor));
539     }
540     c = 0;
541 
542     // NOTE: This behavior has been changed from the Vanilla behavior, where
543     // an infinite loop can occur if players 0-3 all quit the game. Although
544     // technically this is not what Vanilla does, fixing this is highly
545     // desirable, and having the game simply lock up is not acceptable.
546     // stop = (actor->lastlook - 1) & 3;
547     // for (;; actor->lastlook = (actor->lastlook + 1) & 3)
548 
549     stop = (actor->lastlook + maxplayers - 1) % maxplayers;
550     for (;; actor->lastlook = (actor->lastlook + 1) % maxplayers)
551     {
552         if (!playeringame[actor->lastlook])
553             continue;
554 
555         if (c++ == 2 || actor->lastlook == stop)
556             return false;       // done looking
557 
558         player = &players[actor->lastlook];
559         if (player->health <= 0)
560             continue;           // dead
561         if (!P_CheckSight(actor, player->mo))
562             continue;           // out of sight
563 
564         if (!allaround)
565         {
566             an = R_PointToAngle2(actor->x, actor->y,
567                                  player->mo->x, player->mo->y) - actor->angle;
568             if (an > ANG90 && an < ANG270)
569             {
570                 dist = P_AproxDistance(player->mo->x - actor->x,
571                                        player->mo->y - actor->y);
572                 // if real close, react anyway
573                 if (dist > MELEERANGE)
574                     continue;   // behind back
575             }
576         }
577         if (player->mo->flags & MF_SHADOW)
578         {                       // Player is invisible
579             if ((P_AproxDistance(player->mo->x - actor->x,
580                                  player->mo->y - actor->y) > 2 * MELEERANGE)
581                 && P_AproxDistance(player->mo->momx, player->mo->momy)
582                 < 5 * FRACUNIT)
583             {                   // Player is sneaking - can't detect
584                 return (false);
585             }
586             if (P_Random() < 225)
587             {                   // Player isn't sneaking, but still didn't detect
588                 return (false);
589             }
590         }
591         if (actor->type == MT_MINOTAUR)
592         {
593             if (actor->special1.p == player)
594             {
595                 continue;       // Don't target master
596             }
597         }
598 
599         actor->target = player->mo;
600         return (true);
601     }
602     return (false);
603 }
604 
605 /*
606 ===============================================================================
607 
608 						ACTION ROUTINES
609 
610 ===============================================================================
611 */
612 
613 /*
614 ==============
615 =
616 = A_Look
617 =
618 = Stay in state until a player is sighted
619 =
620 ==============
621 */
622 
A_Look(mobj_t * actor)623 void A_Look(mobj_t * actor)
624 {
625     mobj_t *targ;
626 
627     actor->threshold = 0;       // any shot will wake up
628     targ = actor->subsector->sector->soundtarget;
629     if (targ && (targ->flags & MF_SHOOTABLE))
630     {
631         actor->target = targ;
632         if (actor->flags & MF_AMBUSH)
633         {
634             if (P_CheckSight(actor, actor->target))
635                 goto seeyou;
636         }
637         else
638             goto seeyou;
639     }
640 
641 
642     if (!P_LookForPlayers(actor, false))
643         return;
644 
645 // go into chase state
646   seeyou:
647     if (actor->info->seesound)
648     {
649         int sound;
650 
651         sound = actor->info->seesound;
652         if (actor->flags2 & MF2_BOSS)
653         {                       // Full volume
654             S_StartSound(NULL, sound);
655         }
656         else
657         {
658             S_StartSound(actor, sound);
659         }
660     }
661     P_SetMobjState(actor, actor->info->seestate);
662 }
663 
664 
665 /*
666 ==============
667 =
668 = A_Chase
669 =
670 = Actor has a melee attack, so it tries to close as fast as possible
671 =
672 ==============
673 */
674 
A_Chase(mobj_t * actor)675 void A_Chase(mobj_t * actor)
676 {
677     int delta;
678 
679     if (actor->reactiontime)
680     {
681         actor->reactiontime--;
682     }
683 
684     // Modify target threshold
685     if (actor->threshold)
686     {
687         actor->threshold--;
688     }
689 
690     if (gameskill == sk_nightmare)
691     {                           // Monsters move faster in nightmare mode
692         actor->tics -= actor->tics / 2;
693         if (actor->tics < 3)
694         {
695             actor->tics = 3;
696         }
697     }
698 
699 //
700 // turn towards movement direction if not there yet
701 //
702     if (actor->movedir < 8)
703     {
704         actor->angle &= (7 << 29);
705         delta = actor->angle - (actor->movedir << 29);
706         if (delta > 0)
707         {
708             actor->angle -= ANG90 / 2;
709         }
710         else if (delta < 0)
711         {
712             actor->angle += ANG90 / 2;
713         }
714     }
715 
716     if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
717     {                           // look for a new target
718         if (P_LookForPlayers(actor, true))
719         {                       // got a new target
720             return;
721         }
722         P_SetMobjState(actor, actor->info->spawnstate);
723         return;
724     }
725 
726 //
727 // don't attack twice in a row
728 //
729     if (actor->flags & MF_JUSTATTACKED)
730     {
731         actor->flags &= ~MF_JUSTATTACKED;
732         if (gameskill != sk_nightmare)
733             P_NewChaseDir(actor);
734         return;
735     }
736 
737 //
738 // check for melee attack
739 //
740     if (actor->info->meleestate && P_CheckMeleeRange(actor))
741     {
742         if (actor->info->attacksound)
743         {
744             S_StartSound(actor, actor->info->attacksound);
745         }
746         P_SetMobjState(actor, actor->info->meleestate);
747         return;
748     }
749 
750 //
751 // check for missile attack
752 //
753     if (actor->info->missilestate)
754     {
755         if (gameskill < sk_nightmare && actor->movecount)
756             goto nomissile;
757         if (!P_CheckMissileRange(actor))
758             goto nomissile;
759         P_SetMobjState(actor, actor->info->missilestate);
760         actor->flags |= MF_JUSTATTACKED;
761         return;
762     }
763   nomissile:
764 
765 //
766 // possibly choose another target
767 //
768     if (netgame && !actor->threshold && !P_CheckSight(actor, actor->target))
769     {
770         if (P_LookForPlayers(actor, true))
771             return;             // got a new target
772     }
773 
774 //
775 // chase towards player
776 //
777     if (--actor->movecount < 0 || !P_Move(actor))
778     {
779         P_NewChaseDir(actor);
780     }
781 
782 //
783 // make active sound
784 //
785     if (actor->info->activesound && P_Random() < 3)
786     {
787         if (actor->type == MT_BISHOP && P_Random() < 128)
788         {
789             S_StartSound(actor, actor->info->seesound);
790         }
791         else if (actor->type == MT_PIG)
792         {
793             S_StartSound(actor, SFX_PIG_ACTIVE1 + (P_Random() & 1));
794         }
795         else if (actor->flags2 & MF2_BOSS)
796         {
797             S_StartSound(NULL, actor->info->activesound);
798         }
799         else
800         {
801             S_StartSound(actor, actor->info->activesound);
802         }
803     }
804 }
805 
806 //----------------------------------------------------------------------------
807 //
808 // PROC A_FaceTarget
809 //
810 //----------------------------------------------------------------------------
811 
A_FaceTarget(mobj_t * actor)812 void A_FaceTarget(mobj_t * actor)
813 {
814     if (!actor->target)
815     {
816         return;
817     }
818     actor->flags &= ~MF_AMBUSH;
819     actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x,
820                                    actor->target->y);
821     if (actor->target->flags & MF_SHADOW)
822     {                           // Target is a ghost
823         actor->angle += P_SubRandom() << 21;
824     }
825 }
826 
827 //----------------------------------------------------------------------------
828 //
829 // PROC A_Pain
830 //
831 //----------------------------------------------------------------------------
832 
A_Pain(mobj_t * actor)833 void A_Pain(mobj_t * actor)
834 {
835     if (actor->info->painsound)
836     {
837         S_StartSound(actor, actor->info->painsound);
838     }
839 }
840 
841 //============================================================================
842 //
843 // A_SetInvulnerable
844 //
845 //============================================================================
846 
A_SetInvulnerable(mobj_t * actor)847 void A_SetInvulnerable(mobj_t * actor)
848 {
849     actor->flags2 |= MF2_INVULNERABLE;
850 }
851 
852 //============================================================================
853 //
854 // A_UnSetInvulnerable
855 //
856 //============================================================================
857 
A_UnSetInvulnerable(mobj_t * actor)858 void A_UnSetInvulnerable(mobj_t * actor)
859 {
860     actor->flags2 &= ~MF2_INVULNERABLE;
861 }
862 
863 //============================================================================
864 //
865 // A_SetReflective
866 //
867 //============================================================================
868 
A_SetReflective(mobj_t * actor)869 void A_SetReflective(mobj_t * actor)
870 {
871     actor->flags2 |= MF2_REFLECTIVE;
872 
873     if ((actor->type == MT_CENTAUR) || (actor->type == MT_CENTAURLEADER))
874     {
875         A_SetInvulnerable(actor);
876     }
877 }
878 
879 //============================================================================
880 //
881 // A_UnSetReflective
882 //
883 //============================================================================
884 
A_UnSetReflective(mobj_t * actor)885 void A_UnSetReflective(mobj_t * actor)
886 {
887     actor->flags2 &= ~MF2_REFLECTIVE;
888 
889     if ((actor->type == MT_CENTAUR) || (actor->type == MT_CENTAURLEADER))
890     {
891         A_UnSetInvulnerable(actor);
892     }
893 }
894 
895 
896 //----------------------------------------------------------------------------
897 //
898 // FUNC P_UpdateMorphedMonster
899 //
900 // Returns true if the pig morphs.
901 //
902 //----------------------------------------------------------------------------
903 
P_UpdateMorphedMonster(mobj_t * actor,int tics)904 boolean P_UpdateMorphedMonster(mobj_t * actor, int tics)
905 {
906     mobj_t *fog;
907     fixed_t x;
908     fixed_t y;
909     fixed_t z;
910     mobjtype_t moType;
911     mobj_t *mo;
912     mobj_t oldMonster;
913 
914     actor->special1.i -= tics;
915     if (actor->special1.i > 0)
916     {
917         return (false);
918     }
919     moType = actor->special2.i;
920     switch (moType)
921     {
922         case MT_WRAITHB:       // These must remain morphed
923         case MT_SERPENT:
924         case MT_SERPENTLEADER:
925         case MT_MINOTAUR:
926             return (false);
927         default:
928             break;
929     }
930     x = actor->x;
931     y = actor->y;
932     z = actor->z;
933     oldMonster = *actor;        // Save pig vars
934 
935     P_RemoveMobjFromTIDList(actor);
936     P_SetMobjState(actor, S_FREETARGMOBJ);
937     mo = P_SpawnMobj(x, y, z, moType);
938 
939     if (P_TestMobjLocation(mo) == false)
940     {                           // Didn't fit
941         P_RemoveMobj(mo);
942         mo = P_SpawnMobj(x, y, z, oldMonster.type);
943         mo->angle = oldMonster.angle;
944         mo->flags = oldMonster.flags;
945         mo->health = oldMonster.health;
946         mo->target = oldMonster.target;
947         mo->special = oldMonster.special;
948         mo->special1.i = 5 * 35;  // Next try in 5 seconds
949         mo->special2.i = moType;
950         mo->tid = oldMonster.tid;
951         memcpy(mo->args, oldMonster.args, 5);
952         P_InsertMobjIntoTIDList(mo, oldMonster.tid);
953         return (false);
954     }
955     mo->angle = oldMonster.angle;
956     mo->target = oldMonster.target;
957     mo->tid = oldMonster.tid;
958     mo->special = oldMonster.special;
959     memcpy(mo->args, oldMonster.args, 5);
960     P_InsertMobjIntoTIDList(mo, oldMonster.tid);
961     fog = P_SpawnMobj(x, y, z + TELEFOGHEIGHT, MT_TFOG);
962     S_StartSound(fog, SFX_TELEPORT);
963     return (true);
964 }
965 
966 //----------------------------------------------------------------------------
967 //
968 // PROC A_PigLook
969 //
970 //----------------------------------------------------------------------------
971 
A_PigLook(mobj_t * actor)972 void A_PigLook(mobj_t * actor)
973 {
974     if (P_UpdateMorphedMonster(actor, 10))
975     {
976         return;
977     }
978     A_Look(actor);
979 }
980 
981 //----------------------------------------------------------------------------
982 //
983 // PROC A_PigChase
984 //
985 //----------------------------------------------------------------------------
986 
A_PigChase(mobj_t * actor)987 void A_PigChase(mobj_t * actor)
988 {
989     if (P_UpdateMorphedMonster(actor, 3))
990     {
991         return;
992     }
993     A_Chase(actor);
994 }
995 
996 //============================================================================
997 //
998 // A_PigAttack
999 //
1000 //============================================================================
1001 
A_PigAttack(mobj_t * actor)1002 void A_PigAttack(mobj_t * actor)
1003 {
1004     if (P_UpdateMorphedMonster(actor, 18))
1005     {
1006         return;
1007     }
1008     if (!actor->target)
1009     {
1010         return;
1011     }
1012     if (P_CheckMeleeRange(actor))
1013     {
1014         P_DamageMobj(actor->target, actor, actor, 2 + (P_Random() & 1));
1015         S_StartSound(actor, SFX_PIG_ATTACK);
1016     }
1017 }
1018 
1019 //============================================================================
1020 //
1021 // A_PigPain
1022 //
1023 //============================================================================
1024 
A_PigPain(mobj_t * actor)1025 void A_PigPain(mobj_t * actor)
1026 {
1027     A_Pain(actor);
1028     if (actor->z <= actor->floorz)
1029     {
1030         actor->momz = 3.5 * FRACUNIT;
1031     }
1032 }
1033 
1034 
1035 
FaceMovementDirection(mobj_t * actor)1036 void FaceMovementDirection(mobj_t * actor)
1037 {
1038     switch (actor->movedir)
1039     {
1040         case DI_EAST:
1041             actor->angle = 0 << 24;
1042             break;
1043         case DI_NORTHEAST:
1044             actor->angle = 32 << 24;
1045             break;
1046         case DI_NORTH:
1047             actor->angle = 64 << 24;
1048             break;
1049         case DI_NORTHWEST:
1050             actor->angle = 96 << 24;
1051             break;
1052         case DI_WEST:
1053             actor->angle = 128 << 24;
1054             break;
1055         case DI_SOUTHWEST:
1056             actor->angle = 160 << 24;
1057             break;
1058         case DI_SOUTH:
1059             actor->angle = 192 << 24;
1060             break;
1061         case DI_SOUTHEAST:
1062             actor->angle = 224 << 24;
1063             break;
1064     }
1065 }
1066 
1067 
1068 //----------------------------------------------------------------------------
1069 //
1070 // Minotaur variables
1071 //
1072 //      special1                pointer to player that spawned it (mobj_t)
1073 //      special2                internal to minotaur AI
1074 //      args[0]                 args[0]-args[3] together make up minotaur start time
1075 //      args[1]                 |
1076 //      args[2]                 |
1077 //      args[3]                 V
1078 //      args[4]                 charge duration countdown
1079 //----------------------------------------------------------------------------
1080 
A_MinotaurFade0(mobj_t * actor)1081 void A_MinotaurFade0(mobj_t * actor)
1082 {
1083     actor->flags &= ~MF_ALTSHADOW;
1084     actor->flags |= MF_SHADOW;
1085 }
1086 
A_MinotaurFade1(mobj_t * actor)1087 void A_MinotaurFade1(mobj_t * actor)
1088 {
1089     // Second level of transparency
1090     actor->flags &= ~MF_SHADOW;
1091     actor->flags |= MF_ALTSHADOW;
1092 }
1093 
A_MinotaurFade2(mobj_t * actor)1094 void A_MinotaurFade2(mobj_t * actor)
1095 {
1096     // Make fully visible
1097     actor->flags &= ~MF_SHADOW;
1098     actor->flags &= ~MF_ALTSHADOW;
1099 }
1100 
1101 
1102 //----------------------------------------------------------------------------
1103 //
1104 // A_MinotaurRoam -
1105 //
1106 //
1107 //----------------------------------------------------------------------------
1108 
1109 void A_MinotaurLook(mobj_t * actor);
1110 
1111 // Check the age of the minotaur and stomp it after MAULATORTICS of time
1112 // have passed. Returns false if killed.
CheckMinotaurAge(mobj_t * mo)1113 static boolean CheckMinotaurAge(mobj_t *mo)
1114 {
1115     unsigned int starttime;
1116 
1117     // The start time is stored in the mobj_t structure, but it is stored
1118     // in little endian format. For Vanilla savegame compatibility we must
1119     // swap it to the native endianness.
1120     memcpy(&starttime, mo->args, sizeof(unsigned int));
1121 
1122     if (leveltime - LONG(starttime) >= MAULATORTICS)
1123     {
1124         P_DamageMobj(mo, NULL, NULL, 10000);
1125         return false;
1126     }
1127 
1128     return true;
1129 }
1130 
A_MinotaurRoam(mobj_t * actor)1131 void A_MinotaurRoam(mobj_t * actor)
1132 {
1133     actor->flags &= ~MF_SHADOW; // In case pain caused him to
1134     actor->flags &= ~MF_ALTSHADOW;      // skip his fade in.
1135 
1136     if (!CheckMinotaurAge(actor))
1137     {
1138         return;
1139     }
1140 
1141     if (P_Random() < 30)
1142         A_MinotaurLook(actor);  // adjust to closest target
1143 
1144     if (P_Random() < 6)
1145     {
1146         //Choose new direction
1147         actor->movedir = P_Random() % 8;
1148         FaceMovementDirection(actor);
1149     }
1150     if (!P_Move(actor))
1151     {
1152         // Turn
1153         if (P_Random() & 1)
1154             actor->movedir = (actor->movedir + 1) % 8;
1155         else
1156             actor->movedir = (actor->movedir + 7) % 8;
1157         FaceMovementDirection(actor);
1158     }
1159 }
1160 
1161 
1162 //----------------------------------------------------------------------------
1163 //
1164 //      PROC A_MinotaurLook
1165 //
1166 // Look for enemy of player
1167 //----------------------------------------------------------------------------
1168 #define MINOTAUR_LOOK_DIST		(16*54*FRACUNIT)
1169 
A_MinotaurLook(mobj_t * actor)1170 void A_MinotaurLook(mobj_t * actor)
1171 {
1172     mobj_t *mo = NULL;
1173     player_t *player;
1174     thinker_t *think;
1175     fixed_t dist;
1176     int i;
1177     mobj_t *master = actor->special1.m;
1178 
1179     actor->target = NULL;
1180     if (deathmatch)             // Quick search for players
1181     {
1182         for (i = 0; i < maxplayers; i++)
1183         {
1184             if (!playeringame[i])
1185                 continue;
1186             player = &players[i];
1187             mo = player->mo;
1188             if (mo == master)
1189                 continue;
1190             if (mo->health <= 0)
1191                 continue;
1192             dist = P_AproxDistance(actor->x - mo->x, actor->y - mo->y);
1193             if (dist > MINOTAUR_LOOK_DIST)
1194                 continue;
1195             actor->target = mo;
1196             break;
1197         }
1198     }
1199 
1200     if (!actor->target)         // Near player monster search
1201     {
1202         if (master && (master->health > 0) && (master->player))
1203             mo = P_RoughMonsterSearch(master, 20);
1204         else
1205             mo = P_RoughMonsterSearch(actor, 20);
1206         actor->target = mo;
1207     }
1208 
1209     if (!actor->target)         // Normal monster search
1210     {
1211         for (think = thinkercap.next; think != &thinkercap;
1212              think = think->next)
1213         {
1214             if (think->function != P_MobjThinker)
1215                 continue;
1216             mo = (mobj_t *) think;
1217             if (!(mo->flags & MF_COUNTKILL))
1218                 continue;
1219             if (mo->health <= 0)
1220                 continue;
1221             if (!(mo->flags & MF_SHOOTABLE))
1222                 continue;
1223             dist = P_AproxDistance(actor->x - mo->x, actor->y - mo->y);
1224             if (dist > MINOTAUR_LOOK_DIST)
1225                 continue;
1226             if ((mo == master) || (mo == actor))
1227                 continue;
1228             if ((mo->type == MT_MINOTAUR) &&
1229                 (mo->special1.m == actor->special1.m))
1230                 continue;
1231             actor->target = mo;
1232             break;              // Found mobj to attack
1233         }
1234     }
1235 
1236     if (actor->target)
1237     {
1238         P_SetMobjStateNF(actor, S_MNTR_WALK1);
1239     }
1240     else
1241     {
1242         P_SetMobjStateNF(actor, S_MNTR_ROAM1);
1243     }
1244 }
1245 
1246 
1247 
1248 
A_MinotaurChase(mobj_t * actor)1249 void A_MinotaurChase(mobj_t * actor)
1250 {
1251     actor->flags &= ~MF_SHADOW; // In case pain caused him to
1252     actor->flags &= ~MF_ALTSHADOW;      // skip his fade in.
1253 
1254     if (!CheckMinotaurAge(actor))
1255     {
1256         return;
1257     }
1258 
1259     if (P_Random() < 30)
1260         A_MinotaurLook(actor);  // adjust to closest target
1261 
1262     if (!actor->target || (actor->target->health <= 0) ||
1263         !(actor->target->flags & MF_SHOOTABLE))
1264     {                           // look for a new target
1265         P_SetMobjState(actor, S_MNTR_LOOK1);
1266         return;
1267     }
1268 
1269     FaceMovementDirection(actor);
1270     actor->reactiontime = 0;
1271 
1272     // Melee attack
1273     if (actor->info->meleestate && P_CheckMeleeRange(actor))
1274     {
1275         if (actor->info->attacksound)
1276         {
1277             S_StartSound(actor, actor->info->attacksound);
1278         }
1279         P_SetMobjState(actor, actor->info->meleestate);
1280         return;
1281     }
1282 
1283     // Missile attack
1284     if (actor->info->missilestate && P_CheckMissileRange(actor))
1285     {
1286         P_SetMobjState(actor, actor->info->missilestate);
1287         return;
1288     }
1289 
1290     // chase towards target
1291     if (!P_Move(actor))
1292     {
1293         P_NewChaseDir(actor);
1294     }
1295 
1296     // Active sound
1297     if (actor->info->activesound && P_Random() < 6)
1298     {
1299         S_StartSound(actor, actor->info->activesound);
1300     }
1301 
1302 }
1303 
1304 
1305 //----------------------------------------------------------------------------
1306 //
1307 // PROC A_MinotaurAtk1
1308 //
1309 // Melee attack.
1310 //
1311 //----------------------------------------------------------------------------
1312 
A_MinotaurAtk1(mobj_t * actor)1313 void A_MinotaurAtk1(mobj_t * actor)
1314 {
1315     if (!actor->target)
1316         return;
1317 
1318     S_StartSound(actor, SFX_MAULATOR_HAMMER_SWING);
1319     if (P_CheckMeleeRange(actor))
1320     {
1321         P_DamageMobj(actor->target, actor, actor, HITDICE(4));
1322     }
1323 }
1324 
1325 //----------------------------------------------------------------------------
1326 //
1327 // PROC A_MinotaurDecide
1328 //
1329 // Choose a missile attack.
1330 //
1331 //----------------------------------------------------------------------------
1332 
1333 #define MNTR_CHARGE_SPEED (23*FRACUNIT)
1334 
A_MinotaurDecide(mobj_t * actor)1335 void A_MinotaurDecide(mobj_t * actor)
1336 {
1337     angle_t angle;
1338     mobj_t *target = actor->target;
1339     int dist;
1340 
1341     if (!target)
1342         return;
1343     dist = P_AproxDistance(actor->x - target->x, actor->y - target->y);
1344 
1345     if (target->z + target->height > actor->z
1346         && target->z + target->height < actor->z + actor->height
1347         && dist < 16 * 64 * FRACUNIT
1348         && dist > 1 * 64 * FRACUNIT && P_Random() < 230)
1349     {                           // Charge attack
1350         // Don't call the state function right away
1351         P_SetMobjStateNF(actor, S_MNTR_ATK4_1);
1352         actor->flags |= MF_SKULLFLY;
1353         A_FaceTarget(actor);
1354         angle = actor->angle >> ANGLETOFINESHIFT;
1355         actor->momx = FixedMul(MNTR_CHARGE_SPEED, finecosine[angle]);
1356         actor->momy = FixedMul(MNTR_CHARGE_SPEED, finesine[angle]);
1357         actor->args[4] = 35 / 2;        // Charge duration
1358     }
1359     else if (target->z == target->floorz
1360              && dist < 9 * 64 * FRACUNIT && P_Random() < 100)
1361     {                           // Floor fire attack
1362         P_SetMobjState(actor, S_MNTR_ATK3_1);
1363         actor->special2.i = 0;
1364     }
1365     else
1366     {                           // Swing attack
1367         A_FaceTarget(actor);
1368         // Don't need to call P_SetMobjState because the current state
1369         // falls through to the swing attack
1370     }
1371 }
1372 
1373 //----------------------------------------------------------------------------
1374 //
1375 // PROC A_MinotaurCharge
1376 //
1377 //----------------------------------------------------------------------------
1378 
A_MinotaurCharge(mobj_t * actor)1379 void A_MinotaurCharge(mobj_t * actor)
1380 {
1381     mobj_t *puff;
1382 
1383     if (!actor->target)
1384         return;
1385 
1386     if (actor->args[4] > 0)
1387     {
1388         puff = P_SpawnMobj(actor->x, actor->y, actor->z, MT_PUNCHPUFF);
1389         puff->momz = 2 * FRACUNIT;
1390         actor->args[4]--;
1391     }
1392     else
1393     {
1394         actor->flags &= ~MF_SKULLFLY;
1395         P_SetMobjState(actor, actor->info->seestate);
1396     }
1397 }
1398 
1399 //----------------------------------------------------------------------------
1400 //
1401 // PROC A_MinotaurAtk2
1402 //
1403 // Swing attack.
1404 //
1405 //----------------------------------------------------------------------------
1406 
A_MinotaurAtk2(mobj_t * actor)1407 void A_MinotaurAtk2(mobj_t * actor)
1408 {
1409     mobj_t *mo;
1410     angle_t angle;
1411     fixed_t momz;
1412 
1413     if (!actor->target)
1414         return;
1415 
1416     S_StartSound(actor, SFX_MAULATOR_HAMMER_SWING);
1417     if (P_CheckMeleeRange(actor))
1418     {
1419         P_DamageMobj(actor->target, actor, actor, HITDICE(3));
1420         return;
1421     }
1422     mo = P_SpawnMissile(actor, actor->target, MT_MNTRFX1);
1423     if (mo)
1424     {
1425         //S_StartSound(mo, sfx_minat2);
1426         momz = mo->momz;
1427         angle = mo->angle;
1428         P_SpawnMissileAngle(actor, MT_MNTRFX1, angle - (ANG45 / 8), momz);
1429         P_SpawnMissileAngle(actor, MT_MNTRFX1, angle + (ANG45 / 8), momz);
1430         P_SpawnMissileAngle(actor, MT_MNTRFX1, angle - (ANG45 / 16), momz);
1431         P_SpawnMissileAngle(actor, MT_MNTRFX1, angle + (ANG45 / 16), momz);
1432     }
1433 }
1434 
1435 //----------------------------------------------------------------------------
1436 //
1437 // PROC A_MinotaurAtk3
1438 //
1439 // Floor fire attack.
1440 //
1441 //----------------------------------------------------------------------------
1442 
A_MinotaurAtk3(mobj_t * actor)1443 void A_MinotaurAtk3(mobj_t * actor)
1444 {
1445     mobj_t *mo;
1446     player_t *player;
1447 
1448     if (!actor->target)
1449     {
1450         return;
1451     }
1452     if (P_CheckMeleeRange(actor))
1453     {
1454         P_DamageMobj(actor->target, actor, actor, HITDICE(3));
1455         if ((player = actor->target->player) != NULL)
1456         {                       // Squish the player
1457             player->deltaviewheight = -16 * FRACUNIT;
1458         }
1459     }
1460     else
1461     {
1462         mo = P_SpawnMissile(actor, actor->target, MT_MNTRFX2);
1463         if (mo != NULL)
1464         {
1465             S_StartSound(mo, SFX_MAULATOR_HAMMER_HIT);
1466         }
1467     }
1468     if (P_Random() < 192 && actor->special2.i == 0)
1469     {
1470         P_SetMobjState(actor, S_MNTR_ATK3_4);
1471         actor->special2.i = 1;
1472     }
1473 }
1474 
1475 //----------------------------------------------------------------------------
1476 //
1477 // PROC A_MntrFloorFire
1478 //
1479 //----------------------------------------------------------------------------
1480 
A_MntrFloorFire(mobj_t * actor)1481 void A_MntrFloorFire(mobj_t * actor)
1482 {
1483     mobj_t *mo;
1484     int r1, r2;
1485 
1486     r1 = P_SubRandom();
1487     r2 = P_SubRandom();
1488 
1489     actor->z = actor->floorz;
1490     mo = P_SpawnMobj(actor->x + (r2 << 10),
1491                      actor->y + (r1 << 10), ONFLOORZ,
1492                      MT_MNTRFX3);
1493     mo->target = actor->target;
1494     mo->momx = 1;               // Force block checking
1495     P_CheckMissileSpawn(mo);
1496 }
1497 
1498 
1499 //----------------------------------------------------------------------------
1500 //
1501 // PROC A_Scream
1502 //
1503 //----------------------------------------------------------------------------
1504 
A_Scream(mobj_t * actor)1505 void A_Scream(mobj_t * actor)
1506 {
1507     int sound;
1508 
1509     S_StopSound(actor);
1510     if (actor->player)
1511     {
1512         if (actor->player->morphTics)
1513         {
1514             S_StartSound(actor, actor->info->deathsound);
1515         }
1516         else
1517         {
1518             // Handle the different player death screams
1519             if (actor->momz <= -39 * FRACUNIT)
1520             {                   // Falling splat
1521                 sound = SFX_PLAYER_FALLING_SPLAT;
1522             }
1523             else if (actor->health > -50)
1524             {                   // Normal death sound
1525                 switch (actor->player->class)
1526                 {
1527                     case PCLASS_FIGHTER:
1528                         sound = SFX_PLAYER_FIGHTER_NORMAL_DEATH;
1529                         break;
1530                     case PCLASS_CLERIC:
1531                         sound = SFX_PLAYER_CLERIC_NORMAL_DEATH;
1532                         break;
1533                     case PCLASS_MAGE:
1534                         sound = SFX_PLAYER_MAGE_NORMAL_DEATH;
1535                         break;
1536                     default:
1537                         sound = SFX_NONE;
1538                         break;
1539                 }
1540             }
1541             else if (actor->health > -100)
1542             {                   // Crazy death sound
1543                 switch (actor->player->class)
1544                 {
1545                     case PCLASS_FIGHTER:
1546                         sound = SFX_PLAYER_FIGHTER_CRAZY_DEATH;
1547                         break;
1548                     case PCLASS_CLERIC:
1549                         sound = SFX_PLAYER_CLERIC_CRAZY_DEATH;
1550                         break;
1551                     case PCLASS_MAGE:
1552                         sound = SFX_PLAYER_MAGE_CRAZY_DEATH;
1553                         break;
1554                     default:
1555                         sound = SFX_NONE;
1556                         break;
1557                 }
1558             }
1559             else
1560             {                   // Extreme death sound
1561                 switch (actor->player->class)
1562                 {
1563                     case PCLASS_FIGHTER:
1564                         sound = SFX_PLAYER_FIGHTER_EXTREME1_DEATH;
1565                         break;
1566                     case PCLASS_CLERIC:
1567                         sound = SFX_PLAYER_CLERIC_EXTREME1_DEATH;
1568                         break;
1569                     case PCLASS_MAGE:
1570                         sound = SFX_PLAYER_MAGE_EXTREME1_DEATH;
1571                         break;
1572                     default:
1573                         sound = SFX_NONE;
1574                         break;
1575                 }
1576                 sound += P_Random() % 3;        // Three different extreme deaths
1577             }
1578             S_StartSound(actor, sound);
1579         }
1580     }
1581     else
1582     {
1583         S_StartSound(actor, actor->info->deathsound);
1584     }
1585 }
1586 
1587 //---------------------------------------------------------------------------
1588 //
1589 // PROC P_DropItem
1590 //
1591 //---------------------------------------------------------------------------
1592 
1593 /*
1594 void P_DropItem(mobj_t *source, mobjtype_t type, int special, int chance)
1595 {
1596 	mobj_t *mo;
1597 
1598 	if(P_Random() > chance)
1599 	{
1600 		return;
1601 	}
1602 	mo = P_SpawnMobj(source->x, source->y,
1603 		source->z+(source->height>>1), type);
1604 	mo->momx = P_SubRandom()<<8;
1605 	mo->momy = P_SubRandom()<<8;
1606 	mo->momz = FRACUNIT*5+(P_Random()<<10);
1607 	mo->flags2 |= MF2_DROPPED;
1608 	mo->health = special;
1609 }
1610 */
1611 
1612 //----------------------------------------------------------------------------
1613 //
1614 // PROC A_NoBlocking
1615 //
1616 //----------------------------------------------------------------------------
1617 
A_NoBlocking(mobj_t * actor)1618 void A_NoBlocking(mobj_t * actor)
1619 {
1620     actor->flags &= ~MF_SOLID;
1621 
1622     // Check for monsters dropping things
1623 /*	switch(actor->type)
1624 	{
1625 		// Add the monster dropped items here
1626 		case MT_MUMMYLEADERGHOST:
1627 			P_DropItem(actor, MT_AMGWNDWIMPY, 3, 84);
1628 			break;
1629 		default:
1630 			break;
1631 	}
1632 */
1633 }
1634 
1635 //----------------------------------------------------------------------------
1636 //
1637 // PROC A_Explode
1638 //
1639 // Handles a bunch of exploding things.
1640 //
1641 //----------------------------------------------------------------------------
1642 
A_Explode(mobj_t * actor)1643 void A_Explode(mobj_t * actor)
1644 {
1645     int damage;
1646     int distance;
1647     boolean damageSelf;
1648 
1649     damage = 128;
1650     distance = 128;
1651     damageSelf = true;
1652     switch (actor->type)
1653     {
1654         case MT_FIREBOMB:      // Time Bombs
1655             actor->z += 32 * FRACUNIT;
1656             actor->flags &= ~MF_SHADOW;
1657             break;
1658         case MT_MNTRFX2:       // Minotaur floor fire
1659             damage = 24;
1660             break;
1661         case MT_BISHOP:        // Bishop radius death
1662             damage = 25 + (P_Random() & 15);
1663             break;
1664         case MT_HAMMER_MISSILE:        // Fighter Hammer
1665             damage = 128;
1666             damageSelf = false;
1667             break;
1668         case MT_FSWORD_MISSILE:        // Fighter Runesword
1669             damage = 64;
1670             damageSelf = false;
1671             break;
1672         case MT_CIRCLEFLAME:   // Cleric Flame secondary flames
1673             damage = 20;
1674             damageSelf = false;
1675             break;
1676         case MT_SORCBALL1:     // Sorcerer balls
1677         case MT_SORCBALL2:
1678         case MT_SORCBALL3:
1679             distance = 255;
1680             damage = 255;
1681             actor->args[0] = 1; // don't play bounce
1682             break;
1683         case MT_SORCFX1:       // Sorcerer spell 1
1684             damage = 30;
1685             break;
1686         case MT_SORCFX4:       // Sorcerer spell 4
1687             damage = 20;
1688             break;
1689         case MT_TREEDESTRUCTIBLE:
1690             damage = 10;
1691             break;
1692         case MT_DRAGON_FX2:
1693             damage = 80;
1694             damageSelf = false;
1695             break;
1696         case MT_MSTAFF_FX:
1697             damage = 64;
1698             distance = 192;
1699             damageSelf = false;
1700             break;
1701         case MT_MSTAFF_FX2:
1702             damage = 80;
1703             distance = 192;
1704             damageSelf = false;
1705             break;
1706         case MT_POISONCLOUD:
1707             damage = 4;
1708             distance = 40;
1709             break;
1710         case MT_ZXMAS_TREE:
1711         case MT_ZSHRUB2:
1712             damage = 30;
1713             distance = 64;
1714             break;
1715         default:
1716             break;
1717     }
1718     P_RadiusAttack(actor, actor->target, damage, distance, damageSelf);
1719     if (actor->z <= actor->floorz + (distance << FRACBITS)
1720         && actor->type != MT_POISONCLOUD)
1721     {
1722         P_HitFloor(actor);
1723     }
1724 }
1725 
1726 //----------------------------------------------------------------------------
1727 //
1728 // PROC P_Massacre
1729 //
1730 // Kills all monsters.
1731 //
1732 //----------------------------------------------------------------------------
1733 
P_Massacre(void)1734 int P_Massacre(void)
1735 {
1736     int count;
1737     mobj_t *mo;
1738     thinker_t *think;
1739 
1740     count = 0;
1741     for (think = thinkercap.next; think != &thinkercap; think = think->next)
1742     {
1743         if (think->function != P_MobjThinker)
1744         {                       // Not a mobj thinker
1745             continue;
1746         }
1747         mo = (mobj_t *) think;
1748         if ((mo->flags & MF_COUNTKILL) && (mo->health > 0))
1749         {
1750             mo->flags2 &= ~(MF2_NONSHOOTABLE + MF2_INVULNERABLE);
1751             mo->flags |= MF_SHOOTABLE;
1752             P_DamageMobj(mo, NULL, NULL, 10000);
1753             count++;
1754         }
1755     }
1756     return count;
1757 }
1758 
1759 
1760 
1761 //----------------------------------------------------------------------------
1762 //
1763 // PROC A_SkullPop
1764 //
1765 //----------------------------------------------------------------------------
1766 
A_SkullPop(mobj_t * actor)1767 void A_SkullPop(mobj_t * actor)
1768 {
1769     mobj_t *mo;
1770     player_t *player;
1771 
1772     if (!actor->player)
1773     {
1774         return;
1775     }
1776     actor->flags &= ~MF_SOLID;
1777     mo = P_SpawnMobj(actor->x, actor->y, actor->z + 48 * FRACUNIT,
1778                      MT_BLOODYSKULL);
1779     //mo->target = actor;
1780     mo->momx = P_SubRandom() << 9;
1781     mo->momy = P_SubRandom() << 9;
1782     mo->momz = FRACUNIT * 2 + (P_Random() << 6);
1783     // Attach player mobj to bloody skull
1784     player = actor->player;
1785     actor->player = NULL;
1786     actor->special1.i = player->class;
1787     mo->player = player;
1788     mo->health = actor->health;
1789     mo->angle = actor->angle;
1790     player->mo = mo;
1791     player->lookdir = 0;
1792     player->damagecount = 32;
1793 }
1794 
1795 //----------------------------------------------------------------------------
1796 //
1797 // PROC A_CheckSkullFloor
1798 //
1799 //----------------------------------------------------------------------------
1800 
A_CheckSkullFloor(mobj_t * actor)1801 void A_CheckSkullFloor(mobj_t * actor)
1802 {
1803     if (actor->z <= actor->floorz)
1804     {
1805         P_SetMobjState(actor, S_BLOODYSKULLX1);
1806         S_StartSound(actor, SFX_DRIP);
1807     }
1808 }
1809 
1810 //----------------------------------------------------------------------------
1811 //
1812 // PROC A_CheckSkullDone
1813 //
1814 //----------------------------------------------------------------------------
1815 
A_CheckSkullDone(mobj_t * actor)1816 void A_CheckSkullDone(mobj_t * actor)
1817 {
1818     if (actor->special2.i == 666)
1819     {
1820         P_SetMobjState(actor, S_BLOODYSKULLX2);
1821     }
1822 }
1823 
1824 //----------------------------------------------------------------------------
1825 //
1826 // PROC A_CheckBurnGone
1827 //
1828 //----------------------------------------------------------------------------
1829 
A_CheckBurnGone(mobj_t * actor)1830 void A_CheckBurnGone(mobj_t * actor)
1831 {
1832     if (actor->special2.i == 666)
1833     {
1834         P_SetMobjState(actor, S_PLAY_FDTH20);
1835     }
1836 }
1837 
1838 //----------------------------------------------------------------------------
1839 //
1840 // PROC A_FreeTargMobj
1841 //
1842 //----------------------------------------------------------------------------
1843 
A_FreeTargMobj(mobj_t * mo)1844 void A_FreeTargMobj(mobj_t * mo)
1845 {
1846     mo->momx = mo->momy = mo->momz = 0;
1847     mo->z = mo->ceilingz + 4 * FRACUNIT;
1848     mo->flags &=
1849         ~(MF_SHOOTABLE | MF_FLOAT | MF_SKULLFLY | MF_SOLID | MF_COUNTKILL);
1850     mo->flags |= MF_CORPSE | MF_DROPOFF | MF_NOGRAVITY;
1851     mo->flags2 &= ~(MF2_PASSMOBJ | MF2_LOGRAV);
1852     mo->flags2 |= MF2_DONTDRAW;
1853     mo->player = NULL;
1854     mo->health = -1000;         // Don't resurrect
1855 }
1856 
1857 
1858 //----------------------------------------------------------------------------
1859 //
1860 // CorpseQueue Routines
1861 //
1862 //----------------------------------------------------------------------------
1863 
1864 // Corpse queue for monsters - this should be saved out
1865 #define CORPSEQUEUESIZE	64
1866 mobj_t *corpseQueue[CORPSEQUEUESIZE];
1867 int corpseQueueSlot;
1868 
1869 // throw another corpse on the queue
A_QueueCorpse(mobj_t * actor)1870 void A_QueueCorpse(mobj_t * actor)
1871 {
1872     mobj_t *corpse;
1873 
1874     if (corpseQueueSlot >= CORPSEQUEUESIZE)
1875     {                           // Too many corpses - remove an old one
1876         corpse = corpseQueue[corpseQueueSlot % CORPSEQUEUESIZE];
1877         if (corpse)
1878             P_RemoveMobj(corpse);
1879     }
1880     corpseQueue[corpseQueueSlot % CORPSEQUEUESIZE] = actor;
1881     corpseQueueSlot++;
1882 }
1883 
1884 // Remove a mobj from the queue (for resurrection)
A_DeQueueCorpse(mobj_t * actor)1885 void A_DeQueueCorpse(mobj_t * actor)
1886 {
1887     int slot;
1888 
1889     for (slot = 0; slot < CORPSEQUEUESIZE; slot++)
1890     {
1891         if (corpseQueue[slot] == actor)
1892         {
1893             corpseQueue[slot] = NULL;
1894             break;
1895         }
1896     }
1897 }
1898 
P_InitCreatureCorpseQueue(boolean corpseScan)1899 void P_InitCreatureCorpseQueue(boolean corpseScan)
1900 {
1901     thinker_t *think;
1902     mobj_t *mo;
1903 
1904     // Initialize queue
1905     corpseQueueSlot = 0;
1906     memset(corpseQueue, 0, sizeof(mobj_t *) * CORPSEQUEUESIZE);
1907 
1908     if (!corpseScan)
1909         return;
1910 
1911     // Search mobj list for corpses and place them in this queue
1912     for (think = thinkercap.next; think != &thinkercap; think = think->next)
1913     {
1914         if (think->function != P_MobjThinker)
1915             continue;
1916         mo = (mobj_t *) think;
1917         if (!(mo->flags & MF_CORPSE))
1918             continue;           // Must be a corpse
1919         if (mo->flags & MF_ICECORPSE)
1920             continue;           // Not ice corpses
1921         // Only corpses that call A_QueueCorpse from death routine
1922         switch (mo->type)
1923         {
1924             case MT_CENTAUR:
1925             case MT_CENTAURLEADER:
1926             case MT_DEMON:
1927             case MT_DEMON2:
1928             case MT_WRAITH:
1929             case MT_WRAITHB:
1930             case MT_BISHOP:
1931             case MT_ETTIN:
1932             case MT_PIG:
1933             case MT_CENTAUR_SHIELD:
1934             case MT_CENTAUR_SWORD:
1935             case MT_DEMONCHUNK1:
1936             case MT_DEMONCHUNK2:
1937             case MT_DEMONCHUNK3:
1938             case MT_DEMONCHUNK4:
1939             case MT_DEMONCHUNK5:
1940             case MT_DEMON2CHUNK1:
1941             case MT_DEMON2CHUNK2:
1942             case MT_DEMON2CHUNK3:
1943             case MT_DEMON2CHUNK4:
1944             case MT_DEMON2CHUNK5:
1945             case MT_FIREDEMON_SPLOTCH1:
1946             case MT_FIREDEMON_SPLOTCH2:
1947                 A_QueueCorpse(mo);      // Add corpse to queue
1948                 break;
1949             default:
1950                 break;
1951         }
1952     }
1953 }
1954 
1955 
1956 //----------------------------------------------------------------------------
1957 //
1958 // PROC A_AddPlayerCorpse
1959 //
1960 //----------------------------------------------------------------------------
1961 
1962 #define BODYQUESIZE 32
1963 mobj_t *bodyque[BODYQUESIZE];
1964 int bodyqueslot;
1965 
A_AddPlayerCorpse(mobj_t * actor)1966 void A_AddPlayerCorpse(mobj_t * actor)
1967 {
1968     if (bodyqueslot >= BODYQUESIZE)
1969     {                           // Too many player corpses - remove an old one
1970         P_RemoveMobj(bodyque[bodyqueslot % BODYQUESIZE]);
1971     }
1972     bodyque[bodyqueslot % BODYQUESIZE] = actor;
1973     bodyqueslot++;
1974 }
1975 
1976 //============================================================================
1977 //
1978 // A_SerpentUnHide
1979 //
1980 //============================================================================
1981 
A_SerpentUnHide(mobj_t * actor)1982 void A_SerpentUnHide(mobj_t * actor)
1983 {
1984     actor->flags2 &= ~MF2_DONTDRAW;
1985     actor->floorclip = 24 * FRACUNIT;
1986 }
1987 
1988 //============================================================================
1989 //
1990 // A_SerpentHide
1991 //
1992 //============================================================================
1993 
A_SerpentHide(mobj_t * actor)1994 void A_SerpentHide(mobj_t * actor)
1995 {
1996     actor->flags2 |= MF2_DONTDRAW;
1997     actor->floorclip = 0;
1998 }
1999 
2000 //============================================================================
2001 //
2002 // A_SerpentChase
2003 //
2004 //============================================================================
2005 
A_SerpentChase(mobj_t * actor)2006 void A_SerpentChase(mobj_t * actor)
2007 {
2008     int delta;
2009     int oldX, oldY, oldFloor;
2010 
2011     if (actor->reactiontime)
2012     {
2013         actor->reactiontime--;
2014     }
2015 
2016     // Modify target threshold
2017     if (actor->threshold)
2018     {
2019         actor->threshold--;
2020     }
2021 
2022     if (gameskill == sk_nightmare)
2023     {                           // Monsters move faster in nightmare mode
2024         actor->tics -= actor->tics / 2;
2025         if (actor->tics < 3)
2026         {
2027             actor->tics = 3;
2028         }
2029     }
2030 
2031 //
2032 // turn towards movement direction if not there yet
2033 //
2034     if (actor->movedir < 8)
2035     {
2036         actor->angle &= (7 << 29);
2037         delta = actor->angle - (actor->movedir << 29);
2038         if (delta > 0)
2039         {
2040             actor->angle -= ANG90 / 2;
2041         }
2042         else if (delta < 0)
2043         {
2044             actor->angle += ANG90 / 2;
2045         }
2046     }
2047 
2048     if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
2049     {                           // look for a new target
2050         if (P_LookForPlayers(actor, true))
2051         {                       // got a new target
2052             return;
2053         }
2054         P_SetMobjState(actor, actor->info->spawnstate);
2055         return;
2056     }
2057 
2058 //
2059 // don't attack twice in a row
2060 //
2061     if (actor->flags & MF_JUSTATTACKED)
2062     {
2063         actor->flags &= ~MF_JUSTATTACKED;
2064         if (gameskill != sk_nightmare)
2065             P_NewChaseDir(actor);
2066         return;
2067     }
2068 
2069 //
2070 // check for melee attack
2071 //
2072     if (actor->info->meleestate && P_CheckMeleeRange(actor))
2073     {
2074         if (actor->info->attacksound)
2075         {
2076             S_StartSound(actor, actor->info->attacksound);
2077         }
2078         P_SetMobjState(actor, actor->info->meleestate);
2079         return;
2080     }
2081 
2082 //
2083 // possibly choose another target
2084 //
2085     if (netgame && !actor->threshold && !P_CheckSight(actor, actor->target))
2086     {
2087         if (P_LookForPlayers(actor, true))
2088             return;             // got a new target
2089     }
2090 
2091 //
2092 // chase towards player
2093 //
2094     oldX = actor->x;
2095     oldY = actor->y;
2096     oldFloor = actor->subsector->sector->floorpic;
2097     if (--actor->movecount < 0 || !P_Move(actor))
2098     {
2099         P_NewChaseDir(actor);
2100     }
2101     if (actor->subsector->sector->floorpic != oldFloor)
2102     {
2103         P_TryMove(actor, oldX, oldY);
2104         P_NewChaseDir(actor);
2105     }
2106 
2107 //
2108 // make active sound
2109 //
2110     if (actor->info->activesound && P_Random() < 3)
2111     {
2112         S_StartSound(actor, actor->info->activesound);
2113     }
2114 }
2115 
2116 //============================================================================
2117 //
2118 // A_SerpentRaiseHump
2119 //
2120 // Raises the hump above the surface by raising the floorclip level
2121 //============================================================================
2122 
A_SerpentRaiseHump(mobj_t * actor)2123 void A_SerpentRaiseHump(mobj_t * actor)
2124 {
2125     actor->floorclip -= 4 * FRACUNIT;
2126 }
2127 
2128 //============================================================================
2129 //
2130 // A_SerpentLowerHump
2131 //
2132 //============================================================================
2133 
A_SerpentLowerHump(mobj_t * actor)2134 void A_SerpentLowerHump(mobj_t * actor)
2135 {
2136     actor->floorclip += 4 * FRACUNIT;
2137 }
2138 
2139 //============================================================================
2140 //
2141 // A_SerpentHumpDecide
2142 //
2143 //              Decided whether to hump up, or if the mobj is a serpent leader,
2144 //                      to missile attack
2145 //============================================================================
2146 
A_SerpentHumpDecide(mobj_t * actor)2147 void A_SerpentHumpDecide(mobj_t * actor)
2148 {
2149     if (actor->type == MT_SERPENTLEADER)
2150     {
2151         if (P_Random() > 30)
2152         {
2153             return;
2154         }
2155         else if (P_Random() < 40)
2156         {                       // Missile attack
2157             P_SetMobjState(actor, S_SERPENT_SURFACE1);
2158             return;
2159         }
2160     }
2161     else if (P_Random() > 3)
2162     {
2163         return;
2164     }
2165     if (!P_CheckMeleeRange(actor))
2166     {                           // The hump shouldn't occur when within melee range
2167         if (actor->type == MT_SERPENTLEADER && P_Random() < 128)
2168         {
2169             P_SetMobjState(actor, S_SERPENT_SURFACE1);
2170         }
2171         else
2172         {
2173             P_SetMobjState(actor, S_SERPENT_HUMP1);
2174             S_StartSound(actor, SFX_SERPENT_ACTIVE);
2175         }
2176     }
2177 }
2178 
2179 //============================================================================
2180 //
2181 // A_SerpentBirthScream
2182 //
2183 //============================================================================
2184 
A_SerpentBirthScream(mobj_t * actor)2185 void A_SerpentBirthScream(mobj_t * actor)
2186 {
2187     S_StartSound(actor, SFX_SERPENT_BIRTH);
2188 }
2189 
2190 //============================================================================
2191 //
2192 // A_SerpentDiveSound
2193 //
2194 //============================================================================
2195 
A_SerpentDiveSound(mobj_t * actor)2196 void A_SerpentDiveSound(mobj_t * actor)
2197 {
2198     S_StartSound(actor, SFX_SERPENT_ACTIVE);
2199 }
2200 
2201 //============================================================================
2202 //
2203 // A_SerpentWalk
2204 //
2205 // Similar to A_Chase, only has a hardcoded entering of meleestate
2206 //============================================================================
2207 
A_SerpentWalk(mobj_t * actor)2208 void A_SerpentWalk(mobj_t * actor)
2209 {
2210     int delta;
2211 
2212     if (actor->reactiontime)
2213     {
2214         actor->reactiontime--;
2215     }
2216 
2217     // Modify target threshold
2218     if (actor->threshold)
2219     {
2220         actor->threshold--;
2221     }
2222 
2223     if (gameskill == sk_nightmare)
2224     {                           // Monsters move faster in nightmare mode
2225         actor->tics -= actor->tics / 2;
2226         if (actor->tics < 3)
2227         {
2228             actor->tics = 3;
2229         }
2230     }
2231 
2232 //
2233 // turn towards movement direction if not there yet
2234 //
2235     if (actor->movedir < 8)
2236     {
2237         actor->angle &= (7 << 29);
2238         delta = actor->angle - (actor->movedir << 29);
2239         if (delta > 0)
2240         {
2241             actor->angle -= ANG90 / 2;
2242         }
2243         else if (delta < 0)
2244         {
2245             actor->angle += ANG90 / 2;
2246         }
2247     }
2248 
2249     if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
2250     {                           // look for a new target
2251         if (P_LookForPlayers(actor, true))
2252         {                       // got a new target
2253             return;
2254         }
2255         P_SetMobjState(actor, actor->info->spawnstate);
2256         return;
2257     }
2258 
2259 //
2260 // don't attack twice in a row
2261 //
2262     if (actor->flags & MF_JUSTATTACKED)
2263     {
2264         actor->flags &= ~MF_JUSTATTACKED;
2265         if (gameskill != sk_nightmare)
2266             P_NewChaseDir(actor);
2267         return;
2268     }
2269 
2270 //
2271 // check for melee attack
2272 //
2273     if (actor->info->meleestate && P_CheckMeleeRange(actor))
2274     {
2275         if (actor->info->attacksound)
2276         {
2277             S_StartSound(actor, actor->info->attacksound);
2278         }
2279         P_SetMobjState(actor, S_SERPENT_ATK1);
2280         return;
2281     }
2282 //
2283 // possibly choose another target
2284 //
2285     if (netgame && !actor->threshold && !P_CheckSight(actor, actor->target))
2286     {
2287         if (P_LookForPlayers(actor, true))
2288             return;             // got a new target
2289     }
2290 
2291 //
2292 // chase towards player
2293 //
2294     if (--actor->movecount < 0 || !P_Move(actor))
2295     {
2296         P_NewChaseDir(actor);
2297     }
2298 }
2299 
2300 //============================================================================
2301 //
2302 // A_SerpentCheckForAttack
2303 //
2304 //============================================================================
2305 
A_SerpentCheckForAttack(mobj_t * actor)2306 void A_SerpentCheckForAttack(mobj_t * actor)
2307 {
2308     if (!actor->target)
2309     {
2310         return;
2311     }
2312     if (actor->type == MT_SERPENTLEADER)
2313     {
2314         if (!P_CheckMeleeRange(actor))
2315         {
2316             P_SetMobjState(actor, S_SERPENT_ATK1);
2317             return;
2318         }
2319     }
2320     if (P_CheckMeleeRange2(actor))
2321     {
2322         P_SetMobjState(actor, S_SERPENT_WALK1);
2323     }
2324     else if (P_CheckMeleeRange(actor))
2325     {
2326         if (P_Random() < 32)
2327         {
2328             P_SetMobjState(actor, S_SERPENT_WALK1);
2329         }
2330         else
2331         {
2332             P_SetMobjState(actor, S_SERPENT_ATK1);
2333         }
2334     }
2335 }
2336 
2337 //============================================================================
2338 //
2339 // A_SerpentChooseAttack
2340 //
2341 //============================================================================
2342 
A_SerpentChooseAttack(mobj_t * actor)2343 void A_SerpentChooseAttack(mobj_t * actor)
2344 {
2345     if (!actor->target || P_CheckMeleeRange(actor))
2346     {
2347         return;
2348     }
2349     if (actor->type == MT_SERPENTLEADER)
2350     {
2351         P_SetMobjState(actor, S_SERPENT_MISSILE1);
2352     }
2353 }
2354 
2355 //============================================================================
2356 //
2357 // A_SerpentMeleeAttack
2358 //
2359 //============================================================================
2360 
A_SerpentMeleeAttack(mobj_t * actor)2361 void A_SerpentMeleeAttack(mobj_t * actor)
2362 {
2363     if (!actor->target)
2364     {
2365         return;
2366     }
2367     if (P_CheckMeleeRange(actor))
2368     {
2369         P_DamageMobj(actor->target, actor, actor, HITDICE(5));
2370         S_StartSound(actor, SFX_SERPENT_MELEEHIT);
2371     }
2372     if (P_Random() < 96)
2373     {
2374         A_SerpentCheckForAttack(actor);
2375     }
2376 }
2377 
2378 //============================================================================
2379 //
2380 // A_SerpentMissileAttack
2381 //
2382 //============================================================================
2383 
A_SerpentMissileAttack(mobj_t * actor)2384 void A_SerpentMissileAttack(mobj_t * actor)
2385 {
2386     if (!actor->target)
2387     {
2388         return;
2389     }
2390 
2391     P_SpawnMissile(actor, actor->target, MT_SERPENTFX);
2392 }
2393 
2394 //============================================================================
2395 //
2396 // A_SerpentHeadPop
2397 //
2398 //============================================================================
2399 
A_SerpentHeadPop(mobj_t * actor)2400 void A_SerpentHeadPop(mobj_t * actor)
2401 {
2402     P_SpawnMobj(actor->x, actor->y, actor->z + 45 * FRACUNIT,
2403                 MT_SERPENT_HEAD);
2404 }
2405 
2406 //============================================================================
2407 //
2408 // A_SerpentSpawnGibs
2409 //
2410 //============================================================================
2411 
A_SerpentSpawnGibs(mobj_t * actor)2412 void A_SerpentSpawnGibs(mobj_t * actor)
2413 {
2414     mobj_t *mo;
2415     int r1, r2;
2416 
2417     r1 = P_Random();
2418     r2 = P_Random();
2419     mo = P_SpawnMobj(actor->x + ((r2 - 128) << 12),
2420                      actor->y + ((r1 - 128) << 12),
2421                      actor->floorz + FRACUNIT, MT_SERPENT_GIB1);
2422     if (mo)
2423     {
2424         mo->momx = (P_Random() - 128) << 6;
2425         mo->momy = (P_Random() - 128) << 6;
2426         mo->floorclip = 6 * FRACUNIT;
2427     }
2428     r1 = P_Random();
2429     r2 = P_Random();
2430     mo = P_SpawnMobj(actor->x + ((r2 - 128) << 12),
2431                      actor->y + ((r1 - 128) << 12),
2432                      actor->floorz + FRACUNIT, MT_SERPENT_GIB2);
2433     if (mo)
2434     {
2435         mo->momx = (P_Random() - 128) << 6;
2436         mo->momy = (P_Random() - 128) << 6;
2437         mo->floorclip = 6 * FRACUNIT;
2438     }
2439     r1 = P_Random();
2440     r2 = P_Random();
2441     mo = P_SpawnMobj(actor->x + ((r2 - 128) << 12),
2442                      actor->y + ((r1 - 128) << 12),
2443                      actor->floorz + FRACUNIT, MT_SERPENT_GIB3);
2444     if (mo)
2445     {
2446         mo->momx = (P_Random() - 128) << 6;
2447         mo->momy = (P_Random() - 128) << 6;
2448         mo->floorclip = 6 * FRACUNIT;
2449     }
2450 }
2451 
2452 //============================================================================
2453 //
2454 // A_FloatGib
2455 //
2456 //============================================================================
2457 
A_FloatGib(mobj_t * actor)2458 void A_FloatGib(mobj_t * actor)
2459 {
2460     actor->floorclip -= FRACUNIT;
2461 }
2462 
2463 //============================================================================
2464 //
2465 // A_SinkGib
2466 //
2467 //============================================================================
2468 
A_SinkGib(mobj_t * actor)2469 void A_SinkGib(mobj_t * actor)
2470 {
2471     actor->floorclip += FRACUNIT;
2472 }
2473 
2474 //============================================================================
2475 //
2476 // A_DelayGib
2477 //
2478 //============================================================================
2479 
A_DelayGib(mobj_t * actor)2480 void A_DelayGib(mobj_t * actor)
2481 {
2482     actor->tics -= P_Random() >> 2;
2483 }
2484 
2485 //============================================================================
2486 //
2487 // A_SerpentHeadCheck
2488 //
2489 //============================================================================
2490 
A_SerpentHeadCheck(mobj_t * actor)2491 void A_SerpentHeadCheck(mobj_t * actor)
2492 {
2493     if (actor->z <= actor->floorz)
2494     {
2495         if (P_GetThingFloorType(actor) >= FLOOR_LIQUID)
2496         {
2497             P_HitFloor(actor);
2498             P_SetMobjState(actor, S_NULL);
2499         }
2500         else
2501         {
2502             P_SetMobjState(actor, S_SERPENT_HEAD_X1);
2503         }
2504     }
2505 }
2506 
2507 //============================================================================
2508 //
2509 // A_CentaurAttack
2510 //
2511 //============================================================================
2512 
A_CentaurAttack(mobj_t * actor)2513 void A_CentaurAttack(mobj_t * actor)
2514 {
2515     if (!actor->target)
2516     {
2517         return;
2518     }
2519     if (P_CheckMeleeRange(actor))
2520     {
2521         P_DamageMobj(actor->target, actor, actor, P_Random() % 7 + 3);
2522     }
2523 }
2524 
2525 //============================================================================
2526 //
2527 // A_CentaurAttack2
2528 //
2529 //============================================================================
2530 
A_CentaurAttack2(mobj_t * actor)2531 void A_CentaurAttack2(mobj_t * actor)
2532 {
2533     if (!actor->target)
2534     {
2535         return;
2536     }
2537     P_SpawnMissile(actor, actor->target, MT_CENTAUR_FX);
2538     S_StartSound(actor, SFX_CENTAURLEADER_ATTACK);
2539 }
2540 
2541 //============================================================================
2542 //
2543 // A_CentaurDropStuff
2544 //
2545 //      Spawn shield/sword sprites when the centaur pulps //============================================================================
2546 
A_CentaurDropStuff(mobj_t * actor)2547 void A_CentaurDropStuff(mobj_t * actor)
2548 {
2549     mobj_t *mo;
2550     angle_t angle;
2551 
2552     mo = P_SpawnMobj(actor->x, actor->y, actor->z + 45 * FRACUNIT,
2553                      MT_CENTAUR_SHIELD);
2554     if (mo)
2555     {
2556         angle = actor->angle + ANG90;
2557         mo->momz = FRACUNIT * 8 + (P_Random() << 10);
2558         mo->momx = FixedMul(((P_Random() - 128) << 11) + FRACUNIT,
2559                             finecosine[angle >> ANGLETOFINESHIFT]);
2560         mo->momy = FixedMul(((P_Random() - 128) << 11) + FRACUNIT,
2561                             finesine[angle >> ANGLETOFINESHIFT]);
2562         mo->target = actor;
2563     }
2564     mo = P_SpawnMobj(actor->x, actor->y, actor->z + 45 * FRACUNIT,
2565                      MT_CENTAUR_SWORD);
2566     if (mo)
2567     {
2568         angle = actor->angle - ANG90;
2569         mo->momz = FRACUNIT * 8 + (P_Random() << 10);
2570         mo->momx = FixedMul(((P_Random() - 128) << 11) + FRACUNIT,
2571                             finecosine[angle >> ANGLETOFINESHIFT]);
2572         mo->momy = FixedMul(((P_Random() - 128) << 11) + FRACUNIT,
2573                             finesine[angle >> ANGLETOFINESHIFT]);
2574         mo->target = actor;
2575     }
2576 }
2577 
2578 //============================================================================
2579 //
2580 // A_CentaurDefend
2581 //
2582 //============================================================================
2583 
A_CentaurDefend(mobj_t * actor)2584 void A_CentaurDefend(mobj_t * actor)
2585 {
2586     A_FaceTarget(actor);
2587     if (P_CheckMeleeRange(actor) && P_Random() < 32)
2588     {
2589         A_UnSetInvulnerable(actor);
2590         P_SetMobjState(actor, actor->info->meleestate);
2591     }
2592 }
2593 
2594 //============================================================================
2595 //
2596 // A_BishopAttack
2597 //
2598 //============================================================================
2599 
A_BishopAttack(mobj_t * actor)2600 void A_BishopAttack(mobj_t * actor)
2601 {
2602     if (!actor->target)
2603     {
2604         return;
2605     }
2606     S_StartSound(actor, actor->info->attacksound);
2607     if (P_CheckMeleeRange(actor))
2608     {
2609         P_DamageMobj(actor->target, actor, actor, HITDICE(4));
2610         return;
2611     }
2612     actor->special1.i = (P_Random() & 3) + 5;
2613 }
2614 
2615 //============================================================================
2616 //
2617 // A_BishopAttack2
2618 //
2619 //              Spawns one of a string of bishop missiles
2620 //============================================================================
2621 
A_BishopAttack2(mobj_t * actor)2622 void A_BishopAttack2(mobj_t * actor)
2623 {
2624     mobj_t *mo;
2625 
2626     if (!actor->target || !actor->special1.i)
2627     {
2628         actor->special1.i = 0;
2629         P_SetMobjState(actor, S_BISHOP_WALK1);
2630         return;
2631     }
2632     mo = P_SpawnMissile(actor, actor->target, MT_BISH_FX);
2633     if (mo)
2634     {
2635         mo->special1.m = actor->target;
2636         mo->special2.i = 16;      // High word == x/y, Low word == z
2637     }
2638     actor->special1.i--;
2639 }
2640 
2641 //============================================================================
2642 //
2643 // A_BishopMissileWeave
2644 //
2645 //============================================================================
2646 
A_BishopMissileWeave(mobj_t * actor)2647 void A_BishopMissileWeave(mobj_t * actor)
2648 {
2649     fixed_t newX, newY;
2650     int weaveXY, weaveZ;
2651     int angle;
2652 
2653     weaveXY = actor->special2.i >> 16;
2654     weaveZ = actor->special2.i & 0xFFFF;
2655     angle = (actor->angle + ANG90) >> ANGLETOFINESHIFT;
2656     newX = actor->x - FixedMul(finecosine[angle],
2657                                FloatBobOffsets[weaveXY] << 1);
2658     newY = actor->y - FixedMul(finesine[angle],
2659                                FloatBobOffsets[weaveXY] << 1);
2660     weaveXY = (weaveXY + 2) & 63;
2661     newX += FixedMul(finecosine[angle], FloatBobOffsets[weaveXY] << 1);
2662     newY += FixedMul(finesine[angle], FloatBobOffsets[weaveXY] << 1);
2663     P_TryMove(actor, newX, newY);
2664     actor->z -= FloatBobOffsets[weaveZ];
2665     weaveZ = (weaveZ + 2) & 63;
2666     actor->z += FloatBobOffsets[weaveZ];
2667     actor->special2.i = weaveZ + (weaveXY << 16);
2668 }
2669 
2670 //============================================================================
2671 //
2672 // A_BishopMissileSeek
2673 //
2674 //============================================================================
2675 
A_BishopMissileSeek(mobj_t * actor)2676 void A_BishopMissileSeek(mobj_t * actor)
2677 {
2678     P_SeekerMissile(actor, ANG1 * 2, ANG1 * 3);
2679 }
2680 
2681 //============================================================================
2682 //
2683 // A_BishopDecide
2684 //
2685 //============================================================================
2686 
A_BishopDecide(mobj_t * actor)2687 void A_BishopDecide(mobj_t * actor)
2688 {
2689     if (P_Random() < 220)
2690     {
2691         return;
2692     }
2693     else
2694     {
2695         P_SetMobjState(actor, S_BISHOP_BLUR1);
2696     }
2697 }
2698 
2699 //============================================================================
2700 //
2701 // A_BishopDoBlur
2702 //
2703 //============================================================================
2704 
A_BishopDoBlur(mobj_t * actor)2705 void A_BishopDoBlur(mobj_t * actor)
2706 {
2707     actor->special1.i = (P_Random() & 3) + 3;     // Random number of blurs
2708     if (P_Random() < 120)
2709     {
2710         P_ThrustMobj(actor, actor->angle + ANG90, 11 * FRACUNIT);
2711     }
2712     else if (P_Random() > 125)
2713     {
2714         P_ThrustMobj(actor, actor->angle - ANG90, 11 * FRACUNIT);
2715     }
2716     else
2717     {                           // Thrust forward
2718         P_ThrustMobj(actor, actor->angle, 11 * FRACUNIT);
2719     }
2720     S_StartSound(actor, SFX_BISHOP_BLUR);
2721 }
2722 
2723 //============================================================================
2724 //
2725 // A_BishopSpawnBlur
2726 //
2727 //============================================================================
2728 
A_BishopSpawnBlur(mobj_t * actor)2729 void A_BishopSpawnBlur(mobj_t * actor)
2730 {
2731     mobj_t *mo;
2732 
2733     if (!--actor->special1.i)
2734     {
2735         actor->momx = 0;
2736         actor->momy = 0;
2737         if (P_Random() > 96)
2738         {
2739             P_SetMobjState(actor, S_BISHOP_WALK1);
2740         }
2741         else
2742         {
2743             P_SetMobjState(actor, S_BISHOP_ATK1);
2744         }
2745     }
2746     mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_BISHOPBLUR);
2747     if (mo)
2748     {
2749         mo->angle = actor->angle;
2750     }
2751 }
2752 
2753 //============================================================================
2754 //
2755 // A_BishopChase
2756 //
2757 //============================================================================
2758 
A_BishopChase(mobj_t * actor)2759 void A_BishopChase(mobj_t * actor)
2760 {
2761     actor->z -= FloatBobOffsets[actor->special2.i] >> 1;
2762     actor->special2.i = (actor->special2.i + 4) & 63;
2763     actor->z += FloatBobOffsets[actor->special2.i] >> 1;
2764 }
2765 
2766 //============================================================================
2767 //
2768 // A_BishopPuff
2769 //
2770 //============================================================================
2771 
A_BishopPuff(mobj_t * actor)2772 void A_BishopPuff(mobj_t * actor)
2773 {
2774     mobj_t *mo;
2775 
2776     mo = P_SpawnMobj(actor->x, actor->y, actor->z + 40 * FRACUNIT,
2777                      MT_BISHOP_PUFF);
2778     if (mo)
2779     {
2780         mo->momz = FRACUNIT / 2;
2781     }
2782 }
2783 
2784 //============================================================================
2785 //
2786 // A_BishopPainBlur
2787 //
2788 //============================================================================
2789 
A_BishopPainBlur(mobj_t * actor)2790 void A_BishopPainBlur(mobj_t * actor)
2791 {
2792     mobj_t *mo;
2793     int r1,r2,r3;
2794 
2795     if (P_Random() < 64)
2796     {
2797         P_SetMobjState(actor, S_BISHOP_BLUR1);
2798         return;
2799     }
2800 
2801     r1 = P_SubRandom();
2802     r2 = P_SubRandom();
2803     r3 = P_SubRandom();
2804 
2805     mo = P_SpawnMobj(actor->x + (r3 << 12), actor->y
2806                      + (r2 << 12),
2807                      actor->z + (r1 << 11),
2808                      MT_BISHOPPAINBLUR);
2809     if (mo)
2810     {
2811         mo->angle = actor->angle;
2812     }
2813 }
2814 
2815 //============================================================================
2816 //
2817 // DragonSeek
2818 //
2819 //============================================================================
2820 
DragonSeek(mobj_t * actor,angle_t thresh,angle_t turnMax)2821 static void DragonSeek(mobj_t * actor, angle_t thresh, angle_t turnMax)
2822 {
2823     int dir;
2824     int dist;
2825     angle_t delta;
2826     angle_t angle;
2827     mobj_t *target;
2828     int search;
2829     int i;
2830     int bestArg;
2831     angle_t bestAngle;
2832     angle_t angleToSpot, angleToTarget;
2833     mobj_t *mo;
2834 
2835     target = actor->special1.m;
2836     if (target == NULL)
2837     {
2838         return;
2839     }
2840     dir = P_FaceMobj(actor, target, &delta);
2841     if (delta > thresh)
2842     {
2843         delta >>= 1;
2844         if (delta > turnMax)
2845         {
2846             delta = turnMax;
2847         }
2848     }
2849     if (dir)
2850     {                           // Turn clockwise
2851         actor->angle += delta;
2852     }
2853     else
2854     {                           // Turn counter clockwise
2855         actor->angle -= delta;
2856     }
2857     angle = actor->angle >> ANGLETOFINESHIFT;
2858     actor->momx = FixedMul(actor->info->speed, finecosine[angle]);
2859     actor->momy = FixedMul(actor->info->speed, finesine[angle]);
2860     if (actor->z + actor->height < target->z
2861         || target->z + target->height < actor->z)
2862     {
2863         dist = P_AproxDistance(target->x - actor->x, target->y - actor->y);
2864         dist = dist / actor->info->speed;
2865         if (dist < 1)
2866         {
2867             dist = 1;
2868         }
2869         actor->momz = (target->z - actor->z) / dist;
2870     }
2871     else
2872     {
2873         dist = P_AproxDistance(target->x - actor->x, target->y - actor->y);
2874         dist = dist / actor->info->speed;
2875     }
2876     if (target->flags & MF_SHOOTABLE && P_Random() < 64)
2877     {                           // attack the destination mobj if it's attackable
2878         mobj_t *oldTarget;
2879 
2880         if (abs(actor->angle - R_PointToAngle2(actor->x, actor->y,
2881                                                target->x,
2882                                                target->y)) < ANG45 / 2)
2883         {
2884             oldTarget = actor->target;
2885             actor->target = target;
2886             if (P_CheckMeleeRange(actor))
2887             {
2888                 P_DamageMobj(actor->target, actor, actor, HITDICE(10));
2889                 S_StartSound(actor, SFX_DRAGON_ATTACK);
2890             }
2891             else if (P_Random() < 128 && P_CheckMissileRange(actor))
2892             {
2893                 P_SpawnMissile(actor, target, MT_DRAGON_FX);
2894                 S_StartSound(actor, SFX_DRAGON_ATTACK);
2895             }
2896             actor->target = oldTarget;
2897         }
2898     }
2899     if (dist < 4)
2900     {                           // Hit the target thing
2901         if (actor->target && P_Random() < 200)
2902         {
2903             bestArg = -1;
2904             bestAngle = ANG_MAX;
2905             angleToTarget = R_PointToAngle2(actor->x, actor->y,
2906                                             actor->target->x,
2907                                             actor->target->y);
2908             for (i = 0; i < 5; i++)
2909             {
2910                 if (!target->args[i])
2911                 {
2912                     continue;
2913                 }
2914                 search = -1;
2915                 mo = P_FindMobjFromTID(target->args[i], &search);
2916                 angleToSpot = R_PointToAngle2(actor->x, actor->y,
2917                                               mo->x, mo->y);
2918                 if (abs(angleToSpot - angleToTarget) < bestAngle)
2919                 {
2920                     bestAngle = abs(angleToSpot - angleToTarget);
2921                     bestArg = i;
2922                 }
2923             }
2924             if (bestArg != -1)
2925             {
2926                 search = -1;
2927                 actor->special1.m =
2928                     P_FindMobjFromTID(target->args[bestArg], &search);
2929             }
2930         }
2931         else
2932         {
2933             do
2934             {
2935                 i = (P_Random() >> 2) % 5;
2936             }
2937             while (!target->args[i]);
2938             search = -1;
2939             actor->special1.m =
2940                 P_FindMobjFromTID(target->args[i], &search);
2941         }
2942     }
2943 }
2944 
2945 //============================================================================
2946 //
2947 // A_DragonInitFlight
2948 //
2949 //============================================================================
2950 
A_DragonInitFlight(mobj_t * actor)2951 void A_DragonInitFlight(mobj_t * actor)
2952 {
2953     int search;
2954 
2955     search = -1;
2956     do
2957     {                           // find the first tid identical to the dragon's tid
2958         actor->special1.m = P_FindMobjFromTID(actor->tid, &search);
2959         if (search == -1)
2960         {
2961             P_SetMobjState(actor, actor->info->spawnstate);
2962             return;
2963         }
2964     }
2965     while (actor->special1.m == actor);
2966     P_RemoveMobjFromTIDList(actor);
2967 }
2968 
2969 //============================================================================
2970 //
2971 // A_DragonFlight
2972 //
2973 //============================================================================
2974 
A_DragonFlight(mobj_t * actor)2975 void A_DragonFlight(mobj_t * actor)
2976 {
2977     angle_t angle;
2978 
2979     DragonSeek(actor, 4 * ANG1, 8 * ANG1);
2980     if (actor->target)
2981     {
2982         if (!(actor->target->flags & MF_SHOOTABLE))
2983         {                       // target died
2984             actor->target = NULL;
2985             return;
2986         }
2987         angle = R_PointToAngle2(actor->x, actor->y, actor->target->x,
2988                                 actor->target->y);
2989         if (abs(actor->angle - angle) < ANG45 / 2
2990             && P_CheckMeleeRange(actor))
2991         {
2992             P_DamageMobj(actor->target, actor, actor, HITDICE(8));
2993             S_StartSound(actor, SFX_DRAGON_ATTACK);
2994         }
2995         else if (abs(actor->angle - angle) <= ANG1 * 20)
2996         {
2997             P_SetMobjState(actor, actor->info->missilestate);
2998             S_StartSound(actor, SFX_DRAGON_ATTACK);
2999         }
3000     }
3001     else
3002     {
3003         P_LookForPlayers(actor, true);
3004     }
3005 }
3006 
3007 //============================================================================
3008 //
3009 // A_DragonFlap
3010 //
3011 //============================================================================
3012 
A_DragonFlap(mobj_t * actor)3013 void A_DragonFlap(mobj_t * actor)
3014 {
3015     A_DragonFlight(actor);
3016     if (P_Random() < 240)
3017     {
3018         S_StartSound(actor, SFX_DRAGON_WINGFLAP);
3019     }
3020     else
3021     {
3022         S_StartSound(actor, actor->info->activesound);
3023     }
3024 }
3025 
3026 //============================================================================
3027 //
3028 // A_DragonAttack
3029 //
3030 //============================================================================
3031 
A_DragonAttack(mobj_t * actor)3032 void A_DragonAttack(mobj_t * actor)
3033 {
3034     P_SpawnMissile(actor, actor->target, MT_DRAGON_FX);
3035 }
3036 
3037 //============================================================================
3038 //
3039 // A_DragonFX2
3040 //
3041 //============================================================================
3042 
A_DragonFX2(mobj_t * actor)3043 void A_DragonFX2(mobj_t * actor)
3044 {
3045     mobj_t *mo;
3046     int i;
3047     int r1,r2,r3;
3048     int delay;
3049 
3050     delay = 16 + (P_Random() >> 3);
3051     for (i = 1 + (P_Random() & 3); i; i--)
3052     {
3053         r1 = P_Random();
3054         r2 = P_Random();
3055         r3 = P_Random();
3056         mo = P_SpawnMobj(actor->x + ((r3 - 128) << 14),
3057                          actor->y + ((r2 - 128) << 14),
3058                          actor->z + ((r1 - 128) << 12),
3059                          MT_DRAGON_FX2);
3060         if (mo)
3061         {
3062             mo->tics = delay + (P_Random() & 3) * i * 2;
3063             mo->target = actor->target;
3064         }
3065     }
3066 }
3067 
3068 //============================================================================
3069 //
3070 // A_DragonPain
3071 //
3072 //============================================================================
3073 
A_DragonPain(mobj_t * actor)3074 void A_DragonPain(mobj_t * actor)
3075 {
3076     A_Pain(actor);
3077     if (!actor->special1.i)
3078     {                           // no destination spot yet
3079         P_SetMobjState(actor, S_DRAGON_INIT);
3080     }
3081 }
3082 
3083 //============================================================================
3084 //
3085 // A_DragonCheckCrash
3086 //
3087 //============================================================================
3088 
A_DragonCheckCrash(mobj_t * actor)3089 void A_DragonCheckCrash(mobj_t * actor)
3090 {
3091     if (actor->z <= actor->floorz)
3092     {
3093         P_SetMobjState(actor, S_DRAGON_CRASH1);
3094     }
3095 }
3096 
3097 //============================================================================
3098 // Demon AI
3099 //============================================================================
3100 
3101 //
3102 // A_DemonAttack1 (melee)
3103 //
A_DemonAttack1(mobj_t * actor)3104 void A_DemonAttack1(mobj_t * actor)
3105 {
3106     if (P_CheckMeleeRange(actor))
3107     {
3108         P_DamageMobj(actor->target, actor, actor, HITDICE(2));
3109     }
3110 }
3111 
3112 
3113 //
3114 // A_DemonAttack2 (missile)
3115 //
A_DemonAttack2(mobj_t * actor)3116 void A_DemonAttack2(mobj_t * actor)
3117 {
3118     mobj_t *mo;
3119     int fireBall;
3120 
3121     if (actor->type == MT_DEMON)
3122     {
3123         fireBall = MT_DEMONFX1;
3124     }
3125     else
3126     {
3127         fireBall = MT_DEMON2FX1;
3128     }
3129     mo = P_SpawnMissile(actor, actor->target, fireBall);
3130     if (mo)
3131     {
3132         mo->z += 30 * FRACUNIT;
3133         S_StartSound(actor, SFX_DEMON_MISSILE_FIRE);
3134     }
3135 }
3136 
3137 //
3138 // A_DemonDeath
3139 //
3140 
A_DemonDeath(mobj_t * actor)3141 void A_DemonDeath(mobj_t * actor)
3142 {
3143     mobj_t *mo;
3144     angle_t angle;
3145 
3146     mo = P_SpawnMobj(actor->x, actor->y, actor->z + 45 * FRACUNIT,
3147                      MT_DEMONCHUNK1);
3148     if (mo)
3149     {
3150         angle = actor->angle + ANG90;
3151         mo->momz = 8 * FRACUNIT;
3152         mo->momx = FixedMul((P_Random() << 10) + FRACUNIT,
3153                             finecosine[angle >> ANGLETOFINESHIFT]);
3154         mo->momy = FixedMul((P_Random() << 10) + FRACUNIT,
3155                             finesine[angle >> ANGLETOFINESHIFT]);
3156         mo->target = actor;
3157     }
3158     mo = P_SpawnMobj(actor->x, actor->y, actor->z + 45 * FRACUNIT,
3159                      MT_DEMONCHUNK2);
3160     if (mo)
3161     {
3162         angle = actor->angle - ANG90;
3163         mo->momz = 8 * FRACUNIT;
3164         mo->momx = FixedMul((P_Random() << 10) + FRACUNIT,
3165                             finecosine[angle >> ANGLETOFINESHIFT]);
3166         mo->momy = FixedMul((P_Random() << 10) + FRACUNIT,
3167                             finesine[angle >> ANGLETOFINESHIFT]);
3168         mo->target = actor;
3169     }
3170     mo = P_SpawnMobj(actor->x, actor->y, actor->z + 45 * FRACUNIT,
3171                      MT_DEMONCHUNK3);
3172     if (mo)
3173     {
3174         angle = actor->angle - ANG90;
3175         mo->momz = 8 * FRACUNIT;
3176         mo->momx = FixedMul((P_Random() << 10) + FRACUNIT,
3177                             finecosine[angle >> ANGLETOFINESHIFT]);
3178         mo->momy = FixedMul((P_Random() << 10) + FRACUNIT,
3179                             finesine[angle >> ANGLETOFINESHIFT]);
3180         mo->target = actor;
3181     }
3182     mo = P_SpawnMobj(actor->x, actor->y, actor->z + 45 * FRACUNIT,
3183                      MT_DEMONCHUNK4);
3184     if (mo)
3185     {
3186         angle = actor->angle - ANG90;
3187         mo->momz = 8 * FRACUNIT;
3188         mo->momx = FixedMul((P_Random() << 10) + FRACUNIT,
3189                             finecosine[angle >> ANGLETOFINESHIFT]);
3190         mo->momy = FixedMul((P_Random() << 10) + FRACUNIT,
3191                             finesine[angle >> ANGLETOFINESHIFT]);
3192         mo->target = actor;
3193     }
3194     mo = P_SpawnMobj(actor->x, actor->y, actor->z + 45 * FRACUNIT,
3195                      MT_DEMONCHUNK5);
3196     if (mo)
3197     {
3198         angle = actor->angle - ANG90;
3199         mo->momz = 8 * FRACUNIT;
3200         mo->momx = FixedMul((P_Random() << 10) + FRACUNIT,
3201                             finecosine[angle >> ANGLETOFINESHIFT]);
3202         mo->momy = FixedMul((P_Random() << 10) + FRACUNIT,
3203                             finesine[angle >> ANGLETOFINESHIFT]);
3204         mo->target = actor;
3205     }
3206 }
3207 
3208 //===========================================================================
3209 //
3210 // A_Demon2Death
3211 //
3212 //===========================================================================
3213 
A_Demon2Death(mobj_t * actor)3214 void A_Demon2Death(mobj_t * actor)
3215 {
3216     mobj_t *mo;
3217     angle_t angle;
3218 
3219     mo = P_SpawnMobj(actor->x, actor->y, actor->z + 45 * FRACUNIT,
3220                      MT_DEMON2CHUNK1);
3221     if (mo)
3222     {
3223         angle = actor->angle + ANG90;
3224         mo->momz = 8 * FRACUNIT;
3225         mo->momx = FixedMul((P_Random() << 10) + FRACUNIT,
3226                             finecosine[angle >> ANGLETOFINESHIFT]);
3227         mo->momy = FixedMul((P_Random() << 10) + FRACUNIT,
3228                             finesine[angle >> ANGLETOFINESHIFT]);
3229         mo->target = actor;
3230     }
3231     mo = P_SpawnMobj(actor->x, actor->y, actor->z + 45 * FRACUNIT,
3232                      MT_DEMON2CHUNK2);
3233     if (mo)
3234     {
3235         angle = actor->angle - ANG90;
3236         mo->momz = 8 * FRACUNIT;
3237         mo->momx = FixedMul((P_Random() << 10) + FRACUNIT,
3238                             finecosine[angle >> ANGLETOFINESHIFT]);
3239         mo->momy = FixedMul((P_Random() << 10) + FRACUNIT,
3240                             finesine[angle >> ANGLETOFINESHIFT]);
3241         mo->target = actor;
3242     }
3243     mo = P_SpawnMobj(actor->x, actor->y, actor->z + 45 * FRACUNIT,
3244                      MT_DEMON2CHUNK3);
3245     if (mo)
3246     {
3247         angle = actor->angle - ANG90;
3248         mo->momz = 8 * FRACUNIT;
3249         mo->momx = FixedMul((P_Random() << 10) + FRACUNIT,
3250                             finecosine[angle >> ANGLETOFINESHIFT]);
3251         mo->momy = FixedMul((P_Random() << 10) + FRACUNIT,
3252                             finesine[angle >> ANGLETOFINESHIFT]);
3253         mo->target = actor;
3254     }
3255     mo = P_SpawnMobj(actor->x, actor->y, actor->z + 45 * FRACUNIT,
3256                      MT_DEMON2CHUNK4);
3257     if (mo)
3258     {
3259         angle = actor->angle - ANG90;
3260         mo->momz = 8 * FRACUNIT;
3261         mo->momx = FixedMul((P_Random() << 10) + FRACUNIT,
3262                             finecosine[angle >> ANGLETOFINESHIFT]);
3263         mo->momy = FixedMul((P_Random() << 10) + FRACUNIT,
3264                             finesine[angle >> ANGLETOFINESHIFT]);
3265         mo->target = actor;
3266     }
3267     mo = P_SpawnMobj(actor->x, actor->y, actor->z + 45 * FRACUNIT,
3268                      MT_DEMON2CHUNK5);
3269     if (mo)
3270     {
3271         angle = actor->angle - ANG90;
3272         mo->momz = 8 * FRACUNIT;
3273         mo->momx = FixedMul((P_Random() << 10) + FRACUNIT,
3274                             finecosine[angle >> ANGLETOFINESHIFT]);
3275         mo->momy = FixedMul((P_Random() << 10) + FRACUNIT,
3276                             finesine[angle >> ANGLETOFINESHIFT]);
3277         mo->target = actor;
3278     }
3279 }
3280 
3281 
3282 
3283 //
3284 // A_SinkMobj
3285 // Sink a mobj incrementally into the floor
3286 //
3287 
A_SinkMobj(mobj_t * actor)3288 boolean A_SinkMobj(mobj_t * actor)
3289 {
3290     if (actor->floorclip < actor->info->height)
3291     {
3292         switch (actor->type)
3293         {
3294             case MT_THRUSTFLOOR_DOWN:
3295             case MT_THRUSTFLOOR_UP:
3296                 actor->floorclip += 6 * FRACUNIT;
3297                 break;
3298             default:
3299                 actor->floorclip += FRACUNIT;
3300                 break;
3301         }
3302         return false;
3303     }
3304     return true;
3305 }
3306 
3307 //
3308 // A_RaiseMobj
3309 // Raise a mobj incrementally from the floor to
3310 //
3311 
A_RaiseMobj(mobj_t * actor)3312 boolean A_RaiseMobj(mobj_t * actor)
3313 {
3314     int done = true;
3315 
3316     // Raise a mobj from the ground
3317     if (actor->floorclip > 0)
3318     {
3319         switch (actor->type)
3320         {
3321             case MT_WRAITHB:
3322                 actor->floorclip -= 2 * FRACUNIT;
3323                 break;
3324             case MT_THRUSTFLOOR_DOWN:
3325             case MT_THRUSTFLOOR_UP:
3326                 actor->floorclip -= actor->special2.i * FRACUNIT;
3327                 break;
3328             default:
3329                 actor->floorclip -= 2 * FRACUNIT;
3330                 break;
3331         }
3332         if (actor->floorclip <= 0)
3333         {
3334             actor->floorclip = 0;
3335             done = true;
3336         }
3337         else
3338         {
3339             done = false;
3340         }
3341     }
3342     return done;                // Reached target height
3343 }
3344 
3345 
3346 //============================================================================
3347 // Wraith Variables
3348 //
3349 //      special1                                Internal index into floatbob
3350 //      special2
3351 //============================================================================
3352 
3353 //
3354 // A_WraithInit
3355 //
3356 
A_WraithInit(mobj_t * actor)3357 void A_WraithInit(mobj_t * actor)
3358 {
3359     actor->z += 48 << FRACBITS;
3360     actor->special1.i = 0;        // index into floatbob
3361 }
3362 
A_WraithRaiseInit(mobj_t * actor)3363 void A_WraithRaiseInit(mobj_t * actor)
3364 {
3365     actor->flags2 &= ~MF2_DONTDRAW;
3366     actor->flags2 &= ~MF2_NONSHOOTABLE;
3367     actor->flags |= MF_SHOOTABLE | MF_SOLID;
3368     actor->floorclip = actor->info->height;
3369 }
3370 
A_WraithRaise(mobj_t * actor)3371 void A_WraithRaise(mobj_t * actor)
3372 {
3373     if (A_RaiseMobj(actor))
3374     {
3375         // Reached it's target height
3376         P_SetMobjState(actor, S_WRAITH_CHASE1);
3377     }
3378 
3379     P_SpawnDirt(actor, actor->radius);
3380 }
3381 
3382 
A_WraithMelee(mobj_t * actor)3383 void A_WraithMelee(mobj_t * actor)
3384 {
3385     int amount;
3386 
3387     // Steal health from target and give to player
3388     if (P_CheckMeleeRange(actor) && (P_Random() < 220))
3389     {
3390         amount = HITDICE(2);
3391         P_DamageMobj(actor->target, actor, actor, amount);
3392         actor->health += amount;
3393     }
3394 }
3395 
A_WraithMissile(mobj_t * actor)3396 void A_WraithMissile(mobj_t * actor)
3397 {
3398     mobj_t *mo;
3399 
3400     mo = P_SpawnMissile(actor, actor->target, MT_WRAITHFX1);
3401     if (mo)
3402     {
3403         S_StartSound(actor, SFX_WRAITH_MISSILE_FIRE);
3404     }
3405 }
3406 
3407 
3408 //
3409 // A_WraithFX2 - spawns sparkle tail of missile
3410 //
3411 
A_WraithFX2(mobj_t * actor)3412 void A_WraithFX2(mobj_t * actor)
3413 {
3414     mobj_t *mo;
3415     angle_t angle;
3416     int i;
3417 
3418     for (i = 0; i < 2; i++)
3419     {
3420         mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_WRAITHFX2);
3421         if (mo)
3422         {
3423             if (P_Random() < 128)
3424             {
3425                 angle = actor->angle + (P_Random() << 22);
3426             }
3427             else
3428             {
3429                 angle = actor->angle - (P_Random() << 22);
3430             }
3431             mo->momz = 0;
3432             mo->momx = FixedMul((P_Random() << 7) + FRACUNIT,
3433                                 finecosine[angle >> ANGLETOFINESHIFT]);
3434             mo->momy = FixedMul((P_Random() << 7) + FRACUNIT,
3435                                 finesine[angle >> ANGLETOFINESHIFT]);
3436             mo->target = actor;
3437             mo->floorclip = 10 * FRACUNIT;
3438         }
3439     }
3440 }
3441 
3442 
3443 // Spawn an FX3 around the actor during attacks
A_WraithFX3(mobj_t * actor)3444 void A_WraithFX3(mobj_t * actor)
3445 {
3446     mobj_t *mo;
3447     int numdropped = P_Random() % 15;
3448     int i;
3449 
3450     for (i = 0; i < numdropped; i++)
3451     {
3452         mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_WRAITHFX3);
3453         if (mo)
3454         {
3455             mo->x += (P_Random() - 128) << 11;
3456             mo->y += (P_Random() - 128) << 11;
3457             mo->z += (P_Random() << 10);
3458             mo->target = actor;
3459         }
3460     }
3461 }
3462 
3463 // Spawn an FX4 during movement
A_WraithFX4(mobj_t * actor)3464 void A_WraithFX4(mobj_t * actor)
3465 {
3466     mobj_t *mo;
3467     int chance = P_Random();
3468     int spawn4, spawn5;
3469 
3470     if (chance < 10)
3471     {
3472         spawn4 = true;
3473         spawn5 = false;
3474     }
3475     else if (chance < 20)
3476     {
3477         spawn4 = false;
3478         spawn5 = true;
3479     }
3480     else if (chance < 25)
3481     {
3482         spawn4 = true;
3483         spawn5 = true;
3484     }
3485     else
3486     {
3487         spawn4 = false;
3488         spawn5 = false;
3489     }
3490 
3491     if (spawn4)
3492     {
3493         mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_WRAITHFX4);
3494         if (mo)
3495         {
3496             mo->x += (P_Random() - 128) << 12;
3497             mo->y += (P_Random() - 128) << 12;
3498             mo->z += (P_Random() << 10);
3499             mo->target = actor;
3500         }
3501     }
3502     if (spawn5)
3503     {
3504         mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_WRAITHFX5);
3505         if (mo)
3506         {
3507             mo->x += (P_Random() - 128) << 11;
3508             mo->y += (P_Random() - 128) << 11;
3509             mo->z += (P_Random() << 10);
3510             mo->target = actor;
3511         }
3512     }
3513 }
3514 
3515 
A_WraithLook(mobj_t * actor)3516 void A_WraithLook(mobj_t * actor)
3517 {
3518 //      A_WraithFX4(actor);             // too expensive
3519     A_Look(actor);
3520 }
3521 
3522 
A_WraithChase(mobj_t * actor)3523 void A_WraithChase(mobj_t * actor)
3524 {
3525     int weaveindex = actor->special1.i;
3526     actor->z += FloatBobOffsets[weaveindex];
3527     actor->special1.i = (weaveindex + 2) & 63;
3528 //      if (actor->floorclip > 0)
3529 //      {
3530 //              P_SetMobjState(actor, S_WRAITH_RAISE2);
3531 //              return;
3532 //      }
3533     A_Chase(actor);
3534     A_WraithFX4(actor);
3535 }
3536 
3537 
3538 
3539 //============================================================================
3540 // Ettin AI
3541 //============================================================================
3542 
A_EttinAttack(mobj_t * actor)3543 void A_EttinAttack(mobj_t * actor)
3544 {
3545     if (P_CheckMeleeRange(actor))
3546     {
3547         P_DamageMobj(actor->target, actor, actor, HITDICE(2));
3548     }
3549 }
3550 
3551 
A_DropMace(mobj_t * actor)3552 void A_DropMace(mobj_t * actor)
3553 {
3554     mobj_t *mo;
3555 
3556     mo = P_SpawnMobj(actor->x, actor->y,
3557                      actor->z + (actor->height >> 1), MT_ETTIN_MACE);
3558     if (mo)
3559     {
3560         mo->momx = (P_Random() - 128) << 11;
3561         mo->momy = (P_Random() - 128) << 11;
3562         mo->momz = FRACUNIT * 10 + (P_Random() << 10);
3563         mo->target = actor;
3564     }
3565 }
3566 
3567 
3568 //============================================================================
3569 // Fire Demon AI
3570 //
3571 // special1                     index into floatbob
3572 // special2                     whether strafing or not
3573 //============================================================================
3574 
A_FiredSpawnRock(mobj_t * actor)3575 void A_FiredSpawnRock(mobj_t * actor)
3576 {
3577     mobj_t *mo;
3578     int x, y, z;
3579     int rtype = 0;
3580 
3581     switch (P_Random() % 5)
3582     {
3583         case 0:
3584             rtype = MT_FIREDEMON_FX1;
3585             break;
3586         case 1:
3587             rtype = MT_FIREDEMON_FX2;
3588             break;
3589         case 2:
3590             rtype = MT_FIREDEMON_FX3;
3591             break;
3592         case 3:
3593             rtype = MT_FIREDEMON_FX4;
3594             break;
3595         case 4:
3596             rtype = MT_FIREDEMON_FX5;
3597             break;
3598     }
3599 
3600     x = actor->x + ((P_Random() - 128) << 12);
3601     y = actor->y + ((P_Random() - 128) << 12);
3602     z = actor->z + ((P_Random()) << 11);
3603     mo = P_SpawnMobj(x, y, z, rtype);
3604     if (mo)
3605     {
3606         mo->target = actor;
3607         mo->momx = (P_Random() - 128) << 10;
3608         mo->momy = (P_Random() - 128) << 10;
3609         mo->momz = (P_Random() << 10);
3610         mo->special1.i = 2;       // Number bounces
3611     }
3612 
3613     // Initialize fire demon
3614     actor->special2.i = 0;
3615     actor->flags &= ~MF_JUSTATTACKED;
3616 }
3617 
A_FiredRocks(mobj_t * actor)3618 void A_FiredRocks(mobj_t * actor)
3619 {
3620     A_FiredSpawnRock(actor);
3621     A_FiredSpawnRock(actor);
3622     A_FiredSpawnRock(actor);
3623     A_FiredSpawnRock(actor);
3624     A_FiredSpawnRock(actor);
3625 }
3626 
A_FiredAttack(mobj_t * actor)3627 void A_FiredAttack(mobj_t * actor)
3628 {
3629     mobj_t *mo;
3630     mo = P_SpawnMissile(actor, actor->target, MT_FIREDEMON_FX6);
3631     if (mo)
3632         S_StartSound(actor, SFX_FIRED_ATTACK);
3633 }
3634 
A_SmBounce(mobj_t * actor)3635 void A_SmBounce(mobj_t * actor)
3636 {
3637     // give some more momentum (x,y,&z)
3638     actor->z = actor->floorz + FRACUNIT;
3639     actor->momz = (2 * FRACUNIT) + (P_Random() << 10);
3640     actor->momx = P_Random() % 3 << FRACBITS;
3641     actor->momy = P_Random() % 3 << FRACBITS;
3642 }
3643 
3644 
3645 #define FIREDEMON_ATTACK_RANGE	64*8*FRACUNIT
3646 
A_FiredChase(mobj_t * actor)3647 void A_FiredChase(mobj_t * actor)
3648 {
3649     int weaveindex = actor->special1.i;
3650     mobj_t *target = actor->target;
3651     angle_t ang;
3652     fixed_t dist;
3653 
3654     if (actor->reactiontime)
3655         actor->reactiontime--;
3656     if (actor->threshold)
3657         actor->threshold--;
3658 
3659     // Float up and down
3660     actor->z += FloatBobOffsets[weaveindex];
3661     actor->special1.i = (weaveindex + 2) & 63;
3662 
3663     // Insure it stays above certain height
3664     if (actor->z < actor->floorz + (64 * FRACUNIT))
3665     {
3666         actor->z += 2 * FRACUNIT;
3667     }
3668 
3669     if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
3670     {                           // Invalid target
3671         P_LookForPlayers(actor, true);
3672         return;
3673     }
3674 
3675     // Strafe
3676     if (actor->special2.i > 0)
3677     {
3678         actor->special2.i--;
3679     }
3680     else
3681     {
3682         actor->special2.i = 0;
3683         actor->momx = actor->momy = 0;
3684         dist = P_AproxDistance(actor->x - target->x, actor->y - target->y);
3685         if (dist < FIREDEMON_ATTACK_RANGE)
3686         {
3687             if (P_Random() < 30)
3688             {
3689                 ang =
3690                     R_PointToAngle2(actor->x, actor->y, target->x, target->y);
3691                 if (P_Random() < 128)
3692                     ang += ANG90;
3693                 else
3694                     ang -= ANG90;
3695                 ang >>= ANGLETOFINESHIFT;
3696                 actor->momx = FixedMul(8 * FRACUNIT, finecosine[ang]);
3697                 actor->momy = FixedMul(8 * FRACUNIT, finesine[ang]);
3698                 actor->special2.i = 3;    // strafe time
3699             }
3700         }
3701     }
3702 
3703     FaceMovementDirection(actor);
3704 
3705     // Normal movement
3706     if (!actor->special2.i)
3707     {
3708         if (--actor->movecount < 0 || !P_Move(actor))
3709         {
3710             P_NewChaseDir(actor);
3711         }
3712     }
3713 
3714     // Do missile attack
3715     if (!(actor->flags & MF_JUSTATTACKED))
3716     {
3717         if (P_CheckMissileRange(actor) && (P_Random() < 20))
3718         {
3719             P_SetMobjState(actor, actor->info->missilestate);
3720             actor->flags |= MF_JUSTATTACKED;
3721             return;
3722         }
3723     }
3724     else
3725     {
3726         actor->flags &= ~MF_JUSTATTACKED;
3727     }
3728 
3729     // make active sound
3730     if (actor->info->activesound && P_Random() < 3)
3731     {
3732         S_StartSound(actor, actor->info->activesound);
3733     }
3734 }
3735 
A_FiredSplotch(mobj_t * actor)3736 void A_FiredSplotch(mobj_t * actor)
3737 {
3738     mobj_t *mo;
3739 
3740     mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_FIREDEMON_SPLOTCH1);
3741     if (mo)
3742     {
3743         mo->momx = (P_Random() - 128) << 11;
3744         mo->momy = (P_Random() - 128) << 11;
3745         mo->momz = FRACUNIT * 3 + (P_Random() << 10);
3746     }
3747     mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_FIREDEMON_SPLOTCH2);
3748     if (mo)
3749     {
3750         mo->momx = (P_Random() - 128) << 11;
3751         mo->momy = (P_Random() - 128) << 11;
3752         mo->momz = FRACUNIT * 3 + (P_Random() << 10);
3753     }
3754 }
3755 
3756 
3757 //============================================================================
3758 //
3759 // A_IceGuyLook
3760 //
3761 //============================================================================
3762 
A_IceGuyLook(mobj_t * actor)3763 void A_IceGuyLook(mobj_t * actor)
3764 {
3765     fixed_t dist;
3766     fixed_t an;
3767 
3768     A_Look(actor);
3769     if (P_Random() < 64)
3770     {
3771         dist = ((P_Random() - 128) * actor->radius) >> 7;
3772         an = (actor->angle + ANG90) >> ANGLETOFINESHIFT;
3773 
3774         P_SpawnMobj(actor->x + FixedMul(dist, finecosine[an]),
3775                     actor->y + FixedMul(dist, finesine[an]),
3776                     actor->z + 60 * FRACUNIT,
3777                     MT_ICEGUY_WISP1 + (P_Random() & 1));
3778     }
3779 }
3780 
3781 //============================================================================
3782 //
3783 // A_IceGuyChase
3784 //
3785 //============================================================================
3786 
A_IceGuyChase(mobj_t * actor)3787 void A_IceGuyChase(mobj_t * actor)
3788 {
3789     fixed_t dist;
3790     fixed_t an;
3791     mobj_t *mo;
3792 
3793     A_Chase(actor);
3794     if (P_Random() < 128)
3795     {
3796         dist = ((P_Random() - 128) * actor->radius) >> 7;
3797         an = (actor->angle + ANG90) >> ANGLETOFINESHIFT;
3798 
3799         mo = P_SpawnMobj(actor->x + FixedMul(dist, finecosine[an]),
3800                          actor->y + FixedMul(dist, finesine[an]),
3801                          actor->z + 60 * FRACUNIT,
3802                          MT_ICEGUY_WISP1 + (P_Random() & 1));
3803         if (mo)
3804         {
3805             mo->momx = actor->momx;
3806             mo->momy = actor->momy;
3807             mo->momz = actor->momz;
3808             mo->target = actor;
3809         }
3810     }
3811 }
3812 
3813 //============================================================================
3814 //
3815 // A_IceGuyAttack
3816 //
3817 //============================================================================
3818 
A_IceGuyAttack(mobj_t * actor)3819 void A_IceGuyAttack(mobj_t * actor)
3820 {
3821     fixed_t an;
3822 
3823     if (!actor->target)
3824     {
3825         return;
3826     }
3827     an = (actor->angle + ANG90) >> ANGLETOFINESHIFT;
3828     P_SpawnMissileXYZ(actor->x + FixedMul(actor->radius >> 1,
3829                                           finecosine[an]),
3830                       actor->y + FixedMul(actor->radius >> 1, finesine[an]),
3831                       actor->z + 40 * FRACUNIT, actor, actor->target,
3832                       MT_ICEGUY_FX);
3833     an = (actor->angle - ANG90) >> ANGLETOFINESHIFT;
3834     P_SpawnMissileXYZ(actor->x + FixedMul(actor->radius >> 1,
3835                                           finecosine[an]),
3836                       actor->y + FixedMul(actor->radius >> 1, finesine[an]),
3837                       actor->z + 40 * FRACUNIT, actor, actor->target,
3838                       MT_ICEGUY_FX);
3839     S_StartSound(actor, actor->info->attacksound);
3840 }
3841 
3842 //============================================================================
3843 //
3844 // A_IceGuyMissilePuff
3845 //
3846 //============================================================================
3847 
A_IceGuyMissilePuff(mobj_t * actor)3848 void A_IceGuyMissilePuff(mobj_t * actor)
3849 {
3850     P_SpawnMobj(actor->x, actor->y, actor->z + 2 * FRACUNIT, MT_ICEFX_PUFF);
3851 }
3852 
3853 //============================================================================
3854 //
3855 // A_IceGuyDie
3856 //
3857 //============================================================================
3858 
A_IceGuyDie(mobj_t * actor)3859 void A_IceGuyDie(mobj_t * actor)
3860 {
3861     void A_FreezeDeathChunks(mobj_t * actor);
3862 
3863     actor->momx = 0;
3864     actor->momy = 0;
3865     actor->momz = 0;
3866     actor->height <<= 2;
3867     A_FreezeDeathChunks(actor);
3868 }
3869 
3870 //============================================================================
3871 //
3872 // A_IceGuyMissileExplode
3873 //
3874 //============================================================================
3875 
A_IceGuyMissileExplode(mobj_t * actor)3876 void A_IceGuyMissileExplode(mobj_t * actor)
3877 {
3878     mobj_t *mo;
3879     unsigned int i;
3880 
3881     for (i = 0; i < 8; i++)
3882     {
3883         mo = P_SpawnMissileAngle(actor, MT_ICEGUY_FX2, i * ANG45,
3884                                  -0.3 * FRACUNIT);
3885         if (mo)
3886         {
3887             mo->target = actor->target;
3888         }
3889     }
3890 }
3891 
3892 
3893 
3894 
3895 
3896 
3897 
3898 
3899 
3900 //============================================================================
3901 //
3902 //      Sorcerer stuff
3903 //
3904 // Sorcerer Variables
3905 //              special1                Angle of ball 1 (all others relative to that)
3906 //              special2                which ball to stop at in stop mode (MT_???)
3907 //              args[0]                 Denfense time
3908 //              args[1]                 Number of full rotations since stopping mode
3909 //              args[2]                 Target orbit speed for acceleration/deceleration
3910 //              args[3]                 Movement mode (see SORC_ macros)
3911 //              args[4]                 Current ball orbit speed
3912 //      Sorcerer Ball Variables
3913 //              special1                Previous angle of ball (for woosh)
3914 //              special2                Countdown of rapid fire (FX4)
3915 //              args[0]                 If set, don't play the bounce sound when bouncing
3916 //============================================================================
3917 
3918 #define SORCBALL_INITIAL_SPEED 		7
3919 #define SORCBALL_TERMINAL_SPEED		25
3920 #define SORCBALL_SPEED_ROTATIONS 	5
3921 #define SORC_DEFENSE_TIME			255
3922 #define SORC_DEFENSE_HEIGHT			45
3923 #define BOUNCE_TIME_UNIT			(35/2)
3924 #define SORCFX4_RAPIDFIRE_TIME		(6*3)   // 3 seconds
3925 #define SORCFX4_SPREAD_ANGLE		20
3926 
3927 #define SORC_DECELERATE		0
3928 #define SORC_ACCELERATE 	1
3929 #define SORC_STOPPING		2
3930 #define SORC_FIRESPELL		3
3931 #define SORC_STOPPED		4
3932 #define SORC_NORMAL			5
3933 #define SORC_FIRING_SPELL	6
3934 
3935 #define BALL1_ANGLEOFFSET	0
3936 #define BALL2_ANGLEOFFSET	(ANG_MAX/3)
3937 #define BALL3_ANGLEOFFSET	((ANG_MAX/3)*2)
3938 
3939 void A_SorcBallOrbit(mobj_t * actor);
3940 void A_SorcSpinBalls(mobj_t * actor);
3941 void A_SpeedBalls(mobj_t * actor);
3942 void A_SlowBalls(mobj_t * actor);
3943 void A_StopBalls(mobj_t * actor);
3944 void A_AccelBalls(mobj_t * actor);
3945 void A_DecelBalls(mobj_t * actor);
3946 void A_SorcBossAttack(mobj_t * actor);
3947 void A_SpawnFizzle(mobj_t * actor);
3948 void A_CastSorcererSpell(mobj_t * actor);
3949 void A_SorcUpdateBallAngle(mobj_t * actor);
3950 void A_BounceCheck(mobj_t * actor);
3951 void A_SorcFX1Seek(mobj_t * actor);
3952 void A_SorcOffense1(mobj_t * actor);
3953 void A_SorcOffense2(mobj_t * actor);
3954 
3955 
3956 // Spawn spinning balls above head - actor is sorcerer
A_SorcSpinBalls(mobj_t * actor)3957 void A_SorcSpinBalls(mobj_t * actor)
3958 {
3959     mobj_t *mo;
3960     fixed_t z;
3961 
3962     A_SlowBalls(actor);
3963     actor->args[0] = 0;         // Currently no defense
3964     actor->args[3] = SORC_NORMAL;
3965     actor->args[4] = SORCBALL_INITIAL_SPEED;    // Initial orbit speed
3966     actor->special1.i = ANG1;
3967     z = actor->z - actor->floorclip + actor->info->height;
3968 
3969     mo = P_SpawnMobj(actor->x, actor->y, z, MT_SORCBALL1);
3970     if (mo)
3971     {
3972         mo->target = actor;
3973         mo->special2.i = SORCFX4_RAPIDFIRE_TIME;
3974     }
3975     mo = P_SpawnMobj(actor->x, actor->y, z, MT_SORCBALL2);
3976     if (mo)
3977         mo->target = actor;
3978     mo = P_SpawnMobj(actor->x, actor->y, z, MT_SORCBALL3);
3979     if (mo)
3980         mo->target = actor;
3981 }
3982 
3983 
3984 //
3985 // A_SorcBallOrbit() ==========================================
3986 //
3987 
A_SorcBallOrbit(mobj_t * actor)3988 void A_SorcBallOrbit(mobj_t * actor)
3989 {
3990     int x, y;
3991     angle_t angle, baseangle;
3992     int mode = actor->target->args[3];
3993     mobj_t *parent = (mobj_t *) actor->target;
3994     int dist = parent->radius - (actor->radius << 1);
3995     angle_t prevangle = actor->special1.i;
3996 
3997     if (actor->target->health <= 0)
3998         P_SetMobjState(actor, actor->info->painstate);
3999 
4000     baseangle = (angle_t) parent->special1.i;
4001     switch (actor->type)
4002     {
4003         case MT_SORCBALL1:
4004             angle = baseangle + BALL1_ANGLEOFFSET;
4005             break;
4006         case MT_SORCBALL2:
4007             angle = baseangle + BALL2_ANGLEOFFSET;
4008             break;
4009         case MT_SORCBALL3:
4010             angle = baseangle + BALL3_ANGLEOFFSET;
4011             break;
4012         default:
4013             I_Error("corrupted sorcerer");
4014             return;
4015     }
4016     actor->angle = angle;
4017     angle >>= ANGLETOFINESHIFT;
4018 
4019     switch (mode)
4020     {
4021         case SORC_NORMAL:      // Balls rotating normally
4022             A_SorcUpdateBallAngle(actor);
4023             break;
4024         case SORC_DECELERATE:  // Balls decelerating
4025             A_DecelBalls(actor);
4026             A_SorcUpdateBallAngle(actor);
4027             break;
4028         case SORC_ACCELERATE:  // Balls accelerating
4029             A_AccelBalls(actor);
4030             A_SorcUpdateBallAngle(actor);
4031             break;
4032         case SORC_STOPPING:    // Balls stopping
4033             if ((parent->special2.i == actor->type) &&
4034                 (parent->args[1] > SORCBALL_SPEED_ROTATIONS) &&
4035                 (abs(angle - (parent->angle >> ANGLETOFINESHIFT)) <
4036                  (30 << 5)))
4037             {
4038                 // Can stop now
4039                 actor->target->args[3] = SORC_FIRESPELL;
4040                 actor->target->args[4] = 0;
4041                 // Set angle so ball angle == sorcerer angle
4042                 switch (actor->type)
4043                 {
4044                     case MT_SORCBALL1:
4045                         parent->special1.i = (int) (parent->angle -
4046                                                     BALL1_ANGLEOFFSET);
4047                         break;
4048                     case MT_SORCBALL2:
4049                         parent->special1.i = (int) (parent->angle -
4050                                                     BALL2_ANGLEOFFSET);
4051                         break;
4052                     case MT_SORCBALL3:
4053                         parent->special1.i = (int) (parent->angle -
4054                                                     BALL3_ANGLEOFFSET);
4055                         break;
4056                     default:
4057                         break;
4058                 }
4059             }
4060             else
4061             {
4062                 A_SorcUpdateBallAngle(actor);
4063             }
4064             break;
4065         case SORC_FIRESPELL:   // Casting spell
4066             if (parent->special2.i == actor->type)
4067             {
4068                 // Put sorcerer into special throw spell anim
4069                 if (parent->health > 0)
4070                     P_SetMobjStateNF(parent, S_SORC_ATTACK1);
4071 
4072                 if (actor->type == MT_SORCBALL1 && P_Random() < 200)
4073                 {
4074                     S_StartSound(NULL, SFX_SORCERER_SPELLCAST);
4075                     actor->special2.i = SORCFX4_RAPIDFIRE_TIME;
4076                     actor->args[4] = 128;
4077                     parent->args[3] = SORC_FIRING_SPELL;
4078                 }
4079                 else
4080                 {
4081                     A_CastSorcererSpell(actor);
4082                     parent->args[3] = SORC_STOPPED;
4083                 }
4084             }
4085             break;
4086         case SORC_FIRING_SPELL:
4087             if (parent->special2.i == actor->type)
4088             {
4089                 if (actor->special2.i-- <= 0)
4090                 {
4091                     // Done rapid firing
4092                     parent->args[3] = SORC_STOPPED;
4093                     // Back to orbit balls
4094                     if (parent->health > 0)
4095                         P_SetMobjStateNF(parent, S_SORC_ATTACK4);
4096                 }
4097                 else
4098                 {
4099                     // Do rapid fire spell
4100                     A_SorcOffense2(actor);
4101                 }
4102             }
4103             break;
4104         case SORC_STOPPED:     // Balls stopped
4105         default:
4106             break;
4107     }
4108 
4109     if ((angle < prevangle) && (parent->args[4] == SORCBALL_TERMINAL_SPEED))
4110     {
4111         parent->args[1]++;      // Bump rotation counter
4112         // Completed full rotation - make woosh sound
4113         S_StartSound(actor, SFX_SORCERER_BALLWOOSH);
4114     }
4115     actor->special1.i = angle;    // Set previous angle
4116     x = parent->x + FixedMul(dist, finecosine[angle]);
4117     y = parent->y + FixedMul(dist, finesine[angle]);
4118     actor->x = x;
4119     actor->y = y;
4120     actor->z = parent->z - parent->floorclip + parent->info->height;
4121 }
4122 
4123 
4124 //
4125 // Set balls to speed mode - actor is sorcerer
4126 //
A_SpeedBalls(mobj_t * actor)4127 void A_SpeedBalls(mobj_t * actor)
4128 {
4129     actor->args[3] = SORC_ACCELERATE;   // speed mode
4130     actor->args[2] = SORCBALL_TERMINAL_SPEED;   // target speed
4131 }
4132 
4133 
4134 //
4135 // Set balls to slow mode - actor is sorcerer
4136 //
A_SlowBalls(mobj_t * actor)4137 void A_SlowBalls(mobj_t * actor)
4138 {
4139     actor->args[3] = SORC_DECELERATE;   // slow mode
4140     actor->args[2] = SORCBALL_INITIAL_SPEED;    // target speed
4141 }
4142 
4143 
4144 //
4145 // Instant stop when rotation gets to ball in special2
4146 //              actor is sorcerer
4147 //
A_StopBalls(mobj_t * actor)4148 void A_StopBalls(mobj_t * actor)
4149 {
4150     int chance = P_Random();
4151     actor->args[3] = SORC_STOPPING;     // stopping mode
4152     actor->args[1] = 0;         // Reset rotation counter
4153 
4154     if ((actor->args[0] <= 0) && (chance < 200))
4155     {
4156         actor->special2.i = MT_SORCBALL2; // Blue
4157     }
4158     else if ((actor->health < (actor->info->spawnhealth >> 1)) &&
4159              (chance < 200))
4160     {
4161         actor->special2.i = MT_SORCBALL3; // Green
4162     }
4163     else
4164     {
4165         actor->special2.i = MT_SORCBALL1; // Yellow
4166     }
4167 
4168 
4169 }
4170 
4171 
4172 //
4173 // Increase ball orbit speed - actor is ball
4174 //
A_AccelBalls(mobj_t * actor)4175 void A_AccelBalls(mobj_t * actor)
4176 {
4177     mobj_t *sorc = actor->target;
4178 
4179     if (sorc->args[4] < sorc->args[2])
4180     {
4181         sorc->args[4]++;
4182     }
4183     else
4184     {
4185         sorc->args[3] = SORC_NORMAL;
4186         if (sorc->args[4] >= SORCBALL_TERMINAL_SPEED)
4187         {
4188             // Reached terminal velocity - stop balls
4189             A_StopBalls(sorc);
4190         }
4191     }
4192 }
4193 
4194 
4195 // Decrease ball orbit speed - actor is ball
A_DecelBalls(mobj_t * actor)4196 void A_DecelBalls(mobj_t * actor)
4197 {
4198     mobj_t *sorc = actor->target;
4199 
4200     if (sorc->args[4] > sorc->args[2])
4201     {
4202         sorc->args[4]--;
4203     }
4204     else
4205     {
4206         sorc->args[3] = SORC_NORMAL;
4207     }
4208 }
4209 
4210 
4211 // Update angle if first ball - actor is ball
A_SorcUpdateBallAngle(mobj_t * actor)4212 void A_SorcUpdateBallAngle(mobj_t * actor)
4213 {
4214     if (actor->type == MT_SORCBALL1)
4215     {
4216         actor->target->special1.i += ANG1 * actor->target->args[4];
4217     }
4218 }
4219 
4220 
4221 // actor is ball
A_CastSorcererSpell(mobj_t * actor)4222 void A_CastSorcererSpell(mobj_t * actor)
4223 {
4224     mobj_t *mo;
4225     int spell = actor->type;
4226     angle_t ang1, ang2;
4227     fixed_t z;
4228     mobj_t *parent = actor->target;
4229 
4230     S_StartSound(NULL, SFX_SORCERER_SPELLCAST);
4231 
4232     // Put sorcerer into throw spell animation
4233     if (parent->health > 0)
4234         P_SetMobjStateNF(parent, S_SORC_ATTACK4);
4235 
4236     switch (spell)
4237     {
4238         case MT_SORCBALL1:     // Offensive
4239             A_SorcOffense1(actor);
4240             break;
4241         case MT_SORCBALL2:     // Defensive
4242             z = parent->z - parent->floorclip +
4243                 SORC_DEFENSE_HEIGHT * FRACUNIT;
4244             mo = P_SpawnMobj(actor->x, actor->y, z, MT_SORCFX2);
4245             parent->flags2 |= MF2_REFLECTIVE | MF2_INVULNERABLE;
4246             parent->args[0] = SORC_DEFENSE_TIME;
4247             if (mo)
4248                 mo->target = parent;
4249             break;
4250         case MT_SORCBALL3:     // Reinforcements
4251             ang1 = actor->angle - ANG45;
4252             ang2 = actor->angle + ANG45;
4253             if (actor->health < (actor->info->spawnhealth / 3))
4254             {                   // Spawn 2 at a time
4255                 mo = P_SpawnMissileAngle(parent, MT_SORCFX3, ang1,
4256                                          4 * FRACUNIT);
4257                 if (mo)
4258                     mo->target = parent;
4259                 mo = P_SpawnMissileAngle(parent, MT_SORCFX3, ang2,
4260                                          4 * FRACUNIT);
4261                 if (mo)
4262                     mo->target = parent;
4263             }
4264             else
4265             {
4266                 if (P_Random() < 128)
4267                     ang1 = ang2;
4268                 mo = P_SpawnMissileAngle(parent, MT_SORCFX3, ang1,
4269                                          4 * FRACUNIT);
4270                 if (mo)
4271                     mo->target = parent;
4272             }
4273             break;
4274         default:
4275             break;
4276     }
4277 }
4278 
4279 /*
4280 void A_SpawnReinforcements(mobj_t *actor)
4281 {
4282 	mobj_t *parent = actor->target;
4283 	mobj_t *mo;
4284 	angle_t ang;
4285 
4286 	ang = ANG1 * P_Random();
4287 	mo = P_SpawnMissileAngle(actor, MT_SORCFX3, ang, 5*FRACUNIT);
4288 	if (mo) mo->target = parent;
4289 }
4290 */
4291 
4292 // actor is ball
A_SorcOffense1(mobj_t * actor)4293 void A_SorcOffense1(mobj_t * actor)
4294 {
4295     mobj_t *mo;
4296     angle_t ang1, ang2;
4297     mobj_t *parent = (mobj_t *) actor->target;
4298 
4299     ang1 = actor->angle + ANG1 * 70;
4300     ang2 = actor->angle - ANG1 * 70;
4301     mo = P_SpawnMissileAngle(parent, MT_SORCFX1, ang1, 0);
4302     if (mo)
4303     {
4304         mo->target = parent;
4305         mo->special1.m = parent->target;
4306         mo->args[4] = BOUNCE_TIME_UNIT;
4307         mo->args[3] = 15;       // Bounce time in seconds
4308     }
4309     mo = P_SpawnMissileAngle(parent, MT_SORCFX1, ang2, 0);
4310     if (mo)
4311     {
4312         mo->target = parent;
4313         mo->special1.m = parent->target;
4314         mo->args[4] = BOUNCE_TIME_UNIT;
4315         mo->args[3] = 15;       // Bounce time in seconds
4316     }
4317 }
4318 
4319 
4320 // Actor is ball
A_SorcOffense2(mobj_t * actor)4321 void A_SorcOffense2(mobj_t * actor)
4322 {
4323     angle_t ang1;
4324     mobj_t *mo;
4325     int delta, index;
4326     mobj_t *parent = actor->target;
4327     mobj_t *dest = parent->target;
4328     int dist;
4329 
4330     index = actor->args[4] << 5;
4331     actor->args[4] += 15;
4332     delta = (finesine[index]) * SORCFX4_SPREAD_ANGLE;
4333     delta = (delta >> FRACBITS) * ANG1;
4334     ang1 = actor->angle + delta;
4335     mo = P_SpawnMissileAngle(parent, MT_SORCFX4, ang1, 0);
4336     if (mo)
4337     {
4338         mo->special2.i = 35 * 5 / 2;      // 5 seconds
4339         dist = P_AproxDistance(dest->x - mo->x, dest->y - mo->y);
4340         dist = dist / mo->info->speed;
4341         if (dist < 1)
4342             dist = 1;
4343         mo->momz = (dest->z - mo->z) / dist;
4344     }
4345 }
4346 
4347 
4348 // Resume ball spinning
A_SorcBossAttack(mobj_t * actor)4349 void A_SorcBossAttack(mobj_t * actor)
4350 {
4351     actor->args[3] = SORC_ACCELERATE;
4352     actor->args[2] = SORCBALL_INITIAL_SPEED;
4353 }
4354 
4355 
4356 // spell cast magic fizzle
A_SpawnFizzle(mobj_t * actor)4357 void A_SpawnFizzle(mobj_t * actor)
4358 {
4359     fixed_t x, y, z;
4360     fixed_t dist = 5 * FRACUNIT;
4361     angle_t angle = actor->angle >> ANGLETOFINESHIFT;
4362     fixed_t speed = actor->info->speed;
4363     angle_t rangle;
4364     mobj_t *mo;
4365     int ix;
4366 
4367     x = actor->x + FixedMul(dist, finecosine[angle]);
4368     y = actor->y + FixedMul(dist, finesine[angle]);
4369     z = actor->z - actor->floorclip + (actor->height >> 1);
4370     for (ix = 0; ix < 5; ix++)
4371     {
4372         mo = P_SpawnMobj(x, y, z, MT_SORCSPARK1);
4373         if (mo)
4374         {
4375             rangle = angle + ((P_Random() % 5) << 1);
4376             mo->momx = FixedMul(P_Random() % speed, finecosine[rangle]);
4377             mo->momy = FixedMul(P_Random() % speed, finesine[rangle]);
4378             mo->momz = FRACUNIT * 2;
4379         }
4380     }
4381 }
4382 
4383 
4384 //============================================================================
4385 // Yellow spell - offense
4386 //============================================================================
4387 
A_SorcFX1Seek(mobj_t * actor)4388 void A_SorcFX1Seek(mobj_t * actor)
4389 {
4390     A_BounceCheck(actor);
4391     P_SeekerMissile(actor, ANG1 * 2, ANG1 * 6);
4392 }
4393 
4394 
4395 //============================================================================
4396 // Blue spell - defense
4397 //============================================================================
4398 //
4399 // FX2 Variables
4400 //              special1                current angle
4401 //              special2
4402 //              args[0]         0 = CW,  1 = CCW
4403 //              args[1]
4404 //============================================================================
4405 
4406 // Split ball in two
A_SorcFX2Split(mobj_t * actor)4407 void A_SorcFX2Split(mobj_t * actor)
4408 {
4409     mobj_t *mo;
4410 
4411     mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SORCFX2);
4412     if (mo)
4413     {
4414         mo->target = actor->target;
4415         mo->args[0] = 0;        // CW
4416         mo->special1.i = actor->angle;    // Set angle
4417         P_SetMobjStateNF(mo, S_SORCFX2_ORBIT1);
4418     }
4419     mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SORCFX2);
4420     if (mo)
4421     {
4422         mo->target = actor->target;
4423         mo->args[0] = 1;        // CCW
4424         mo->special1.i = actor->angle;    // Set angle
4425         P_SetMobjStateNF(mo, S_SORCFX2_ORBIT1);
4426     }
4427     P_SetMobjStateNF(actor, S_NULL);
4428 }
4429 
4430 
4431 // Orbit FX2 about sorcerer
A_SorcFX2Orbit(mobj_t * actor)4432 void A_SorcFX2Orbit(mobj_t * actor)
4433 {
4434     angle_t angle;
4435     fixed_t x, y, z;
4436     mobj_t *parent = actor->target;
4437     fixed_t dist = parent->info->radius;
4438 
4439     if ((parent->health <= 0) ||        // Sorcerer is dead
4440         (!parent->args[0]))     // Time expired
4441     {
4442         P_SetMobjStateNF(actor, actor->info->deathstate);
4443         parent->args[0] = 0;
4444         parent->flags2 &= ~MF2_REFLECTIVE;
4445         parent->flags2 &= ~MF2_INVULNERABLE;
4446     }
4447 
4448     if (actor->args[0] && (parent->args[0]-- <= 0))     // Time expired
4449     {
4450         P_SetMobjStateNF(actor, actor->info->deathstate);
4451         parent->args[0] = 0;
4452         parent->flags2 &= ~MF2_REFLECTIVE;
4453     }
4454 
4455     // Move to new position based on angle
4456     if (actor->args[0])         // Counter clock-wise
4457     {
4458         actor->special1.i += ANG1 * 10;
4459         angle = ((angle_t) actor->special1.i) >> ANGLETOFINESHIFT;
4460         x = parent->x + FixedMul(dist, finecosine[angle]);
4461         y = parent->y + FixedMul(dist, finesine[angle]);
4462         z = parent->z - parent->floorclip + SORC_DEFENSE_HEIGHT * FRACUNIT;
4463         z += FixedMul(15 * FRACUNIT, finecosine[angle]);
4464         // Spawn trailer
4465         P_SpawnMobj(x, y, z, MT_SORCFX2_T1);
4466     }
4467     else                        // Clock wise
4468     {
4469         actor->special1.i -= ANG1 * 10;
4470         angle = ((angle_t) actor->special1.i) >> ANGLETOFINESHIFT;
4471         x = parent->x + FixedMul(dist, finecosine[angle]);
4472         y = parent->y + FixedMul(dist, finesine[angle]);
4473         z = parent->z - parent->floorclip + SORC_DEFENSE_HEIGHT * FRACUNIT;
4474         z += FixedMul(20 * FRACUNIT, finesine[angle]);
4475         // Spawn trailer
4476         P_SpawnMobj(x, y, z, MT_SORCFX2_T1);
4477     }
4478 
4479     actor->x = x;
4480     actor->y = y;
4481     actor->z = z;
4482 }
4483 
4484 
4485 
4486 //============================================================================
4487 // Green spell - spawn bishops
4488 //============================================================================
4489 
A_SpawnBishop(mobj_t * actor)4490 void A_SpawnBishop(mobj_t * actor)
4491 {
4492     mobj_t *mo;
4493     mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_BISHOP);
4494     if (mo)
4495     {
4496         if (!P_TestMobjLocation(mo))
4497         {
4498             P_SetMobjState(mo, S_NULL);
4499         }
4500     }
4501     P_SetMobjState(actor, S_NULL);
4502 }
4503 
4504 /*
4505 void A_SmokePuffEntry(mobj_t *actor)
4506 {
4507 	P_SpawnMobj(actor->x, actor->y, actor->z, MT_MNTRSMOKE);
4508 }
4509 */
4510 
A_SmokePuffExit(mobj_t * actor)4511 void A_SmokePuffExit(mobj_t * actor)
4512 {
4513     P_SpawnMobj(actor->x, actor->y, actor->z, MT_MNTRSMOKEEXIT);
4514 }
4515 
A_SorcererBishopEntry(mobj_t * actor)4516 void A_SorcererBishopEntry(mobj_t * actor)
4517 {
4518     P_SpawnMobj(actor->x, actor->y, actor->z, MT_SORCFX3_EXPLOSION);
4519     S_StartSound(actor, actor->info->seesound);
4520 }
4521 
4522 
4523 //============================================================================
4524 // FX4 - rapid fire balls
4525 //============================================================================
4526 
A_SorcFX4Check(mobj_t * actor)4527 void A_SorcFX4Check(mobj_t * actor)
4528 {
4529     if (actor->special2.i-- <= 0)
4530     {
4531         P_SetMobjStateNF(actor, actor->info->deathstate);
4532     }
4533 }
4534 
4535 //============================================================================
4536 // Ball death - spawn stuff
4537 //============================================================================
4538 
A_SorcBallPop(mobj_t * actor)4539 void A_SorcBallPop(mobj_t * actor)
4540 {
4541     S_StartSound(NULL, SFX_SORCERER_BALLPOP);
4542     actor->flags &= ~MF_NOGRAVITY;
4543     actor->flags2 |= MF2_LOGRAV;
4544     actor->momx = ((P_Random() % 10) - 5) << FRACBITS;
4545     actor->momy = ((P_Random() % 10) - 5) << FRACBITS;
4546     actor->momz = (2 + (P_Random() % 3)) << FRACBITS;
4547     actor->special2.i = 4 * FRACUNIT;     // Initial bounce factor
4548     actor->args[4] = BOUNCE_TIME_UNIT;  // Bounce time unit
4549     actor->args[3] = 5;         // Bounce time in seconds
4550 }
4551 
4552 
4553 
A_BounceCheck(mobj_t * actor)4554 void A_BounceCheck(mobj_t * actor)
4555 {
4556     if (actor->args[4]-- <= 0)
4557     {
4558         if (actor->args[3]-- <= 0)
4559         {
4560             P_SetMobjState(actor, actor->info->deathstate);
4561             switch (actor->type)
4562             {
4563                 case MT_SORCBALL1:
4564                 case MT_SORCBALL2:
4565                 case MT_SORCBALL3:
4566                     S_StartSound(NULL, SFX_SORCERER_BIGBALLEXPLODE);
4567                     break;
4568                 case MT_SORCFX1:
4569                     S_StartSound(NULL, SFX_SORCERER_HEADSCREAM);
4570                     break;
4571                 default:
4572                     break;
4573             }
4574         }
4575         else
4576         {
4577             actor->args[4] = BOUNCE_TIME_UNIT;
4578         }
4579     }
4580 }
4581 
4582 
4583 
4584 
4585 //============================================================================
4586 // Class Bosses
4587 //============================================================================
4588 #define CLASS_BOSS_STRAFE_RANGE	64*10*FRACUNIT
4589 
A_FastChase(mobj_t * actor)4590 void A_FastChase(mobj_t * actor)
4591 {
4592     int delta;
4593     fixed_t dist;
4594     angle_t ang;
4595     mobj_t *target;
4596 
4597     if (actor->reactiontime)
4598     {
4599         actor->reactiontime--;
4600     }
4601 
4602     // Modify target threshold
4603     if (actor->threshold)
4604     {
4605         actor->threshold--;
4606     }
4607 
4608     if (gameskill == sk_nightmare)
4609     {                           // Monsters move faster in nightmare mode
4610         actor->tics -= actor->tics / 2;
4611         if (actor->tics < 3)
4612         {
4613             actor->tics = 3;
4614         }
4615     }
4616 
4617 //
4618 // turn towards movement direction if not there yet
4619 //
4620     if (actor->movedir < 8)
4621     {
4622         actor->angle &= (7 << 29);
4623         delta = actor->angle - (actor->movedir << 29);
4624         if (delta > 0)
4625         {
4626             actor->angle -= ANG90 / 2;
4627         }
4628         else if (delta < 0)
4629         {
4630             actor->angle += ANG90 / 2;
4631         }
4632     }
4633 
4634     if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
4635     {                           // look for a new target
4636         if (P_LookForPlayers(actor, true))
4637         {                       // got a new target
4638             return;
4639         }
4640         P_SetMobjState(actor, actor->info->spawnstate);
4641         return;
4642     }
4643 
4644 //
4645 // don't attack twice in a row
4646 //
4647     if (actor->flags & MF_JUSTATTACKED)
4648     {
4649         actor->flags &= ~MF_JUSTATTACKED;
4650         if (gameskill != sk_nightmare)
4651             P_NewChaseDir(actor);
4652         return;
4653     }
4654 
4655     // Strafe
4656     if (actor->special2.i > 0)
4657     {
4658         actor->special2.i--;
4659     }
4660     else
4661     {
4662         target = actor->target;
4663         actor->special2.i = 0;
4664         actor->momx = actor->momy = 0;
4665         dist = P_AproxDistance(actor->x - target->x, actor->y - target->y);
4666         if (dist < CLASS_BOSS_STRAFE_RANGE)
4667         {
4668             if (P_Random() < 100)
4669             {
4670                 ang = R_PointToAngle2(actor->x, actor->y,
4671                                       target->x, target->y);
4672                 if (P_Random() < 128)
4673                     ang += ANG90;
4674                 else
4675                     ang -= ANG90;
4676                 ang >>= ANGLETOFINESHIFT;
4677                 actor->momx = FixedMul(13 * FRACUNIT, finecosine[ang]);
4678                 actor->momy = FixedMul(13 * FRACUNIT, finesine[ang]);
4679                 actor->special2.i = 3;    // strafe time
4680             }
4681         }
4682     }
4683 
4684 //
4685 // check for missile attack
4686 //
4687     if (actor->info->missilestate)
4688     {
4689         if (gameskill < sk_nightmare && actor->movecount)
4690             goto nomissile;
4691         if (!P_CheckMissileRange(actor))
4692             goto nomissile;
4693         P_SetMobjState(actor, actor->info->missilestate);
4694         actor->flags |= MF_JUSTATTACKED;
4695         return;
4696     }
4697   nomissile:
4698 
4699 //
4700 // possibly choose another target
4701 //
4702     if (netgame && !actor->threshold && !P_CheckSight(actor, actor->target))
4703     {
4704         if (P_LookForPlayers(actor, true))
4705             return;             // got a new target
4706     }
4707 
4708 //
4709 // chase towards player
4710 //
4711     if (!actor->special2.i)
4712     {
4713         if (--actor->movecount < 0 || !P_Move(actor))
4714         {
4715             P_NewChaseDir(actor);
4716         }
4717     }
4718 }
4719 
4720 
A_FighterAttack(mobj_t * actor)4721 void A_FighterAttack(mobj_t * actor)
4722 {
4723     extern void A_FSwordAttack2(mobj_t * actor);
4724 
4725     if (!actor->target)
4726         return;
4727     A_FSwordAttack2(actor);
4728 }
4729 
4730 
A_ClericAttack(mobj_t * actor)4731 void A_ClericAttack(mobj_t * actor)
4732 {
4733     extern void A_CHolyAttack3(mobj_t * actor);
4734 
4735     if (!actor->target)
4736         return;
4737     A_CHolyAttack3(actor);
4738 }
4739 
4740 
4741 
A_MageAttack(mobj_t * actor)4742 void A_MageAttack(mobj_t * actor)
4743 {
4744     extern void A_MStaffAttack2(mobj_t * actor);
4745 
4746     if (!actor->target)
4747         return;
4748     A_MStaffAttack2(actor);
4749 }
4750 
A_ClassBossHealth(mobj_t * actor)4751 void A_ClassBossHealth(mobj_t * actor)
4752 {
4753     if (netgame && !deathmatch) // co-op only
4754     {
4755         if (!actor->special1.i)
4756         {
4757             actor->health *= 5;
4758             actor->special1.i = true;     // has been initialized
4759         }
4760     }
4761 }
4762 
4763 
4764 //===========================================================================
4765 //
4766 // A_CheckFloor - Checks if an object hit the floor
4767 //
4768 //===========================================================================
4769 
A_CheckFloor(mobj_t * actor)4770 void A_CheckFloor(mobj_t * actor)
4771 {
4772     if (actor->z <= actor->floorz)
4773     {
4774         actor->z = actor->floorz;
4775         actor->flags2 &= ~MF2_LOGRAV;
4776         P_SetMobjState(actor, actor->info->deathstate);
4777     }
4778 }
4779 
4780 //============================================================================
4781 //
4782 // A_FreezeDeath
4783 //
4784 //============================================================================
4785 
A_FreezeDeath(mobj_t * actor)4786 void A_FreezeDeath(mobj_t * actor)
4787 {
4788     int r = P_Random();
4789     actor->tics = 75 + r + P_Random();
4790     actor->flags |= MF_SOLID | MF_SHOOTABLE | MF_NOBLOOD;
4791     actor->flags2 |= MF2_PUSHABLE | MF2_TELESTOMP | MF2_PASSMOBJ | MF2_SLIDE;
4792     actor->height <<= 2;
4793     S_StartSound(actor, SFX_FREEZE_DEATH);
4794 
4795     if (actor->player)
4796     {
4797         actor->player->damagecount = 0;
4798         actor->player->poisoncount = 0;
4799         actor->player->bonuscount = 0;
4800         if (actor->player == &players[consoleplayer])
4801         {
4802             SB_PaletteFlash(false);
4803         }
4804     }
4805     else if (actor->flags & MF_COUNTKILL && actor->special)
4806     {
4807         // Initiate monster death actions.
4808         P_ExecuteLineSpecial(actor->special, actor->args, NULL, 0, actor);
4809     }
4810 }
4811 
4812 //============================================================================
4813 //
4814 // A_IceSetTics
4815 //
4816 //============================================================================
4817 
A_IceSetTics(mobj_t * actor)4818 void A_IceSetTics(mobj_t * actor)
4819 {
4820     int floor;
4821 
4822     actor->tics = 70 + (P_Random() & 63);
4823     floor = P_GetThingFloorType(actor);
4824     if (floor == FLOOR_LAVA)
4825     {
4826         actor->tics >>= 2;
4827     }
4828     else if (floor == FLOOR_ICE)
4829     {
4830         actor->tics <<= 1;
4831     }
4832 }
4833 
4834 //============================================================================
4835 //
4836 // A_IceCheckHeadDone
4837 //
4838 //============================================================================
4839 
A_IceCheckHeadDone(mobj_t * actor)4840 void A_IceCheckHeadDone(mobj_t * actor)
4841 {
4842     if (actor->special2.i == 666)
4843     {
4844         P_SetMobjState(actor, S_ICECHUNK_HEAD2);
4845     }
4846 }
4847 
4848 //============================================================================
4849 //
4850 // A_FreezeDeathChunks
4851 //
4852 //============================================================================
4853 
A_FreezeDeathChunks(mobj_t * actor)4854 void A_FreezeDeathChunks(mobj_t * actor)
4855 {
4856     int i;
4857     int r1,r2,r3;
4858     mobj_t *mo;
4859 
4860     if (actor->momx || actor->momy || actor->momz)
4861     {
4862         actor->tics = 105;
4863         return;
4864     }
4865     S_StartSound(actor, SFX_FREEZE_SHATTER);
4866 
4867     for (i = 12 + (P_Random() & 15); i >= 0; i--)
4868     {
4869         r1 = P_Random();
4870         r2 = P_Random();
4871         r3 = P_Random();
4872         mo = P_SpawnMobj(actor->x +
4873                          (((r3 - 128) * actor->radius) >> 7),
4874                          actor->y +
4875                          (((r2 - 128) * actor->radius) >> 7),
4876                          actor->z + (r1 * actor->height / 255),
4877                          MT_ICECHUNK);
4878         P_SetMobjState(mo, mo->info->spawnstate + (P_Random() % 3));
4879         if (mo)
4880         {
4881             mo->momz = FixedDiv(mo->z - actor->z, actor->height) << 2;
4882             mo->momx = P_SubRandom() << (FRACBITS - 7);
4883             mo->momy = P_SubRandom() << (FRACBITS - 7);
4884             A_IceSetTics(mo);   // set a random tic wait
4885         }
4886     }
4887     for (i = 12 + (P_Random() & 15); i >= 0; i--)
4888     {
4889         r1 = P_Random();
4890         r2 = P_Random();
4891         r3 = P_Random();
4892         mo = P_SpawnMobj(actor->x +
4893                          (((r3 - 128) * actor->radius) >> 7),
4894                          actor->y +
4895                          (((r2 - 128) * actor->radius) >> 7),
4896                          actor->z + (r1 * actor->height / 255),
4897                          MT_ICECHUNK);
4898         P_SetMobjState(mo, mo->info->spawnstate + (P_Random() % 3));
4899         if (mo)
4900         {
4901             mo->momz = FixedDiv(mo->z - actor->z, actor->height) << 2;
4902             mo->momx = P_SubRandom() << (FRACBITS - 7);
4903             mo->momy = P_SubRandom() << (FRACBITS - 7);
4904             A_IceSetTics(mo);   // set a random tic wait
4905         }
4906     }
4907     if (actor->player)
4908     {                           // attach the player's view to a chunk of ice
4909         mo = P_SpawnMobj(actor->x, actor->y, actor->z + VIEWHEIGHT,
4910                          MT_ICECHUNK);
4911         P_SetMobjState(mo, S_ICECHUNK_HEAD);
4912         mo->momz = FixedDiv(mo->z - actor->z, actor->height) << 2;
4913         mo->momx = P_SubRandom() << (FRACBITS - 7);
4914         mo->momy = P_SubRandom() << (FRACBITS - 7);
4915         mo->flags2 |= MF2_ICEDAMAGE;    // used to force blue palette
4916         mo->flags2 &= ~MF2_FLOORCLIP;
4917         mo->player = actor->player;
4918         actor->player = NULL;
4919         mo->health = actor->health;
4920         mo->angle = actor->angle;
4921         mo->player->mo = mo;
4922         mo->player->lookdir = 0;
4923     }
4924     P_RemoveMobjFromTIDList(actor);
4925     P_SetMobjState(actor, S_FREETARGMOBJ);
4926     actor->flags2 |= MF2_DONTDRAW;
4927 }
4928 
4929 //===========================================================================
4930 // Korax Variables
4931 //      special1        last teleport destination
4932 //      special2        set if "below half" script not yet run
4933 //
4934 // Korax Scripts (reserved)
4935 //      249             Tell scripts that we are below half health
4936 //      250-254 Control scripts
4937 //      255             Death script
4938 //
4939 // Korax TIDs (reserved)
4940 //      245             Reserved for Korax himself
4941 //  248         Initial teleport destination
4942 //      249             Teleport destination
4943 //      250-254 For use in respective control scripts
4944 //      255             For use in death script (spawn spots)
4945 //===========================================================================
4946 #define KORAX_SPIRIT_LIFETIME	(5*(35/5))      // 5 seconds
4947 #define KORAX_COMMAND_HEIGHT	(120*FRACUNIT)
4948 #define KORAX_COMMAND_OFFSET	(27*FRACUNIT)
4949 
4950 void KoraxFire1(mobj_t * actor, int type);
4951 void KoraxFire2(mobj_t * actor, int type);
4952 void KoraxFire3(mobj_t * actor, int type);
4953 void KoraxFire4(mobj_t * actor, int type);
4954 void KoraxFire5(mobj_t * actor, int type);
4955 void KoraxFire6(mobj_t * actor, int type);
4956 void KSpiritInit(mobj_t * spirit, mobj_t * korax);
4957 
4958 #define KORAX_TID					(245)
4959 #define KORAX_FIRST_TELEPORT_TID	(248)
4960 #define KORAX_TELEPORT_TID			(249)
4961 
A_KoraxChase(mobj_t * actor)4962 void A_KoraxChase(mobj_t * actor)
4963 {
4964     mobj_t *spot;
4965     int lastfound;
4966     byte args[3] = {0, 0, 0};
4967 
4968     if ((!actor->special2.i) &&
4969         (actor->health <= (actor->info->spawnhealth / 2)))
4970     {
4971         lastfound = 0;
4972         spot = P_FindMobjFromTID(KORAX_FIRST_TELEPORT_TID, &lastfound);
4973         if (spot)
4974         {
4975             P_Teleport(actor, spot->x, spot->y, spot->angle, true);
4976         }
4977 
4978         CheckACSPresent(249);
4979         P_StartACS(249, 0, args, actor, NULL, 0);
4980         actor->special2.i = 1;    // Don't run again
4981 
4982         return;
4983     }
4984 
4985     if (!actor->target)
4986         return;
4987     if (P_Random() < 30)
4988     {
4989         P_SetMobjState(actor, actor->info->missilestate);
4990     }
4991     else if (P_Random() < 30)
4992     {
4993         S_StartSound(NULL, SFX_KORAX_ACTIVE);
4994     }
4995 
4996     // Teleport away
4997     if (actor->health < (actor->info->spawnhealth >> 1))
4998     {
4999         if (P_Random() < 10)
5000         {
5001             lastfound = actor->special1.i;
5002             spot = P_FindMobjFromTID(KORAX_TELEPORT_TID, &lastfound);
5003             actor->special1.i = lastfound;
5004             if (spot)
5005             {
5006                 P_Teleport(actor, spot->x, spot->y, spot->angle, true);
5007             }
5008         }
5009     }
5010 }
5011 
A_KoraxStep(mobj_t * actor)5012 void A_KoraxStep(mobj_t * actor)
5013 {
5014     A_Chase(actor);
5015 }
5016 
A_KoraxStep2(mobj_t * actor)5017 void A_KoraxStep2(mobj_t * actor)
5018 {
5019     S_StartSound(NULL, SFX_KORAX_STEP);
5020     A_Chase(actor);
5021 }
5022 
A_KoraxBonePop(mobj_t * actor)5023 void A_KoraxBonePop(mobj_t * actor)
5024 {
5025     mobj_t *mo;
5026     byte args[5];
5027 
5028     args[0] = args[1] = args[2] = args[3] = args[4] = 0;
5029 
5030     // Spawn 6 spirits equalangularly
5031     mo = P_SpawnMissileAngle(actor, MT_KORAX_SPIRIT1, ANG60 * 0,
5032                              5 * FRACUNIT);
5033     if (mo)
5034         KSpiritInit(mo, actor);
5035     mo = P_SpawnMissileAngle(actor, MT_KORAX_SPIRIT2, ANG60 * 1,
5036                              5 * FRACUNIT);
5037     if (mo)
5038         KSpiritInit(mo, actor);
5039     mo = P_SpawnMissileAngle(actor, MT_KORAX_SPIRIT3, ANG60 * 2,
5040                              5 * FRACUNIT);
5041     if (mo)
5042         KSpiritInit(mo, actor);
5043     mo = P_SpawnMissileAngle(actor, MT_KORAX_SPIRIT4, ANG60 * 3,
5044                              5 * FRACUNIT);
5045     if (mo)
5046         KSpiritInit(mo, actor);
5047     mo = P_SpawnMissileAngle(actor, MT_KORAX_SPIRIT5, ANG60 * 4,
5048                              5 * FRACUNIT);
5049     if (mo)
5050         KSpiritInit(mo, actor);
5051     mo = P_SpawnMissileAngle(actor, MT_KORAX_SPIRIT6, ANG60 * 5,
5052                              5 * FRACUNIT);
5053     if (mo)
5054         KSpiritInit(mo, actor);
5055 
5056     CheckACSPresent(255);
5057     P_StartACS(255, 0, args, actor, NULL, 0);   // Death script
5058 }
5059 
KSpiritInit(mobj_t * spirit,mobj_t * korax)5060 void KSpiritInit(mobj_t * spirit, mobj_t * korax)
5061 {
5062     int i;
5063     mobj_t *tail, *next;
5064 
5065     spirit->health = KORAX_SPIRIT_LIFETIME;
5066 
5067     spirit->special1.m = korax;     // Swarm around korax
5068     spirit->special2.i = 32 + (P_Random() & 7);   // Float bob index
5069     spirit->args[0] = 10;       // initial turn value
5070     spirit->args[1] = 0;        // initial look angle
5071 
5072     // Spawn a tail for spirit
5073     tail = P_SpawnMobj(spirit->x, spirit->y, spirit->z, MT_HOLY_TAIL);
5074     tail->special2.m = spirit;      // parent
5075     for (i = 1; i < 3; i++)
5076     {
5077         next = P_SpawnMobj(spirit->x, spirit->y, spirit->z, MT_HOLY_TAIL);
5078         P_SetMobjState(next, next->info->spawnstate + 1);
5079         tail->special1.m = next;
5080         tail = next;
5081     }
5082     tail->special1.m = NULL;         // last tail bit
5083 }
5084 
A_KoraxDecide(mobj_t * actor)5085 void A_KoraxDecide(mobj_t * actor)
5086 {
5087     if (P_Random() < 220)
5088     {
5089         P_SetMobjState(actor, S_KORAX_MISSILE1);
5090     }
5091     else
5092     {
5093         P_SetMobjState(actor, S_KORAX_COMMAND1);
5094     }
5095 }
5096 
A_KoraxMissile(mobj_t * actor)5097 void A_KoraxMissile(mobj_t * actor)
5098 {
5099     int type = P_Random() % 6;
5100     int sound = 0;
5101 
5102     S_StartSound(actor, SFX_KORAX_ATTACK);
5103 
5104     switch (type)
5105     {
5106         case 0:
5107             type = MT_WRAITHFX1;
5108             sound = SFX_WRAITH_MISSILE_FIRE;
5109             break;
5110         case 1:
5111             type = MT_DEMONFX1;
5112             sound = SFX_DEMON_MISSILE_FIRE;
5113             break;
5114         case 2:
5115             type = MT_DEMON2FX1;
5116             sound = SFX_DEMON_MISSILE_FIRE;
5117             break;
5118         case 3:
5119             type = MT_FIREDEMON_FX6;
5120             sound = SFX_FIRED_ATTACK;
5121             break;
5122         case 4:
5123             type = MT_CENTAUR_FX;
5124             sound = SFX_CENTAURLEADER_ATTACK;
5125             break;
5126         case 5:
5127             type = MT_SERPENTFX;
5128             sound = SFX_CENTAURLEADER_ATTACK;
5129             break;
5130     }
5131 
5132     // Fire all 6 missiles at once
5133     S_StartSound(NULL, sound);
5134     KoraxFire1(actor, type);
5135     KoraxFire2(actor, type);
5136     KoraxFire3(actor, type);
5137     KoraxFire4(actor, type);
5138     KoraxFire5(actor, type);
5139     KoraxFire6(actor, type);
5140 }
5141 
5142 
5143 // Call action code scripts (250-254)
A_KoraxCommand(mobj_t * actor)5144 void A_KoraxCommand(mobj_t * actor)
5145 {
5146     byte args[5];
5147     fixed_t x, y, z;
5148     angle_t ang;
5149     int numcommands;
5150 
5151     S_StartSound(actor, SFX_KORAX_COMMAND);
5152 
5153     // Shoot stream of lightning to ceiling
5154     ang = (actor->angle - ANG90) >> ANGLETOFINESHIFT;
5155     x = actor->x + FixedMul(KORAX_COMMAND_OFFSET, finecosine[ang]);
5156     y = actor->y + FixedMul(KORAX_COMMAND_OFFSET, finesine[ang]);
5157     z = actor->z + KORAX_COMMAND_HEIGHT;
5158     P_SpawnMobj(x, y, z, MT_KORAX_BOLT);
5159 
5160     args[0] = args[1] = args[2] = args[3] = args[4] = 0;
5161 
5162     if (actor->health <= (actor->info->spawnhealth >> 1))
5163     {
5164         numcommands = 5;
5165     }
5166     else
5167     {
5168         numcommands = 4;
5169     }
5170 
5171     switch (P_Random() % numcommands)
5172     {
5173         case 0:
5174             CheckACSPresent(250);
5175             P_StartACS(250, 0, args, actor, NULL, 0);
5176             break;
5177         case 1:
5178             CheckACSPresent(251);
5179             P_StartACS(251, 0, args, actor, NULL, 0);
5180             break;
5181         case 2:
5182             CheckACSPresent(252);
5183             P_StartACS(252, 0, args, actor, NULL, 0);
5184             break;
5185         case 3:
5186             CheckACSPresent(253);
5187             P_StartACS(253, 0, args, actor, NULL, 0);
5188             break;
5189         case 4:
5190             CheckACSPresent(254);
5191             P_StartACS(254, 0, args, actor, NULL, 0);
5192             break;
5193     }
5194 }
5195 
5196 
5197 #define KORAX_DELTAANGLE			(85*ANG1)
5198 #define KORAX_ARM_EXTENSION_SHORT	(40*FRACUNIT)
5199 #define KORAX_ARM_EXTENSION_LONG	(55*FRACUNIT)
5200 
5201 #define KORAX_ARM1_HEIGHT			(108*FRACUNIT)
5202 #define KORAX_ARM2_HEIGHT			(82*FRACUNIT)
5203 #define KORAX_ARM3_HEIGHT			(54*FRACUNIT)
5204 #define KORAX_ARM4_HEIGHT			(104*FRACUNIT)
5205 #define KORAX_ARM5_HEIGHT			(86*FRACUNIT)
5206 #define KORAX_ARM6_HEIGHT			(53*FRACUNIT)
5207 
5208 
5209 // Arm projectiles
5210 //              arm positions numbered:
5211 //                      1       top left
5212 //                      2       middle left
5213 //                      3       lower left
5214 //                      4       top right
5215 //                      5       middle right
5216 //                      6       lower right
5217 
5218 
5219 // Arm 1 projectile
KoraxFire1(mobj_t * actor,int type)5220 void KoraxFire1(mobj_t * actor, int type)
5221 {
5222     angle_t ang;
5223     fixed_t x, y, z;
5224 
5225     ang = (actor->angle - KORAX_DELTAANGLE) >> ANGLETOFINESHIFT;
5226     x = actor->x + FixedMul(KORAX_ARM_EXTENSION_SHORT, finecosine[ang]);
5227     y = actor->y + FixedMul(KORAX_ARM_EXTENSION_SHORT, finesine[ang]);
5228     z = actor->z - actor->floorclip + KORAX_ARM1_HEIGHT;
5229     P_SpawnKoraxMissile(x, y, z, actor, actor->target, type);
5230 }
5231 
5232 
5233 // Arm 2 projectile
KoraxFire2(mobj_t * actor,int type)5234 void KoraxFire2(mobj_t * actor, int type)
5235 {
5236     angle_t ang;
5237     fixed_t x, y, z;
5238 
5239     ang = (actor->angle - KORAX_DELTAANGLE) >> ANGLETOFINESHIFT;
5240     x = actor->x + FixedMul(KORAX_ARM_EXTENSION_LONG, finecosine[ang]);
5241     y = actor->y + FixedMul(KORAX_ARM_EXTENSION_LONG, finesine[ang]);
5242     z = actor->z - actor->floorclip + KORAX_ARM2_HEIGHT;
5243     P_SpawnKoraxMissile(x, y, z, actor, actor->target, type);
5244 }
5245 
5246 // Arm 3 projectile
KoraxFire3(mobj_t * actor,int type)5247 void KoraxFire3(mobj_t * actor, int type)
5248 {
5249     angle_t ang;
5250     fixed_t x, y, z;
5251 
5252     ang = (actor->angle - KORAX_DELTAANGLE) >> ANGLETOFINESHIFT;
5253     x = actor->x + FixedMul(KORAX_ARM_EXTENSION_LONG, finecosine[ang]);
5254     y = actor->y + FixedMul(KORAX_ARM_EXTENSION_LONG, finesine[ang]);
5255     z = actor->z - actor->floorclip + KORAX_ARM3_HEIGHT;
5256     P_SpawnKoraxMissile(x, y, z, actor, actor->target, type);
5257 }
5258 
5259 // Arm 4 projectile
KoraxFire4(mobj_t * actor,int type)5260 void KoraxFire4(mobj_t * actor, int type)
5261 {
5262     angle_t ang;
5263     fixed_t x, y, z;
5264 
5265     ang = (actor->angle + KORAX_DELTAANGLE) >> ANGLETOFINESHIFT;
5266     x = actor->x + FixedMul(KORAX_ARM_EXTENSION_SHORT, finecosine[ang]);
5267     y = actor->y + FixedMul(KORAX_ARM_EXTENSION_SHORT, finesine[ang]);
5268     z = actor->z - actor->floorclip + KORAX_ARM4_HEIGHT;
5269     P_SpawnKoraxMissile(x, y, z, actor, actor->target, type);
5270 }
5271 
5272 // Arm 5 projectile
KoraxFire5(mobj_t * actor,int type)5273 void KoraxFire5(mobj_t * actor, int type)
5274 {
5275     angle_t ang;
5276     fixed_t x, y, z;
5277 
5278     ang = (actor->angle + KORAX_DELTAANGLE) >> ANGLETOFINESHIFT;
5279     x = actor->x + FixedMul(KORAX_ARM_EXTENSION_LONG, finecosine[ang]);
5280     y = actor->y + FixedMul(KORAX_ARM_EXTENSION_LONG, finesine[ang]);
5281     z = actor->z - actor->floorclip + KORAX_ARM5_HEIGHT;
5282     P_SpawnKoraxMissile(x, y, z, actor, actor->target, type);
5283 }
5284 
5285 // Arm 6 projectile
KoraxFire6(mobj_t * actor,int type)5286 void KoraxFire6(mobj_t * actor, int type)
5287 {
5288     angle_t ang;
5289     fixed_t x, y, z;
5290 
5291     ang = (actor->angle + KORAX_DELTAANGLE) >> ANGLETOFINESHIFT;
5292     x = actor->x + FixedMul(KORAX_ARM_EXTENSION_LONG, finecosine[ang]);
5293     y = actor->y + FixedMul(KORAX_ARM_EXTENSION_LONG, finesine[ang]);
5294     z = actor->z - actor->floorclip + KORAX_ARM6_HEIGHT;
5295     P_SpawnKoraxMissile(x, y, z, actor, actor->target, type);
5296 }
5297 
5298 
A_KSpiritWeave(mobj_t * actor)5299 void A_KSpiritWeave(mobj_t * actor)
5300 {
5301     fixed_t newX, newY;
5302     int weaveXY, weaveZ;
5303     int angle;
5304 
5305     weaveXY = actor->special2.i >> 16;
5306     weaveZ = actor->special2.i & 0xFFFF;
5307     angle = (actor->angle + ANG90) >> ANGLETOFINESHIFT;
5308     newX = actor->x - FixedMul(finecosine[angle],
5309                                FloatBobOffsets[weaveXY] << 2);
5310     newY = actor->y - FixedMul(finesine[angle],
5311                                FloatBobOffsets[weaveXY] << 2);
5312     weaveXY = (weaveXY + (P_Random() % 5)) & 63;
5313     newX += FixedMul(finecosine[angle], FloatBobOffsets[weaveXY] << 2);
5314     newY += FixedMul(finesine[angle], FloatBobOffsets[weaveXY] << 2);
5315     P_TryMove(actor, newX, newY);
5316     actor->z -= FloatBobOffsets[weaveZ] << 1;
5317     weaveZ = (weaveZ + (P_Random() % 5)) & 63;
5318     actor->z += FloatBobOffsets[weaveZ] << 1;
5319     actor->special2.i = weaveZ + (weaveXY << 16);
5320 }
5321 
A_KSpiritSeeker(mobj_t * actor,angle_t thresh,angle_t turnMax)5322 void A_KSpiritSeeker(mobj_t * actor, angle_t thresh, angle_t turnMax)
5323 {
5324     int dir;
5325     int dist;
5326     angle_t delta;
5327     angle_t angle;
5328     mobj_t *target;
5329     fixed_t newZ;
5330     fixed_t deltaZ;
5331 
5332     target = actor->special1.m;
5333     if (target == NULL)
5334     {
5335         return;
5336     }
5337     dir = P_FaceMobj(actor, target, &delta);
5338     if (delta > thresh)
5339     {
5340         delta >>= 1;
5341         if (delta > turnMax)
5342         {
5343             delta = turnMax;
5344         }
5345     }
5346     if (dir)
5347     {                           // Turn clockwise
5348         actor->angle += delta;
5349     }
5350     else
5351     {                           // Turn counter clockwise
5352         actor->angle -= delta;
5353     }
5354     angle = actor->angle >> ANGLETOFINESHIFT;
5355     actor->momx = FixedMul(actor->info->speed, finecosine[angle]);
5356     actor->momy = FixedMul(actor->info->speed, finesine[angle]);
5357 
5358     if (!(leveltime & 15)
5359         || actor->z > target->z + (target->info->height)
5360         || actor->z + actor->height < target->z)
5361     {
5362         newZ = target->z + ((P_Random() * target->info->height) >> 8);
5363         deltaZ = newZ - actor->z;
5364         if (abs(deltaZ) > 15 * FRACUNIT)
5365         {
5366             if (deltaZ > 0)
5367             {
5368                 deltaZ = 15 * FRACUNIT;
5369             }
5370             else
5371             {
5372                 deltaZ = -15 * FRACUNIT;
5373             }
5374         }
5375         dist = P_AproxDistance(target->x - actor->x, target->y - actor->y);
5376         dist = dist / actor->info->speed;
5377         if (dist < 1)
5378         {
5379             dist = 1;
5380         }
5381         actor->momz = deltaZ / dist;
5382     }
5383     return;
5384 }
5385 
5386 
A_KSpiritRoam(mobj_t * actor)5387 void A_KSpiritRoam(mobj_t * actor)
5388 {
5389     if (actor->health-- <= 0)
5390     {
5391         S_StartSound(actor, SFX_SPIRIT_DIE);
5392         P_SetMobjState(actor, S_KSPIRIT_DEATH1);
5393     }
5394     else
5395     {
5396         if (actor->special1.m)
5397         {
5398             A_KSpiritSeeker(actor, actor->args[0] * ANG1,
5399                             actor->args[0] * ANG1 * 2);
5400         }
5401         A_KSpiritWeave(actor);
5402         if (P_Random() < 50)
5403         {
5404             S_StartSound(NULL, SFX_SPIRIT_ACTIVE);
5405         }
5406     }
5407 }
5408 
A_KBolt(mobj_t * actor)5409 void A_KBolt(mobj_t * actor)
5410 {
5411     // Countdown lifetime
5412     if (actor->special1.i-- <= 0)
5413     {
5414         P_SetMobjState(actor, S_NULL);
5415     }
5416 }
5417 
5418 
5419 #define KORAX_BOLT_HEIGHT		48*FRACUNIT
5420 #define KORAX_BOLT_LIFETIME		3
5421 
A_KBoltRaise(mobj_t * actor)5422 void A_KBoltRaise(mobj_t * actor)
5423 {
5424     mobj_t *mo;
5425     fixed_t z;
5426 
5427     // Spawn a child upward
5428     z = actor->z + KORAX_BOLT_HEIGHT;
5429 
5430     if ((z + KORAX_BOLT_HEIGHT) < actor->ceilingz)
5431     {
5432         mo = P_SpawnMobj(actor->x, actor->y, z, MT_KORAX_BOLT);
5433         if (mo)
5434         {
5435             mo->special1.i = KORAX_BOLT_LIFETIME;
5436         }
5437     }
5438     else
5439     {
5440         // Maybe cap it off here
5441     }
5442 }
5443