]> icculus.org git repositories - theoddone33/hheretic.git/blob - base/p_enemy.c
More 64bit fixes
[theoddone33/hheretic.git] / base / p_enemy.c
1
2 // P_enemy.c
3
4 #include <stdlib.h>
5 #include "doomdef.h"
6 #include "p_local.h"
7 #include "soundst.h"
8
9 // Macros
10
11 #define MAX_BOSS_SPOTS 8
12
13 // Types
14
15 typedef struct
16 {
17         fixed_t x;
18         fixed_t y;
19         angle_t angle;
20 } BossSpot_t;
21
22 // Private Data
23
24 static int BossSpotCount;
25 static BossSpot_t BossSpots[MAX_BOSS_SPOTS];
26
27 //----------------------------------------------------------------------------
28 //
29 // PROC P_InitMonsters
30 //
31 // Called at level load.
32 //
33 //----------------------------------------------------------------------------
34
35 void P_InitMonsters(void)
36 {
37         BossSpotCount = 0;
38 }
39
40 //----------------------------------------------------------------------------
41 //
42 // PROC P_AddBossSpot
43 //
44 //----------------------------------------------------------------------------
45
46 void P_AddBossSpot(fixed_t x, fixed_t y, angle_t angle)
47 {
48         if(BossSpotCount == MAX_BOSS_SPOTS)
49         {
50                 I_Error("Too many boss spots.");
51         }
52         BossSpots[BossSpotCount].x = x;
53         BossSpots[BossSpotCount].y = y;
54         BossSpots[BossSpotCount].angle = angle;
55         BossSpotCount++;
56 }
57
58 //----------------------------------------------------------------------------
59 //
60 // PROC P_RecursiveSound
61 //
62 //----------------------------------------------------------------------------
63
64 mobj_t *soundtarget;
65
66 void P_RecursiveSound(sector_t *sec, int soundblocks)
67 {
68         int i;
69         line_t *check;
70         sector_t *other;
71
72         // Wake up all monsters in this sector
73         if(sec->validcount == validcount && sec->soundtraversed <= soundblocks+1)
74         { // Already flooded
75                 return;
76         }
77         sec->validcount = validcount;
78         sec->soundtraversed = soundblocks+1;
79         sec->soundtarget = soundtarget;
80         for(i = 0; i < sec->linecount; i++)
81         {
82                 check = sec->lines[i];
83                 if(!(check->flags&ML_TWOSIDED))
84                 {
85                         continue;
86                 }
87                 P_LineOpening(check);
88                 if(openrange <= 0)
89                 { // Closed door
90                         continue;
91                 }
92                 if(sides[check->sidenum[0]].sector == sec)
93                 {
94                         other = sides[check->sidenum[1]].sector;
95                 }
96                 else
97                 {
98                         other = sides[check->sidenum[0]].sector;
99                 }
100                 if(check->flags&ML_SOUNDBLOCK)
101                 {
102                         if(!soundblocks)
103                         {
104                                 P_RecursiveSound(other, 1);
105                         }
106                 }
107                 else
108                 {
109                         P_RecursiveSound(other, soundblocks);
110                 }
111         }
112 }
113
114 //----------------------------------------------------------------------------
115 //
116 // PROC P_NoiseAlert
117 //
118 // If a monster yells at a player, it will alert other monsters to the
119 // player.
120 //
121 //----------------------------------------------------------------------------
122
123 void P_NoiseAlert(mobj_t *target, mobj_t *emmiter)
124 {
125         soundtarget = target;
126         validcount++;
127         P_RecursiveSound(emmiter->subsector->sector, 0);
128 }
129
130 //----------------------------------------------------------------------------
131 //
132 // FUNC P_CheckMeleeRange
133 //
134 //----------------------------------------------------------------------------
135
136 boolean P_CheckMeleeRange(mobj_t *actor)
137 {
138         mobj_t *mo;
139         fixed_t dist;
140
141         if(!actor->target)
142         {
143                 return(false);
144         }
145         mo = actor->target;
146         dist = P_AproxDistance(mo->x-actor->x, mo->y-actor->y);
147         if(dist >= MELEERANGE)
148         {
149                 return(false);
150         }
151         if(!P_CheckSight(actor, mo))
152         {
153                 return(false);
154         }
155         if(mo->z > actor->z+actor->height)
156         { // Target is higher than the attacker
157                 return(false);
158         }
159         else if(actor->z > mo->z+mo->height)
160         { // Attacker is higher
161                 return(false);
162         }
163         return(true);
164 }
165
166 //----------------------------------------------------------------------------
167 //
168 // FUNC P_CheckMissileRange
169 //
170 //----------------------------------------------------------------------------
171
172 boolean P_CheckMissileRange(mobj_t *actor)
173 {
174         fixed_t dist;
175
176         if(!P_CheckSight(actor, actor->target))
177         {
178                 return(false);
179         }
180         if(actor->flags&MF_JUSTHIT)
181         { // The target just hit the enemy, so fight back!
182                 actor->flags &= ~MF_JUSTHIT;
183                 return(true);
184         }
185         if(actor->reactiontime)
186         { // Don't attack yet
187                 return(false);
188         }
189         dist = (P_AproxDistance(actor->x-actor->target->x,
190                 actor->y-actor->target->y)>>FRACBITS)-64;
191         if(!actor->info->meleestate)
192         { // No melee attack, so fire more frequently
193                 dist -= 128;
194         }
195         if(actor->type == MT_IMP)
196         { // Imp's fly attack from far away
197                 dist >>= 1;
198         }
199         if(dist > 200)
200         {
201                 dist = 200;
202         }
203         if(P_Random() < dist)
204         {
205                 return(false);
206         }
207         return(true);
208 }
209
210 /*
211 ================
212 =
213 = P_Move
214 =
215 = Move in the current direction
216 = returns false if the move is blocked
217 ================
218 */
219
220 fixed_t xspeed[8] = {FRACUNIT,47000,0,-47000,-FRACUNIT,-47000,0,47000};
221 fixed_t yspeed[8] = {0,47000,FRACUNIT,47000,0,-47000,-FRACUNIT,-47000};
222
223 #define MAXSPECIALCROSS         8
224 extern  line_t  *spechit[MAXSPECIALCROSS];
225 extern  int                      numspechit;
226
227 boolean P_Move(mobj_t *actor)
228 {
229         fixed_t tryx, tryy;
230         line_t *ld;
231         boolean good;
232
233         if(actor->movedir == DI_NODIR)
234         {
235                 return(false);
236         }
237         tryx = actor->x+actor->info->speed*xspeed[actor->movedir];
238         tryy = actor->y+actor->info->speed*yspeed[actor->movedir];
239         if(!P_TryMove(actor, tryx, tryy))
240         { // open any specials
241                 if(actor->flags&MF_FLOAT && floatok)
242                 { // must adjust height
243                         if(actor->z < tmfloorz)
244                         {
245                                 actor->z += FLOATSPEED;
246                         }
247                         else
248                         {
249                                 actor->z -= FLOATSPEED;
250                         }
251                         actor->flags |= MF_INFLOAT;
252                         return(true);
253                 }
254                 if(!numspechit)
255                 {
256                         return false;
257                 }
258                 actor->movedir = DI_NODIR;
259                 good = false;
260                 while(numspechit--)
261                 {
262                         ld = spechit[numspechit];
263                         // if the special isn't a door that can be opened, return false
264                         if(P_UseSpecialLine(actor, ld))
265                         {
266                                 good = true;
267                         }
268                 }
269                 return(good);
270         }
271         else
272         {
273                 actor->flags &= ~MF_INFLOAT;
274         }
275         if(!(actor->flags&MF_FLOAT))
276         {
277                 if(actor->z > actor->floorz)
278                 {
279                         P_HitFloor(actor);
280                 }
281                 actor->z = actor->floorz;
282         }
283         return(true);
284 }
285
286 //----------------------------------------------------------------------------
287 //
288 // FUNC P_TryWalk
289 //
290 // Attempts to move actor in its current (ob->moveangle) direction.
291 // If blocked by either a wall or an actor returns FALSE.
292 // If move is either clear of block only by a door, returns TRUE and sets.
293 // If a door is in the way, an OpenDoor call is made to start it opening.
294 //
295 //----------------------------------------------------------------------------
296
297 boolean P_TryWalk(mobj_t *actor)
298 {
299         if(!P_Move(actor))
300         {
301                 return(false);
302         }
303         actor->movecount = P_Random()&15;
304         return(true);
305 }
306
307 /*
308 ================
309 =
310 = P_NewChaseDir
311 =
312 ================
313 */
314
315 dirtype_t opposite[] =
316 {DI_WEST, DI_SOUTHWEST, DI_SOUTH, DI_SOUTHEAST, DI_EAST, DI_NORTHEAST,
317 DI_NORTH, DI_NORTHWEST, DI_NODIR};
318
319 dirtype_t diags[] = {DI_NORTHWEST,DI_NORTHEAST,DI_SOUTHWEST,DI_SOUTHEAST};
320
321 void P_NewChaseDir (mobj_t *actor)
322 {
323         fixed_t         deltax,deltay;
324         dirtype_t       d[3];
325         dirtype_t       tdir, olddir, turnaround;
326
327         if (!actor->target)
328                 I_Error ("P_NewChaseDir: called with no target");
329                 
330         olddir = actor->movedir;
331         turnaround=opposite[olddir];
332
333         deltax = actor->target->x - actor->x;
334         deltay = actor->target->y - actor->y;
335         if (deltax>10*FRACUNIT)
336                 d[1]= DI_EAST;
337         else if (deltax<-10*FRACUNIT)
338                 d[1]= DI_WEST;
339         else
340                 d[1]=DI_NODIR;
341         if (deltay<-10*FRACUNIT)
342                 d[2]= DI_SOUTH;
343         else if (deltay>10*FRACUNIT)
344                 d[2]= DI_NORTH;
345         else
346                 d[2]=DI_NODIR;
347
348 // try direct route
349         if (d[1] != DI_NODIR && d[2] != DI_NODIR)
350         {
351                 actor->movedir = diags[((deltay<0)<<1)+(deltax>0)];
352                 if (actor->movedir != turnaround && P_TryWalk(actor))
353                         return;
354         }
355
356 // try other directions
357         if (P_Random() > 200 ||  abs(deltay)>abs(deltax))
358         {
359                 tdir=d[1];
360                 d[1]=d[2];
361                 d[2]=tdir;
362         }
363
364         if (d[1]==turnaround)
365                 d[1]=DI_NODIR;
366         if (d[2]==turnaround)
367                 d[2]=DI_NODIR;
368         
369         if (d[1]!=DI_NODIR)
370         {
371                 actor->movedir = d[1];
372                 if (P_TryWalk(actor))
373                         return;     /*either moved forward or attacked*/
374         }
375
376         if (d[2]!=DI_NODIR)
377         {
378                 actor->movedir =d[2];
379                 if (P_TryWalk(actor))
380                         return;
381         }
382
383 /* there is no direct path to the player, so pick another direction */
384
385         if (olddir!=DI_NODIR)
386         {
387                 actor->movedir =olddir;
388                 if (P_TryWalk(actor))
389                         return;
390         }
391
392         if (P_Random()&1)       /*randomly determine direction of search*/
393         {
394                 for (tdir=DI_EAST ; tdir<=DI_SOUTHEAST ; tdir++)
395                 {
396                         if (tdir!=turnaround)
397                         {
398                                 actor->movedir =tdir;
399                                 if ( P_TryWalk(actor) )
400                                         return;
401                         }
402                 }
403         }
404         else
405         {
406                 for (tdir=DI_SOUTHEAST ; (int)tdir >= DI_EAST;tdir--)
407                 {
408                         if (tdir!=turnaround)
409                         {
410                                 actor->movedir =tdir;
411                                 if ( P_TryWalk(actor) )
412                                 return;
413                         }
414                 }
415         }
416
417         if (turnaround !=  DI_NODIR)
418         {
419                 actor->movedir =turnaround;
420                 if ( P_TryWalk(actor) )
421                         return;
422         }
423
424         actor->movedir = DI_NODIR;              // can't move
425 }
426
427 //---------------------------------------------------------------------------
428 //
429 // FUNC P_LookForMonsters
430 //
431 //---------------------------------------------------------------------------
432
433 #define MONS_LOOK_RANGE (20*64*FRACUNIT)
434 #define MONS_LOOK_LIMIT 64
435
436 boolean P_LookForMonsters(mobj_t *actor)
437 {
438         int count;
439         mobj_t *mo;
440         thinker_t *think;
441
442         if(!P_CheckSight(players[0].mo, actor))
443         { // Player can't see monster
444                 return(false);
445         }
446         count = 0;
447         for(think = thinkercap.next; think != &thinkercap; think = think->next)
448         {
449                 if(think->function != P_MobjThinker)
450                 { // Not a mobj thinker
451                         continue;
452                 }
453                 mo = (mobj_t *)think;
454                 if(!(mo->flags&MF_COUNTKILL) || (mo == actor) || (mo->health <= 0))
455                 { // Not a valid monster
456                         continue;
457                 }
458                 if(P_AproxDistance(actor->x-mo->x, actor->y-mo->y)
459                         > MONS_LOOK_RANGE)
460                 { // Out of range
461                         continue;
462                 }
463                 if(P_Random() < 16)
464                 { // Skip
465                         continue;
466                 }
467                 if(count++ > MONS_LOOK_LIMIT)
468                 { // Stop searching
469                         return(false);
470                 }
471                 if(!P_CheckSight(actor, mo))
472                 { // Out of sight
473                         continue;
474                 }
475                 // Found a target monster
476                 actor->target = mo;
477                 return(true);
478         }
479         return(false);
480 }
481
482 /*
483 ================
484 =
485 = P_LookForPlayers
486 =
487 = If allaround is false, only look 180 degrees in front
488 = returns true if a player is targeted
489 ================
490 */
491
492 boolean P_LookForPlayers(mobj_t *actor, boolean allaround)
493 {
494         int c;
495         int stop;
496         player_t *player;
497         sector_t *sector;
498         angle_t an;
499         fixed_t dist;
500
501         if(!netgame && players[0].health <= 0)
502         { // Single player game and player is dead, look for monsters
503                 return(P_LookForMonsters(actor));
504         }
505         sector = actor->subsector->sector;
506         c = 0;
507         stop = (actor->lastlook-1)&3;
508         for( ; ; actor->lastlook = (actor->lastlook+1)&3 )
509         {
510                 if (!playeringame[actor->lastlook])
511                         continue;
512                         
513                 if (c++ == 2 || actor->lastlook == stop)
514                         return false;           // done looking
515
516                 player = &players[actor->lastlook];
517                 if (player->health <= 0)
518                         continue;               // dead
519                 if (!P_CheckSight (actor, player->mo))
520                         continue;               // out of sight
521                         
522                 if (!allaround)
523                 {
524                         an = R_PointToAngle2 (actor->x, actor->y, 
525                         player->mo->x, player->mo->y) - actor->angle;
526                         if (an > ANG90 && an < ANG270)
527                         {
528                                 dist = P_AproxDistance (player->mo->x - actor->x,
529                                         player->mo->y - actor->y);
530                                 // if real close, react anyway
531                                 if (dist > MELEERANGE)
532                                         continue;               // behind back
533                         }
534                 }
535                 if(player->mo->flags&MF_SHADOW)
536                 { // Player is invisible
537                         if((P_AproxDistance(player->mo->x-actor->x,
538                                 player->mo->y-actor->y) > 2*MELEERANGE)
539                                 && P_AproxDistance(player->mo->momx, player->mo->momy)
540                                 < 5*FRACUNIT)
541                         { // Player is sneaking - can't detect
542                                 return(false);
543                         }
544                         if(P_Random() < 225)
545                         { // Player isn't sneaking, but still didn't detect
546                                 return(false);
547                         }
548                 }
549                 actor->target = player->mo;
550                 return(true);
551         }
552         return(false);
553 }
554
555 /*
556 ===============================================================================
557
558                                                 ACTION ROUTINES
559
560 ===============================================================================
561 */
562
563 /*
564 ==============
565 =
566 = A_Look
567 =
568 = Stay in state until a player is sighted
569 =
570 ==============
571 */
572
573 void A_Look (mobj_t *actor)
574 {
575         mobj_t          *targ;
576         
577         actor->threshold = 0;           // any shot will wake up
578         targ = actor->subsector->sector->soundtarget;
579         if (targ && (targ->flags & MF_SHOOTABLE) )
580         {
581                 actor->target = targ;
582                 if ( actor->flags & MF_AMBUSH )
583                 {
584                         if (P_CheckSight (actor, actor->target))
585                                 goto seeyou;
586                 }
587                 else
588                         goto seeyou;
589         }
590         
591         
592         if (!P_LookForPlayers (actor, false) )
593                 return;
594                 
595 // go into chase state
596 seeyou:
597         if (actor->info->seesound)
598         {
599                 int             sound;
600                 
601 /*
602                 switch (actor->info->seesound)
603                 {
604                 case sfx_posit1:
605                 case sfx_posit2:
606                 case sfx_posit3:
607                         sound = sfx_posit1+P_Random()%3;
608                         break;
609                 case sfx_bgsit1:
610                 case sfx_bgsit2:
611                         sound = sfx_bgsit1+P_Random()%2;
612                         break;
613                 default:
614                         sound = actor->info->seesound;
615                         break;
616                 }
617 */
618                 sound = actor->info->seesound;
619                 if(actor->flags2&MF2_BOSS)
620                 { // Full volume
621                         S_StartSound(NULL, sound);
622                 }
623                 else
624                 {
625                         S_StartSound(actor, sound);
626                 }
627         }
628         P_SetMobjState(actor, actor->info->seestate);
629 }
630
631
632 /*
633 ==============
634 =
635 = A_Chase
636 =
637 = Actor has a melee attack, so it tries to close as fast as possible
638 =
639 ==============
640 */
641
642 void A_Chase(mobj_t *actor)
643 {
644         int delta;
645
646         if(actor->reactiontime)
647         {
648                 actor->reactiontime--;
649         }
650
651         // Modify target threshold
652         if(actor->threshold)
653         {
654                 actor->threshold--;
655         }
656
657         if(gameskill == sk_nightmare)
658         { // Monsters move faster in nightmare mode
659                 actor->tics -= actor->tics/2;
660                 if(actor->tics < 3)
661                 {
662                         actor->tics = 3;
663                 }
664         }
665
666 //
667 // turn towards movement direction if not there yet
668 //
669         if(actor->movedir < 8)
670         {
671                 actor->angle &= (7<<29);
672                 delta = actor->angle-(actor->movedir << 29);
673                 if(delta > 0)
674                 {
675                         actor->angle -= ANG90/2;
676                 }
677                 else if(delta < 0)
678                 {
679                         actor->angle += ANG90/2;
680                 }
681         }
682
683         if(!actor->target || !(actor->target->flags&MF_SHOOTABLE))
684         { // look for a new target
685                 if(P_LookForPlayers(actor, true))
686                 { // got a new target
687                         return;
688                 }
689                 P_SetMobjState(actor, actor->info->spawnstate);
690                 return;
691         }
692         
693 //
694 // don't attack twice in a row
695 //
696         if(actor->flags & MF_JUSTATTACKED)
697         {
698                 actor->flags &= ~MF_JUSTATTACKED;
699                 if (gameskill != sk_nightmare)
700                         P_NewChaseDir (actor);
701                 return;
702         }
703         
704 //
705 // check for melee attack
706 //      
707         if (actor->info->meleestate && P_CheckMeleeRange (actor))
708         {
709                 if (actor->info->attacksound)
710                         S_StartSound (actor, actor->info->attacksound);
711                 P_SetMobjState (actor, actor->info->meleestate);
712                 return;
713         }
714
715 //
716 // check for missile attack
717 //
718         if (actor->info->missilestate)
719         {
720                 if (gameskill < sk_nightmare && actor->movecount)
721                         goto nomissile;
722                 if (!P_CheckMissileRange (actor))
723                         goto nomissile;
724                 P_SetMobjState (actor, actor->info->missilestate);
725                 actor->flags |= MF_JUSTATTACKED;
726                 return;
727         }
728 nomissile:
729
730 //
731 // possibly choose another target
732 //
733         if (netgame && !actor->threshold && !P_CheckSight (actor, actor->target) )
734         {
735                 if (P_LookForPlayers(actor,true))
736                         return;         // got a new target
737         }
738         
739 //
740 // chase towards player
741 //
742         if (--actor->movecount<0 || !P_Move (actor))
743         {
744                 P_NewChaseDir (actor);
745         }
746
747 //
748 // make active sound
749 //
750         if(actor->info->activesound && P_Random() < 3)
751         {
752                 if(actor->type == MT_WIZARD && P_Random() < 128)
753                 {
754                         S_StartSound(actor, actor->info->seesound);
755                 }
756                 else if(actor->type == MT_SORCERER2)
757                 {
758                         S_StartSound(NULL, actor->info->activesound);
759                 }
760                 else
761                 {
762                         S_StartSound(actor, actor->info->activesound);
763                 }
764         }
765 }
766
767 //----------------------------------------------------------------------------
768 //
769 // PROC A_FaceTarget
770 //
771 //----------------------------------------------------------------------------
772
773 void A_FaceTarget(mobj_t *actor)
774 {
775         if(!actor->target)
776         {
777                 return;
778         }
779         actor->flags &= ~MF_AMBUSH;
780         actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x,
781                 actor->target->y);
782         if(actor->target->flags&MF_SHADOW)
783         { // Target is a ghost
784                 actor->angle += (P_Random()-P_Random())<<21;
785         }
786 }
787
788 //----------------------------------------------------------------------------
789 //
790 // PROC A_Pain
791 //
792 //----------------------------------------------------------------------------
793
794 void A_Pain(mobj_t *actor)
795 {
796         if(actor->info->painsound)
797         {
798                 S_StartSound(actor, actor->info->painsound);
799         }
800 }
801
802 //----------------------------------------------------------------------------
803 //
804 // PROC A_DripBlood
805 //
806 //----------------------------------------------------------------------------
807
808 void A_DripBlood(mobj_t *actor)
809 {
810         mobj_t *mo;
811
812         mo = P_SpawnMobj(actor->x+((P_Random()-P_Random())<<11),
813                 actor->y+((P_Random()-P_Random())<<11), actor->z, MT_BLOOD);
814         mo->momx = (P_Random()-P_Random())<<10;
815         mo->momy = (P_Random()-P_Random())<<10;
816         mo->flags2 |= MF2_LOGRAV;
817 }
818
819 //----------------------------------------------------------------------------
820 //
821 // PROC A_KnightAttack
822 //
823 //----------------------------------------------------------------------------
824
825 void A_KnightAttack(mobj_t *actor)
826 {
827         if(!actor->target)
828         {
829                 return;
830         }
831         if(P_CheckMeleeRange(actor))
832         {
833                 P_DamageMobj(actor->target, actor, actor, HITDICE(3));
834                 S_StartSound(actor, sfx_kgtat2);
835                 return;
836         }
837         // Throw axe
838         S_StartSound(actor, actor->info->attacksound);
839         if(actor->type == MT_KNIGHTGHOST || P_Random() < 40)
840         { // Red axe
841                 P_SpawnMissile(actor, actor->target, MT_REDAXE);
842                 return;
843         }
844         // Green axe
845         P_SpawnMissile(actor, actor->target, MT_KNIGHTAXE);
846 }
847
848 //----------------------------------------------------------------------------
849 //
850 // PROC A_ImpExplode
851 //
852 //----------------------------------------------------------------------------
853
854 void A_ImpExplode(mobj_t *actor)
855 {
856         mobj_t *mo;
857
858         mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_IMPCHUNK1);
859         mo->momx = (P_Random() - P_Random ())<<10;
860         mo->momy = (P_Random() - P_Random ())<<10;
861         mo->momz = 9*FRACUNIT;
862         mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_IMPCHUNK2);
863         mo->momx = (P_Random() - P_Random ())<<10;
864         mo->momy = (P_Random() - P_Random ())<<10;
865         mo->momz = 9*FRACUNIT;
866         if(actor->special1 == 666)
867         { // Extreme death crash
868                 P_SetMobjState(actor, S_IMP_XCRASH1);
869         }
870 }
871
872 //----------------------------------------------------------------------------
873 //
874 // PROC A_BeastPuff
875 //
876 //----------------------------------------------------------------------------
877
878 void A_BeastPuff(mobj_t *actor)
879 {
880         if(P_Random() > 64)
881         {
882                 P_SpawnMobj(actor->x+((P_Random()-P_Random())<<10),
883                         actor->y+((P_Random()-P_Random())<<10),
884                         actor->z+((P_Random()-P_Random())<<10), MT_PUFFY);
885         }
886 }
887
888 //----------------------------------------------------------------------------
889 //
890 // PROC A_ImpMeAttack
891 //
892 //----------------------------------------------------------------------------
893
894 void A_ImpMeAttack(mobj_t *actor)
895 {
896         if(!actor->target)
897         {
898                 return;
899         }
900         S_StartSound(actor, actor->info->attacksound);
901         if(P_CheckMeleeRange(actor))
902         {
903                 P_DamageMobj(actor->target, actor, actor, 5+(P_Random()&7));
904         }
905 }
906
907 //----------------------------------------------------------------------------
908 //
909 // PROC A_ImpMsAttack
910 //
911 //----------------------------------------------------------------------------
912
913 void A_ImpMsAttack(mobj_t *actor)
914 {
915         mobj_t *dest;
916         angle_t an;
917         int dist;
918
919         if(!actor->target || P_Random() > 64)
920         {
921                 P_SetMobjState(actor, actor->info->seestate);
922                 return;
923         }
924         dest = actor->target;
925         actor->flags |= MF_SKULLFLY;
926         S_StartSound(actor, actor->info->attacksound);
927         A_FaceTarget(actor);
928         an = actor->angle >> ANGLETOFINESHIFT;
929         actor->momx = FixedMul(12*FRACUNIT, finecosine[an]);
930         actor->momy = FixedMul(12*FRACUNIT, finesine[an]);
931         dist = P_AproxDistance(dest->x-actor->x, dest->y-actor->y);
932         dist = dist/(12*FRACUNIT);
933         if(dist < 1)
934         {
935                 dist = 1;
936         }
937         actor->momz = (dest->z+(dest->height>>1)-actor->z)/dist;
938 }
939
940 //----------------------------------------------------------------------------
941 //
942 // PROC A_ImpMsAttack2
943 //
944 // Fireball attack of the imp leader.
945 //
946 //----------------------------------------------------------------------------
947
948 void A_ImpMsAttack2(mobj_t *actor)
949 {
950         if(!actor->target)
951         {
952                 return;
953         }
954         S_StartSound(actor, actor->info->attacksound);
955         if(P_CheckMeleeRange(actor))
956         {
957                 P_DamageMobj(actor->target, actor, actor, 5+(P_Random()&7));
958                 return;
959         }
960         P_SpawnMissile(actor, actor->target, MT_IMPBALL);
961 }
962
963 //----------------------------------------------------------------------------
964 //
965 // PROC A_ImpDeath
966 //
967 //----------------------------------------------------------------------------
968
969 void A_ImpDeath(mobj_t *actor)
970 {
971         actor->flags &= ~MF_SOLID;
972         actor->flags2 |= MF2_FOOTCLIP;
973         if(actor->z <= actor->floorz)
974         {
975                 P_SetMobjState(actor, S_IMP_CRASH1);
976         }
977 }
978
979 //----------------------------------------------------------------------------
980 //
981 // PROC A_ImpXDeath1
982 //
983 //----------------------------------------------------------------------------
984
985 void A_ImpXDeath1(mobj_t *actor)
986 {
987         actor->flags &= ~MF_SOLID;
988         actor->flags |= MF_NOGRAVITY;
989         actor->flags2 |= MF2_FOOTCLIP;
990         actor->special1 = 666; // Flag the crash routine
991 }
992
993 //----------------------------------------------------------------------------
994 //
995 // PROC A_ImpXDeath2
996 //
997 //----------------------------------------------------------------------------
998
999 void A_ImpXDeath2(mobj_t *actor)
1000 {
1001         actor->flags &= ~MF_NOGRAVITY;
1002         if(actor->z <= actor->floorz)
1003         {
1004                 P_SetMobjState(actor, S_IMP_CRASH1);
1005         }
1006 }
1007
1008 //----------------------------------------------------------------------------
1009 //
1010 // FUNC P_UpdateChicken
1011 //
1012 // Returns true if the chicken morphs.
1013 //
1014 //----------------------------------------------------------------------------
1015
1016 boolean P_UpdateChicken(mobj_t *actor, int tics)
1017 {
1018         mobj_t *fog;
1019         fixed_t x;
1020         fixed_t y;
1021         fixed_t z;
1022         mobjtype_t moType;
1023         mobj_t *mo;
1024         mobj_t oldChicken;
1025
1026         actor->special1 -= tics;
1027         if(actor->special1 > 0)
1028         {
1029                 return(false);
1030         }
1031         moType = actor->special2;
1032         x = actor->x;
1033         y = actor->y;
1034         z = actor->z;
1035         oldChicken = *actor;
1036         P_SetMobjState(actor, S_FREETARGMOBJ);
1037         mo = P_SpawnMobj(x, y, z, moType);
1038         if(P_TestMobjLocation(mo) == false)
1039         { // Didn't fit
1040                 P_RemoveMobj(mo);
1041                 mo = P_SpawnMobj(x, y, z, MT_CHICKEN);
1042                 mo->angle = oldChicken.angle;
1043                 mo->flags = oldChicken.flags;
1044                 mo->health = oldChicken.health;
1045                 mo->target = oldChicken.target;
1046                 mo->special1 = 5*35; // Next try in 5 seconds
1047                 mo->special2 = moType;
1048                 return(false);
1049         }
1050         mo->angle = oldChicken.angle;
1051         mo->target = oldChicken.target;
1052         fog = P_SpawnMobj(x, y, z+TELEFOGHEIGHT, MT_TFOG);
1053         S_StartSound(fog, sfx_telept);
1054         return(true);
1055 }
1056
1057 //----------------------------------------------------------------------------
1058 //
1059 // PROC A_ChicAttack
1060 //
1061 //----------------------------------------------------------------------------
1062
1063 void A_ChicAttack(mobj_t *actor)
1064 {
1065         if(P_UpdateChicken(actor, 18))
1066         {
1067                 return;
1068         }
1069         if(!actor->target)
1070         {
1071                 return;
1072         }
1073         if(P_CheckMeleeRange(actor))
1074         {
1075                 P_DamageMobj(actor->target, actor, actor, 1+(P_Random()&1));
1076         }
1077 }
1078
1079 //----------------------------------------------------------------------------
1080 //
1081 // PROC A_ChicLook
1082 //
1083 //----------------------------------------------------------------------------
1084
1085 void A_ChicLook(mobj_t *actor)
1086 {
1087         if(P_UpdateChicken(actor, 10))
1088         {
1089                 return;
1090         }
1091         A_Look(actor);
1092 }
1093
1094 //----------------------------------------------------------------------------
1095 //
1096 // PROC A_ChicChase
1097 //
1098 //----------------------------------------------------------------------------
1099
1100 void A_ChicChase(mobj_t *actor)
1101 {
1102         if(P_UpdateChicken(actor, 3))
1103         {
1104                 return;
1105         }
1106         A_Chase(actor);
1107 }
1108
1109 //----------------------------------------------------------------------------
1110 //
1111 // PROC A_ChicPain
1112 //
1113 //----------------------------------------------------------------------------
1114
1115 void A_ChicPain(mobj_t *actor)
1116 {
1117         if(P_UpdateChicken(actor, 10))
1118         {
1119                 return;
1120         }
1121         S_StartSound(actor, actor->info->painsound);
1122 }
1123
1124 //----------------------------------------------------------------------------
1125 //
1126 // PROC A_Feathers
1127 //
1128 //----------------------------------------------------------------------------
1129
1130 void A_Feathers(mobj_t *actor)
1131 {
1132         int i;
1133         int count;
1134         mobj_t *mo;
1135
1136         if(actor->health > 0)
1137         { // Pain
1138                 count = P_Random() < 32 ? 2 : 1;
1139         }
1140         else
1141         { // Death
1142                 count = 5+(P_Random()&3);
1143         }
1144         for(i = 0; i < count; i++)
1145         {
1146                 mo = P_SpawnMobj(actor->x, actor->y, actor->z+20*FRACUNIT,
1147                         MT_FEATHER);
1148                 mo->target = actor;
1149                 mo->momx = (P_Random()-P_Random())<<8;
1150                 mo->momy = (P_Random()-P_Random())<<8;
1151                 mo->momz = FRACUNIT+(P_Random()<<9);
1152                 P_SetMobjState(mo, S_FEATHER1+(P_Random()&7));
1153         }
1154 }
1155
1156 //----------------------------------------------------------------------------
1157 //
1158 // PROC A_MummyAttack
1159 //
1160 //----------------------------------------------------------------------------
1161
1162 void A_MummyAttack(mobj_t *actor)
1163 {
1164         if(!actor->target)
1165         {
1166                 return;
1167         }
1168         S_StartSound(actor, actor->info->attacksound);
1169         if(P_CheckMeleeRange(actor))
1170         {
1171                 P_DamageMobj(actor->target, actor, actor, HITDICE(2));
1172                 S_StartSound(actor, sfx_mumat2);
1173                 return;
1174         }
1175         S_StartSound(actor, sfx_mumat1);
1176 }
1177
1178 //----------------------------------------------------------------------------
1179 //
1180 // PROC A_MummyAttack2
1181 //
1182 // Mummy leader missile attack.
1183 //
1184 //----------------------------------------------------------------------------
1185
1186 void A_MummyAttack2(mobj_t *actor)
1187 {
1188         mobj_t *mo;
1189
1190         if(!actor->target)
1191         {
1192                 return;
1193         }
1194         //S_StartSound(actor, actor->info->attacksound);
1195         if(P_CheckMeleeRange(actor))
1196         {
1197                 P_DamageMobj(actor->target, actor, actor, HITDICE(2));
1198                 return;
1199         }
1200         mo = P_SpawnMissile(actor, actor->target, MT_MUMMYFX1);
1201         //mo = P_SpawnMissile(actor, actor->target, MT_EGGFX);
1202         if(mo != NULL)
1203         {
1204                 mo->special1 = (intptr_t)actor->target;
1205         }
1206 }
1207
1208 //----------------------------------------------------------------------------
1209 //
1210 // PROC A_MummyFX1Seek
1211 //
1212 //----------------------------------------------------------------------------
1213
1214 void A_MummyFX1Seek(mobj_t *actor)
1215 {
1216         P_SeekerMissile(actor, ANGLE_1*10, ANGLE_1*20);
1217 }
1218
1219 //----------------------------------------------------------------------------
1220 //
1221 // PROC A_MummySoul
1222 //
1223 //----------------------------------------------------------------------------
1224
1225 void A_MummySoul(mobj_t *mummy)
1226 {
1227         mobj_t *mo;
1228
1229         mo = P_SpawnMobj(mummy->x, mummy->y, mummy->z+10*FRACUNIT, MT_MUMMYSOUL);
1230         mo->momz = FRACUNIT;
1231 }
1232
1233 //----------------------------------------------------------------------------
1234 //
1235 // PROC A_Sor1Pain
1236 //
1237 //----------------------------------------------------------------------------
1238
1239 void A_Sor1Pain(mobj_t *actor)
1240 {
1241         actor->special1 = 20; // Number of steps to walk fast
1242         A_Pain(actor);
1243 }
1244
1245 //----------------------------------------------------------------------------
1246 //
1247 // PROC A_Sor1Chase
1248 //
1249 //----------------------------------------------------------------------------
1250
1251 void A_Sor1Chase(mobj_t *actor)
1252 {
1253         if(actor->special1)
1254         {
1255                 actor->special1--;
1256                 actor->tics -= 3;
1257         }
1258         A_Chase(actor);
1259 }
1260
1261 //----------------------------------------------------------------------------
1262 //
1263 // PROC A_Srcr1Attack
1264 //
1265 // Sorcerer demon attack.
1266 //
1267 //----------------------------------------------------------------------------
1268
1269 void A_Srcr1Attack(mobj_t *actor)
1270 {
1271         mobj_t *mo;
1272         fixed_t momz;
1273         angle_t angle;
1274
1275         if(!actor->target)
1276         {
1277                 return;
1278         }
1279         S_StartSound(actor, actor->info->attacksound);
1280         if(P_CheckMeleeRange(actor))
1281         {
1282                 P_DamageMobj(actor->target, actor, actor, HITDICE(8));
1283                 return;
1284         }
1285         if(actor->health > (actor->info->spawnhealth/3)*2)
1286         { // Spit one fireball
1287                 P_SpawnMissile(actor, actor->target, MT_SRCRFX1);
1288         }
1289         else
1290         { // Spit three fireballs
1291                 mo = P_SpawnMissile(actor, actor->target, MT_SRCRFX1);
1292                 if(mo)
1293                 {
1294                         momz = mo->momz;
1295                         angle = mo->angle;
1296                         P_SpawnMissileAngle(actor, MT_SRCRFX1, angle-ANGLE_1*3, momz);
1297                         P_SpawnMissileAngle(actor, MT_SRCRFX1, angle+ANGLE_1*3, momz);
1298                 }
1299                 if(actor->health < actor->info->spawnhealth/3)
1300                 { // Maybe attack again
1301                         if(actor->special1)
1302                         { // Just attacked, so don't attack again
1303                                 actor->special1 = 0;
1304                         }
1305                         else
1306                         { // Set state to attack again
1307                                 actor->special1 = 1;
1308                                 P_SetMobjState(actor, S_SRCR1_ATK4);
1309                         }
1310                 }
1311         }
1312 }
1313
1314 //----------------------------------------------------------------------------
1315 //
1316 // PROC A_SorcererRise
1317 //
1318 //----------------------------------------------------------------------------
1319
1320 void A_SorcererRise(mobj_t *actor)
1321 {
1322         mobj_t *mo;
1323
1324         actor->flags &= ~MF_SOLID;
1325         mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SORCERER2);
1326         P_SetMobjState(mo, S_SOR2_RISE1);
1327         mo->angle = actor->angle;
1328         mo->target = actor->target;
1329 }
1330
1331 //----------------------------------------------------------------------------
1332 //
1333 // PROC P_DSparilTeleport
1334 //
1335 //----------------------------------------------------------------------------
1336
1337 void P_DSparilTeleport(mobj_t *actor)
1338 {
1339         int i;
1340         fixed_t x;
1341         fixed_t y;
1342         fixed_t prevX;
1343         fixed_t prevY;
1344         fixed_t prevZ;
1345         mobj_t *mo;
1346
1347         if(!BossSpotCount)
1348         { // No spots
1349                 return;
1350         }
1351         i = P_Random();
1352         do
1353         {
1354                 i++;
1355                 x = BossSpots[i%BossSpotCount].x;
1356                 y = BossSpots[i%BossSpotCount].y;
1357         } while(P_AproxDistance(actor->x-x, actor->y-y) < 128*FRACUNIT);
1358         prevX = actor->x;
1359         prevY = actor->y;
1360         prevZ = actor->z;
1361         if(P_TeleportMove(actor, x, y))
1362         {
1363                 mo = P_SpawnMobj(prevX, prevY, prevZ, MT_SOR2TELEFADE);
1364                 S_StartSound(mo, sfx_telept);
1365                 P_SetMobjState(actor, S_SOR2_TELE1);
1366                 S_StartSound(actor, sfx_telept);
1367                 actor->z = actor->floorz;
1368                 actor->angle = BossSpots[i%BossSpotCount].angle;
1369                 actor->momx = actor->momy = actor->momz = 0;
1370         }
1371 }
1372
1373 //----------------------------------------------------------------------------
1374 //
1375 // PROC A_Srcr2Decide
1376 //
1377 //----------------------------------------------------------------------------
1378
1379 void A_Srcr2Decide(mobj_t *actor)
1380 {
1381         static int chance[] =
1382         {
1383                 192, 120, 120, 120, 64, 64, 32, 16, 0
1384         };
1385
1386         if(!BossSpotCount)
1387         { // No spots
1388                 return;
1389         }
1390         if(P_Random() < chance[actor->health/(actor->info->spawnhealth/8)])
1391         {
1392                 P_DSparilTeleport(actor);
1393         }
1394 }
1395
1396 //----------------------------------------------------------------------------
1397 //
1398 // PROC A_Srcr2Attack
1399 //
1400 //----------------------------------------------------------------------------
1401
1402 void A_Srcr2Attack(mobj_t *actor)
1403 {
1404         int chance;
1405
1406         if(!actor->target)
1407         {
1408                 return;
1409         }
1410         S_StartSound(NULL, actor->info->attacksound);
1411         if(P_CheckMeleeRange(actor))
1412         {
1413                 P_DamageMobj(actor->target, actor, actor, HITDICE(20));
1414                 return;
1415         }
1416         chance = actor->health < actor->info->spawnhealth/2 ? 96 : 48;
1417         if(P_Random() < chance)
1418         { // Wizard spawners
1419                 P_SpawnMissileAngle(actor, MT_SOR2FX2,
1420                         actor->angle-ANG45, FRACUNIT/2);
1421                 P_SpawnMissileAngle(actor, MT_SOR2FX2,
1422                         actor->angle+ANG45, FRACUNIT/2);
1423         }
1424         else
1425         { // Blue bolt
1426                 P_SpawnMissile(actor, actor->target, MT_SOR2FX1);
1427         }
1428 }
1429
1430 //----------------------------------------------------------------------------
1431 //
1432 // PROC A_BlueSpark
1433 //
1434 //----------------------------------------------------------------------------
1435
1436 void A_BlueSpark(mobj_t *actor)
1437 {
1438         int i;
1439         mobj_t *mo;
1440
1441         for(i = 0; i < 2; i++)
1442         {
1443                 mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SOR2FXSPARK);
1444                 mo->momx = (P_Random()-P_Random())<<9;
1445                 mo->momy = (P_Random()-P_Random())<<9;
1446                 mo->momz = FRACUNIT+(P_Random()<<8);
1447         }
1448 }
1449
1450 //----------------------------------------------------------------------------
1451 //
1452 // PROC A_GenWizard
1453 //
1454 //----------------------------------------------------------------------------
1455
1456 void A_GenWizard(mobj_t *actor)
1457 {
1458         mobj_t *mo;
1459         mobj_t *fog;
1460
1461         mo = P_SpawnMobj(actor->x, actor->y,
1462                 actor->z-mobjinfo[MT_WIZARD].height/2, MT_WIZARD);
1463         if(P_TestMobjLocation(mo) == false)
1464         { // Didn't fit
1465                 P_RemoveMobj(mo);
1466                 return;
1467         }
1468         actor->momx = actor->momy = actor->momz = 0;
1469         P_SetMobjState(actor, mobjinfo[actor->type].deathstate);
1470         actor->flags &= ~MF_MISSILE;
1471         fog = P_SpawnMobj(actor->x, actor->y, actor->z, MT_TFOG);
1472         S_StartSound(fog, sfx_telept);
1473 }
1474
1475 //----------------------------------------------------------------------------
1476 //
1477 // PROC A_Sor2DthInit
1478 //
1479 //----------------------------------------------------------------------------
1480
1481 void A_Sor2DthInit(mobj_t *actor)
1482 {
1483         actor->special1 = 7; // Animation loop counter
1484         P_Massacre(); // Kill monsters early
1485 }
1486
1487 //----------------------------------------------------------------------------
1488 //
1489 // PROC A_Sor2DthLoop
1490 //
1491 //----------------------------------------------------------------------------
1492
1493 void A_Sor2DthLoop(mobj_t *actor)
1494 {
1495         if(--actor->special1)
1496         { // Need to loop
1497                 P_SetMobjState(actor, S_SOR2_DIE4);
1498         }
1499 }
1500
1501 //----------------------------------------------------------------------------
1502 //
1503 // D'Sparil Sound Routines
1504 //
1505 //----------------------------------------------------------------------------
1506
1507 void A_SorZap(mobj_t *actor) {S_StartSound(NULL, sfx_sorzap);}
1508 void A_SorRise(mobj_t *actor) {S_StartSound(NULL, sfx_sorrise);}
1509 void A_SorDSph(mobj_t *actor) {S_StartSound(NULL, sfx_sordsph);}
1510 void A_SorDExp(mobj_t *actor) {S_StartSound(NULL, sfx_sordexp);}
1511 void A_SorDBon(mobj_t *actor) {S_StartSound(NULL, sfx_sordbon);}
1512 void A_SorSightSnd(mobj_t *actor) {S_StartSound(NULL, sfx_sorsit);}
1513
1514 //----------------------------------------------------------------------------
1515 //
1516 // PROC A_MinotaurAtk1
1517 //
1518 // Melee attack.
1519 //
1520 //----------------------------------------------------------------------------
1521
1522 void A_MinotaurAtk1(mobj_t *actor)
1523 {
1524         player_t *player;
1525
1526         if(!actor->target)
1527         {
1528                 return;
1529         }
1530         S_StartSound(actor, sfx_stfpow);
1531         if(P_CheckMeleeRange(actor))
1532         {
1533                 P_DamageMobj(actor->target, actor, actor, HITDICE(4));
1534                 if((player = actor->target->player) != NULL)
1535                 { // Squish the player
1536                         player->deltaviewheight = -16*FRACUNIT;
1537                 }
1538         }
1539 }
1540
1541 //----------------------------------------------------------------------------
1542 //
1543 // PROC A_MinotaurDecide
1544 //
1545 // Choose a missile attack.
1546 //
1547 //----------------------------------------------------------------------------
1548
1549 #define MNTR_CHARGE_SPEED (13*FRACUNIT)
1550
1551 void A_MinotaurDecide(mobj_t *actor)
1552 {
1553         angle_t angle;
1554         mobj_t *target;
1555         int dist;
1556
1557         target = actor->target;
1558         if(!target)
1559         {
1560                 return;
1561         }
1562         S_StartSound(actor, sfx_minsit);
1563         dist = P_AproxDistance(actor->x-target->x, actor->y-target->y);
1564         if(target->z+target->height > actor->z
1565                 && target->z+target->height < actor->z+actor->height
1566                 && dist < 8*64*FRACUNIT
1567                 && dist > 1*64*FRACUNIT
1568                 && P_Random() < 150)
1569         { // Charge attack
1570                 // Don't call the state function right away
1571                 P_SetMobjStateNF(actor, S_MNTR_ATK4_1);
1572                 actor->flags |= MF_SKULLFLY;
1573                 A_FaceTarget(actor);
1574                 angle = actor->angle>>ANGLETOFINESHIFT;
1575                 actor->momx = FixedMul(MNTR_CHARGE_SPEED, finecosine[angle]);
1576                 actor->momy = FixedMul(MNTR_CHARGE_SPEED, finesine[angle]);
1577                 actor->special1 = 35/2; // Charge duration
1578         }
1579         else if(target->z == target->floorz
1580                 && dist < 9*64*FRACUNIT
1581                 && P_Random() < 220)
1582         { // Floor fire attack
1583                 P_SetMobjState(actor, S_MNTR_ATK3_1);
1584                 actor->special2 = 0;
1585         }
1586         else
1587         { // Swing attack
1588                 A_FaceTarget(actor);
1589                 // Don't need to call P_SetMobjState because the current state
1590                 // falls through to the swing attack
1591         }
1592 }
1593
1594 //----------------------------------------------------------------------------
1595 //
1596 // PROC A_MinotaurCharge
1597 //
1598 //----------------------------------------------------------------------------
1599
1600 void A_MinotaurCharge(mobj_t *actor)
1601 {
1602         mobj_t *puff;
1603
1604         if(actor->special1)
1605         {
1606                 puff = P_SpawnMobj(actor->x, actor->y, actor->z, MT_PHOENIXPUFF);
1607                 puff->momz = 2*FRACUNIT;
1608                 actor->special1--;
1609         }
1610         else
1611         {
1612                 actor->flags &= ~MF_SKULLFLY;
1613                 P_SetMobjState(actor, actor->info->seestate);
1614         }
1615 }
1616
1617 //----------------------------------------------------------------------------
1618 //
1619 // PROC A_MinotaurAtk2
1620 //
1621 // Swing attack.
1622 //
1623 //----------------------------------------------------------------------------
1624
1625 void A_MinotaurAtk2(mobj_t *actor)
1626 {
1627         mobj_t *mo;
1628         angle_t angle;
1629         fixed_t momz;
1630
1631         if(!actor->target)
1632         {
1633                 return;
1634         }
1635         S_StartSound(actor, sfx_minat2);
1636         if(P_CheckMeleeRange(actor))
1637         {
1638                 P_DamageMobj(actor->target, actor, actor, HITDICE(5));
1639                 return;
1640         }
1641         mo = P_SpawnMissile(actor, actor->target, MT_MNTRFX1);
1642         if(mo)
1643         {
1644                 S_StartSound(mo, sfx_minat2);
1645                 momz = mo->momz;
1646                 angle = mo->angle;
1647                 P_SpawnMissileAngle(actor, MT_MNTRFX1, angle-(ANG45/8), momz);
1648                 P_SpawnMissileAngle(actor, MT_MNTRFX1, angle+(ANG45/8), momz);
1649                 P_SpawnMissileAngle(actor, MT_MNTRFX1, angle-(ANG45/16), momz);
1650                 P_SpawnMissileAngle(actor, MT_MNTRFX1, angle+(ANG45/16), momz);
1651         }
1652 }
1653
1654 //----------------------------------------------------------------------------
1655 //
1656 // PROC A_MinotaurAtk3
1657 //
1658 // Floor fire attack.
1659 //
1660 //----------------------------------------------------------------------------
1661
1662 void A_MinotaurAtk3(mobj_t *actor)
1663 {
1664         mobj_t *mo;
1665         player_t *player;
1666
1667         if(!actor->target)
1668         {
1669                 return;
1670         }
1671         if(P_CheckMeleeRange(actor))
1672         {
1673                 P_DamageMobj(actor->target, actor, actor, HITDICE(5));
1674                 if((player = actor->target->player) != NULL)
1675                 { // Squish the player
1676                         player->deltaviewheight = -16*FRACUNIT;
1677                 }
1678         }
1679         else
1680         {
1681                 mo = P_SpawnMissile(actor, actor->target, MT_MNTRFX2);
1682                 if(mo != NULL)
1683                 {
1684                         S_StartSound(mo, sfx_minat1);
1685                 }
1686         }
1687         if(P_Random() < 192 && actor->special2 == 0)
1688         {
1689                 P_SetMobjState(actor, S_MNTR_ATK3_4);
1690                 actor->special2 = 1;
1691         }
1692 }
1693
1694 //----------------------------------------------------------------------------
1695 //
1696 // PROC A_MntrFloorFire
1697 //
1698 //----------------------------------------------------------------------------
1699
1700 void A_MntrFloorFire(mobj_t *actor)
1701 {
1702         mobj_t *mo;
1703
1704         actor->z = actor->floorz;
1705         mo = P_SpawnMobj(actor->x+((P_Random()-P_Random())<<10),
1706                 actor->y+((P_Random()-P_Random())<<10), ONFLOORZ, MT_MNTRFX3);
1707         mo->target = actor->target;
1708         mo->momx = 1; // Force block checking
1709         P_CheckMissileSpawn(mo);
1710 }
1711
1712 //----------------------------------------------------------------------------
1713 //
1714 // PROC A_BeastAttack
1715 //
1716 //----------------------------------------------------------------------------
1717
1718 void A_BeastAttack(mobj_t *actor)
1719 {
1720         if(!actor->target)
1721         {
1722                 return;
1723         }
1724         S_StartSound(actor, actor->info->attacksound);
1725         if(P_CheckMeleeRange(actor))
1726         {
1727                 P_DamageMobj(actor->target, actor, actor, HITDICE(3));
1728                 return;
1729         }
1730         P_SpawnMissile(actor, actor->target, MT_BEASTBALL);
1731 }
1732
1733 //----------------------------------------------------------------------------
1734 //
1735 // PROC A_HeadAttack
1736 //
1737 //----------------------------------------------------------------------------
1738
1739 void A_HeadAttack(mobj_t *actor)
1740 {
1741         int i;
1742         mobj_t *fire;
1743         mobj_t *baseFire;
1744         mobj_t *mo;
1745         mobj_t *target;
1746         int randAttack;
1747         static int atkResolve1[] = { 50, 150 };
1748         static int atkResolve2[] = { 150, 200 };
1749         int dist;
1750
1751         // Ice ball             (close 20% : far 60%)
1752         // Fire column  (close 40% : far 20%)
1753         // Whirlwind    (close 40% : far 20%)
1754         // Distance threshold = 8 cells
1755
1756         target = actor->target;
1757         if(target == NULL)
1758         {
1759                 return;
1760         }
1761         A_FaceTarget(actor);
1762         if(P_CheckMeleeRange(actor))
1763         {
1764                 P_DamageMobj(target, actor, actor, HITDICE(6));
1765                 return;
1766         }
1767         dist = P_AproxDistance(actor->x-target->x, actor->y-target->y)
1768                 > 8*64*FRACUNIT;
1769         randAttack = P_Random();
1770         if(randAttack < atkResolve1[dist])
1771         { // Ice ball
1772                 P_SpawnMissile(actor, target, MT_HEADFX1);
1773                 S_StartSound(actor, sfx_hedat2);        
1774         }
1775         else if(randAttack < atkResolve2[dist])
1776         { // Fire column
1777                 baseFire = P_SpawnMissile(actor, target, MT_HEADFX3);
1778                 if(baseFire != NULL)
1779                 {
1780                         P_SetMobjState(baseFire, S_HEADFX3_4); // Don't grow
1781                         for(i = 0; i < 5; i++)
1782                         {
1783                                 fire = P_SpawnMobj(baseFire->x, baseFire->y,
1784                                         baseFire->z, MT_HEADFX3);
1785                                 if(i == 0)
1786                                 {
1787                                         S_StartSound(actor, sfx_hedat1);
1788                                 }
1789                                 fire->target = baseFire->target;
1790                                 fire->angle = baseFire->angle;
1791                                 fire->momx = baseFire->momx;
1792                                 fire->momy = baseFire->momy;
1793                                 fire->momz = baseFire->momz;
1794                                 fire->damage = 0;
1795                                 fire->health = (i+1)*2;
1796                                 P_CheckMissileSpawn(fire);
1797                         }
1798                 }
1799         }
1800         else
1801         { // Whirlwind
1802                 mo = P_SpawnMissile(actor, target, MT_WHIRLWIND);
1803                 if(mo != NULL)
1804                 {
1805                         mo->z -= 32*FRACUNIT;
1806                         mo->special1 = (intptr_t)target;
1807                         mo->special2 = 50; // Timer for active sound
1808                         mo->health = 20*TICSPERSEC; // Duration
1809                         S_StartSound(actor, sfx_hedat3);                        
1810                 }
1811         }
1812 }
1813
1814 //----------------------------------------------------------------------------
1815 //
1816 // PROC A_WhirlwindSeek
1817 //
1818 //----------------------------------------------------------------------------
1819
1820 void A_WhirlwindSeek(mobj_t *actor)
1821 {
1822         actor->health -= 3;
1823         if(actor->health < 0)
1824         {
1825                 actor->momx = actor->momy = actor->momz = 0;
1826                 P_SetMobjState(actor, mobjinfo[actor->type].deathstate);
1827                 actor->flags &= ~MF_MISSILE;
1828                 return;
1829         }
1830         if((actor->special2 -= 3) < 0)
1831         {
1832                 actor->special2 = 58+(P_Random()&31);
1833                 S_StartSound(actor, sfx_hedat3);
1834         }
1835         if(actor->special1
1836                 && (((mobj_t *)(actor->special1))->flags&MF_SHADOW))
1837         {
1838                 return;
1839         }
1840         P_SeekerMissile(actor, ANGLE_1*10, ANGLE_1*30);
1841 }
1842
1843 //----------------------------------------------------------------------------
1844 //
1845 // PROC A_HeadIceImpact
1846 //
1847 //----------------------------------------------------------------------------
1848
1849 void A_HeadIceImpact(mobj_t *ice)
1850 {
1851         int i;
1852         angle_t angle;
1853         mobj_t *shard;
1854
1855         for(i = 0; i < 8; i++)
1856         {
1857                 shard = P_SpawnMobj(ice->x, ice->y, ice->z, MT_HEADFX2);
1858                 angle = i*ANG45;
1859                 shard->target = ice->target;
1860                 shard->angle = angle;
1861                 angle >>= ANGLETOFINESHIFT;
1862                 shard->momx = FixedMul(shard->info->speed, finecosine[angle]);
1863                 shard->momy = FixedMul(shard->info->speed, finesine[angle]);
1864                 shard->momz = -.6*FRACUNIT;
1865                 P_CheckMissileSpawn(shard);
1866         }
1867 }
1868
1869 //----------------------------------------------------------------------------
1870 //
1871 // PROC A_HeadFireGrow
1872 //
1873 //----------------------------------------------------------------------------
1874
1875 void A_HeadFireGrow(mobj_t *fire)
1876 {
1877         fire->health--;
1878         fire->z += 9*FRACUNIT;
1879         if(fire->health == 0)
1880         {
1881                 fire->damage = fire->info->damage;
1882                 P_SetMobjState(fire, S_HEADFX3_4);
1883         }
1884 }
1885
1886 //----------------------------------------------------------------------------
1887 //
1888 // PROC A_SnakeAttack
1889 //
1890 //----------------------------------------------------------------------------
1891
1892 void A_SnakeAttack(mobj_t *actor)
1893 {
1894         if(!actor->target)
1895         {
1896                 P_SetMobjState(actor, S_SNAKE_WALK1);
1897                 return;
1898         }
1899         S_StartSound(actor, actor->info->attacksound);
1900         A_FaceTarget(actor);
1901         P_SpawnMissile(actor, actor->target, MT_SNAKEPRO_A);
1902 }
1903
1904 //----------------------------------------------------------------------------
1905 //
1906 // PROC A_SnakeAttack2
1907 //
1908 //----------------------------------------------------------------------------
1909
1910 void A_SnakeAttack2(mobj_t *actor)
1911 {
1912         if(!actor->target)
1913         {
1914                 P_SetMobjState(actor, S_SNAKE_WALK1);
1915                 return;
1916         }
1917         S_StartSound(actor, actor->info->attacksound);
1918         A_FaceTarget(actor);
1919         P_SpawnMissile(actor, actor->target, MT_SNAKEPRO_B);
1920 }
1921
1922 //----------------------------------------------------------------------------
1923 //
1924 // PROC A_ClinkAttack
1925 //
1926 //----------------------------------------------------------------------------
1927
1928 void A_ClinkAttack(mobj_t *actor)
1929 {
1930         int damage;
1931
1932         if(!actor->target)
1933         {
1934                 return;
1935         }
1936         S_StartSound(actor, actor->info->attacksound);
1937         if(P_CheckMeleeRange(actor))
1938         {
1939                 damage = ((P_Random()%7)+3);
1940                 P_DamageMobj(actor->target, actor, actor, damage);
1941         }
1942 }
1943
1944 //----------------------------------------------------------------------------
1945 //
1946 // PROC A_GhostOff
1947 //
1948 //----------------------------------------------------------------------------
1949
1950 void A_GhostOff(mobj_t *actor)
1951 {
1952         actor->flags &= ~MF_SHADOW;
1953 }
1954
1955 //----------------------------------------------------------------------------
1956 //
1957 // PROC A_WizAtk1
1958 //
1959 //----------------------------------------------------------------------------
1960
1961 void A_WizAtk1(mobj_t *actor)
1962 {
1963         A_FaceTarget(actor);
1964         actor->flags &= ~MF_SHADOW;
1965 }
1966
1967 //----------------------------------------------------------------------------
1968 //
1969 // PROC A_WizAtk2
1970 //
1971 //----------------------------------------------------------------------------
1972
1973 void A_WizAtk2(mobj_t *actor)
1974 {
1975         A_FaceTarget(actor);
1976         actor->flags |= MF_SHADOW;
1977 }
1978
1979 //----------------------------------------------------------------------------
1980 //
1981 // PROC A_WizAtk3
1982 //
1983 //----------------------------------------------------------------------------
1984
1985 void A_WizAtk3(mobj_t *actor)
1986 {
1987         mobj_t *mo;
1988         angle_t angle;
1989         fixed_t momz;
1990
1991         actor->flags &= ~MF_SHADOW;
1992         if(!actor->target)
1993         {
1994                 return;
1995         }
1996         S_StartSound(actor, actor->info->attacksound);
1997         if(P_CheckMeleeRange(actor))
1998         {
1999                 P_DamageMobj(actor->target, actor, actor, HITDICE(4));
2000                 return;
2001         }
2002         mo = P_SpawnMissile(actor, actor->target, MT_WIZFX1);
2003         if(mo)
2004         {
2005                 momz = mo->momz;
2006                 angle = mo->angle;
2007                 P_SpawnMissileAngle(actor, MT_WIZFX1, angle-(ANG45/8), momz);
2008                 P_SpawnMissileAngle(actor, MT_WIZFX1, angle+(ANG45/8), momz);
2009         }
2010 }
2011
2012 //----------------------------------------------------------------------------
2013 //
2014 // PROC A_Scream
2015 //
2016 //----------------------------------------------------------------------------
2017
2018 void A_Scream(mobj_t *actor)
2019 {
2020         switch(actor->type)
2021         {
2022                 case MT_CHICPLAYER:
2023                 case MT_SORCERER1:
2024                 case MT_MINOTAUR:
2025                         // Make boss death sounds full volume
2026                         S_StartSound(NULL, actor->info->deathsound);
2027                         break;
2028                 case MT_PLAYER:
2029                         // Handle the different player death screams
2030                         if(actor->special1 < 10)
2031                         { // Wimpy death sound
2032                                 S_StartSound(actor, sfx_plrwdth);
2033                         }
2034                         else if(actor->health > -50)
2035                         { // Normal death sound
2036                                 S_StartSound(actor, actor->info->deathsound);
2037                         }
2038                         else if(actor->health > -100)
2039                         { // Crazy death sound
2040                                 S_StartSound(actor, sfx_plrcdth);
2041                         }
2042                         else
2043                         { // Extreme death sound
2044                                 S_StartSound(actor, sfx_gibdth);
2045                         }
2046                         break;
2047                 default:
2048                         S_StartSound(actor, actor->info->deathsound);
2049                         break;
2050         }
2051 }
2052
2053 //---------------------------------------------------------------------------
2054 //
2055 // PROC P_DropItem
2056 //
2057 //---------------------------------------------------------------------------
2058
2059 void P_DropItem(mobj_t *source, mobjtype_t type, int special, int chance)
2060 {
2061         mobj_t *mo;
2062
2063         if(P_Random() > chance)
2064         {
2065                 return;
2066         }
2067         mo = P_SpawnMobj(source->x, source->y,
2068                 source->z+(source->height>>1), type);
2069         mo->momx = (P_Random()-P_Random())<<8;
2070         mo->momy = (P_Random()-P_Random())<<8;
2071         mo->momz = FRACUNIT*5+(P_Random()<<10);
2072         mo->flags |= MF_DROPPED;
2073         mo->health = special;
2074 }
2075
2076 //----------------------------------------------------------------------------
2077 //
2078 // PROC A_NoBlocking
2079 //
2080 //----------------------------------------------------------------------------
2081
2082 void A_NoBlocking(mobj_t *actor)
2083 {
2084         actor->flags &= ~MF_SOLID;
2085         // Check for monsters dropping things
2086         switch(actor->type)
2087         {
2088                 case MT_MUMMY:
2089                 case MT_MUMMYLEADER:
2090                 case MT_MUMMYGHOST:
2091                 case MT_MUMMYLEADERGHOST:
2092                         P_DropItem(actor, MT_AMGWNDWIMPY, 3, 84);
2093                         break;
2094                 case MT_KNIGHT:
2095                 case MT_KNIGHTGHOST:
2096                         P_DropItem(actor, MT_AMCBOWWIMPY, 5, 84);
2097                         break;
2098                 case MT_WIZARD:
2099                         P_DropItem(actor, MT_AMBLSRWIMPY, 10, 84);
2100                         P_DropItem(actor, MT_ARTITOMEOFPOWER, 0, 4);
2101                         break;
2102                 case MT_HEAD:
2103                         P_DropItem(actor, MT_AMBLSRWIMPY, 10, 84);
2104                         P_DropItem(actor, MT_ARTIEGG, 0, 51);
2105                         break;
2106                 case MT_BEAST:
2107                         P_DropItem(actor, MT_AMCBOWWIMPY, 10, 84);
2108                         break;
2109                 case MT_CLINK:
2110                         P_DropItem(actor, MT_AMSKRDWIMPY, 20, 84);
2111                         break;
2112                 case MT_SNAKE:
2113                         P_DropItem(actor, MT_AMPHRDWIMPY, 5, 84);
2114                         break;
2115                 case MT_MINOTAUR:
2116                         P_DropItem(actor, MT_ARTISUPERHEAL, 0, 51);
2117                         P_DropItem(actor, MT_AMPHRDWIMPY, 10, 84);
2118                         break;
2119                 default:
2120                         break;
2121         }
2122 }
2123
2124 //----------------------------------------------------------------------------
2125 //
2126 // PROC A_Explode
2127 //
2128 // Handles a bunch of exploding things.
2129 //
2130 //----------------------------------------------------------------------------
2131
2132 void A_Explode(mobj_t *actor)
2133 {
2134         int damage;
2135
2136         damage = 128;
2137         switch(actor->type)
2138         {
2139                 case MT_FIREBOMB: // Time Bombs
2140                         actor->z += 32*FRACUNIT;
2141                         actor->flags &= ~MF_SHADOW;
2142                         break;
2143                 case MT_MNTRFX2: // Minotaur floor fire
2144                         damage = 24;
2145                         break;
2146                 case MT_SOR2FX1: // D'Sparil missile
2147                         damage = 80+(P_Random()&31);
2148                         break;
2149                 default:
2150                         break;
2151         }
2152         P_RadiusAttack(actor, actor->target, damage);
2153         P_HitFloor(actor);
2154 }
2155
2156 //----------------------------------------------------------------------------
2157 //
2158 // PROC A_PodPain
2159 //
2160 //----------------------------------------------------------------------------
2161
2162 void A_PodPain(mobj_t *actor)
2163 {
2164         int i;
2165         int count;
2166         int chance;
2167         mobj_t *goo;
2168
2169         chance = P_Random();
2170         if(chance < 128)
2171         {
2172                 return;
2173         }
2174         count = chance > 240 ? 2 : 1;
2175         for(i = 0; i < count; i++)
2176         {
2177                 goo = P_SpawnMobj(actor->x, actor->y,
2178                         actor->z+48*FRACUNIT, MT_PODGOO);
2179                 goo->target = actor;
2180                 goo->momx = (P_Random()-P_Random())<<9;
2181                 goo->momy = (P_Random()-P_Random())<<9;
2182                 goo->momz = FRACUNIT/2+(P_Random()<<9);
2183         }
2184 }
2185
2186 //----------------------------------------------------------------------------
2187 //
2188 // PROC A_RemovePod
2189 //
2190 //----------------------------------------------------------------------------
2191
2192 void A_RemovePod(mobj_t *actor)
2193 {
2194         mobj_t *mo;
2195
2196         if(actor->special2)
2197         {
2198                 mo = (mobj_t *)actor->special2;
2199                 if(mo->special1 > 0)
2200                 {
2201                         mo->special1--;
2202                 }
2203         }
2204 }
2205
2206 //----------------------------------------------------------------------------
2207 //
2208 // PROC A_MakePod
2209 //
2210 //----------------------------------------------------------------------------
2211
2212 #define MAX_GEN_PODS 16
2213
2214 void A_MakePod(mobj_t *actor)
2215 {
2216         mobj_t *mo;
2217         fixed_t x;
2218         fixed_t y;
2219         fixed_t z;
2220
2221         if(actor->special1 == MAX_GEN_PODS)
2222         { // Too many generated pods
2223                 return;
2224         }
2225         x = actor->x;
2226         y = actor->y;
2227         z = actor->z;
2228         mo = P_SpawnMobj(x, y, ONFLOORZ, MT_POD);
2229         if(P_CheckPosition(mo, x, y) == false)
2230         { // Didn't fit
2231                 P_RemoveMobj(mo);
2232                 return;
2233         }
2234         P_SetMobjState(mo, S_POD_GROW1);
2235         P_ThrustMobj(mo, P_Random()<<24, (fixed_t)(4.5*FRACUNIT));
2236         S_StartSound(mo, sfx_newpod);
2237         actor->special1++; // Increment generated pod count
2238         mo->special2 = (intptr_t)actor; // Link the generator to the pod
2239         return;
2240 }
2241
2242 //----------------------------------------------------------------------------
2243 //
2244 // PROC P_Massacre
2245 //
2246 // Kills all monsters.
2247 //
2248 //----------------------------------------------------------------------------
2249
2250 void P_Massacre(void)
2251 {
2252         mobj_t *mo;
2253         thinker_t *think;
2254
2255         for(think = thinkercap.next; think != &thinkercap;
2256                 think = think->next)
2257         {
2258                 if(think->function != P_MobjThinker)
2259                 { // Not a mobj thinker
2260                         continue;
2261                 }
2262                 mo = (mobj_t *)think;
2263                 if((mo->flags&MF_COUNTKILL) && (mo->health > 0))
2264                 {
2265                         P_DamageMobj(mo, NULL, NULL, 10000);
2266                 }
2267         }
2268 }
2269
2270 //----------------------------------------------------------------------------
2271 //
2272 // PROC A_BossDeath
2273 //
2274 // Trigger special effects if all bosses are dead.
2275 //
2276 //----------------------------------------------------------------------------
2277
2278 void A_BossDeath(mobj_t *actor)
2279 {
2280         mobj_t *mo;
2281         thinker_t *think;
2282         line_t dummyLine;
2283         static mobjtype_t bossType[6] =
2284         {
2285                 MT_HEAD,
2286                 MT_MINOTAUR,
2287                 MT_SORCERER2,
2288                 MT_HEAD,
2289                 MT_MINOTAUR,
2290                 -1
2291         };
2292
2293         if(gamemap != 8)
2294         { // Not a boss level
2295                 return;
2296         }
2297         if(actor->type != bossType[gameepisode-1])
2298         { // Not considered a boss in this episode
2299                 return;
2300         }
2301         // Make sure all bosses are dead
2302         for(think = thinkercap.next; think != &thinkercap; think = think->next)
2303         {
2304                 if(think->function != P_MobjThinker)
2305                 { // Not a mobj thinker
2306                         continue;
2307                 }
2308                 mo = (mobj_t *)think;
2309                 if((mo != actor) && (mo->type == actor->type) && (mo->health > 0))
2310                 { // Found a living boss
2311                         return;
2312                 }
2313         }
2314         if(gameepisode > 1)
2315         { // Kill any remaining monsters
2316                 P_Massacre();
2317         }
2318         dummyLine.tag = 666;
2319         EV_DoFloor(&dummyLine, lowerFloor);
2320 }
2321
2322 //----------------------------------------------------------------------------
2323 //
2324 // PROC A_ESound
2325 //
2326 //----------------------------------------------------------------------------
2327
2328 void A_ESound(mobj_t *mo)
2329 {
2330         int sound=0;
2331
2332         switch(mo->type)
2333         {
2334                 case MT_SOUNDWATERFALL:
2335                         sound = sfx_waterfl;
2336                         break;
2337                 case MT_SOUNDWIND:
2338                         sound = sfx_wind;
2339                         break;
2340                 default:
2341                         break;
2342         }
2343         S_StartSound(mo, sound);
2344 }
2345
2346 //----------------------------------------------------------------------------
2347 //
2348 // PROC A_SpawnTeleGlitter
2349 //
2350 //----------------------------------------------------------------------------
2351
2352 void A_SpawnTeleGlitter(mobj_t *actor)
2353 {
2354         mobj_t *mo;
2355
2356         mo = P_SpawnMobj(actor->x+((P_Random()&31)-16)*FRACUNIT,
2357                 actor->y+((P_Random()&31)-16)*FRACUNIT,
2358                 actor->subsector->sector->floorheight, MT_TELEGLITTER);
2359         mo->momz = FRACUNIT/4;
2360 }
2361
2362 //----------------------------------------------------------------------------
2363 //
2364 // PROC A_SpawnTeleGlitter2
2365 //
2366 //----------------------------------------------------------------------------
2367
2368 void A_SpawnTeleGlitter2(mobj_t *actor)
2369 {
2370         mobj_t *mo;
2371
2372         mo = P_SpawnMobj(actor->x+((P_Random()&31)-16)*FRACUNIT,
2373                 actor->y+((P_Random()&31)-16)*FRACUNIT,
2374                 actor->subsector->sector->floorheight, MT_TELEGLITTER2);
2375         mo->momz = FRACUNIT/4;
2376 }
2377
2378 //----------------------------------------------------------------------------
2379 //
2380 // PROC A_AccTeleGlitter
2381 //
2382 //----------------------------------------------------------------------------
2383
2384 void A_AccTeleGlitter(mobj_t *actor)
2385 {
2386         if(++actor->health > 35)
2387         {
2388                 actor->momz += actor->momz/2;
2389         }
2390 }
2391
2392 //----------------------------------------------------------------------------
2393 //
2394 // PROC A_InitKeyGizmo
2395 //
2396 //----------------------------------------------------------------------------
2397
2398 void A_InitKeyGizmo(mobj_t *gizmo)
2399 {
2400         mobj_t *mo;
2401         statenum_t state=0;
2402
2403         switch(gizmo->type)
2404         {
2405                 case MT_KEYGIZMOBLUE:
2406                         state = S_KGZ_BLUEFLOAT1;
2407                         break;
2408                 case MT_KEYGIZMOGREEN:
2409                         state = S_KGZ_GREENFLOAT1;
2410                         break;
2411                 case MT_KEYGIZMOYELLOW:
2412                         state = S_KGZ_YELLOWFLOAT1;
2413                         break;
2414                 default:
2415                         break;
2416         }
2417         mo = P_SpawnMobj(gizmo->x, gizmo->y, gizmo->z+60*FRACUNIT,
2418                 MT_KEYGIZMOFLOAT);
2419         P_SetMobjState(mo, state);
2420 }
2421
2422 //----------------------------------------------------------------------------
2423 //
2424 // PROC A_VolcanoSet
2425 //
2426 //----------------------------------------------------------------------------
2427
2428 void A_VolcanoSet(mobj_t *volcano)
2429 {
2430         volcano->tics = 105+(P_Random()&127);
2431 }
2432
2433 //----------------------------------------------------------------------------
2434 //
2435 // PROC A_VolcanoBlast
2436 //
2437 //----------------------------------------------------------------------------
2438
2439 void A_VolcanoBlast(mobj_t *volcano)
2440 {
2441         int i;
2442         int count;
2443         mobj_t *blast;
2444         angle_t angle;
2445
2446         count = 1+(P_Random()%3);
2447         for(i = 0; i < count; i++)
2448         {
2449                 blast = P_SpawnMobj(volcano->x, volcano->y,
2450                         volcano->z+44*FRACUNIT, MT_VOLCANOBLAST); // MT_VOLCANOBLAST
2451                 blast->target = volcano;
2452                 angle = P_Random()<<24;
2453                 blast->angle = angle;
2454                 angle >>= ANGLETOFINESHIFT;
2455                 blast->momx = FixedMul(1*FRACUNIT, finecosine[angle]);
2456                 blast->momy = FixedMul(1*FRACUNIT, finesine[angle]);
2457                 blast->momz = (2.5*FRACUNIT)+(P_Random()<<10);
2458                 S_StartSound(blast, sfx_volsht);
2459                 P_CheckMissileSpawn(blast);
2460         }
2461 }
2462
2463 //----------------------------------------------------------------------------
2464 //
2465 // PROC A_VolcBallImpact
2466 //
2467 //----------------------------------------------------------------------------
2468
2469 void A_VolcBallImpact(mobj_t *ball)
2470 {
2471         int i;
2472         mobj_t *tiny;
2473         angle_t angle;
2474
2475         if(ball->z <= ball->floorz)
2476         {
2477                 ball->flags |= MF_NOGRAVITY;
2478                 ball->flags2 &= ~MF2_LOGRAV;
2479                 ball->z += 28*FRACUNIT;
2480                 //ball->momz = 3*FRACUNIT;
2481         }
2482         P_RadiusAttack(ball, ball->target, 25);
2483         for(i = 0; i < 4; i++)
2484         {
2485                 tiny = P_SpawnMobj(ball->x, ball->y, ball->z, MT_VOLCANOTBLAST);
2486                 tiny->target = ball;
2487                 angle = i*ANG90;
2488                 tiny->angle = angle;
2489                 angle >>= ANGLETOFINESHIFT;
2490                 tiny->momx = FixedMul(FRACUNIT*.7, finecosine[angle]);
2491                 tiny->momy = FixedMul(FRACUNIT*.7, finesine[angle]);
2492                 tiny->momz = FRACUNIT+(P_Random()<<9);
2493                 P_CheckMissileSpawn(tiny);
2494         }
2495 }
2496
2497 //----------------------------------------------------------------------------
2498 //
2499 // PROC A_SkullPop
2500 //
2501 //----------------------------------------------------------------------------
2502
2503 void A_SkullPop(mobj_t *actor)
2504 {
2505         mobj_t *mo;
2506         player_t *player;
2507
2508         actor->flags &= ~MF_SOLID;
2509         mo = P_SpawnMobj(actor->x, actor->y, actor->z+48*FRACUNIT,
2510                 MT_BLOODYSKULL);
2511         //mo->target = actor;
2512         mo->momx = (P_Random()-P_Random())<<9;
2513         mo->momy = (P_Random()-P_Random())<<9;
2514         mo->momz = FRACUNIT*2+(P_Random()<<6);
2515         // Attach player mobj to bloody skull
2516         player = actor->player;
2517         actor->player = NULL;
2518         mo->player = player;
2519         mo->health = actor->health;
2520         mo->angle = actor->angle;
2521         player->mo = mo;
2522         player->lookdir = 0;
2523         player->damagecount = 32;
2524 }
2525
2526 //----------------------------------------------------------------------------
2527 //
2528 // PROC A_CheckSkullFloor
2529 //
2530 //----------------------------------------------------------------------------
2531
2532 void A_CheckSkullFloor(mobj_t *actor)
2533 {
2534         if(actor->z <= actor->floorz)
2535         {
2536                 P_SetMobjState(actor, S_BLOODYSKULLX1);
2537         }
2538 }
2539
2540 //----------------------------------------------------------------------------
2541 //
2542 // PROC A_CheckSkullDone
2543 //
2544 //----------------------------------------------------------------------------
2545
2546 void A_CheckSkullDone(mobj_t *actor)
2547 {
2548         if(actor->special2 == 666)
2549         {
2550                 P_SetMobjState(actor, S_BLOODYSKULLX2);
2551         }
2552 }
2553
2554 //----------------------------------------------------------------------------
2555 //
2556 // PROC A_CheckBurnGone
2557 //
2558 //----------------------------------------------------------------------------
2559
2560 void A_CheckBurnGone(mobj_t *actor)
2561 {
2562         if(actor->special2 == 666)
2563         {
2564                 P_SetMobjState(actor, S_PLAY_FDTH20);
2565         }
2566 }
2567
2568 //----------------------------------------------------------------------------
2569 //
2570 // PROC A_FreeTargMobj
2571 //
2572 //----------------------------------------------------------------------------
2573
2574 void A_FreeTargMobj(mobj_t *mo)
2575 {
2576         mo->momx = mo->momy = mo->momz = 0;
2577         mo->z = mo->ceilingz+4*FRACUNIT;
2578         mo->flags &= ~(MF_SHOOTABLE|MF_FLOAT|MF_SKULLFLY|MF_SOLID);
2579         mo->flags |= MF_CORPSE|MF_DROPOFF|MF_NOGRAVITY;
2580         mo->flags2 &= ~(MF2_PASSMOBJ|MF2_LOGRAV);
2581         mo->player = NULL;
2582 }
2583
2584 //----------------------------------------------------------------------------
2585 //
2586 // PROC A_AddPlayerCorpse
2587 //
2588 //----------------------------------------------------------------------------
2589
2590 #define BODYQUESIZE 32
2591 mobj_t *bodyque[BODYQUESIZE];
2592 int bodyqueslot;
2593
2594 void A_AddPlayerCorpse(mobj_t *actor)
2595 {
2596         if(bodyqueslot >= BODYQUESIZE)
2597         { // Too many player corpses - remove an old one
2598                 P_RemoveMobj(bodyque[bodyqueslot%BODYQUESIZE]);
2599         }
2600         bodyque[bodyqueslot%BODYQUESIZE] = actor;
2601         bodyqueslot++;
2602 }
2603
2604 //----------------------------------------------------------------------------
2605 //
2606 // PROC A_FlameSnd
2607 //
2608 //----------------------------------------------------------------------------
2609
2610 void A_FlameSnd(mobj_t *actor)
2611 {
2612         S_StartSound(actor, sfx_hedat1); // Burn sound
2613 }
2614
2615 //----------------------------------------------------------------------------
2616 //
2617 // PROC A_HideThing
2618 //
2619 //----------------------------------------------------------------------------
2620
2621 void A_HideThing(mobj_t *actor)
2622 {
2623         //P_UnsetThingPosition(actor);
2624         actor->flags2 |= MF2_DONTDRAW;
2625 }
2626
2627 //----------------------------------------------------------------------------
2628 //
2629 // PROC A_UnHideThing
2630 //
2631 //----------------------------------------------------------------------------
2632
2633 void A_UnHideThing(mobj_t *actor)
2634 {
2635         //P_SetThingPosition(actor);
2636         actor->flags2 &= ~MF2_DONTDRAW;
2637 }