#include "actor.h" #include "info.h" #include "p_local.h" #include "s_sound.h" #include "p_enemy.h" #include "a_action.h" #include "m_random.h" #include "p_terrain.h" static FRandom pr_serpentchase ("SerpentChase"); static FRandom pr_serpenthump ("SerpentHump"); static FRandom pr_serpentattack ("SerpentAttack"); static FRandom pr_serpentmeattack ("SerpentMeAttack"); static FRandom pr_serpentgibs ("SerpentGibs"); static FRandom pr_delaygib ("DelayGib"); void A_DoChase(AActor * actor, bool fastchase, FState * meleestate, FState * missilestate, bool playactive, bool nightmarefast); void A_SerpentChase (AActor *); void A_SerpentHumpDecide (AActor *); void A_SerpentDiveSound (AActor *); void A_SerpentHide (AActor *); void A_SerpentBirthScream (AActor *); void A_SerpentDiveSound (AActor *); void A_SerpentCheckForAttack (AActor *); void A_SerpentHeadPop (AActor *); void A_SerpentSpawnGibs (AActor *); void A_SerpentWalk (AActor *); void A_SerpentUnHide (AActor *); void A_SerpentRaiseHump (AActor *); void A_SerpentLowerHump (AActor *); void A_SerpentMeleeAttack (AActor *); void A_SerpentMissileAttack (AActor *); void A_SerpentChooseAttack (AActor *); void A_SerpentFXSound (AActor *); void A_StopSerpentFXSound (AActor *); void A_SerpentHeadCheck (AActor *); void A_FloatGib (AActor *); void A_DelayGib (AActor *); void A_SinkGib (AActor *); // Serpent ------------------------------------------------------------------ class ASerpent : public AActor { DECLARE_ACTOR (ASerpent, AActor) public: bool bLeader; void Serialize (FArchive &arc); }; FState ASerpent::States[] = { #define S_SERPENT_LOOK1 0 S_NORMAL (SSPT, 'H', 10, A_Look , &States[S_SERPENT_LOOK1]), #define S_SERPENT_SWIM1 (S_SERPENT_LOOK1+1) S_NORMAL (SSPT, 'H', 1, A_SerpentChase , &States[S_SERPENT_SWIM1+1]), S_NORMAL (SSPT, 'H', 1, A_SerpentChase , &States[S_SERPENT_SWIM1+2]), S_NORMAL (SSPT, 'H', 2, A_SerpentHumpDecide , &States[S_SERPENT_SWIM1]), #define S_SERPENT_PAIN1 (S_SERPENT_SWIM1+3) S_NORMAL (SSPT, 'L', 5, NULL , &States[S_SERPENT_PAIN1+1]), S_NORMAL (SSPT, 'L', 5, A_Pain , &States[S_SERPENT_PAIN1+2]), S_NORMAL (SSDV, 'A', 4, NULL , &States[S_SERPENT_PAIN1+3]), S_NORMAL (SSDV, 'B', 4, NULL , &States[S_SERPENT_PAIN1+4]), S_NORMAL (SSDV, 'C', 4, NULL , &States[S_SERPENT_PAIN1+5]), S_NORMAL (SSDV, 'D', 4, A_UnSetShootable , &States[S_SERPENT_PAIN1+6]), S_NORMAL (SSDV, 'E', 3, A_SerpentDiveSound , &States[S_SERPENT_PAIN1+7]), S_NORMAL (SSDV, 'F', 3, NULL , &States[S_SERPENT_PAIN1+8]), S_NORMAL (SSDV, 'G', 4, NULL , &States[S_SERPENT_PAIN1+9]), S_NORMAL (SSDV, 'H', 4, NULL , &States[S_SERPENT_PAIN1+10]), S_NORMAL (SSDV, 'I', 3, NULL , &States[S_SERPENT_PAIN1+11]), S_NORMAL (SSDV, 'J', 3, A_SerpentHide , &States[S_SERPENT_SWIM1]), #define S_SERPENT_SURFACE1 (S_SERPENT_PAIN1+12) S_NORMAL (SSPT, 'A', 1, A_UnHideThing , &States[S_SERPENT_SURFACE1+1]), S_NORMAL (SSPT, 'A', 1, A_SerpentBirthScream , &States[S_SERPENT_SURFACE1+2]), S_NORMAL (SSPT, 'B', 3, A_SetShootable , &States[S_SERPENT_SURFACE1+3]), S_NORMAL (SSPT, 'C', 3, NULL , &States[S_SERPENT_SURFACE1+4]), S_NORMAL (SSPT, 'D', 4, A_SerpentCheckForAttack , &States[S_SERPENT_PAIN1+2]), #define S_SERPENT_DIE1 (S_SERPENT_SURFACE1+5) S_NORMAL (SSPT, 'O', 4, NULL , &States[S_SERPENT_DIE1+1]), S_NORMAL (SSPT, 'P', 4, A_Scream , &States[S_SERPENT_DIE1+2]), S_NORMAL (SSPT, 'Q', 4, A_NoBlocking , &States[S_SERPENT_DIE1+3]), S_NORMAL (SSPT, 'R', 4, NULL , &States[S_SERPENT_DIE1+4]), S_NORMAL (SSPT, 'S', 4, NULL , &States[S_SERPENT_DIE1+5]), S_NORMAL (SSPT, 'T', 4, NULL , &States[S_SERPENT_DIE1+6]), S_NORMAL (SSPT, 'U', 4, NULL , &States[S_SERPENT_DIE1+7]), S_NORMAL (SSPT, 'V', 4, NULL , &States[S_SERPENT_DIE1+8]), S_NORMAL (SSPT, 'W', 4, NULL , &States[S_SERPENT_DIE1+9]), S_NORMAL (SSPT, 'X', 4, NULL , &States[S_SERPENT_DIE1+10]), S_NORMAL (SSPT, 'Y', 4, NULL , &States[S_SERPENT_DIE1+11]), S_NORMAL (SSPT, 'Z', 4, NULL , NULL), #define S_SERPENT_XDIE1 (S_SERPENT_DIE1+12) S_NORMAL (SSXD, 'A', 4, NULL , &States[S_SERPENT_XDIE1+1]), S_NORMAL (SSXD, 'B', 4, A_SerpentHeadPop , &States[S_SERPENT_XDIE1+2]), S_NORMAL (SSXD, 'C', 4, A_NoBlocking , &States[S_SERPENT_XDIE1+3]), S_NORMAL (SSXD, 'D', 4, NULL , &States[S_SERPENT_XDIE1+4]), S_NORMAL (SSXD, 'E', 4, NULL , &States[S_SERPENT_XDIE1+5]), S_NORMAL (SSXD, 'F', 3, NULL , &States[S_SERPENT_XDIE1+6]), S_NORMAL (SSXD, 'G', 3, NULL , &States[S_SERPENT_XDIE1+7]), S_NORMAL (SSXD, 'H', 3, A_SerpentSpawnGibs , NULL), #define S_SERPENT_WALK1 (S_SERPENT_XDIE1+8) S_NORMAL (SSPT, 'I', 5, A_SerpentWalk , &States[S_SERPENT_WALK1+1]), S_NORMAL (SSPT, 'J', 5, A_SerpentWalk , &States[S_SERPENT_WALK1+2]), S_NORMAL (SSPT, 'I', 5, A_SerpentWalk , &States[S_SERPENT_WALK1+3]), S_NORMAL (SSPT, 'J', 5, A_SerpentCheckForAttack , &States[S_SERPENT_PAIN1+2]), #define S_SERPENT_HUMP1 (S_SERPENT_WALK1+4) S_NORMAL (SSPT, 'H', 3, A_SerpentUnHide , &States[S_SERPENT_HUMP1+1]), S_NORMAL (SSPT, 'E', 3, A_SerpentRaiseHump , &States[S_SERPENT_HUMP1+2]), S_NORMAL (SSPT, 'F', 3, A_SerpentRaiseHump , &States[S_SERPENT_HUMP1+3]), S_NORMAL (SSPT, 'G', 3, A_SerpentRaiseHump , &States[S_SERPENT_HUMP1+4]), S_NORMAL (SSPT, 'E', 3, A_SerpentRaiseHump , &States[S_SERPENT_HUMP1+5]), S_NORMAL (SSPT, 'F', 3, A_SerpentRaiseHump , &States[S_SERPENT_HUMP1+6]), S_NORMAL (SSPT, 'G', 3, NULL , &States[S_SERPENT_HUMP1+7]), S_NORMAL (SSPT, 'E', 3, NULL , &States[S_SERPENT_HUMP1+8]), S_NORMAL (SSPT, 'F', 3, NULL , &States[S_SERPENT_HUMP1+9]), S_NORMAL (SSPT, 'G', 3, A_SerpentLowerHump , &States[S_SERPENT_HUMP1+10]), S_NORMAL (SSPT, 'E', 3, A_SerpentLowerHump , &States[S_SERPENT_HUMP1+11]), S_NORMAL (SSPT, 'F', 3, A_SerpentLowerHump , &States[S_SERPENT_HUMP1+12]), S_NORMAL (SSPT, 'G', 3, A_SerpentLowerHump , &States[S_SERPENT_HUMP1+13]), S_NORMAL (SSPT, 'E', 3, A_SerpentLowerHump , &States[S_SERPENT_HUMP1+14]), S_NORMAL (SSPT, 'F', 3, A_SerpentHide , &States[S_SERPENT_SWIM1]), #define S_SERPENT_MELEE1 (S_SERPENT_HUMP1+15) S_NORMAL (SSPT, 'N', 5, A_SerpentMeleeAttack , &ASerpent::States[S_SERPENT_PAIN1+2]), #define S_SERPENT_MISSILE1 (S_SERPENT_MELEE1+1) S_NORMAL (SSPT, 'N', 5, A_SerpentMissileAttack , &ASerpent::States[S_SERPENT_PAIN1+2]), #define S_SERPENT_ATK1 (S_SERPENT_MISSILE1+1) S_NORMAL (SSPT, 'K', 6, A_FaceTarget , &States[S_SERPENT_ATK1+1]), S_NORMAL (SSPT, 'L', 5, A_SerpentChooseAttack , &States[S_SERPENT_MELEE1]), #define S_SERPENT_ICE (S_SERPENT_ATK1+2) S_NORMAL (SSPT, '[', 5, A_FreezeDeath , &States[S_SERPENT_ICE+1]), S_NORMAL (SSPT, '[', 1, A_FreezeDeathChunks , &States[S_SERPENT_ICE+1]), }; IMPLEMENT_ACTOR (ASerpent, Hexen, 121, 6) PROP_SpawnHealth (90) PROP_PainChance (96) PROP_SpeedFixed (12) PROP_RadiusFixed (32) PROP_HeightFixed (70) PROP_MassLong (0x7fffffff) // [RH] Is this a mistake? PROP_Flags (MF_SOLID|MF_NOBLOOD|MF_COUNTKILL) PROP_Flags2 (MF2_PASSMOBJ|MF2_MCROSS|MF2_CANTLEAVEFLOORPIC|MF2_NONSHOOTABLE) PROP_Flags3 (MF3_STAYMORPHED|MF3_DONTBLAST|MF3_NOTELEOTHER) PROP_RenderFlags (RF_INVISIBLE) PROP_SpawnState (S_SERPENT_LOOK1) PROP_SeeState (S_SERPENT_SWIM1) PROP_PainState (S_SERPENT_PAIN1) PROP_MeleeState (S_SERPENT_SURFACE1) PROP_DeathState (S_SERPENT_DIE1) PROP_XDeathState (S_SERPENT_XDIE1) PROP_IDeathState (S_SERPENT_ICE) PROP_SeeSound ("SerpentSight") PROP_AttackSound ("SerpentAttack") PROP_PainSound ("SerpentPain") PROP_DeathSound ("SerpentDeath") END_DEFAULTS void ASerpent::Serialize (FArchive &arc) { Super::Serialize (arc); arc << bLeader; } // Serpent Leader ----------------------------------------------------------- class ASerpentLeader : public ASerpent { DECLARE_STATELESS_ACTOR (ASerpentLeader, ASerpent) public: void BeginPlay (); }; IMPLEMENT_STATELESS_ACTOR (ASerpentLeader, Hexen, 120, 7) PROP_Mass (200) END_DEFAULTS void ASerpentLeader::BeginPlay () { bLeader = true; } // Serpent Missile Ball ----------------------------------------------------- class ASerpentFX : public AActor { DECLARE_ACTOR (ASerpentFX, AActor) public: void Tick () { Super::Tick (); } }; FState ASerpentFX::States[] = { #define S_SERPENT_FX1 0 // [RH] This 0-length state was added so that the looping sound can start // playing as soon as possible, because action functions are not called // when an actor is spawned. (Should I change that? No.) S_BRIGHT (SSFX, 'A', 0, NULL , &States[S_SERPENT_FX1+1]), S_BRIGHT (SSFX, 'A', 3, A_SerpentFXSound , &States[S_SERPENT_FX1+2]), S_BRIGHT (SSFX, 'B', 3, NULL , &States[S_SERPENT_FX1+3]), S_BRIGHT (SSFX, 'A', 3, NULL , &States[S_SERPENT_FX1+4]), S_BRIGHT (SSFX, 'B', 3, NULL , &States[S_SERPENT_FX1+1]), #define S_SERPENT_FX_X1 (S_SERPENT_FX1+5) S_BRIGHT (SSFX, 'C', 4, A_StopSerpentFXSound , &States[S_SERPENT_FX_X1+1]), S_BRIGHT (SSFX, 'D', 4, NULL , &States[S_SERPENT_FX_X1+2]), S_BRIGHT (SSFX, 'E', 4, NULL , &States[S_SERPENT_FX_X1+3]), S_BRIGHT (SSFX, 'F', 4, NULL , &States[S_SERPENT_FX_X1+4]), S_BRIGHT (SSFX, 'G', 4, NULL , &States[S_SERPENT_FX_X1+5]), S_BRIGHT (SSFX, 'H', 4, NULL , NULL), }; IMPLEMENT_ACTOR (ASerpentFX, Hexen, -1, 0) PROP_SpeedFixed (15) PROP_RadiusFixed (8) PROP_HeightFixed (10) PROP_Damage (4) PROP_Flags (MF_NOBLOCKMAP|MF_NOGRAVITY|MF_DROPOFF|MF_MISSILE) PROP_Flags2 (MF2_NOTELEPORT) PROP_RenderStyle (STYLE_Add) PROP_SpawnState (S_SERPENT_FX1) PROP_DeathState (S_SERPENT_FX_X1) PROP_DeathSound ("SerpentFXHit") END_DEFAULTS // Serpent Head ------------------------------------------------------------- class ASerpentHead : public AActor { DECLARE_ACTOR (ASerpentHead, AActor) }; FState ASerpentHead::States[] = { #define S_SERPENT_HEAD1 0 S_NORMAL (SSXD, 'I', 4, A_SerpentHeadCheck , &States[S_SERPENT_HEAD1+1]), S_NORMAL (SSXD, 'J', 4, A_SerpentHeadCheck , &States[S_SERPENT_HEAD1+2]), S_NORMAL (SSXD, 'K', 4, A_SerpentHeadCheck , &States[S_SERPENT_HEAD1+3]), S_NORMAL (SSXD, 'L', 4, A_SerpentHeadCheck , &States[S_SERPENT_HEAD1+4]), S_NORMAL (SSXD, 'M', 4, A_SerpentHeadCheck , &States[S_SERPENT_HEAD1+5]), S_NORMAL (SSXD, 'N', 4, A_SerpentHeadCheck , &States[S_SERPENT_HEAD1+6]), S_NORMAL (SSXD, 'O', 4, A_SerpentHeadCheck , &States[S_SERPENT_HEAD1+7]), S_NORMAL (SSXD, 'P', 4, A_SerpentHeadCheck , &States[S_SERPENT_HEAD1]), #define S_SERPENT_HEAD_X1 (S_SERPENT_HEAD1+8) S_NORMAL (SSXD, 'S', -1, NULL , &States[S_SERPENT_HEAD_X1]), }; IMPLEMENT_ACTOR (ASerpentHead, Hexen, -1, 0) PROP_RadiusFixed (5) PROP_HeightFixed (10) PROP_Flags (MF_NOBLOCKMAP) PROP_Flags2 (MF2_LOGRAV) PROP_SpawnState (S_SERPENT_HEAD1) PROP_DeathState (S_SERPENT_HEAD_X1) END_DEFAULTS // Serpent Gib 1 ------------------------------------------------------------ class ASerpentGib1 : public AActor { DECLARE_ACTOR (ASerpentGib1, AActor) }; FState ASerpentGib1::States[] = { #define S_SERPENT_GIB1_1 0 S_NORMAL (SSXD, 'Q', 6, NULL , &States[S_SERPENT_GIB1_1+1]), S_NORMAL (SSXD, 'Q', 6, A_FloatGib , &States[S_SERPENT_GIB1_1+2]), S_NORMAL (SSXD, 'Q', 8, A_FloatGib , &States[S_SERPENT_GIB1_1+3]), S_NORMAL (SSXD, 'Q', 8, A_FloatGib , &States[S_SERPENT_GIB1_1+4]), S_NORMAL (SSXD, 'Q', 12, A_FloatGib , &States[S_SERPENT_GIB1_1+5]), S_NORMAL (SSXD, 'Q', 12, A_FloatGib , &States[S_SERPENT_GIB1_1+6]), S_NORMAL (SSXD, 'Q', 232, A_DelayGib , &States[S_SERPENT_GIB1_1+7]), S_NORMAL (SSXD, 'Q', 12, A_SinkGib , &States[S_SERPENT_GIB1_1+8]), S_NORMAL (SSXD, 'Q', 12, A_SinkGib , &States[S_SERPENT_GIB1_1+9]), S_NORMAL (SSXD, 'Q', 8, A_SinkGib , &States[S_SERPENT_GIB1_1+10]), S_NORMAL (SSXD, 'Q', 8, A_SinkGib , &States[S_SERPENT_GIB1_1+11]), S_NORMAL (SSXD, 'Q', 8, A_SinkGib , NULL), }; IMPLEMENT_ACTOR (ASerpentGib1, Hexen, -1, 0) PROP_RadiusFixed (3) PROP_HeightFixed (3) PROP_Flags (MF_NOBLOCKMAP|MF_NOGRAVITY) PROP_SpawnState (S_SERPENT_GIB1_1) END_DEFAULTS // Serpent Gib 2 ------------------------------------------------------------ class ASerpentGib2 : public AActor { DECLARE_ACTOR (ASerpentGib2, AActor) }; FState ASerpentGib2::States[] = { #define S_SERPENT_GIB2_1 0 S_NORMAL (SSXD, 'R', 6, NULL , &States[S_SERPENT_GIB2_1+1]), S_NORMAL (SSXD, 'R', 6, A_FloatGib , &States[S_SERPENT_GIB2_1+2]), S_NORMAL (SSXD, 'R', 8, A_FloatGib , &States[S_SERPENT_GIB2_1+3]), S_NORMAL (SSXD, 'R', 8, A_FloatGib , &States[S_SERPENT_GIB2_1+4]), S_NORMAL (SSXD, 'R', 12, A_FloatGib , &States[S_SERPENT_GIB2_1+5]), S_NORMAL (SSXD, 'R', 12, A_FloatGib , &States[S_SERPENT_GIB2_1+6]), S_NORMAL (SSXD, 'R', 232, A_DelayGib , &States[S_SERPENT_GIB2_1+7]), S_NORMAL (SSXD, 'R', 12, A_SinkGib , &States[S_SERPENT_GIB2_1+8]), S_NORMAL (SSXD, 'R', 12, A_SinkGib , &States[S_SERPENT_GIB2_1+9]), S_NORMAL (SSXD, 'R', 8, A_SinkGib , &States[S_SERPENT_GIB2_1+10]), S_NORMAL (SSXD, 'R', 8, A_SinkGib , &States[S_SERPENT_GIB2_1+11]), S_NORMAL (SSXD, 'R', 8, A_SinkGib , NULL), }; IMPLEMENT_ACTOR (ASerpentGib2, Hexen, -1, 0) PROP_RadiusFixed (3) PROP_HeightFixed (3) PROP_Flags (MF_NOBLOCKMAP|MF_NOGRAVITY) PROP_SpawnState (S_SERPENT_GIB2_1) END_DEFAULTS // Serpent Gib 3 ------------------------------------------------------------ class ASerpentGib3 : public AActor { DECLARE_ACTOR (ASerpentGib3, AActor) }; FState ASerpentGib3::States[] = { #define S_SERPENT_GIB3_1 0 S_NORMAL (SSXD, 'T', 6, NULL , &States[S_SERPENT_GIB3_1+1]), S_NORMAL (SSXD, 'T', 6, A_FloatGib , &States[S_SERPENT_GIB3_1+2]), S_NORMAL (SSXD, 'T', 8, A_FloatGib , &States[S_SERPENT_GIB3_1+3]), S_NORMAL (SSXD, 'T', 8, A_FloatGib , &States[S_SERPENT_GIB3_1+4]), S_NORMAL (SSXD, 'T', 12, A_FloatGib , &States[S_SERPENT_GIB3_1+5]), S_NORMAL (SSXD, 'T', 12, A_FloatGib , &States[S_SERPENT_GIB3_1+6]), S_NORMAL (SSXD, 'T', 232, A_DelayGib , &States[S_SERPENT_GIB3_1+7]), S_NORMAL (SSXD, 'T', 12, A_SinkGib , &States[S_SERPENT_GIB3_1+8]), S_NORMAL (SSXD, 'T', 12, A_SinkGib , &States[S_SERPENT_GIB3_1+9]), S_NORMAL (SSXD, 'T', 8, A_SinkGib , &States[S_SERPENT_GIB3_1+10]), S_NORMAL (SSXD, 'T', 8, A_SinkGib , &States[S_SERPENT_GIB3_1+11]), S_NORMAL (SSXD, 'T', 8, A_SinkGib , NULL), }; IMPLEMENT_ACTOR (ASerpentGib3, Hexen, -1, 0) PROP_RadiusFixed (3) PROP_HeightFixed (3) PROP_Flags (MF_NOBLOCKMAP|MF_NOGRAVITY) PROP_SpawnState (S_SERPENT_GIB3_1) END_DEFAULTS //============================================================================ // // A_SerpentUnHide // //============================================================================ void A_SerpentUnHide (AActor *actor) { actor->renderflags &= ~RF_INVISIBLE; actor->floorclip = 24*FRACUNIT; } //============================================================================ // // A_SerpentHide // //============================================================================ void A_SerpentHide (AActor *actor) { actor->renderflags |= RF_INVISIBLE; actor->floorclip = 0; } //============================================================================ // // A_SerpentChase // //============================================================================ void A_SerpentChase (AActor *actor) { A_DoChase (actor, false, actor->MeleeState, NULL, false, true); } //============================================================================ // // A_SerpentRaiseHump // // Raises the hump above the surface by raising the floorclip level //============================================================================ void A_SerpentRaiseHump (AActor *actor) { actor->floorclip -= 4*FRACUNIT; } //============================================================================ // // A_SerpentLowerHump // //============================================================================ void A_SerpentLowerHump (AActor *actor) { actor->floorclip += 4*FRACUNIT; } //============================================================================ // // A_SerpentHumpDecide // // Decided whether to hump up, or if the mobj is a serpent leader, // to missile attack //============================================================================ void A_SerpentHumpDecide (AActor *actor) { if (static_cast(actor)->bLeader) { if (pr_serpenthump() > 30) { return; } else if (pr_serpenthump() < 40) { // Missile attack actor->SetState (&ASerpent::States[S_SERPENT_SURFACE1]); return; } } else if (pr_serpenthump() > 3) { return; } if (!actor->CheckMeleeRange ()) { // The hump shouldn't occur when within melee range if (static_cast(actor)->bLeader && pr_serpenthump() < 128) { actor->SetState (&ASerpent::States[S_SERPENT_SURFACE1]); } else { actor->SetState (&ASerpent::States[S_SERPENT_HUMP1]); S_Sound (actor, CHAN_BODY, "SerpentActive", 1, ATTN_NORM); } } } //============================================================================ // // A_SerpentBirthScream // //============================================================================ void A_SerpentBirthScream (AActor *actor) { S_Sound (actor, CHAN_VOICE, "SerpentBirth", 1, ATTN_NORM); } //============================================================================ // // A_SerpentDiveSound // //============================================================================ void A_SerpentDiveSound (AActor *actor) { S_Sound (actor, CHAN_BODY, "SerpentActive", 1, ATTN_NORM); } //============================================================================ // // A_SerpentWalk // // Similar to A_Chase, only has a hardcoded entering of meleestate //============================================================================ void A_SerpentWalk (AActor *actor) { A_DoChase (actor, false, &ASerpent::States[S_SERPENT_ATK1], NULL, true, true); } //============================================================================ // // A_SerpentCheckForAttack // //============================================================================ void A_SerpentCheckForAttack (AActor *actor) { if (!actor->target) { return; } if (static_cast(actor)->bLeader) { if (!actor->CheckMeleeRange ()) { actor->SetState (&ASerpent::States[S_SERPENT_ATK1]); return; } } if (P_CheckMeleeRange2 (actor)) { actor->SetState (&ASerpent::States[S_SERPENT_WALK1]); } else if (actor->CheckMeleeRange ()) { if (pr_serpentattack() < 32) { actor->SetState (&ASerpent::States[S_SERPENT_WALK1]); } else { actor->SetState (&ASerpent::States[S_SERPENT_ATK1]); } } } //============================================================================ // // A_SerpentChooseAttack // //============================================================================ void A_SerpentChooseAttack (AActor *actor) { if (!actor->target || actor->CheckMeleeRange()) { return; } if (static_cast(actor)->bLeader) { actor->SetState (&ASerpent::States[S_SERPENT_MISSILE1]); } } //============================================================================ // // A_SerpentMeleeAttack // //============================================================================ void A_SerpentMeleeAttack (AActor *actor) { if (!actor->target) { return; } if (actor->CheckMeleeRange ()) { int damage = pr_serpentmeattack.HitDice (5); P_DamageMobj (actor->target, actor, actor, damage, MOD_HIT); P_TraceBleed (damage, actor->target, actor); S_Sound (actor, CHAN_BODY, "SerpentMeleeHit", 1, ATTN_NORM); } if (pr_serpentmeattack() < 96) { A_SerpentCheckForAttack (actor); } } //============================================================================ // // A_SerpentMissileAttack // //============================================================================ void A_SerpentMissileAttack (AActor *actor) { AActor *mo; if (!actor->target) { return; } mo = P_SpawnMissile (actor, actor->target, RUNTIME_CLASS(ASerpentFX)); } //============================================================================ // // A_SerpentHeadPop // //============================================================================ void A_SerpentHeadPop (AActor *actor) { Spawn (actor->x, actor->y, actor->z+45*FRACUNIT); } //============================================================================ // // A_SerpentSpawnGibs // //============================================================================ void A_SerpentSpawnGibs (AActor *actor) { AActor *mo; static const PClass *const GibTypes[] = { RUNTIME_CLASS(ASerpentGib3), RUNTIME_CLASS(ASerpentGib2), RUNTIME_CLASS(ASerpentGib1) }; for (int i = countof(GibTypes)-1; i >= 0; --i) { mo = Spawn (GibTypes[i], actor->x+((pr_serpentgibs()-128)<<12), actor->y+((pr_serpentgibs()-128)<<12), actor->floorz+FRACUNIT); if (mo) { mo->momx = (pr_serpentgibs()-128)<<6; mo->momy = (pr_serpentgibs()-128)<<6; mo->floorclip = 6*FRACUNIT; } } } //============================================================================ // // A_FloatGib // //============================================================================ void A_FloatGib (AActor *actor) { actor->floorclip -= FRACUNIT; } //============================================================================ // // A_SinkGib // //============================================================================ void A_SinkGib (AActor *actor) { actor->floorclip += FRACUNIT; } //============================================================================ // // A_DelayGib // //============================================================================ void A_DelayGib (AActor *actor) { actor->tics -= pr_delaygib()>>2; } //============================================================================ // // A_SerpentHeadCheck // //============================================================================ void A_SerpentHeadCheck (AActor *actor) { if (actor->z <= actor->floorz) { if (Terrains[P_GetThingFloorType(actor)].IsLiquid) { P_HitFloor (actor); actor->SetState (NULL); } else { actor->SetState (actor->DeathState); } } } //============================================================================ // // A_SerpentFXSound // //============================================================================ void A_SerpentFXSound (AActor *actor) { if (!S_IsActorPlayingSomething (actor, CHAN_BODY)) { S_LoopedSound (actor, CHAN_BODY, "SerpentFXContinuous", 1, ATTN_NORM); } } //============================================================================ // // A_StopSerpentFXSound // //============================================================================ void A_StopSerpentFXSound (AActor *actor) { S_StopSound (actor, CHAN_BODY); }