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