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