2 //**************************************************************************
4 //** p_enemy.c : Heretic 2 : Raven Software, Corp.
11 //**************************************************************************
21 extern fixed_t FloatBobOffsets[64];
24 //----------------------------------------------------------------------------
26 // PROC P_RecursiveSound
28 //----------------------------------------------------------------------------
32 void P_RecursiveSound(sector_t *sec, int soundblocks)
38 // Wake up all monsters in this sector
39 if(sec->validcount == validcount && sec->soundtraversed <= soundblocks+1)
43 sec->validcount = validcount;
44 sec->soundtraversed = soundblocks+1;
45 sec->soundtarget = soundtarget;
46 for(i = 0; i < sec->linecount; i++)
48 check = sec->lines[i];
49 if(!(check->flags&ML_TWOSIDED))
58 if(sides[check->sidenum[0]].sector == sec)
60 other = sides[check->sidenum[1]].sector;
64 other = sides[check->sidenum[0]].sector;
66 if(check->flags&ML_SOUNDBLOCK)
70 P_RecursiveSound(other, 1);
75 P_RecursiveSound(other, soundblocks);
80 //----------------------------------------------------------------------------
84 // If a monster yells at a player, it will alert other monsters to the
87 //----------------------------------------------------------------------------
89 void P_NoiseAlert(mobj_t *target, mobj_t *emmiter)
93 P_RecursiveSound(emmiter->subsector->sector, 0);
96 //----------------------------------------------------------------------------
98 // FUNC P_CheckMeleeRange
100 //----------------------------------------------------------------------------
102 boolean P_CheckMeleeRange(mobj_t *actor)
112 dist = P_AproxDistance(mo->x-actor->x, mo->y-actor->y);
113 if(dist >= MELEERANGE)
117 if(!P_CheckSight(actor, mo))
121 if(mo->z > actor->z+actor->height)
122 { // Target is higher than the attacker
125 else if(actor->z > mo->z+mo->height)
126 { // Attacker is higher
132 //----------------------------------------------------------------------------
134 // FUNC P_CheckMeleeRange2
136 //----------------------------------------------------------------------------
138 boolean P_CheckMeleeRange2(mobj_t *actor)
148 dist = P_AproxDistance(mo->x-actor->x, mo->y-actor->y);
149 if(dist >= MELEERANGE*2 || dist < MELEERANGE)
153 if(!P_CheckSight(actor, mo))
157 if(mo->z > actor->z+actor->height)
158 { // Target is higher than the attacker
161 else if(actor->z > mo->z+mo->height)
162 { // Attacker is higher
168 //----------------------------------------------------------------------------
170 // FUNC P_CheckMissileRange
172 //----------------------------------------------------------------------------
174 boolean P_CheckMissileRange(mobj_t *actor)
178 if(!P_CheckSight(actor, actor->target))
182 if(actor->flags&MF_JUSTHIT)
183 { // The target just hit the enemy, so fight back!
184 actor->flags &= ~MF_JUSTHIT;
187 if(actor->reactiontime)
188 { // Don't attack yet
191 dist = (P_AproxDistance(actor->x-actor->target->x,
192 actor->y-actor->target->y)>>FRACBITS)-64;
193 if(!actor->info->meleestate)
194 { // No melee attack, so fire more frequently
201 if(P_Random() < dist)
213 = Move in the current direction
214 = returns false if the move is blocked
218 fixed_t xspeed[8] = {FRACUNIT,47000,0,-47000,-FRACUNIT,-47000,0,47000};
219 fixed_t yspeed[8] = {0,47000,FRACUNIT,47000,0,-47000,-FRACUNIT,-47000};
221 #define MAXSPECIALCROSS 8
222 extern line_t *spechit[MAXSPECIALCROSS];
223 extern int numspechit;
225 boolean P_Move(mobj_t *actor)
231 if(actor->flags2&MF2_BLASTED) return(true);
232 if(actor->movedir == DI_NODIR)
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)
244 actor->z += FLOATSPEED;
248 actor->z -= FLOATSPEED;
250 actor->flags |= MF_INFLOAT;
257 actor->movedir = DI_NODIR;
261 ld = spechit[numspechit];
262 // if the special isn't a door that can be opened, return false
263 if(P_ActivateLine(ld, actor, 0, SPAC_USE))
267 /* Old version before use/cross/impact specials were combined
268 if(P_UseSpecialLine(actor, ld))
278 actor->flags &= ~MF_INFLOAT;
280 if(!(actor->flags&MF_FLOAT))
282 if(actor->z > actor->floorz)
286 actor->z = actor->floorz;
291 //----------------------------------------------------------------------------
295 // Attempts to move actor in its current (ob->moveangle) direction.
296 // If blocked by either a wall or an actor returns FALSE.
297 // If move is either clear of block only by a door, returns TRUE and sets.
298 // If a door is in the way, an OpenDoor call is made to start it opening.
300 //----------------------------------------------------------------------------
302 boolean P_TryWalk(mobj_t *actor)
308 actor->movecount = P_Random()&15;
320 dirtype_t opposite[] =
321 {DI_WEST, DI_SOUTHWEST, DI_SOUTH, DI_SOUTHEAST, DI_EAST, DI_NORTHEAST,
322 DI_NORTH, DI_NORTHWEST, DI_NODIR};
324 dirtype_t diags[] = {DI_NORTHWEST,DI_NORTHEAST,DI_SOUTHWEST,DI_SOUTHEAST};
326 void P_NewChaseDir (mobj_t *actor)
328 fixed_t deltax,deltay;
330 dirtype_t tdir, olddir, turnaround;
333 I_Error ("P_NewChaseDir: called with no target");
335 olddir = actor->movedir;
336 turnaround=opposite[olddir];
338 deltax = actor->target->x - actor->x;
339 deltay = actor->target->y - actor->y;
340 if (deltax>10*FRACUNIT)
342 else if (deltax<-10*FRACUNIT)
346 if (deltay<-10*FRACUNIT)
348 else if (deltay>10*FRACUNIT)
354 if (d[1] != DI_NODIR && d[2] != DI_NODIR)
356 actor->movedir = diags[((deltay<0)<<1)+(deltax>0)];
357 if (actor->movedir != turnaround && P_TryWalk(actor))
361 // try other directions
362 if (P_Random() > 200 || abs(deltay)>abs(deltax))
369 if (d[1]==turnaround)
371 if (d[2]==turnaround)
376 actor->movedir = d[1];
377 if (P_TryWalk(actor))
378 return; /*either moved forward or attacked*/
383 actor->movedir =d[2];
384 if (P_TryWalk(actor))
388 /* there is no direct path to the player, so pick another direction */
390 if (olddir!=DI_NODIR)
392 actor->movedir =olddir;
393 if (P_TryWalk(actor))
397 if (P_Random()&1) /*randomly determine direction of search*/
399 for (tdir=DI_EAST ; tdir<=DI_SOUTHEAST ; tdir++)
401 if (tdir!=turnaround)
403 actor->movedir =tdir;
404 if ( P_TryWalk(actor) )
411 for (tdir=DI_SOUTHEAST ; (int)tdir >= DI_EAST;tdir--)
413 if (tdir!=turnaround)
415 actor->movedir =tdir;
416 if ( P_TryWalk(actor) )
422 if (turnaround != DI_NODIR)
424 actor->movedir =turnaround;
425 if ( P_TryWalk(actor) )
429 actor->movedir = DI_NODIR; // can't move
432 //---------------------------------------------------------------------------
434 // FUNC P_LookForMonsters
436 //---------------------------------------------------------------------------
438 #define MONS_LOOK_RANGE (16*64*FRACUNIT)
439 #define MONS_LOOK_LIMIT 64
441 boolean P_LookForMonsters(mobj_t *actor)
447 if(!P_CheckSight(players[0].mo, actor))
448 { // Player can't see monster
452 for(think = thinkercap.next; think != &thinkercap; think = think->next)
454 if(think->function != P_MobjThinker)
455 { // Not a mobj thinker
458 mo = (mobj_t *)think;
459 if(!(mo->flags&MF_COUNTKILL) || (mo == actor) || (mo->health <= 0))
460 { // Not a valid monster
463 if(P_AproxDistance(actor->x-mo->x, actor->y-mo->y)
472 if(count++ > MONS_LOOK_LIMIT)
476 if(!P_CheckSight(actor, mo))
480 if (actor->type == MT_MINOTAUR)
482 if ((mo->type == MT_MINOTAUR) &&
483 (mo->target != ((player_t *)actor->special1)->mo))
488 // Found a target monster
500 = If allaround is false, only look 180 degrees in front
501 = returns true if a player is targeted
505 boolean P_LookForPlayers(mobj_t *actor, boolean allaround)
514 if(!netgame && players[0].health <= 0)
515 { // Single player game and player is dead, look for monsters
516 return(P_LookForMonsters(actor));
518 sector = actor->subsector->sector;
520 stop = (actor->lastlook-1)&3;
521 for( ; ; actor->lastlook = (actor->lastlook+1)&3 )
523 if (!playeringame[actor->lastlook])
526 if (c++ == 2 || actor->lastlook == stop)
527 return false; // done looking
529 player = &players[actor->lastlook];
530 if (player->health <= 0)
532 if (!P_CheckSight (actor, player->mo))
533 continue; // out of sight
537 an = R_PointToAngle2 (actor->x, actor->y,
538 player->mo->x, player->mo->y) - actor->angle;
539 if (an > ANG90 && an < ANG270)
541 dist = P_AproxDistance (player->mo->x - actor->x,
542 player->mo->y - actor->y);
543 // if real close, react anyway
544 if (dist > MELEERANGE)
545 continue; // behind back
548 if(player->mo->flags&MF_SHADOW)
549 { // Player is invisible
550 if((P_AproxDistance(player->mo->x-actor->x,
551 player->mo->y-actor->y) > 2*MELEERANGE)
552 && P_AproxDistance(player->mo->momx, player->mo->momy)
554 { // Player is sneaking - can't detect
558 { // Player isn't sneaking, but still didn't detect
562 if (actor->type == MT_MINOTAUR)
564 if(((player_t *)(actor->special1)) == player)
566 continue; // Don't target master
570 actor->target = player->mo;
577 ===============================================================================
581 ===============================================================================
589 = Stay in state until a player is sighted
594 void A_Look (mobj_t *actor)
598 actor->threshold = 0; // any shot will wake up
599 targ = actor->subsector->sector->soundtarget;
600 if (targ && (targ->flags & MF_SHOOTABLE) )
602 actor->target = targ;
603 if ( actor->flags & MF_AMBUSH )
605 if (P_CheckSight (actor, actor->target))
613 if (!P_LookForPlayers (actor, false) )
616 // go into chase state
618 if (actor->info->seesound)
622 sound = actor->info->seesound;
623 if(actor->flags2&MF2_BOSS)
625 S_StartSound(NULL, sound);
629 S_StartSound(actor, sound);
632 P_SetMobjState(actor, actor->info->seestate);
641 = Actor has a melee attack, so it tries to close as fast as possible
646 void A_Chase(mobj_t *actor)
650 if(actor->reactiontime)
652 actor->reactiontime--;
655 // Modify target threshold
661 if(gameskill == sk_nightmare)
662 { // Monsters move faster in nightmare mode
663 actor->tics -= actor->tics/2;
671 // turn towards movement direction if not there yet
673 if(actor->movedir < 8)
675 actor->angle &= (7<<29);
676 delta = actor->angle-(actor->movedir << 29);
679 actor->angle -= ANG90/2;
683 actor->angle += ANG90/2;
687 if(!actor->target || !(actor->target->flags&MF_SHOOTABLE))
688 { // look for a new target
689 if(P_LookForPlayers(actor, true))
690 { // got a new target
693 P_SetMobjState(actor, actor->info->spawnstate);
698 // don't attack twice in a row
700 if(actor->flags & MF_JUSTATTACKED)
702 actor->flags &= ~MF_JUSTATTACKED;
703 if (gameskill != sk_nightmare)
704 P_NewChaseDir (actor);
709 // check for melee attack
711 if (actor->info->meleestate && P_CheckMeleeRange (actor))
713 if(actor->info->attacksound)
715 S_StartSound (actor, actor->info->attacksound);
717 P_SetMobjState (actor, actor->info->meleestate);
722 // check for missile attack
724 if (actor->info->missilestate)
726 if (gameskill < sk_nightmare && actor->movecount)
728 if (!P_CheckMissileRange (actor))
730 P_SetMobjState (actor, actor->info->missilestate);
731 actor->flags |= MF_JUSTATTACKED;
737 // possibly choose another target
739 if (netgame && !actor->threshold && !P_CheckSight (actor, actor->target) )
741 if (P_LookForPlayers(actor,true))
742 return; // got a new target
746 // chase towards player
748 if (--actor->movecount<0 || !P_Move (actor))
750 P_NewChaseDir (actor);
756 if(actor->info->activesound && P_Random() < 3)
758 if(actor->type == MT_BISHOP && P_Random() < 128)
760 S_StartSound(actor, actor->info->seesound);
762 else if(actor->type == MT_PIG)
764 S_StartSound(actor, SFX_PIG_ACTIVE1+(P_Random()&1));
766 else if(actor->flags2&MF2_BOSS)
768 S_StartSound(NULL, actor->info->activesound);
772 S_StartSound(actor, actor->info->activesound);
777 //----------------------------------------------------------------------------
781 //----------------------------------------------------------------------------
783 void A_FaceTarget(mobj_t *actor)
789 actor->flags &= ~MF_AMBUSH;
790 actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x,
792 if(actor->target->flags&MF_SHADOW)
793 { // Target is a ghost
794 actor->angle += (P_Random()-P_Random())<<21;
798 //----------------------------------------------------------------------------
802 //----------------------------------------------------------------------------
804 void A_Pain(mobj_t *actor)
806 if(actor->info->painsound)
808 S_StartSound(actor, actor->info->painsound);
812 //============================================================================
816 //============================================================================
818 void A_SetInvulnerable(mobj_t *actor)
820 actor->flags2 |= MF2_INVULNERABLE;
823 //============================================================================
825 // A_UnSetInvulnerable
827 //============================================================================
829 void A_UnSetInvulnerable(mobj_t *actor)
831 actor->flags2 &= ~MF2_INVULNERABLE;
834 //============================================================================
838 //============================================================================
840 void A_SetReflective(mobj_t *actor)
842 actor->flags2 |= MF2_REFLECTIVE;
844 if ((actor->type == MT_CENTAUR) ||
845 (actor->type == MT_CENTAURLEADER))
847 A_SetInvulnerable(actor);
851 //============================================================================
855 //============================================================================
857 void A_UnSetReflective(mobj_t *actor)
859 actor->flags2 &= ~MF2_REFLECTIVE;
861 if ((actor->type == MT_CENTAUR) ||
862 (actor->type == MT_CENTAURLEADER))
864 A_UnSetInvulnerable(actor);
869 //----------------------------------------------------------------------------
871 // FUNC P_UpdateMorphedMonster
873 // Returns true if the pig morphs.
875 //----------------------------------------------------------------------------
877 boolean P_UpdateMorphedMonster(mobj_t *actor, int tics)
887 actor->special1 -= tics;
888 if(actor->special1 > 0)
892 moType = actor->special2;
895 case MT_WRAITHB: // These must remain morphed
897 case MT_SERPENTLEADER:
906 oldMonster = *actor; // Save pig vars
908 P_RemoveMobjFromTIDList(actor);
909 P_SetMobjState(actor, S_FREETARGMOBJ);
910 mo = P_SpawnMobj(x, y, z, moType);
912 if(P_TestMobjLocation(mo) == false)
915 mo = P_SpawnMobj(x, y, z, oldMonster.type);
916 mo->angle = oldMonster.angle;
917 mo->flags = oldMonster.flags;
918 mo->health = oldMonster.health;
919 mo->target = oldMonster.target;
920 mo->special = oldMonster.special;
921 mo->special1 = 5*35; // Next try in 5 seconds
922 mo->special2 = moType;
923 mo->tid = oldMonster.tid;
924 memcpy(mo->args, oldMonster.args, 5);
925 P_InsertMobjIntoTIDList(mo, oldMonster.tid);
928 mo->angle = oldMonster.angle;
929 mo->target = oldMonster.target;
930 mo->tid = oldMonster.tid;
931 mo->special = oldMonster.special;
932 memcpy(mo->args, oldMonster.args, 5);
933 P_InsertMobjIntoTIDList(mo, oldMonster.tid);
934 fog = P_SpawnMobj(x, y, z+TELEFOGHEIGHT, MT_TFOG);
935 S_StartSound(fog, SFX_TELEPORT);
939 //----------------------------------------------------------------------------
943 //----------------------------------------------------------------------------
945 void A_PigLook(mobj_t *actor)
947 if(P_UpdateMorphedMonster(actor, 10))
954 //----------------------------------------------------------------------------
958 //----------------------------------------------------------------------------
960 void A_PigChase(mobj_t *actor)
962 if(P_UpdateMorphedMonster(actor, 3))
969 //============================================================================
973 //============================================================================
975 void A_PigAttack(mobj_t *actor)
977 if(P_UpdateMorphedMonster(actor, 18))
985 if(P_CheckMeleeRange(actor))
987 P_DamageMobj(actor->target, actor, actor, 2+(P_Random()&1));
988 S_StartSound(actor, SFX_PIG_ATTACK);
992 //============================================================================
996 //============================================================================
998 void A_PigPain(mobj_t *actor)
1001 if(actor->z <= actor->floorz)
1003 actor->momz = 3.5*FRACUNIT;
1009 void FaceMovementDirection(mobj_t *actor)
1011 switch(actor->movedir)
1014 actor->angle = 0<<24;
1017 actor->angle = 32<<24;
1020 actor->angle = 64<<24;
1023 actor->angle = 96<<24;
1026 actor->angle = 128<<24;
1029 actor->angle = 160<<24;
1032 actor->angle = 192<<24;
1035 actor->angle = 224<<24;
1041 //----------------------------------------------------------------------------
1043 // Minotaur variables
1045 // special1 pointer to player that spawned it (mobj_t)
1046 // special2 internal to minotaur AI
1047 // args[0] args[0]-args[3] together make up minotaur start time
1051 // args[4] charge duration countdown
1052 //----------------------------------------------------------------------------
1054 void A_MinotaurFade0(mobj_t *actor)
1056 actor->flags &= ~MF_ALTSHADOW;
1057 actor->flags |= MF_SHADOW;
1060 void A_MinotaurFade1(mobj_t *actor)
1062 // Second level of transparency
1063 actor->flags &= ~MF_SHADOW;
1064 actor->flags |= MF_ALTSHADOW;
1067 void A_MinotaurFade2(mobj_t *actor)
1069 // Make fully visible
1070 actor->flags &= ~MF_SHADOW;
1071 actor->flags &= ~MF_ALTSHADOW;
1075 //----------------------------------------------------------------------------
1080 //----------------------------------------------------------------------------
1082 void A_MinotaurLook(mobj_t *actor);
1084 void A_MinotaurRoam(mobj_t *actor)
1086 unsigned int *starttime = (unsigned int *)actor->args;
1088 actor->flags &= ~MF_SHADOW; // In case pain caused him to
1089 actor->flags &= ~MF_ALTSHADOW; // skip his fade in.
1091 if ((leveltime - *starttime) >= MAULATORTICS)
1093 P_DamageMobj(actor,NULL,NULL,10000);
1098 A_MinotaurLook(actor); // adjust to closest target
1102 //Choose new direction
1103 actor->movedir = P_Random() % 8;
1104 FaceMovementDirection(actor);
1110 actor->movedir = (++actor->movedir)%8;
1112 actor->movedir = (actor->movedir+7)%8;
1113 FaceMovementDirection(actor);
1118 //----------------------------------------------------------------------------
1120 // PROC A_MinotaurLook
1122 // Look for enemy of player
1123 //----------------------------------------------------------------------------
1124 #define MINOTAUR_LOOK_DIST (16*54*FRACUNIT)
1126 void A_MinotaurLook(mobj_t *actor)
1133 mobj_t *master = (mobj_t *)(actor->special1);
1135 actor->target = NULL;
1136 if (deathmatch) // Quick search for players
1138 for (i=0; i<MAXPLAYERS; i++)
1140 if (!playeringame[i]) continue;
1141 player = &players[i];
1143 if (mo == master) continue;
1144 if (mo->health <= 0) continue;
1145 dist = P_AproxDistance(actor->x - mo->x, actor->y - mo->y);
1146 if (dist > MINOTAUR_LOOK_DIST) continue;
1152 if (!actor->target) // Near player monster search
1154 if (master && (master->health>0) && (master->player))
1155 mo = P_RoughMonsterSearch(master, 20);
1157 mo = P_RoughMonsterSearch(actor, 20);
1161 if (!actor->target) // Normal monster search
1163 for(think = thinkercap.next; think != &thinkercap; think = think->next)
1165 if(think->function != P_MobjThinker) continue;
1166 mo = (mobj_t *)think;
1167 if (!(mo->flags&MF_COUNTKILL)) continue;
1168 if (mo->health <= 0) continue;
1169 if (!(mo->flags&MF_SHOOTABLE)) continue;
1170 dist = P_AproxDistance(actor->x - mo->x, actor->y - mo->y);
1171 if (dist > MINOTAUR_LOOK_DIST) continue;
1172 if ((mo == master) || (mo == actor)) continue;
1173 if ((mo->type == MT_MINOTAUR) &&
1174 (mo->special1 == actor->special1)) continue;
1176 break; // Found mobj to attack
1182 P_SetMobjStateNF(actor, S_MNTR_WALK1);
1186 P_SetMobjStateNF(actor, S_MNTR_ROAM1);
1193 void A_MinotaurChase(mobj_t *actor)
1195 unsigned int *starttime = (unsigned int *)actor->args;
1197 actor->flags &= ~MF_SHADOW; // In case pain caused him to
1198 actor->flags &= ~MF_ALTSHADOW; // skip his fade in.
1200 if ((leveltime - *starttime) >= MAULATORTICS)
1202 P_DamageMobj(actor,NULL,NULL,10000);
1207 A_MinotaurLook(actor); // adjust to closest target
1209 if (!actor->target || (actor->target->health <= 0) ||
1210 !(actor->target->flags&MF_SHOOTABLE))
1211 { // look for a new target
1212 P_SetMobjState(actor, S_MNTR_LOOK1);
1216 FaceMovementDirection(actor);
1217 actor->reactiontime=0;
1220 if (actor->info->meleestate && P_CheckMeleeRange(actor))
1222 if(actor->info->attacksound)
1224 S_StartSound (actor, actor->info->attacksound);
1226 P_SetMobjState (actor, actor->info->meleestate);
1231 if (actor->info->missilestate && P_CheckMissileRange(actor))
1233 P_SetMobjState (actor, actor->info->missilestate);
1237 // chase towards target
1240 P_NewChaseDir(actor);
1244 if(actor->info->activesound && P_Random() < 6)
1246 S_StartSound(actor, actor->info->activesound);
1252 //----------------------------------------------------------------------------
1254 // PROC A_MinotaurAtk1
1258 //----------------------------------------------------------------------------
1260 void A_MinotaurAtk1(mobj_t *actor)
1262 if (!actor->target) return;
1264 S_StartSound(actor, SFX_MAULATOR_HAMMER_SWING);
1265 if(P_CheckMeleeRange(actor))
1267 P_DamageMobj(actor->target, actor, actor, HITDICE(4));
1271 //----------------------------------------------------------------------------
1273 // PROC A_MinotaurDecide
1275 // Choose a missile attack.
1277 //----------------------------------------------------------------------------
1279 #define MNTR_CHARGE_SPEED (23*FRACUNIT)
1281 void A_MinotaurDecide(mobj_t *actor)
1284 mobj_t *target = actor->target;
1287 if (!target) return;
1288 dist = P_AproxDistance(actor->x-target->x, actor->y-target->y);
1290 if(target->z+target->height > actor->z
1291 && target->z+target->height < actor->z+actor->height
1292 && dist < 16*64*FRACUNIT
1293 && dist > 1*64*FRACUNIT
1294 && P_Random() < 230)
1296 // Don't call the state function right away
1297 P_SetMobjStateNF(actor, S_MNTR_ATK4_1);
1298 actor->flags |= MF_SKULLFLY;
1299 A_FaceTarget(actor);
1300 angle = actor->angle>>ANGLETOFINESHIFT;
1301 actor->momx = FixedMul(MNTR_CHARGE_SPEED, finecosine[angle]);
1302 actor->momy = FixedMul(MNTR_CHARGE_SPEED, finesine[angle]);
1303 actor->args[4] = 35/2; // Charge duration
1305 else if(target->z == target->floorz
1306 && dist < 9*64*FRACUNIT
1307 && P_Random() < 100)
1308 { // Floor fire attack
1309 P_SetMobjState(actor, S_MNTR_ATK3_1);
1310 actor->special2 = 0;
1314 A_FaceTarget(actor);
1315 // Don't need to call P_SetMobjState because the current state
1316 // falls through to the swing attack
1320 //----------------------------------------------------------------------------
1322 // PROC A_MinotaurCharge
1324 //----------------------------------------------------------------------------
1326 void A_MinotaurCharge(mobj_t *actor)
1330 if (!actor->target) return;
1332 if(actor->args[4] > 0)
1334 puff = P_SpawnMobj(actor->x, actor->y, actor->z, MT_PUNCHPUFF);
1335 puff->momz = 2*FRACUNIT;
1340 actor->flags &= ~MF_SKULLFLY;
1341 P_SetMobjState(actor, actor->info->seestate);
1345 //----------------------------------------------------------------------------
1347 // PROC A_MinotaurAtk2
1351 //----------------------------------------------------------------------------
1353 void A_MinotaurAtk2(mobj_t *actor)
1359 if(!actor->target) return;
1361 S_StartSound(actor, SFX_MAULATOR_HAMMER_SWING);
1362 if(P_CheckMeleeRange(actor))
1364 P_DamageMobj(actor->target, actor, actor, HITDICE(3));
1367 mo = P_SpawnMissile(actor, actor->target, MT_MNTRFX1);
1370 //S_StartSound(mo, sfx_minat2);
1373 P_SpawnMissileAngle(actor, MT_MNTRFX1, angle-(ANG45/8), momz);
1374 P_SpawnMissileAngle(actor, MT_MNTRFX1, angle+(ANG45/8), momz);
1375 P_SpawnMissileAngle(actor, MT_MNTRFX1, angle-(ANG45/16), momz);
1376 P_SpawnMissileAngle(actor, MT_MNTRFX1, angle+(ANG45/16), momz);
1380 //----------------------------------------------------------------------------
1382 // PROC A_MinotaurAtk3
1384 // Floor fire attack.
1386 //----------------------------------------------------------------------------
1388 void A_MinotaurAtk3(mobj_t *actor)
1397 if(P_CheckMeleeRange(actor))
1399 P_DamageMobj(actor->target, actor, actor, HITDICE(3));
1400 if((player = actor->target->player) != NULL)
1401 { // Squish the player
1402 player->deltaviewheight = -16*FRACUNIT;
1407 mo = P_SpawnMissile(actor, actor->target, MT_MNTRFX2);
1410 S_StartSound(mo, SFX_MAULATOR_HAMMER_HIT);
1413 if(P_Random() < 192 && actor->special2 == 0)
1415 P_SetMobjState(actor, S_MNTR_ATK3_4);
1416 actor->special2 = 1;
1420 //----------------------------------------------------------------------------
1422 // PROC A_MntrFloorFire
1424 //----------------------------------------------------------------------------
1426 void A_MntrFloorFire(mobj_t *actor)
1430 actor->z = actor->floorz;
1431 mo = P_SpawnMobj(actor->x+((P_Random()-P_Random())<<10),
1432 actor->y+((P_Random()-P_Random())<<10), ONFLOORZ, MT_MNTRFX3);
1433 mo->target = actor->target;
1434 mo->momx = 1; // Force block checking
1435 P_CheckMissileSpawn(mo);
1439 //----------------------------------------------------------------------------
1443 //----------------------------------------------------------------------------
1445 void A_Scream(mobj_t *actor)
1452 if(actor->player->morphTics)
1454 S_StartSound(actor, actor->info->deathsound);
1458 // Handle the different player death screams
1459 if(actor->momz <= -39*FRACUNIT)
1461 sound = SFX_PLAYER_FALLING_SPLAT;
1463 else if(actor->health > -50)
1464 { // Normal death sound
1465 switch(actor->player->class)
1467 case PCLASS_FIGHTER:
1468 sound = SFX_PLAYER_FIGHTER_NORMAL_DEATH;
1471 sound = SFX_PLAYER_CLERIC_NORMAL_DEATH;
1474 sound = SFX_PLAYER_MAGE_NORMAL_DEATH;
1481 else if(actor->health > -100)
1482 { // Crazy death sound
1483 switch(actor->player->class)
1485 case PCLASS_FIGHTER:
1486 sound = SFX_PLAYER_FIGHTER_CRAZY_DEATH;
1489 sound = SFX_PLAYER_CLERIC_CRAZY_DEATH;
1492 sound = SFX_PLAYER_MAGE_CRAZY_DEATH;
1500 { // Extreme death sound
1501 switch(actor->player->class)
1503 case PCLASS_FIGHTER:
1504 sound = SFX_PLAYER_FIGHTER_EXTREME1_DEATH;
1507 sound = SFX_PLAYER_CLERIC_EXTREME1_DEATH;
1510 sound = SFX_PLAYER_MAGE_EXTREME1_DEATH;
1516 sound += P_Random()%3; // Three different extreme deaths
1518 S_StartSound(actor, sound);
1523 S_StartSound(actor, actor->info->deathsound);
1527 //---------------------------------------------------------------------------
1531 //---------------------------------------------------------------------------
1534 void P_DropItem(mobj_t *source, mobjtype_t type, int special, int chance)
1538 if(P_Random() > chance)
1542 mo = P_SpawnMobj(source->x, source->y,
1543 source->z+(source->height>>1), type);
1544 mo->momx = (P_Random()-P_Random())<<8;
1545 mo->momy = (P_Random()-P_Random())<<8;
1546 mo->momz = FRACUNIT*5+(P_Random()<<10);
1547 mo->flags2 |= MF2_DROPPED;
1548 mo->health = special;
1552 //----------------------------------------------------------------------------
1554 // PROC A_NoBlocking
1556 //----------------------------------------------------------------------------
1558 void A_NoBlocking(mobj_t *actor)
1560 actor->flags &= ~MF_SOLID;
1562 // Check for monsters dropping things
1563 /* switch(actor->type)
1565 // Add the monster dropped items here
1566 case MT_MUMMYLEADERGHOST:
1567 P_DropItem(actor, MT_AMGWNDWIMPY, 3, 84);
1575 //----------------------------------------------------------------------------
1579 // Handles a bunch of exploding things.
1581 //----------------------------------------------------------------------------
1583 void A_Explode(mobj_t *actor)
1594 case MT_FIREBOMB: // Time Bombs
1595 actor->z += 32*FRACUNIT;
1596 actor->flags &= ~MF_SHADOW;
1598 case MT_MNTRFX2: // Minotaur floor fire
1601 case MT_BISHOP: // Bishop radius death
1602 damage = 25+(P_Random()&15);
1604 case MT_HAMMER_MISSILE: // Fighter Hammer
1608 case MT_FSWORD_MISSILE: // Fighter Runesword
1612 case MT_CIRCLEFLAME: // Cleric Flame secondary flames
1616 case MT_SORCBALL1: // Sorcerer balls
1621 actor->args[0] = 1; // don't play bounce
1623 case MT_SORCFX1: // Sorcerer spell 1
1626 case MT_SORCFX4: // Sorcerer spell 4
1629 case MT_TREEDESTRUCTIBLE:
1646 case MT_POISONCLOUD:
1658 P_RadiusAttack(actor, actor->target, damage, distance, damageSelf);
1659 if(actor->z <= actor->floorz+(distance<<FRACBITS)
1660 && actor->type != MT_POISONCLOUD)
1666 //----------------------------------------------------------------------------
1670 // Kills all monsters.
1672 //----------------------------------------------------------------------------
1674 int P_Massacre(void)
1681 for(think = thinkercap.next; think != &thinkercap;
1682 think = think->next)
1684 if(think->function != P_MobjThinker)
1685 { // Not a mobj thinker
1688 mo = (mobj_t *)think;
1689 if((mo->flags&MF_COUNTKILL) && (mo->health > 0))
1691 mo->flags2 &= ~(MF2_NONSHOOTABLE+MF2_INVULNERABLE);
1692 mo->flags |= MF_SHOOTABLE;
1693 P_DamageMobj(mo, NULL, NULL, 10000);
1702 //----------------------------------------------------------------------------
1706 //----------------------------------------------------------------------------
1708 void A_SkullPop(mobj_t *actor)
1717 actor->flags &= ~MF_SOLID;
1718 mo = P_SpawnMobj(actor->x, actor->y, actor->z+48*FRACUNIT,
1720 //mo->target = actor;
1721 mo->momx = (P_Random()-P_Random())<<9;
1722 mo->momy = (P_Random()-P_Random())<<9;
1723 mo->momz = FRACUNIT*2+(P_Random()<<6);
1724 // Attach player mobj to bloody skull
1725 player = actor->player;
1726 actor->player = NULL;
1727 actor->special1 = player->class;
1728 mo->player = player;
1729 mo->health = actor->health;
1730 mo->angle = actor->angle;
1732 player->lookdir = 0;
1733 player->damagecount = 32;
1736 //----------------------------------------------------------------------------
1738 // PROC A_CheckSkullFloor
1740 //----------------------------------------------------------------------------
1742 void A_CheckSkullFloor(mobj_t *actor)
1744 if(actor->z <= actor->floorz)
1746 P_SetMobjState(actor, S_BLOODYSKULLX1);
1747 S_StartSound(actor, SFX_DRIP);
1751 //----------------------------------------------------------------------------
1753 // PROC A_CheckSkullDone
1755 //----------------------------------------------------------------------------
1757 void A_CheckSkullDone(mobj_t *actor)
1759 if(actor->special2 == 666)
1761 P_SetMobjState(actor, S_BLOODYSKULLX2);
1765 //----------------------------------------------------------------------------
1767 // PROC A_CheckBurnGone
1769 //----------------------------------------------------------------------------
1771 void A_CheckBurnGone(mobj_t *actor)
1773 if(actor->special2 == 666)
1775 P_SetMobjState(actor, S_PLAY_FDTH20);
1779 //----------------------------------------------------------------------------
1781 // PROC A_FreeTargMobj
1783 //----------------------------------------------------------------------------
1785 void A_FreeTargMobj(mobj_t *mo)
1787 mo->momx = mo->momy = mo->momz = 0;
1788 mo->z = mo->ceilingz+4*FRACUNIT;
1789 mo->flags &= ~(MF_SHOOTABLE|MF_FLOAT|MF_SKULLFLY|MF_SOLID|MF_COUNTKILL);
1790 mo->flags |= MF_CORPSE|MF_DROPOFF|MF_NOGRAVITY;
1791 mo->flags2 &= ~(MF2_PASSMOBJ|MF2_LOGRAV);
1792 mo->flags2 |= MF2_DONTDRAW;
1794 mo->health = -1000; // Don't resurrect
1798 //----------------------------------------------------------------------------
1800 // CorpseQueue Routines
1802 //----------------------------------------------------------------------------
1804 // Corpse queue for monsters - this should be saved out
1805 #define CORPSEQUEUESIZE 64
1806 mobj_t *corpseQueue[CORPSEQUEUESIZE];
1807 int corpseQueueSlot;
1809 // throw another corpse on the queue
1810 void A_QueueCorpse(mobj_t *actor)
1814 if(corpseQueueSlot >= CORPSEQUEUESIZE)
1815 { // Too many corpses - remove an old one
1816 corpse = corpseQueue[corpseQueueSlot%CORPSEQUEUESIZE];
1817 if (corpse) P_RemoveMobj(corpse);
1819 corpseQueue[corpseQueueSlot%CORPSEQUEUESIZE] = actor;
1823 // Remove a mobj from the queue (for resurrection)
1824 void A_DeQueueCorpse(mobj_t *actor)
1828 for (slot=0; slot<CORPSEQUEUESIZE; slot++)
1830 if (corpseQueue[slot] == actor)
1832 corpseQueue[slot] = NULL;
1838 void P_InitCreatureCorpseQueue(boolean corpseScan)
1845 memset(corpseQueue, 0, sizeof(mobj_t *)*CORPSEQUEUESIZE);
1847 if (!corpseScan) return;
1849 // Search mobj list for corpses and place them in this queue
1850 for(think = thinkercap.next; think != &thinkercap; think = think->next)
1852 if(think->function != P_MobjThinker) continue;
1853 mo = (mobj_t *)think;
1854 if (!(mo->flags&MF_CORPSE)) continue; // Must be a corpse
1855 if (mo->flags&MF_ICECORPSE) continue; // Not ice corpses
1856 // Only corpses that call A_QueueCorpse from death routine
1860 case MT_CENTAURLEADER:
1868 case MT_CENTAUR_SHIELD:
1869 case MT_CENTAUR_SWORD:
1870 case MT_DEMONCHUNK1:
1871 case MT_DEMONCHUNK2:
1872 case MT_DEMONCHUNK3:
1873 case MT_DEMONCHUNK4:
1874 case MT_DEMONCHUNK5:
1875 case MT_DEMON2CHUNK1:
1876 case MT_DEMON2CHUNK2:
1877 case MT_DEMON2CHUNK3:
1878 case MT_DEMON2CHUNK4:
1879 case MT_DEMON2CHUNK5:
1880 case MT_FIREDEMON_SPLOTCH1:
1881 case MT_FIREDEMON_SPLOTCH2:
1882 A_QueueCorpse(mo); // Add corpse to queue
1891 //----------------------------------------------------------------------------
1893 // PROC A_AddPlayerCorpse
1895 //----------------------------------------------------------------------------
1897 #define BODYQUESIZE 32
1898 mobj_t *bodyque[BODYQUESIZE];
1901 void A_AddPlayerCorpse(mobj_t *actor)
1903 if(bodyqueslot >= BODYQUESIZE)
1904 { // Too many player corpses - remove an old one
1905 P_RemoveMobj(bodyque[bodyqueslot%BODYQUESIZE]);
1907 bodyque[bodyqueslot%BODYQUESIZE] = actor;
1911 //============================================================================
1915 //============================================================================
1917 void A_SerpentUnHide(mobj_t *actor)
1919 actor->flags2 &= ~MF2_DONTDRAW;
1920 actor->floorclip = 24*FRACUNIT;
1923 //============================================================================
1927 //============================================================================
1929 void A_SerpentHide(mobj_t *actor)
1931 actor->flags2 |= MF2_DONTDRAW;
1932 actor->floorclip = 0;
1934 //============================================================================
1938 //============================================================================
1940 void A_SerpentChase(mobj_t *actor)
1943 int oldX, oldY, oldFloor;
1945 if(actor->reactiontime)
1947 actor->reactiontime--;
1950 // Modify target threshold
1951 if(actor->threshold)
1956 if(gameskill == sk_nightmare)
1957 { // Monsters move faster in nightmare mode
1958 actor->tics -= actor->tics/2;
1966 // turn towards movement direction if not there yet
1968 if(actor->movedir < 8)
1970 actor->angle &= (7<<29);
1971 delta = actor->angle-(actor->movedir << 29);
1974 actor->angle -= ANG90/2;
1978 actor->angle += ANG90/2;
1982 if(!actor->target || !(actor->target->flags&MF_SHOOTABLE))
1983 { // look for a new target
1984 if(P_LookForPlayers(actor, true))
1985 { // got a new target
1988 P_SetMobjState(actor, actor->info->spawnstate);
1993 // don't attack twice in a row
1995 if(actor->flags & MF_JUSTATTACKED)
1997 actor->flags &= ~MF_JUSTATTACKED;
1998 if (gameskill != sk_nightmare)
1999 P_NewChaseDir (actor);
2004 // check for melee attack
2006 if (actor->info->meleestate && P_CheckMeleeRange (actor))
2008 if(actor->info->attacksound)
2010 S_StartSound (actor, actor->info->attacksound);
2012 P_SetMobjState (actor, actor->info->meleestate);
2017 // possibly choose another target
2019 if (netgame && !actor->threshold && !P_CheckSight (actor, actor->target) )
2021 if (P_LookForPlayers(actor,true))
2022 return; // got a new target
2026 // chase towards player
2030 oldFloor = actor->subsector->sector->floorpic;
2031 if (--actor->movecount<0 || !P_Move (actor))
2033 P_NewChaseDir (actor);
2035 if(actor->subsector->sector->floorpic != oldFloor)
2037 P_TryMove(actor, oldX, oldY);
2038 P_NewChaseDir (actor);
2042 // make active sound
2044 if(actor->info->activesound && P_Random() < 3)
2046 S_StartSound(actor, actor->info->activesound);
2050 //============================================================================
2052 // A_SerpentRaiseHump
2054 // Raises the hump above the surface by raising the floorclip level
2055 //============================================================================
2057 void A_SerpentRaiseHump(mobj_t *actor)
2059 actor->floorclip -= 4*FRACUNIT;
2062 //============================================================================
2064 // A_SerpentLowerHump
2066 //============================================================================
2068 void A_SerpentLowerHump(mobj_t *actor)
2070 actor->floorclip += 4*FRACUNIT;
2073 //============================================================================
2075 // A_SerpentHumpDecide
2077 // Decided whether to hump up, or if the mobj is a serpent leader,
2078 // to missile attack
2079 //============================================================================
2081 void A_SerpentHumpDecide(mobj_t *actor)
2083 if(actor->type == MT_SERPENTLEADER)
2089 else if(P_Random() < 40)
2091 P_SetMobjState(actor, S_SERPENT_SURFACE1);
2095 else if(P_Random() > 3)
2099 if(!P_CheckMeleeRange(actor))
2100 { // The hump shouldn't occur when within melee range
2101 if(actor->type == MT_SERPENTLEADER && P_Random() < 128)
2103 P_SetMobjState(actor, S_SERPENT_SURFACE1);
2107 P_SetMobjState(actor, S_SERPENT_HUMP1);
2108 S_StartSound(actor, SFX_SERPENT_ACTIVE);
2113 //============================================================================
2115 // A_SerpentBirthScream
2117 //============================================================================
2119 void A_SerpentBirthScream(mobj_t *actor)
2121 S_StartSound(actor, SFX_SERPENT_BIRTH);
2124 //============================================================================
2126 // A_SerpentDiveSound
2128 //============================================================================
2130 void A_SerpentDiveSound(mobj_t *actor)
2132 S_StartSound(actor, SFX_SERPENT_ACTIVE);
2135 //============================================================================
2139 // Similar to A_Chase, only has a hardcoded entering of meleestate
2140 //============================================================================
2142 void A_SerpentWalk(mobj_t *actor)
2146 if(actor->reactiontime)
2148 actor->reactiontime--;
2151 // Modify target threshold
2152 if(actor->threshold)
2157 if(gameskill == sk_nightmare)
2158 { // Monsters move faster in nightmare mode
2159 actor->tics -= actor->tics/2;
2167 // turn towards movement direction if not there yet
2169 if(actor->movedir < 8)
2171 actor->angle &= (7<<29);
2172 delta = actor->angle-(actor->movedir << 29);
2175 actor->angle -= ANG90/2;
2179 actor->angle += ANG90/2;
2183 if(!actor->target || !(actor->target->flags&MF_SHOOTABLE))
2184 { // look for a new target
2185 if(P_LookForPlayers(actor, true))
2186 { // got a new target
2189 P_SetMobjState(actor, actor->info->spawnstate);
2194 // don't attack twice in a row
2196 if(actor->flags & MF_JUSTATTACKED)
2198 actor->flags &= ~MF_JUSTATTACKED;
2199 if (gameskill != sk_nightmare)
2200 P_NewChaseDir (actor);
2205 // check for melee attack
2207 if (actor->info->meleestate && P_CheckMeleeRange (actor))
2209 if (actor->info->attacksound)
2211 S_StartSound (actor, actor->info->attacksound);
2213 P_SetMobjState(actor, S_SERPENT_ATK1);
2217 // possibly choose another target
2219 if (netgame && !actor->threshold && !P_CheckSight (actor, actor->target) )
2221 if (P_LookForPlayers(actor,true))
2222 return; // got a new target
2226 // chase towards player
2228 if (--actor->movecount<0 || !P_Move (actor))
2230 P_NewChaseDir (actor);
2234 //============================================================================
2236 // A_SerpentCheckForAttack
2238 //============================================================================
2240 void A_SerpentCheckForAttack(mobj_t *actor)
2246 if(actor->type == MT_SERPENTLEADER)
2248 if(!P_CheckMeleeRange(actor))
2250 P_SetMobjState(actor, S_SERPENT_ATK1);
2254 if(P_CheckMeleeRange2(actor))
2256 P_SetMobjState(actor, S_SERPENT_WALK1);
2258 else if(P_CheckMeleeRange(actor))
2262 P_SetMobjState(actor, S_SERPENT_WALK1);
2266 P_SetMobjState(actor, S_SERPENT_ATK1);
2271 //============================================================================
2273 // A_SerpentChooseAttack
2275 //============================================================================
2277 void A_SerpentChooseAttack(mobj_t *actor)
2279 if(!actor->target || P_CheckMeleeRange(actor))
2283 if(actor->type == MT_SERPENTLEADER)
2285 P_SetMobjState(actor, S_SERPENT_MISSILE1);
2289 //============================================================================
2291 // A_SerpentMeleeAttack
2293 //============================================================================
2295 void A_SerpentMeleeAttack(mobj_t *actor)
2301 if(P_CheckMeleeRange(actor))
2303 P_DamageMobj(actor->target, actor, actor, HITDICE(5));
2304 S_StartSound(actor, SFX_SERPENT_MELEEHIT);
2308 A_SerpentCheckForAttack(actor);
2312 //============================================================================
2314 // A_SerpentMissileAttack
2316 //============================================================================
2318 void A_SerpentMissileAttack(mobj_t *actor)
2326 mo = P_SpawnMissile(actor, actor->target, MT_SERPENTFX);
2329 //============================================================================
2333 //============================================================================
2335 void A_SerpentHeadPop(mobj_t *actor)
2337 P_SpawnMobj(actor->x, actor->y, actor->z+45*FRACUNIT, MT_SERPENT_HEAD);
2340 //============================================================================
2342 // A_SerpentSpawnGibs
2344 //============================================================================
2346 void A_SerpentSpawnGibs(mobj_t *actor)
2350 mo = P_SpawnMobj(actor->x+((P_Random()-128)<<12),
2351 actor->y+((P_Random()-128)<<12), actor->floorz+FRACUNIT,
2355 mo->momx = (P_Random()-128)<<6;
2356 mo->momy = (P_Random()-128)<<6;
2357 mo->floorclip = 6*FRACUNIT;
2359 mo = P_SpawnMobj(actor->x+((P_Random()-128)<<12),
2360 actor->y+((P_Random()-128)<<12), actor->floorz+FRACUNIT,
2364 mo->momx = (P_Random()-128)<<6;
2365 mo->momy = (P_Random()-128)<<6;
2366 mo->floorclip = 6*FRACUNIT;
2368 mo = P_SpawnMobj(actor->x+((P_Random()-128)<<12),
2369 actor->y+((P_Random()-128)<<12), actor->floorz+FRACUNIT,
2373 mo->momx = (P_Random()-128)<<6;
2374 mo->momy = (P_Random()-128)<<6;
2375 mo->floorclip = 6*FRACUNIT;
2379 //============================================================================
2383 //============================================================================
2385 void A_FloatGib(mobj_t *actor)
2387 actor->floorclip -= FRACUNIT;
2390 //============================================================================
2394 //============================================================================
2396 void A_SinkGib(mobj_t *actor)
2398 actor->floorclip += FRACUNIT;
2401 //============================================================================
2405 //============================================================================
2407 void A_DelayGib(mobj_t *actor)
2409 actor->tics -= P_Random()>>2;
2412 //============================================================================
2414 // A_SerpentHeadCheck
2416 //============================================================================
2418 void A_SerpentHeadCheck(mobj_t *actor)
2420 if(actor->z <= actor->floorz)
2422 if(P_GetThingFloorType(actor) >= FLOOR_LIQUID)
2425 P_SetMobjState(actor, S_NULL);
2429 P_SetMobjState(actor, S_SERPENT_HEAD_X1);
2434 //============================================================================
2438 //============================================================================
2440 void A_CentaurAttack(mobj_t *actor)
2446 if(P_CheckMeleeRange(actor))
2448 P_DamageMobj(actor->target, actor, actor, P_Random()%7+3);
2452 //============================================================================
2456 //============================================================================
2458 void A_CentaurAttack2(mobj_t *actor)
2464 P_SpawnMissile(actor, actor->target, MT_CENTAUR_FX);
2465 S_StartSound(actor, SFX_CENTAURLEADER_ATTACK);
2468 //============================================================================
2470 // A_CentaurDropStuff
2472 // Spawn shield/sword sprites when the centaur pulps //============================================================================
2474 void A_CentaurDropStuff(mobj_t *actor)
2479 mo = P_SpawnMobj(actor->x, actor->y, actor->z+45*FRACUNIT,
2483 angle = actor->angle+ANG90;
2484 mo->momz = FRACUNIT*8+(P_Random()<<10);
2485 mo->momx = FixedMul(((P_Random()-128)<<11)+FRACUNIT,
2486 finecosine[angle>>ANGLETOFINESHIFT]);
2487 mo->momy = FixedMul(((P_Random()-128)<<11)+FRACUNIT,
2488 finesine[angle>>ANGLETOFINESHIFT]);
2491 mo = P_SpawnMobj(actor->x, actor->y, actor->z+45*FRACUNIT,
2495 angle = actor->angle-ANG90;
2496 mo->momz = FRACUNIT*8+(P_Random()<<10);
2497 mo->momx = FixedMul(((P_Random()-128)<<11)+FRACUNIT,
2498 finecosine[angle>>ANGLETOFINESHIFT]);
2499 mo->momy = FixedMul(((P_Random()-128)<<11)+FRACUNIT,
2500 finesine[angle>>ANGLETOFINESHIFT]);
2505 //============================================================================
2509 //============================================================================
2511 void A_CentaurDefend(mobj_t *actor)
2513 A_FaceTarget(actor);
2514 if(P_CheckMeleeRange(actor) && P_Random() < 32)
2516 A_UnSetInvulnerable(actor);
2517 P_SetMobjState(actor, actor->info->meleestate);
2521 //============================================================================
2525 //============================================================================
2527 void A_BishopAttack(mobj_t *actor)
2533 S_StartSound(actor, actor->info->attacksound);
2534 if(P_CheckMeleeRange(actor))
2536 P_DamageMobj(actor->target, actor, actor, HITDICE(4));
2539 actor->special1 = (P_Random()&3)+5;
2542 //============================================================================
2546 // Spawns one of a string of bishop missiles
2547 //============================================================================
2549 void A_BishopAttack2(mobj_t *actor)
2553 if(!actor->target || !actor->special1)
2555 actor->special1 = 0;
2556 P_SetMobjState(actor, S_BISHOP_WALK1);
2559 mo = P_SpawnMissile(actor, actor->target, MT_BISH_FX);
2562 mo->special1 = (int)actor->target;
2563 mo->special2 = 16; // High word == x/y, Low word == z
2568 //============================================================================
2570 // A_BishopMissileWeave
2572 //============================================================================
2574 void A_BishopMissileWeave(mobj_t *actor)
2577 int weaveXY, weaveZ;
2580 weaveXY = actor->special2>>16;
2581 weaveZ = actor->special2&0xFFFF;
2582 angle = (actor->angle+ANG90)>>ANGLETOFINESHIFT;
2583 newX = actor->x-FixedMul(finecosine[angle],
2584 FloatBobOffsets[weaveXY]<<1);
2585 newY = actor->y-FixedMul(finesine[angle],
2586 FloatBobOffsets[weaveXY]<<1);
2587 weaveXY = (weaveXY+2)&63;
2588 newX += FixedMul(finecosine[angle],
2589 FloatBobOffsets[weaveXY]<<1);
2590 newY += FixedMul(finesine[angle],
2591 FloatBobOffsets[weaveXY]<<1);
2592 P_TryMove(actor, newX, newY);
2593 actor->z -= FloatBobOffsets[weaveZ];
2594 weaveZ = (weaveZ+2)&63;
2595 actor->z += FloatBobOffsets[weaveZ];
2596 actor->special2 = weaveZ+(weaveXY<<16);
2599 //============================================================================
2601 // A_BishopMissileSeek
2603 //============================================================================
2605 void A_BishopMissileSeek(mobj_t *actor)
2607 P_SeekerMissile(actor, ANGLE_1*2, ANGLE_1*3);
2610 //============================================================================
2614 //============================================================================
2616 void A_BishopDecide(mobj_t *actor)
2618 if(P_Random() < 220)
2624 P_SetMobjState(actor, S_BISHOP_BLUR1);
2628 //============================================================================
2632 //============================================================================
2634 void A_BishopDoBlur(mobj_t *actor)
2636 actor->special1 = (P_Random()&3)+3; // Random number of blurs
2637 if(P_Random() < 120)
2639 P_ThrustMobj(actor, actor->angle+ANG90, 11*FRACUNIT);
2641 else if(P_Random() > 125)
2643 P_ThrustMobj(actor, actor->angle-ANG90, 11*FRACUNIT);
2647 P_ThrustMobj(actor, actor->angle, 11*FRACUNIT);
2649 S_StartSound(actor, SFX_BISHOP_BLUR);
2652 //============================================================================
2654 // A_BishopSpawnBlur
2656 //============================================================================
2658 void A_BishopSpawnBlur(mobj_t *actor)
2662 if(!--actor->special1)
2668 P_SetMobjState(actor, S_BISHOP_WALK1);
2672 P_SetMobjState(actor, S_BISHOP_ATK1);
2675 mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_BISHOPBLUR);
2678 mo->angle = actor->angle;
2682 //============================================================================
2686 //============================================================================
2688 void A_BishopChase(mobj_t *actor)
2690 actor->z -= FloatBobOffsets[actor->special2]>>1;
2691 actor->special2 = (actor->special2+4)&63;
2692 actor->z += FloatBobOffsets[actor->special2]>>1;
2695 //============================================================================
2699 //============================================================================
2701 void A_BishopPuff(mobj_t *actor)
2705 mo = P_SpawnMobj(actor->x, actor->y, actor->z+40*FRACUNIT,
2709 mo->momz = FRACUNIT/2;
2713 //============================================================================
2717 //============================================================================
2719 void A_BishopPainBlur(mobj_t *actor)
2725 P_SetMobjState(actor, S_BISHOP_BLUR1);
2728 mo = P_SpawnMobj(actor->x+((P_Random()-P_Random())<<12), actor->y
2729 +((P_Random()-P_Random())<<12), actor->z+((P_Random()-P_Random())<<11),
2733 mo->angle = actor->angle;
2737 //============================================================================
2741 //============================================================================
2743 static void DragonSeek(mobj_t *actor, angle_t thresh, angle_t turnMax)
2754 angle_t angleToSpot, angleToTarget;
2757 target = (mobj_t *)actor->special1;
2762 dir = P_FaceMobj(actor, target, &delta);
2773 actor->angle += delta;
2776 { // Turn counter clockwise
2777 actor->angle -= delta;
2779 angle = actor->angle>>ANGLETOFINESHIFT;
2780 actor->momx = FixedMul(actor->info->speed, finecosine[angle]);
2781 actor->momy = FixedMul(actor->info->speed, finesine[angle]);
2782 if(actor->z+actor->height < target->z
2783 || target->z+target->height < actor->z)
2785 dist = P_AproxDistance(target->x-actor->x, target->y-actor->y);
2786 dist = dist/actor->info->speed;
2791 actor->momz = (target->z-actor->z)/dist;
2795 dist = P_AproxDistance(target->x-actor->x, target->y-actor->y);
2796 dist = dist/actor->info->speed;
2798 if(target->flags&MF_SHOOTABLE && P_Random() < 64)
2799 { // attack the destination mobj if it's attackable
2802 if(abs(actor->angle-R_PointToAngle2(actor->x, actor->y,
2803 target->x, target->y)) < ANGLE_45/2)
2805 oldTarget = actor->target;
2806 actor->target = target;
2807 if(P_CheckMeleeRange(actor))
2809 P_DamageMobj(actor->target, actor, actor, HITDICE(10));
2810 S_StartSound(actor, SFX_DRAGON_ATTACK);
2812 else if(P_Random() < 128 && P_CheckMissileRange(actor))
2814 P_SpawnMissile(actor, target, MT_DRAGON_FX);
2815 S_StartSound(actor, SFX_DRAGON_ATTACK);
2817 actor->target = oldTarget;
2821 { // Hit the target thing
2822 if(actor->target && P_Random() < 200)
2825 bestAngle = ANGLE_MAX;
2826 angleToTarget = R_PointToAngle2(actor->x, actor->y,
2827 actor->target->x, actor->target->y);
2828 for(i = 0; i < 5; i++)
2830 if(!target->args[i])
2835 mo = P_FindMobjFromTID(target->args[i], &search);
2836 angleToSpot = R_PointToAngle2(actor->x, actor->y,
2838 if(abs(angleToSpot-angleToTarget) < bestAngle)
2840 bestAngle = abs(angleToSpot-angleToTarget);
2847 actor->special1 = (int)P_FindMobjFromTID(target->args[bestArg],
2855 i = (P_Random()>>2)%5;
2856 } while(!target->args[i]);
2858 actor->special1 = (int)P_FindMobjFromTID(target->args[i], &search);
2863 //============================================================================
2865 // A_DragonInitFlight
2867 //============================================================================
2869 void A_DragonInitFlight(mobj_t *actor)
2875 { // find the first tid identical to the dragon's tid
2876 actor->special1 = (int)P_FindMobjFromTID(actor->tid, &search);
2879 P_SetMobjState(actor, actor->info->spawnstate);
2882 } while(actor->special1 == (int)actor);
2883 P_RemoveMobjFromTIDList(actor);
2886 //============================================================================
2890 //============================================================================
2892 void A_DragonFlight(mobj_t *actor)
2896 DragonSeek(actor, 4*ANGLE_1, 8*ANGLE_1);
2899 if(!(actor->target->flags&MF_SHOOTABLE))
2901 actor->target = NULL;
2904 angle = R_PointToAngle2(actor->x, actor->y, actor->target->x,
2906 if(abs(actor->angle-angle) < ANGLE_45/2 && P_CheckMeleeRange(actor))
2908 P_DamageMobj(actor->target, actor, actor, HITDICE(8));
2909 S_StartSound(actor, SFX_DRAGON_ATTACK);
2911 else if(abs(actor->angle-angle) <= ANGLE_1*20)
2913 P_SetMobjState(actor, actor->info->missilestate);
2914 S_StartSound(actor, SFX_DRAGON_ATTACK);
2919 P_LookForPlayers(actor, true);
2923 //============================================================================
2927 //============================================================================
2929 void A_DragonFlap(mobj_t *actor)
2931 A_DragonFlight(actor);
2932 if(P_Random() < 240)
2934 S_StartSound(actor, SFX_DRAGON_WINGFLAP);
2938 S_StartSound(actor, actor->info->activesound);
2942 //============================================================================
2946 //============================================================================
2948 void A_DragonAttack(mobj_t *actor)
2952 mo = P_SpawnMissile(actor, actor->target, MT_DRAGON_FX);
2955 //============================================================================
2959 //============================================================================
2961 void A_DragonFX2(mobj_t *actor)
2967 delay = 16+(P_Random()>>3);
2968 for(i = 1+(P_Random()&3); i; i--)
2970 mo = P_SpawnMobj(actor->x+((P_Random()-128)<<14),
2971 actor->y+((P_Random()-128)<<14), actor->z+((P_Random()-128)<<12),
2975 mo->tics = delay+(P_Random()&3)*i*2;
2976 mo->target = actor->target;
2981 //============================================================================
2985 //============================================================================
2987 void A_DragonPain(mobj_t *actor)
2990 if(!actor->special1)
2991 { // no destination spot yet
2992 P_SetMobjState(actor, S_DRAGON_INIT);
2996 //============================================================================
2998 // A_DragonCheckCrash
3000 //============================================================================
3002 void A_DragonCheckCrash(mobj_t *actor)
3004 if(actor->z <= actor->floorz)
3006 P_SetMobjState(actor, S_DRAGON_CRASH1);
3010 //============================================================================
3012 //============================================================================
3015 // A_DemonAttack1 (melee)
3017 void A_DemonAttack1(mobj_t *actor)
3019 if(P_CheckMeleeRange(actor))
3021 P_DamageMobj(actor->target, actor, actor, HITDICE(2));
3027 // A_DemonAttack2 (missile)
3029 void A_DemonAttack2(mobj_t *actor)
3034 if(actor->type == MT_DEMON)
3036 fireBall = MT_DEMONFX1;
3040 fireBall = MT_DEMON2FX1;
3042 mo = P_SpawnMissile(actor, actor->target, fireBall);
3045 mo->z += 30*FRACUNIT;
3046 S_StartSound(actor, SFX_DEMON_MISSILE_FIRE);
3054 void A_DemonDeath(mobj_t *actor)
3059 mo = P_SpawnMobj(actor->x, actor->y, actor->z+45*FRACUNIT,
3063 angle = actor->angle+ANG90;
3064 mo->momz = 8*FRACUNIT;
3065 mo->momx = FixedMul((P_Random()<<10)+FRACUNIT,
3066 finecosine[angle>>ANGLETOFINESHIFT]);
3067 mo->momy = FixedMul((P_Random()<<10)+FRACUNIT,
3068 finesine[angle>>ANGLETOFINESHIFT]);
3071 mo = P_SpawnMobj(actor->x, actor->y, actor->z+45*FRACUNIT,
3075 angle = actor->angle-ANG90;
3076 mo->momz = 8*FRACUNIT;
3077 mo->momx = FixedMul((P_Random()<<10)+FRACUNIT,
3078 finecosine[angle>>ANGLETOFINESHIFT]);
3079 mo->momy = FixedMul((P_Random()<<10)+FRACUNIT,
3080 finesine[angle>>ANGLETOFINESHIFT]);
3083 mo = P_SpawnMobj(actor->x, actor->y, actor->z+45*FRACUNIT,
3087 angle = actor->angle-ANG90;
3088 mo->momz = 8*FRACUNIT;
3089 mo->momx = FixedMul((P_Random()<<10)+FRACUNIT,
3090 finecosine[angle>>ANGLETOFINESHIFT]);
3091 mo->momy = FixedMul((P_Random()<<10)+FRACUNIT,
3092 finesine[angle>>ANGLETOFINESHIFT]);
3095 mo = P_SpawnMobj(actor->x, actor->y, actor->z+45*FRACUNIT,
3099 angle = actor->angle-ANG90;
3100 mo->momz = 8*FRACUNIT;
3101 mo->momx = FixedMul((P_Random()<<10)+FRACUNIT,
3102 finecosine[angle>>ANGLETOFINESHIFT]);
3103 mo->momy = FixedMul((P_Random()<<10)+FRACUNIT,
3104 finesine[angle>>ANGLETOFINESHIFT]);
3107 mo = P_SpawnMobj(actor->x, actor->y, actor->z+45*FRACUNIT,
3111 angle = actor->angle-ANG90;
3112 mo->momz = 8*FRACUNIT;
3113 mo->momx = FixedMul((P_Random()<<10)+FRACUNIT,
3114 finecosine[angle>>ANGLETOFINESHIFT]);
3115 mo->momy = FixedMul((P_Random()<<10)+FRACUNIT,
3116 finesine[angle>>ANGLETOFINESHIFT]);
3121 //===========================================================================
3125 //===========================================================================
3127 void A_Demon2Death(mobj_t *actor)
3132 mo = P_SpawnMobj(actor->x, actor->y, actor->z+45*FRACUNIT,
3136 angle = actor->angle+ANG90;
3137 mo->momz = 8*FRACUNIT;
3138 mo->momx = FixedMul((P_Random()<<10)+FRACUNIT,
3139 finecosine[angle>>ANGLETOFINESHIFT]);
3140 mo->momy = FixedMul((P_Random()<<10)+FRACUNIT,
3141 finesine[angle>>ANGLETOFINESHIFT]);
3144 mo = P_SpawnMobj(actor->x, actor->y, actor->z+45*FRACUNIT,
3148 angle = actor->angle-ANG90;
3149 mo->momz = 8*FRACUNIT;
3150 mo->momx = FixedMul((P_Random()<<10)+FRACUNIT,
3151 finecosine[angle>>ANGLETOFINESHIFT]);
3152 mo->momy = FixedMul((P_Random()<<10)+FRACUNIT,
3153 finesine[angle>>ANGLETOFINESHIFT]);
3156 mo = P_SpawnMobj(actor->x, actor->y, actor->z+45*FRACUNIT,
3160 angle = actor->angle-ANG90;
3161 mo->momz = 8*FRACUNIT;
3162 mo->momx = FixedMul((P_Random()<<10)+FRACUNIT,
3163 finecosine[angle>>ANGLETOFINESHIFT]);
3164 mo->momy = FixedMul((P_Random()<<10)+FRACUNIT,
3165 finesine[angle>>ANGLETOFINESHIFT]);
3168 mo = P_SpawnMobj(actor->x, actor->y, actor->z+45*FRACUNIT,
3172 angle = actor->angle-ANG90;
3173 mo->momz = 8*FRACUNIT;
3174 mo->momx = FixedMul((P_Random()<<10)+FRACUNIT,
3175 finecosine[angle>>ANGLETOFINESHIFT]);
3176 mo->momy = FixedMul((P_Random()<<10)+FRACUNIT,
3177 finesine[angle>>ANGLETOFINESHIFT]);
3180 mo = P_SpawnMobj(actor->x, actor->y, actor->z+45*FRACUNIT,
3184 angle = actor->angle-ANG90;
3185 mo->momz = 8*FRACUNIT;
3186 mo->momx = FixedMul((P_Random()<<10)+FRACUNIT,
3187 finecosine[angle>>ANGLETOFINESHIFT]);
3188 mo->momy = FixedMul((P_Random()<<10)+FRACUNIT,
3189 finesine[angle>>ANGLETOFINESHIFT]);
3198 // Sink a mobj incrementally into the floor
3201 boolean A_SinkMobj(mobj_t *actor)
3203 if (actor->floorclip < actor->info->height)
3207 case MT_THRUSTFLOOR_DOWN:
3208 case MT_THRUSTFLOOR_UP:
3209 actor->floorclip += 6*FRACUNIT;
3212 actor->floorclip += FRACUNIT;
3222 // Raise a mobj incrementally from the floor to
3225 boolean A_RaiseMobj(mobj_t *actor)
3229 // Raise a mobj from the ground
3230 if (actor->floorclip > 0)
3235 actor->floorclip -= 2*FRACUNIT;
3237 case MT_THRUSTFLOOR_DOWN:
3238 case MT_THRUSTFLOOR_UP:
3239 actor->floorclip -= actor->special2*FRACUNIT;
3242 actor->floorclip -= 2*FRACUNIT;
3245 if (actor->floorclip <= 0)
3247 actor->floorclip = 0;
3255 return done; // Reached target height
3259 //============================================================================
3262 // special1 Internal index into floatbob
3264 //============================================================================
3270 void A_WraithInit(mobj_t *actor)
3272 actor->z += 48<<FRACBITS;
3273 actor->special1 = 0; // index into floatbob
3276 void A_WraithRaiseInit(mobj_t *actor)
3278 actor->flags2 &= ~MF2_DONTDRAW;
3279 actor->flags2 &= ~MF2_NONSHOOTABLE;
3280 actor->flags |= MF_SHOOTABLE|MF_SOLID;
3281 actor->floorclip = actor->info->height;
3284 void A_WraithRaise(mobj_t *actor)
3286 if (A_RaiseMobj(actor))
3288 // Reached it's target height
3289 P_SetMobjState(actor,S_WRAITH_CHASE1);
3292 P_SpawnDirt(actor, actor->radius);
3296 void A_WraithMelee(mobj_t *actor)
3300 // Steal health from target and give to player
3301 if(P_CheckMeleeRange(actor) && (P_Random()<220))
3303 amount = HITDICE(2);
3304 P_DamageMobj(actor->target, actor, actor, amount);
3305 actor->health += amount;
3309 void A_WraithMissile(mobj_t *actor)
3313 mo = P_SpawnMissile(actor, actor->target, MT_WRAITHFX1);
3316 S_StartSound(actor, SFX_WRAITH_MISSILE_FIRE);
3322 // A_WraithFX2 - spawns sparkle tail of missile
3325 void A_WraithFX2(mobj_t *actor)
3333 mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_WRAITHFX2);
3338 angle = actor->angle+(P_Random()<<22);
3342 angle = actor->angle-(P_Random()<<22);
3345 mo->momx = FixedMul((P_Random()<<7)+FRACUNIT,
3346 finecosine[angle>>ANGLETOFINESHIFT]);
3347 mo->momy = FixedMul((P_Random()<<7)+FRACUNIT,
3348 finesine[angle>>ANGLETOFINESHIFT]);
3350 mo->floorclip = 10*FRACUNIT;
3356 // Spawn an FX3 around the actor during attacks
3357 void A_WraithFX3(mobj_t *actor)
3360 int numdropped=P_Random()%15;
3363 for (i=0; i<numdropped; i++)
3365 mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_WRAITHFX3);
3368 mo->x += (P_Random()-128)<<11;
3369 mo->y += (P_Random()-128)<<11;
3370 mo->z += (P_Random()<<10);
3376 // Spawn an FX4 during movement
3377 void A_WraithFX4(mobj_t *actor)
3380 int chance = P_Random();
3388 else if (chance < 20)
3393 else if (chance < 25)
3406 mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_WRAITHFX4);
3409 mo->x += (P_Random()-128)<<12;
3410 mo->y += (P_Random()-128)<<12;
3411 mo->z += (P_Random()<<10);
3417 mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_WRAITHFX5);
3420 mo->x += (P_Random()-128)<<11;
3421 mo->y += (P_Random()-128)<<11;
3422 mo->z += (P_Random()<<10);
3429 void A_WraithLook(mobj_t *actor)
3431 // A_WraithFX4(actor); // too expensive
3436 void A_WraithChase(mobj_t *actor)
3438 int weaveindex = actor->special1;
3439 actor->z += FloatBobOffsets[weaveindex];
3440 actor->special1 = (weaveindex+2)&63;
3441 // if (actor->floorclip > 0)
3443 // P_SetMobjState(actor, S_WRAITH_RAISE2);
3452 //============================================================================
3454 //============================================================================
3456 void A_EttinAttack(mobj_t *actor)
3458 if(P_CheckMeleeRange(actor))
3460 P_DamageMobj(actor->target, actor, actor, HITDICE(2));
3465 void A_DropMace(mobj_t *actor)
3469 mo = P_SpawnMobj(actor->x, actor->y,
3470 actor->z+(actor->height>>1), MT_ETTIN_MACE);
3473 mo->momx = (P_Random()-128)<<11;
3474 mo->momy = (P_Random()-128)<<11;
3475 mo->momz = FRACUNIT*10+(P_Random()<<10);
3481 //============================================================================
3484 // special1 index into floatbob
3485 // special2 whether strafing or not
3486 //============================================================================
3488 void A_FiredSpawnRock(mobj_t *actor)
3492 int rtype = 0; /* jim gcc disappointed me here */
3494 switch(P_Random()%5)
3497 rtype = MT_FIREDEMON_FX1;
3500 rtype = MT_FIREDEMON_FX2;
3503 rtype = MT_FIREDEMON_FX3;
3506 rtype = MT_FIREDEMON_FX4;
3509 rtype = MT_FIREDEMON_FX5;
3513 x = actor->x + ((P_Random()-128) << 12);
3514 y = actor->y + ((P_Random()-128) << 12);
3515 z = actor->z + ((P_Random()) << 11);
3516 mo = P_SpawnMobj(x,y,z,rtype);
3520 mo->momx = (P_Random()-128)<<10;
3521 mo->momy = (P_Random()-128)<<10;
3522 mo->momz = (P_Random()<<10);
3523 mo->special1 = 2; // Number bounces
3526 // Initialize fire demon
3527 actor->special2 = 0;
3528 actor->flags &= ~MF_JUSTATTACKED;
3531 void A_FiredRocks(mobj_t *actor)
3533 A_FiredSpawnRock(actor);
3534 A_FiredSpawnRock(actor);
3535 A_FiredSpawnRock(actor);
3536 A_FiredSpawnRock(actor);
3537 A_FiredSpawnRock(actor);
3540 void A_FiredAttack(mobj_t *actor)
3543 mo = P_SpawnMissile(actor, actor->target, MT_FIREDEMON_FX6);
3544 if (mo) S_StartSound(actor, SFX_FIRED_ATTACK);
3547 void A_SmBounce(mobj_t *actor)
3549 // give some more momentum (x,y,&z)
3550 actor->z = actor->floorz + FRACUNIT;
3551 actor->momz = (2*FRACUNIT) + (P_Random()<<10);
3552 actor->momx = P_Random()%3<<FRACBITS;
3553 actor->momy = P_Random()%3<<FRACBITS;
3557 #define FIREDEMON_ATTACK_RANGE 64*8*FRACUNIT
3559 void A_FiredChase(mobj_t *actor)
3561 int weaveindex = actor->special1;
3562 mobj_t *target = actor->target;
3566 if(actor->reactiontime) actor->reactiontime--;
3567 if(actor->threshold) actor->threshold--;
3569 // Float up and down
3570 actor->z += FloatBobOffsets[weaveindex];
3571 actor->special1 = (weaveindex+2)&63;
3573 // Insure it stays above certain height
3574 if (actor->z < actor->floorz + (64*FRACUNIT))
3576 actor->z += 2*FRACUNIT;
3579 if(!actor->target || !(actor->target->flags&MF_SHOOTABLE))
3581 P_LookForPlayers(actor,true);
3586 if (actor->special2 > 0)
3592 actor->special2 = 0;
3593 actor->momx = actor->momy = 0;
3594 dist = P_AproxDistance(actor->x - target->x, actor->y - target->y);
3595 if (dist < FIREDEMON_ATTACK_RANGE)
3599 ang = R_PointToAngle2(actor->x, actor->y, target->x, target->y);
3604 ang>>=ANGLETOFINESHIFT;
3605 actor->momx = FixedMul(8*FRACUNIT, finecosine[ang]);
3606 actor->momy = FixedMul(8*FRACUNIT, finesine[ang]);
3607 actor->special2 = 3; // strafe time
3612 FaceMovementDirection(actor);
3615 if (!actor->special2)
3617 if (--actor->movecount<0 || !P_Move (actor))
3619 P_NewChaseDir (actor);
3623 // Do missile attack
3624 if (!(actor->flags&MF_JUSTATTACKED))
3626 if (P_CheckMissileRange(actor) && (P_Random()<20))
3628 P_SetMobjState (actor, actor->info->missilestate);
3629 actor->flags |= MF_JUSTATTACKED;
3635 actor->flags &= ~MF_JUSTATTACKED;
3638 // make active sound
3639 if(actor->info->activesound && P_Random() < 3)
3641 S_StartSound(actor, actor->info->activesound);
3645 void A_FiredSplotch(mobj_t *actor)
3649 mo = P_SpawnMobj(actor->x,actor->y,actor->z, MT_FIREDEMON_SPLOTCH1);
3652 mo->momx = (P_Random()-128)<<11;
3653 mo->momy = (P_Random()-128)<<11;
3654 mo->momz = FRACUNIT*3 + (P_Random()<<10);
3656 mo = P_SpawnMobj(actor->x,actor->y,actor->z, MT_FIREDEMON_SPLOTCH2);
3659 mo->momx = (P_Random()-128)<<11;
3660 mo->momy = (P_Random()-128)<<11;
3661 mo->momz = FRACUNIT*3 + (P_Random()<<10);
3666 //============================================================================
3670 //============================================================================
3672 void A_IceGuyLook(mobj_t *actor)
3680 dist = ((P_Random()-128)*actor->radius)>>7;
3681 an = (actor->angle+ANG90)>>ANGLETOFINESHIFT;
3683 P_SpawnMobj(actor->x+FixedMul(dist, finecosine[an]),
3684 actor->y+FixedMul(dist, finesine[an]), actor->z+60*FRACUNIT,
3685 MT_ICEGUY_WISP1+(P_Random()&1));
3689 //============================================================================
3693 //============================================================================
3695 void A_IceGuyChase(mobj_t *actor)
3702 if(P_Random() < 128)
3704 dist = ((P_Random()-128)*actor->radius)>>7;
3705 an = (actor->angle+ANG90)>>ANGLETOFINESHIFT;
3707 mo = P_SpawnMobj(actor->x+FixedMul(dist, finecosine[an]),
3708 actor->y+FixedMul(dist, finesine[an]), actor->z+60*FRACUNIT,
3709 MT_ICEGUY_WISP1+(P_Random()&1));
3712 mo->momx = actor->momx;
3713 mo->momy = actor->momy;
3714 mo->momz = actor->momz;
3720 //============================================================================
3724 //============================================================================
3726 void A_IceGuyAttack(mobj_t *actor)
3734 an = (actor->angle+ANG90)>>ANGLETOFINESHIFT;
3735 P_SpawnMissileXYZ(actor->x+FixedMul(actor->radius>>1,
3736 finecosine[an]), actor->y+FixedMul(actor->radius>>1,
3737 finesine[an]), actor->z+40*FRACUNIT, actor, actor->target,
3739 an = (actor->angle-ANG90)>>ANGLETOFINESHIFT;
3740 P_SpawnMissileXYZ(actor->x+FixedMul(actor->radius>>1,
3741 finecosine[an]), actor->y+FixedMul(actor->radius>>1,
3742 finesine[an]), actor->z+40*FRACUNIT, actor, actor->target,
3744 S_StartSound(actor, actor->info->attacksound);
3747 //============================================================================
3749 // A_IceGuyMissilePuff
3751 //============================================================================
3753 void A_IceGuyMissilePuff(mobj_t *actor)
3756 mo = P_SpawnMobj(actor->x, actor->y, actor->z+2*FRACUNIT, MT_ICEFX_PUFF);
3759 //============================================================================
3763 //============================================================================
3765 void A_IceGuyDie(mobj_t *actor)
3767 void A_FreezeDeathChunks(mobj_t *actor);
3772 actor->height <<= 2;
3773 A_FreezeDeathChunks(actor);
3776 //============================================================================
3778 // A_IceGuyMissileExplode
3780 //============================================================================
3782 void A_IceGuyMissileExplode(mobj_t *actor)
3787 for(i = 0; i < 8; i++)
3789 mo = P_SpawnMissileAngle(actor, MT_ICEGUY_FX2, i*ANG45, -0.3*FRACUNIT);
3792 mo->target = actor->target;
3805 //============================================================================
3809 // Sorcerer Variables
3810 // special1 Angle of ball 1 (all others relative to that)
3811 // special2 which ball to stop at in stop mode (MT_???)
3812 // args[0] Denfense time
3813 // args[1] Number of full rotations since stopping mode
3814 // args[2] Target orbit speed for acceleration/deceleration
3815 // args[3] Movement mode (see SORC_ macros)
3816 // args[4] Current ball orbit speed
3817 // Sorcerer Ball Variables
3818 // special1 Previous angle of ball (for woosh)
3819 // special2 Countdown of rapid fire (FX4)
3820 // args[0] If set, don't play the bounce sound when bouncing
3821 //============================================================================
3823 #define SORCBALL_INITIAL_SPEED 7
3824 #define SORCBALL_TERMINAL_SPEED 25
3825 #define SORCBALL_SPEED_ROTATIONS 5
3826 #define SORC_DEFENSE_TIME 255
3827 #define SORC_DEFENSE_HEIGHT 45
3828 #define BOUNCE_TIME_UNIT (35/2)
3829 #define SORCFX4_RAPIDFIRE_TIME (6*3) // 3 seconds
3830 #define SORCFX4_SPREAD_ANGLE 20
3832 #define SORC_DECELERATE 0
3833 #define SORC_ACCELERATE 1
3834 #define SORC_STOPPING 2
3835 #define SORC_FIRESPELL 3
3836 #define SORC_STOPPED 4
3837 #define SORC_NORMAL 5
3838 #define SORC_FIRING_SPELL 6
3840 #define BALL1_ANGLEOFFSET 0
3841 #define BALL2_ANGLEOFFSET (ANGLE_MAX/3)
3842 #define BALL3_ANGLEOFFSET ((ANGLE_MAX/3)*2)
3844 void A_SorcBallOrbit(mobj_t *actor);
3845 void A_SorcSpinBalls(mobj_t *actor);
3846 void A_SpeedBalls(mobj_t *actor);
3847 void A_SlowBalls(mobj_t *actor);
3848 void A_StopBalls(mobj_t *actor);
3849 void A_AccelBalls(mobj_t *actor);
3850 void A_DecelBalls(mobj_t *actor);
3851 void A_SorcBossAttack(mobj_t *actor);
3852 void A_SpawnFizzle(mobj_t *actor);
3853 void A_CastSorcererSpell(mobj_t *actor);
3854 void A_SorcUpdateBallAngle(mobj_t *actor);
3855 void A_BounceCheck(mobj_t *actor);
3856 void A_SorcFX1Seek(mobj_t *actor);
3857 void A_SorcOffense1(mobj_t *actor);
3858 void A_SorcOffense2(mobj_t *actor);
3861 // Spawn spinning balls above head - actor is sorcerer
3862 void A_SorcSpinBalls(mobj_t *actor)
3868 actor->args[0] = 0; // Currently no defense
3869 actor->args[3] = SORC_NORMAL;
3870 actor->args[4] = SORCBALL_INITIAL_SPEED; // Initial orbit speed
3871 actor->special1 = ANGLE_1;
3872 z = actor->z - actor->floorclip + actor->info->height;
3874 mo = P_SpawnMobj(actor->x, actor->y, z, MT_SORCBALL1);
3878 mo->special2 = SORCFX4_RAPIDFIRE_TIME;
3880 mo = P_SpawnMobj(actor->x, actor->y, z, MT_SORCBALL2);
3881 if (mo) mo->target = actor;
3882 mo = P_SpawnMobj(actor->x, actor->y, z, MT_SORCBALL3);
3883 if (mo) mo->target = actor;
3888 // A_SorcBallOrbit() ==========================================
3891 void A_SorcBallOrbit(mobj_t *actor)
3894 angle_t angle = 0, baseangle; /* jim initialiser added */
3895 int mode = actor->target->args[3];
3896 mobj_t *parent = (mobj_t *)actor->target;
3897 int dist = parent->radius - (actor->radius<<1);
3898 angle_t prevangle = actor->special1;
3900 if (actor->target->health <= 0)
3901 P_SetMobjState(actor, actor->info->painstate);
3903 baseangle = (angle_t)parent->special1;
3907 angle = baseangle + BALL1_ANGLEOFFSET;
3910 angle = baseangle + BALL2_ANGLEOFFSET;
3913 angle = baseangle + BALL3_ANGLEOFFSET;
3916 I_Error("corrupted sorcerer");
3919 actor->angle = angle;
3920 angle >>= ANGLETOFINESHIFT;
3924 case SORC_NORMAL: // Balls rotating normally
3925 A_SorcUpdateBallAngle(actor);
3927 case SORC_DECELERATE: // Balls decelerating
3928 A_DecelBalls(actor);
3929 A_SorcUpdateBallAngle(actor);
3931 case SORC_ACCELERATE: // Balls accelerating
3932 A_AccelBalls(actor);
3933 A_SorcUpdateBallAngle(actor);
3935 case SORC_STOPPING: // Balls stopping
3936 if ((parent->special2 == actor->type) &&
3937 (parent->args[1] > SORCBALL_SPEED_ROTATIONS) &&
3938 (abs(angle - (parent->angle>>ANGLETOFINESHIFT)) < (30<<5)))
3941 actor->target->args[3] = SORC_FIRESPELL;
3942 actor->target->args[4] = 0;
3943 // Set angle so ball angle == sorcerer angle
3947 parent->special1 = (int)(parent->angle -
3951 parent->special1 = (int)(parent->angle -
3955 parent->special1 = (int)(parent->angle -
3964 A_SorcUpdateBallAngle(actor);
3967 case SORC_FIRESPELL: // Casting spell
3968 if (parent->special2 == actor->type)
3970 // Put sorcerer into special throw spell anim
3971 if (parent->health > 0)
3972 P_SetMobjStateNF(parent, S_SORC_ATTACK1);
3974 if (actor->type==MT_SORCBALL1 && P_Random()<200)
3976 S_StartSound(NULL, SFX_SORCERER_SPELLCAST);
3977 actor->special2 = SORCFX4_RAPIDFIRE_TIME;
3978 actor->args[4] = 128;
3979 parent->args[3] = SORC_FIRING_SPELL;
3983 A_CastSorcererSpell(actor);
3984 parent->args[3] = SORC_STOPPED;
3988 case SORC_FIRING_SPELL:
3989 if (parent->special2 == actor->type)
3991 if (actor->special2-- <= 0)
3993 // Done rapid firing
3994 parent->args[3] = SORC_STOPPED;
3995 // Back to orbit balls
3996 if (parent->health > 0)
3997 P_SetMobjStateNF(parent, S_SORC_ATTACK4);
4001 // Do rapid fire spell
4002 A_SorcOffense2(actor);
4006 case SORC_STOPPED: // Balls stopped
4011 if ((angle < prevangle) && (parent->args[4]==SORCBALL_TERMINAL_SPEED))
4013 parent->args[1]++; // Bump rotation counter
4014 // Completed full rotation - make woosh sound
4015 S_StartSound(actor, SFX_SORCERER_BALLWOOSH);
4017 actor->special1 = angle; // Set previous angle
4018 x = parent->x + FixedMul(dist, finecosine[angle]);
4019 y = parent->y + FixedMul(dist, finesine[angle]);
4022 actor->z = parent->z - parent->floorclip + parent->info->height;
4027 // Set balls to speed mode - actor is sorcerer
4029 void A_SpeedBalls(mobj_t *actor)
4031 actor->args[3] = SORC_ACCELERATE; // speed mode
4032 actor->args[2] = SORCBALL_TERMINAL_SPEED; // target speed
4037 // Set balls to slow mode - actor is sorcerer
4039 void A_SlowBalls(mobj_t *actor)
4041 actor->args[3] = SORC_DECELERATE; // slow mode
4042 actor->args[2] = SORCBALL_INITIAL_SPEED; // target speed
4047 // Instant stop when rotation gets to ball in special2
4048 // actor is sorcerer
4050 void A_StopBalls(mobj_t *actor)
4052 int chance = P_Random();
4053 actor->args[3] = SORC_STOPPING; // stopping mode
4054 actor->args[1] = 0; // Reset rotation counter
4056 if ((actor->args[0] <= 0) && (chance < 200))
4058 actor->special2 = MT_SORCBALL2; // Blue
4060 else if((actor->health < (actor->info->spawnhealth >> 1)) &&
4063 actor->special2 = MT_SORCBALL3; // Green
4067 actor->special2 = MT_SORCBALL1; // Yellow
4075 // Increase ball orbit speed - actor is ball
4077 void A_AccelBalls(mobj_t *actor)
4079 mobj_t *sorc = actor->target;
4081 if (sorc->args[4] < sorc->args[2])
4087 sorc->args[3] = SORC_NORMAL;
4088 if (sorc->args[4] >= SORCBALL_TERMINAL_SPEED)
4090 // Reached terminal velocity - stop balls
4097 // Decrease ball orbit speed - actor is ball
4098 void A_DecelBalls(mobj_t *actor)
4100 mobj_t *sorc = actor->target;
4102 if (sorc->args[4] > sorc->args[2])
4108 sorc->args[3] = SORC_NORMAL;
4113 // Update angle if first ball - actor is ball
4114 void A_SorcUpdateBallAngle(mobj_t *actor)
4116 if (actor->type == MT_SORCBALL1)
4118 actor->target->special1 += ANGLE_1*actor->target->args[4];
4124 void A_CastSorcererSpell(mobj_t *actor)
4127 int spell = actor->type;
4130 mobj_t *parent = actor->target;
4132 S_StartSound(NULL, SFX_SORCERER_SPELLCAST);
4134 // Put sorcerer into throw spell animation
4135 if (parent->health > 0) P_SetMobjStateNF(parent, S_SORC_ATTACK4);
4139 case MT_SORCBALL1: // Offensive
4140 A_SorcOffense1(actor);
4142 case MT_SORCBALL2: // Defensive
4143 z = parent->z - parent->floorclip +
4144 SORC_DEFENSE_HEIGHT*FRACUNIT;
4145 mo = P_SpawnMobj(actor->x, actor->y, z, MT_SORCFX2);
4146 parent->flags2 |= MF2_REFLECTIVE|MF2_INVULNERABLE;
4147 parent->args[0] = SORC_DEFENSE_TIME;
4148 if (mo) mo->target = parent;
4150 case MT_SORCBALL3: // Reinforcements
4151 ang1 = actor->angle - ANGLE_45;
4152 ang2 = actor->angle + ANGLE_45;
4153 if(actor->health < (actor->info->spawnhealth/3))
4154 { // Spawn 2 at a time
4155 mo = P_SpawnMissileAngle(parent, MT_SORCFX3, ang1, 4*FRACUNIT);
4156 if (mo) mo->target = parent;
4157 mo = P_SpawnMissileAngle(parent, MT_SORCFX3, ang2, 4*FRACUNIT);
4158 if (mo) mo->target = parent;
4162 if (P_Random() < 128)
4164 mo = P_SpawnMissileAngle(parent, MT_SORCFX3, ang1, 4*FRACUNIT);
4165 if (mo) mo->target = parent;
4174 void A_SpawnReinforcements(mobj_t *actor)
4176 mobj_t *parent = actor->target;
4180 ang = ANGLE_1 * P_Random();
4181 mo = P_SpawnMissileAngle(actor, MT_SORCFX3, ang, 5*FRACUNIT);
4182 if (mo) mo->target = parent;
4187 void A_SorcOffense1(mobj_t *actor)
4191 mobj_t *parent=(mobj_t *)actor->target;
4193 ang1 = actor->angle + ANGLE_1*70;
4194 ang2 = actor->angle - ANGLE_1*70;
4195 mo = P_SpawnMissileAngle(parent, MT_SORCFX1, ang1, 0);
4198 mo->target = parent;
4199 mo->special1 = (int)parent->target;
4200 mo->args[4] = BOUNCE_TIME_UNIT;
4201 mo->args[3] = 15; // Bounce time in seconds
4203 mo = P_SpawnMissileAngle(parent, MT_SORCFX1, ang2, 0);
4206 mo->target = parent;
4207 mo->special1 = (int)parent->target;
4208 mo->args[4] = BOUNCE_TIME_UNIT;
4209 mo->args[3] = 15; // Bounce time in seconds
4215 void A_SorcOffense2(mobj_t *actor)
4220 mobj_t *parent = actor->target;
4221 mobj_t *dest = parent->target;
4224 index = actor->args[4] << 5;
4225 actor->args[4] += 15;
4226 delta = (finesine[index])*SORCFX4_SPREAD_ANGLE;
4227 delta = (delta>>FRACBITS)*ANGLE_1;
4228 ang1 = actor->angle + delta;
4229 mo = P_SpawnMissileAngle(parent, MT_SORCFX4, ang1, 0);
4232 mo->special2 = 35*5/2; // 5 seconds
4233 dist = P_AproxDistance(dest->x - mo->x, dest->y - mo->y);
4234 dist = dist/mo->info->speed;
4235 if(dist < 1) dist = 1;
4236 mo->momz = (dest->z-mo->z)/dist;
4241 // Resume ball spinning
4242 void A_SorcBossAttack(mobj_t *actor)
4244 actor->args[3] = SORC_ACCELERATE;
4245 actor->args[2] = SORCBALL_INITIAL_SPEED;
4249 // spell cast magic fizzle
4250 void A_SpawnFizzle(mobj_t *actor)
4253 fixed_t dist = 5*FRACUNIT;
4254 angle_t angle = actor->angle >> ANGLETOFINESHIFT;
4255 fixed_t speed = actor->info->speed;
4260 x = actor->x + FixedMul(dist,finecosine[angle]);
4261 y = actor->y + FixedMul(dist,finesine[angle]);
4262 z = actor->z - actor->floorclip + (actor->height>>1);
4263 for (ix=0; ix<5; ix++)
4265 mo = P_SpawnMobj(x,y,z,MT_SORCSPARK1);
4268 rangle = angle + ((P_Random()%5) << 1);
4269 mo->momx = FixedMul(P_Random()%speed,finecosine[rangle]);
4270 mo->momy = FixedMul(P_Random()%speed,finesine[rangle]);
4271 mo->momz = FRACUNIT*2;
4277 //============================================================================
4278 // Yellow spell - offense
4279 //============================================================================
4281 void A_SorcFX1Seek(mobj_t *actor)
4283 A_BounceCheck(actor);
4284 P_SeekerMissile(actor,ANGLE_1*2,ANGLE_1*6);
4288 //============================================================================
4289 // Blue spell - defense
4290 //============================================================================
4293 // special1 current angle
4295 // args[0] 0 = CW, 1 = CCW
4297 //============================================================================
4299 // Split ball in two
4300 void A_SorcFX2Split(mobj_t *actor)
4304 mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SORCFX2);
4307 mo->target = actor->target;
4308 mo->args[0] = 0; // CW
4309 mo->special1 = actor->angle; // Set angle
4310 P_SetMobjStateNF(mo, S_SORCFX2_ORBIT1);
4312 mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SORCFX2);
4315 mo->target = actor->target;
4316 mo->args[0] = 1; // CCW
4317 mo->special1 = actor->angle; // Set angle
4318 P_SetMobjStateNF(mo, S_SORCFX2_ORBIT1);
4320 P_SetMobjStateNF(actor, S_NULL);
4324 // Orbit FX2 about sorcerer
4325 void A_SorcFX2Orbit(mobj_t *actor)
4329 mobj_t *parent = actor->target;
4330 fixed_t dist = parent->info->radius;
4332 if ((parent->health <= 0) || // Sorcerer is dead
4333 (!parent->args[0])) // Time expired
4335 P_SetMobjStateNF(actor, actor->info->deathstate);
4336 parent->args[0] = 0;
4337 parent->flags2 &= ~MF2_REFLECTIVE;
4338 parent->flags2 &= ~MF2_INVULNERABLE;
4341 if (actor->args[0] && (parent->args[0]-- <= 0)) // Time expired
4343 P_SetMobjStateNF(actor, actor->info->deathstate);
4344 parent->args[0] = 0;
4345 parent->flags2 &= ~MF2_REFLECTIVE;
4348 // Move to new position based on angle
4349 if (actor->args[0]) // Counter clock-wise
4351 actor->special1 += ANGLE_1*10;
4352 angle = ((angle_t)actor->special1) >> ANGLETOFINESHIFT;
4353 x = parent->x + FixedMul(dist, finecosine[angle]);
4354 y = parent->y + FixedMul(dist, finesine[angle]);
4355 z = parent->z - parent->floorclip + SORC_DEFENSE_HEIGHT*FRACUNIT;
4356 z += FixedMul(15*FRACUNIT,finecosine[angle]);
4358 P_SpawnMobj(x,y,z, MT_SORCFX2_T1);
4362 actor->special1 -= ANGLE_1*10;
4363 angle = ((angle_t)actor->special1) >> ANGLETOFINESHIFT;
4364 x = parent->x + FixedMul(dist, finecosine[angle]);
4365 y = parent->y + FixedMul(dist, finesine[angle]);
4366 z = parent->z - parent->floorclip + SORC_DEFENSE_HEIGHT*FRACUNIT;
4367 z += FixedMul(20*FRACUNIT,finesine[angle]);
4369 P_SpawnMobj(x,y,z, MT_SORCFX2_T1);
4379 //============================================================================
4380 // Green spell - spawn bishops
4381 //============================================================================
4383 void A_SpawnBishop(mobj_t *actor)
4386 mo=P_SpawnMobj(actor->x, actor->y, actor->z, MT_BISHOP);
4389 if(!P_TestMobjLocation(mo))
4391 P_SetMobjState(mo, S_NULL);
4394 P_SetMobjState(actor, S_NULL);
4398 void A_SmokePuffEntry(mobj_t *actor)
4400 P_SpawnMobj(actor->x, actor->y, actor->z, MT_MNTRSMOKE);
4404 void A_SmokePuffExit(mobj_t *actor)
4406 P_SpawnMobj(actor->x, actor->y, actor->z, MT_MNTRSMOKEEXIT);
4409 void A_SorcererBishopEntry(mobj_t *actor)
4411 P_SpawnMobj(actor->x, actor->y, actor->z, MT_SORCFX3_EXPLOSION);
4412 S_StartSound(actor, actor->info->seesound);
4416 //============================================================================
4417 // FX4 - rapid fire balls
4418 //============================================================================
4420 void A_SorcFX4Check(mobj_t *actor)
4422 if (actor->special2-- <= 0)
4424 P_SetMobjStateNF(actor, actor->info->deathstate);
4428 //============================================================================
4429 // Ball death - spawn stuff
4430 //============================================================================
4432 void A_SorcBallPop(mobj_t *actor)
4434 S_StartSound(NULL, SFX_SORCERER_BALLPOP);
4435 actor->flags &= ~MF_NOGRAVITY;
4436 actor->flags2 |= MF2_LOGRAV;
4437 actor->momx = ((P_Random()%10)-5) << FRACBITS;
4438 actor->momy = ((P_Random()%10)-5) << FRACBITS;
4439 actor->momz = (2+(P_Random()%3)) << FRACBITS;
4440 actor->special2 = 4*FRACUNIT; // Initial bounce factor
4441 actor->args[4] = BOUNCE_TIME_UNIT; // Bounce time unit
4442 actor->args[3] = 5; // Bounce time in seconds
4447 void A_BounceCheck(mobj_t *actor)
4449 if (actor->args[4]-- <= 0)
4451 if (actor->args[3]-- <= 0)
4453 P_SetMobjState(actor, actor->info->deathstate);
4459 S_StartSound(NULL, SFX_SORCERER_BIGBALLEXPLODE);
4462 S_StartSound(NULL, SFX_SORCERER_HEADSCREAM);
4470 actor->args[4] = BOUNCE_TIME_UNIT;
4478 //============================================================================
4480 //============================================================================
4481 #define CLASS_BOSS_STRAFE_RANGE 64*10*FRACUNIT
4483 void A_FastChase(mobj_t *actor)
4490 if(actor->reactiontime)
4492 actor->reactiontime--;
4495 // Modify target threshold
4496 if(actor->threshold)
4501 if(gameskill == sk_nightmare)
4502 { // Monsters move faster in nightmare mode
4503 actor->tics -= actor->tics/2;
4511 // turn towards movement direction if not there yet
4513 if(actor->movedir < 8)
4515 actor->angle &= (7<<29);
4516 delta = actor->angle-(actor->movedir << 29);
4519 actor->angle -= ANG90/2;
4523 actor->angle += ANG90/2;
4527 if(!actor->target || !(actor->target->flags&MF_SHOOTABLE))
4528 { // look for a new target
4529 if(P_LookForPlayers(actor, true))
4530 { // got a new target
4533 P_SetMobjState(actor, actor->info->spawnstate);
4538 // don't attack twice in a row
4540 if(actor->flags & MF_JUSTATTACKED)
4542 actor->flags &= ~MF_JUSTATTACKED;
4543 if (gameskill != sk_nightmare)
4544 P_NewChaseDir (actor);
4549 if (actor->special2 > 0)
4555 target = actor->target;
4556 actor->special2 = 0;
4557 actor->momx = actor->momy = 0;
4558 dist=P_AproxDistance(actor->x - target->x,
4559 actor->y - target->y);
4560 if (dist < CLASS_BOSS_STRAFE_RANGE)
4564 ang = R_PointToAngle2(actor->x, actor->y,
4565 target->x, target->y);
4570 ang>>=ANGLETOFINESHIFT;
4571 actor->momx = FixedMul(13*FRACUNIT, finecosine[ang]);
4572 actor->momy = FixedMul(13*FRACUNIT, finesine[ang]);
4573 actor->special2 = 3; // strafe time
4579 // check for missile attack
4581 if (actor->info->missilestate)
4583 if (gameskill < sk_nightmare && actor->movecount)
4585 if (!P_CheckMissileRange (actor))
4587 P_SetMobjState (actor, actor->info->missilestate);
4588 actor->flags |= MF_JUSTATTACKED;
4594 // possibly choose another target
4596 if (netgame && !actor->threshold && !P_CheckSight (actor, actor->target) )
4598 if (P_LookForPlayers(actor,true))
4599 return; // got a new target
4603 // chase towards player
4605 if (!actor->special2)
4607 if (--actor->movecount<0 || !P_Move (actor))
4609 P_NewChaseDir (actor);
4615 void A_FighterAttack(mobj_t *actor)
4617 extern void A_FSwordAttack2(mobj_t *actor);
4619 if(!actor->target) return;
4620 A_FSwordAttack2(actor);
4624 void A_ClericAttack(mobj_t *actor)
4626 extern void A_CHolyAttack3(mobj_t *actor);
4628 if(!actor->target) return;
4629 A_CHolyAttack3(actor);
4634 void A_MageAttack(mobj_t *actor)
4636 extern void A_MStaffAttack2(mobj_t *actor);
4638 if(!actor->target) return;
4639 A_MStaffAttack2(actor);
4642 void A_ClassBossHealth(mobj_t *actor)
4644 if (netgame && !deathmatch) // co-op only
4646 if (!actor->special1)
4649 actor->special1 = true; // has been initialized
4655 //===========================================================================
4657 // A_CheckFloor - Checks if an object hit the floor
4659 //===========================================================================
4661 void A_CheckFloor(mobj_t *actor)
4663 if(actor->z <= actor->floorz)
4665 actor->z = actor->floorz;
4666 actor->flags2 &= ~MF2_LOGRAV;
4667 P_SetMobjState(actor, actor->info->deathstate);
4671 //============================================================================
4675 //============================================================================
4677 void A_FreezeDeath(mobj_t *actor)
4679 actor->tics = 75+P_Random()+P_Random();
4680 actor->flags |= MF_SOLID|MF_SHOOTABLE|MF_NOBLOOD;
4681 actor->flags2 |= MF2_PUSHABLE|MF2_TELESTOMP|MF2_PASSMOBJ|MF2_SLIDE;
4682 actor->height <<= 2;
4683 S_StartSound(actor, SFX_FREEZE_DEATH);
4687 actor->player->damagecount = 0;
4688 actor->player->poisoncount = 0;
4689 actor->player->bonuscount = 0;
4690 if(actor->player == &players[consoleplayer])
4692 SB_PaletteFlash(false);
4695 else if(actor->flags&MF_COUNTKILL && actor->special)
4696 { // Initiate monster death actions
4697 P_ExecuteLineSpecial(actor->special, actor->args, NULL, 0, actor);
4701 //============================================================================
4705 //============================================================================
4707 void A_IceSetTics(mobj_t *actor)
4711 actor->tics = 70+(P_Random()&63);
4712 floor = P_GetThingFloorType(actor);
4713 if(floor == FLOOR_LAVA)
4717 else if(floor == FLOOR_ICE)
4723 //============================================================================
4725 // A_IceCheckHeadDone
4727 //============================================================================
4729 void A_IceCheckHeadDone(mobj_t *actor)
4731 if(actor->special2 == 666)
4733 P_SetMobjState(actor, S_ICECHUNK_HEAD2);
4737 //============================================================================
4739 // A_FreezeDeathChunks
4741 //============================================================================
4743 void A_FreezeDeathChunks(mobj_t *actor)
4748 if(actor->momx || actor->momy || actor->momz)
4753 S_StartSound(actor, SFX_FREEZE_SHATTER);
4755 for(i = 12+(P_Random()&15); i >= 0; i--)
4757 mo = P_SpawnMobj(actor->x+(((P_Random()-128)*actor->radius)>>7),
4758 actor->y+(((P_Random()-128)*actor->radius)>>7),
4759 actor->z+(P_Random()*actor->height/255), MT_ICECHUNK);
4760 P_SetMobjState(mo, mo->info->spawnstate+(P_Random()%3));
4763 mo->momz = FixedDiv(mo->z-actor->z, actor->height)<<2;
4764 mo->momx = (P_Random()-P_Random())<<(FRACBITS-7);
4765 mo->momy = (P_Random()-P_Random())<<(FRACBITS-7);
4766 A_IceSetTics(mo); // set a random tic wait
4769 for(i = 12+(P_Random()&15); i >= 0; i--)
4771 mo = P_SpawnMobj(actor->x+(((P_Random()-128)*actor->radius)>>7),
4772 actor->y+(((P_Random()-128)*actor->radius)>>7),
4773 actor->z+(P_Random()*actor->height/255), MT_ICECHUNK);
4774 P_SetMobjState(mo, mo->info->spawnstate+(P_Random()%3));
4777 mo->momz = FixedDiv(mo->z-actor->z, actor->height)<<2;
4778 mo->momx = (P_Random()-P_Random())<<(FRACBITS-7);
4779 mo->momy = (P_Random()-P_Random())<<(FRACBITS-7);
4780 A_IceSetTics(mo); // set a random tic wait
4784 { // attach the player's view to a chunk of ice
4785 mo = P_SpawnMobj(actor->x, actor->y, actor->z+VIEWHEIGHT, MT_ICECHUNK);
4786 P_SetMobjState(mo, S_ICECHUNK_HEAD);
4787 mo->momz = FixedDiv(mo->z-actor->z, actor->height)<<2;
4788 mo->momx = (P_Random()-P_Random())<<(FRACBITS-7);
4789 mo->momy = (P_Random()-P_Random())<<(FRACBITS-7);
4790 mo->flags2 |= MF2_ICEDAMAGE; // used to force blue palette
4791 mo->flags2 &= ~MF2_FLOORCLIP;
4792 mo->player = actor->player;
4793 actor->player = NULL;
4794 mo->health = actor->health;
4795 mo->angle = actor->angle;
4796 mo->player->mo = mo;
4797 mo->player->lookdir = 0;
4799 P_RemoveMobjFromTIDList(actor);
4800 P_SetMobjState(actor, S_FREETARGMOBJ);
4801 actor->flags2 |= MF2_DONTDRAW;
4804 //===========================================================================
4806 // special1 last teleport destination
4807 // special2 set if "below half" script not yet run
4809 // Korax Scripts (reserved)
4810 // 249 Tell scripts that we are below half health
4811 // 250-254 Control scripts
4814 // Korax TIDs (reserved)
4815 // 245 Reserved for Korax himself
4816 // 248 Initial teleport destination
4817 // 249 Teleport destination
4818 // 250-254 For use in respective control scripts
4819 // 255 For use in death script (spawn spots)
4820 //===========================================================================
4821 #define KORAX_SPIRIT_LIFETIME (5*(35/5)) // 5 seconds
4822 #define KORAX_COMMAND_HEIGHT (120*FRACUNIT)
4823 #define KORAX_COMMAND_OFFSET (27*FRACUNIT)
4825 void KoraxFire1(mobj_t *actor, int type);
4826 void KoraxFire2(mobj_t *actor, int type);
4827 void KoraxFire3(mobj_t *actor, int type);
4828 void KoraxFire4(mobj_t *actor, int type);
4829 void KoraxFire5(mobj_t *actor, int type);
4830 void KoraxFire6(mobj_t *actor, int type);
4831 void KSpiritInit(mobj_t *spirit, mobj_t *korax);
4833 #define KORAX_TID (245)
4834 #define KORAX_FIRST_TELEPORT_TID (248)
4835 #define KORAX_TELEPORT_TID (249)
4837 void A_KoraxChase(mobj_t *actor)
4841 byte args[3]={0,0,0};
4843 if ((!actor->special2) &&
4844 (actor->health <= (actor->info->spawnhealth/2)))
4847 spot = P_FindMobjFromTID(KORAX_FIRST_TELEPORT_TID, &lastfound);
4850 P_Teleport(actor, spot->x, spot->y, spot->angle, true);
4853 P_StartACS(249, 0, args, actor, NULL, 0);
4854 actor->special2 = 1; // Don't run again
4859 if (!actor->target) return;
4862 P_SetMobjState(actor, actor->info->missilestate);
4864 else if (P_Random()<30)
4866 S_StartSound(NULL, SFX_KORAX_ACTIVE);
4870 if (actor->health < (actor->info->spawnhealth>>1))
4874 lastfound = actor->special1;
4875 spot = P_FindMobjFromTID(KORAX_TELEPORT_TID, &lastfound);
4876 actor->special1 = lastfound;
4879 P_Teleport(actor, spot->x, spot->y, spot->angle, true);
4885 void A_KoraxStep(mobj_t *actor)
4890 void A_KoraxStep2(mobj_t *actor)
4892 S_StartSound(NULL, SFX_KORAX_STEP);
4896 void A_KoraxBonePop(mobj_t *actor)
4902 args[0]=args[1]=args[2]=args[3]=args[4]=0;
4903 x=actor->x, y=actor->y, z=actor->z;
4905 // Spawn 6 spirits equalangularly
4906 mo = P_SpawnMissileAngle(actor,MT_KORAX_SPIRIT1, ANGLE_60*0, 5*FRACUNIT);
4907 if (mo) KSpiritInit(mo,actor);
4908 mo = P_SpawnMissileAngle(actor,MT_KORAX_SPIRIT2, ANGLE_60*1, 5*FRACUNIT);
4909 if (mo) KSpiritInit(mo,actor);
4910 mo = P_SpawnMissileAngle(actor,MT_KORAX_SPIRIT3, ANGLE_60*2, 5*FRACUNIT);
4911 if (mo) KSpiritInit(mo,actor);
4912 mo = P_SpawnMissileAngle(actor,MT_KORAX_SPIRIT4, ANGLE_60*3, 5*FRACUNIT);
4913 if (mo) KSpiritInit(mo,actor);
4914 mo = P_SpawnMissileAngle(actor,MT_KORAX_SPIRIT5, ANGLE_60*4, 5*FRACUNIT);
4915 if (mo) KSpiritInit(mo,actor);
4916 mo = P_SpawnMissileAngle(actor,MT_KORAX_SPIRIT6, ANGLE_60*5, 5*FRACUNIT);
4917 if (mo) KSpiritInit(mo,actor);
4919 P_StartACS(255, 0, args, actor, NULL, 0); // Death script
4922 void KSpiritInit(mobj_t *spirit, mobj_t *korax)
4925 mobj_t *tail, *next;
4927 spirit->health = KORAX_SPIRIT_LIFETIME;
4929 spirit->special1 = (int)korax; // Swarm around korax
4930 spirit->special2 = 32+(P_Random()&7); // Float bob index
4931 spirit->args[0] = 10; // initial turn value
4932 spirit->args[1] = 0; // initial look angle
4934 // Spawn a tail for spirit
4935 tail = P_SpawnMobj(spirit->x, spirit->y, spirit->z, MT_HOLY_TAIL);
4936 tail->special2 = (int)spirit; // parent
4937 for(i = 1; i < 3; i++)
4939 next = P_SpawnMobj(spirit->x, spirit->y, spirit->z, MT_HOLY_TAIL);
4940 P_SetMobjState(next, next->info->spawnstate+1);
4941 tail->special1 = (int)next;
4944 tail->special1 = 0; // last tail bit
4947 void A_KoraxDecide(mobj_t *actor)
4951 P_SetMobjState(actor, S_KORAX_MISSILE1);
4955 P_SetMobjState(actor, S_KORAX_COMMAND1);
4959 void A_KoraxMissile(mobj_t *actor)
4961 int type = P_Random()%6;
4962 int sound = SFX_NONE; /* jim initialiser added */
4964 S_StartSound(actor, SFX_KORAX_ATTACK);
4969 type = MT_WRAITHFX1;
4970 sound = SFX_WRAITH_MISSILE_FIRE;
4974 sound = SFX_DEMON_MISSILE_FIRE;
4977 type = MT_DEMON2FX1;
4978 sound = SFX_DEMON_MISSILE_FIRE;
4981 type = MT_FIREDEMON_FX6;
4982 sound = SFX_FIRED_ATTACK;
4985 type = MT_CENTAUR_FX;
4986 sound = SFX_CENTAURLEADER_ATTACK;
4989 type = MT_SERPENTFX;
4990 sound = SFX_CENTAURLEADER_ATTACK;
4994 // Fire all 6 missiles at once
4995 S_StartSound(NULL, sound);
4996 KoraxFire1(actor, type);
4997 KoraxFire2(actor, type);
4998 KoraxFire3(actor, type);
4999 KoraxFire4(actor, type);
5000 KoraxFire5(actor, type);
5001 KoraxFire6(actor, type);
5005 // Call action code scripts (250-254)
5006 void A_KoraxCommand(mobj_t *actor)
5013 S_StartSound(actor, SFX_KORAX_COMMAND);
5015 // Shoot stream of lightning to ceiling
5016 ang = (actor->angle - ANGLE_90) >> ANGLETOFINESHIFT;
5017 x=actor->x + FixedMul(KORAX_COMMAND_OFFSET,finecosine[ang]);
5018 y=actor->y + FixedMul(KORAX_COMMAND_OFFSET,finesine[ang]);
5019 z=actor->z + KORAX_COMMAND_HEIGHT;
5020 P_SpawnMobj(x,y,z, MT_KORAX_BOLT);
5022 args[0]=args[1]=args[2]=args[3]=args[4]=0;
5024 if (actor->health <= (actor->info->spawnhealth >> 1))
5033 switch(P_Random() % numcommands)
5036 P_StartACS(250, 0, args, actor, NULL, 0);
5039 P_StartACS(251, 0, args, actor, NULL, 0);
5042 P_StartACS(252, 0, args, actor, NULL, 0);
5045 P_StartACS(253, 0, args, actor, NULL, 0);
5048 P_StartACS(254, 0, args, actor, NULL, 0);
5054 #define KORAX_DELTAANGLE (85*ANGLE_1)
5055 #define KORAX_ARM_EXTENSION_SHORT (40*FRACUNIT)
5056 #define KORAX_ARM_EXTENSION_LONG (55*FRACUNIT)
5058 #define KORAX_ARM1_HEIGHT (108*FRACUNIT)
5059 #define KORAX_ARM2_HEIGHT (82*FRACUNIT)
5060 #define KORAX_ARM3_HEIGHT (54*FRACUNIT)
5061 #define KORAX_ARM4_HEIGHT (104*FRACUNIT)
5062 #define KORAX_ARM5_HEIGHT (86*FRACUNIT)
5063 #define KORAX_ARM6_HEIGHT (53*FRACUNIT)
5067 // arm positions numbered:
5077 void KoraxFire1(mobj_t *actor, int type)
5083 ang = (actor->angle - KORAX_DELTAANGLE) >> ANGLETOFINESHIFT;
5084 x = actor->x + FixedMul(KORAX_ARM_EXTENSION_SHORT, finecosine[ang]);
5085 y = actor->y + FixedMul(KORAX_ARM_EXTENSION_SHORT, finesine[ang]);
5086 z = actor->z - actor->floorclip + KORAX_ARM1_HEIGHT;
5087 mo = P_SpawnKoraxMissile(x,y,z,actor, actor->target, type);
5092 void KoraxFire2(mobj_t *actor, int type)
5098 ang = (actor->angle - KORAX_DELTAANGLE) >> ANGLETOFINESHIFT;
5099 x = actor->x + FixedMul(KORAX_ARM_EXTENSION_LONG, finecosine[ang]);
5100 y = actor->y + FixedMul(KORAX_ARM_EXTENSION_LONG, finesine[ang]);
5101 z = actor->z - actor->floorclip + KORAX_ARM2_HEIGHT;
5102 mo = P_SpawnKoraxMissile(x,y,z,actor, actor->target, type);
5106 void KoraxFire3(mobj_t *actor, int type)
5112 ang = (actor->angle - KORAX_DELTAANGLE) >> ANGLETOFINESHIFT;
5113 x = actor->x + FixedMul(KORAX_ARM_EXTENSION_LONG, finecosine[ang]);
5114 y = actor->y + FixedMul(KORAX_ARM_EXTENSION_LONG, finesine[ang]);
5115 z = actor->z - actor->floorclip + KORAX_ARM3_HEIGHT;
5116 mo = P_SpawnKoraxMissile(x,y,z,actor, actor->target, type);
5120 void KoraxFire4(mobj_t *actor, int type)
5126 ang = (actor->angle + KORAX_DELTAANGLE) >> ANGLETOFINESHIFT;
5127 x = actor->x + FixedMul(KORAX_ARM_EXTENSION_SHORT, finecosine[ang]);
5128 y = actor->y + FixedMul(KORAX_ARM_EXTENSION_SHORT, finesine[ang]);
5129 z = actor->z - actor->floorclip + KORAX_ARM4_HEIGHT;
5130 mo = P_SpawnKoraxMissile(x,y,z,actor, actor->target, type);
5134 void KoraxFire5(mobj_t *actor, int type)
5140 ang = (actor->angle + KORAX_DELTAANGLE) >> ANGLETOFINESHIFT;
5141 x = actor->x + FixedMul(KORAX_ARM_EXTENSION_LONG, finecosine[ang]);
5142 y = actor->y + FixedMul(KORAX_ARM_EXTENSION_LONG, finesine[ang]);
5143 z = actor->z - actor->floorclip + KORAX_ARM5_HEIGHT;
5144 mo = P_SpawnKoraxMissile(x,y,z,actor, actor->target, type);
5148 void KoraxFire6(mobj_t *actor, int type)
5154 ang = (actor->angle + KORAX_DELTAANGLE) >> ANGLETOFINESHIFT;
5155 x = actor->x + FixedMul(KORAX_ARM_EXTENSION_LONG, finecosine[ang]);
5156 y = actor->y + FixedMul(KORAX_ARM_EXTENSION_LONG, finesine[ang]);
5157 z = actor->z - actor->floorclip + KORAX_ARM6_HEIGHT;
5158 mo = P_SpawnKoraxMissile(x,y,z,actor, actor->target, type);
5162 void A_KSpiritWeave(mobj_t *actor)
5165 int weaveXY, weaveZ;
5168 weaveXY = actor->special2>>16;
5169 weaveZ = actor->special2&0xFFFF;
5170 angle = (actor->angle+ANG90)>>ANGLETOFINESHIFT;
5171 newX = actor->x-FixedMul(finecosine[angle],
5172 FloatBobOffsets[weaveXY]<<2);
5173 newY = actor->y-FixedMul(finesine[angle],
5174 FloatBobOffsets[weaveXY]<<2);
5175 weaveXY = (weaveXY+(P_Random()%5))&63;
5176 newX += FixedMul(finecosine[angle],
5177 FloatBobOffsets[weaveXY]<<2);
5178 newY += FixedMul(finesine[angle],
5179 FloatBobOffsets[weaveXY]<<2);
5180 P_TryMove(actor, newX, newY);
5181 actor->z -= FloatBobOffsets[weaveZ]<<1;
5182 weaveZ = (weaveZ+(P_Random()%5))&63;
5183 actor->z += FloatBobOffsets[weaveZ]<<1;
5184 actor->special2 = weaveZ+(weaveXY<<16);
5187 void A_KSpiritSeeker(mobj_t *actor, angle_t thresh, angle_t turnMax)
5197 target = (mobj_t *)actor->special1;
5202 dir = P_FaceMobj(actor, target, &delta);
5213 actor->angle += delta;
5216 { // Turn counter clockwise
5217 actor->angle -= delta;
5219 angle = actor->angle>>ANGLETOFINESHIFT;
5220 actor->momx = FixedMul(actor->info->speed, finecosine[angle]);
5221 actor->momy = FixedMul(actor->info->speed, finesine[angle]);
5224 || actor->z > target->z+(target->info->height)
5225 || actor->z+actor->height < target->z)
5227 newZ = target->z+((P_Random()*target->info->height)>>8);
5228 deltaZ = newZ-actor->z;
5229 if(abs(deltaZ) > 15*FRACUNIT)
5233 deltaZ = 15*FRACUNIT;
5237 deltaZ = -15*FRACUNIT;
5240 dist = P_AproxDistance(target->x-actor->x, target->y-actor->y);
5241 dist = dist/actor->info->speed;
5246 actor->momz = deltaZ/dist;
5252 void A_KSpiritRoam(mobj_t *actor)
5254 if (actor->health-- <= 0)
5256 S_StartSound(actor, SFX_SPIRIT_DIE);
5257 P_SetMobjState(actor, S_KSPIRIT_DEATH1);
5261 if (actor->special1)
5263 A_KSpiritSeeker(actor, actor->args[0]*ANGLE_1,
5264 actor->args[0]*ANGLE_1*2);
5266 A_KSpiritWeave(actor);
5269 S_StartSound(NULL, SFX_SPIRIT_ACTIVE);
5274 void A_KBolt(mobj_t *actor)
5276 // Countdown lifetime
5277 if (actor->special1-- <= 0)
5279 P_SetMobjState(actor, S_NULL);
5284 #define KORAX_BOLT_HEIGHT 48*FRACUNIT
5285 #define KORAX_BOLT_LIFETIME 3
5287 void A_KBoltRaise(mobj_t *actor)
5292 // Spawn a child upward
5293 z = actor->z + KORAX_BOLT_HEIGHT;
5295 if ((z + KORAX_BOLT_HEIGHT) < actor->ceilingz)
5297 mo = P_SpawnMobj(actor->x, actor->y, z, MT_KORAX_BOLT);
5300 mo->special1 = KORAX_BOLT_LIFETIME;
5305 // Maybe cap it off here