mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-12-15 06:51:36 +00:00
523 lines
9.8 KiB
Text
523 lines
9.8 KiB
Text
|
|
// Serpent ------------------------------------------------------------------
|
|
|
|
class Serpent : Actor
|
|
{
|
|
Default
|
|
{
|
|
Health 90;
|
|
PainChance 96;
|
|
Speed 12;
|
|
Radius 32;
|
|
Height 70;
|
|
Mass 0x7fffffff;
|
|
Monster;
|
|
-SHOOTABLE
|
|
+NOBLOOD
|
|
+CANTLEAVEFLOORPIC +NONSHOOTABLE
|
|
+STAYMORPHED +DONTBLAST +NOTELEOTHER
|
|
+INVISIBLE
|
|
SeeSound "SerpentSight";
|
|
AttackSound "SerpentAttack";
|
|
PainSound "SerpentPain";
|
|
DeathSound "SerpentDeath";
|
|
HitObituary "$OB_SERPENTHIT";
|
|
Tag "$FN_SERPENT";
|
|
}
|
|
|
|
States
|
|
{
|
|
Spawn:
|
|
SSPT H 10 A_Look;
|
|
Loop;
|
|
See:
|
|
SSPT HH 1 A_Chase("Melee", null, CHF_NIGHTMAREFAST|CHF_NOPLAYACTIVE);
|
|
SSPT H 2 A_SerpentHumpDecide;
|
|
Loop;
|
|
Pain:
|
|
SSPT L 5;
|
|
SSPT L 5 A_Pain;
|
|
Dive:
|
|
SSDV ABC 4;
|
|
SSDV D 4 A_UnSetShootable;
|
|
SSDV E 3 A_StartSound("SerpentActive", CHAN_BODY);
|
|
SSDV F 3;
|
|
SSDV GH 4;
|
|
SSDV I 3;
|
|
SSDV J 3 A_SerpentHide;
|
|
Goto See;
|
|
Melee:
|
|
SSPT A 1 A_UnHideThing;
|
|
SSPT A 1 A_StartSound("SerpentBirth", CHAN_BODY);
|
|
SSPT B 3 A_SetShootable;
|
|
SSPT C 3;
|
|
SSPT D 4 A_SerpentCheckForAttack;
|
|
Goto Dive;
|
|
Death:
|
|
SSPT O 4;
|
|
SSPT P 4 A_Scream;
|
|
SSPT Q 4 A_NoBlocking;
|
|
SSPT RSTUVWXYZ 4;
|
|
Stop;
|
|
XDeath:
|
|
SSXD A 4;
|
|
SSXD B 4 A_SpawnItemEx("SerpentHead", 0, 0, 45);
|
|
SSXD C 4 A_NoBlocking;
|
|
SSXD DE 4;
|
|
SSXD FG 3;
|
|
SSXD H 3 A_SerpentSpawnGibs;
|
|
Stop;
|
|
Ice:
|
|
SSPT [ 5 A_FreezeDeath;
|
|
SSPT [ 1 A_FreezeDeathChunks;
|
|
Wait;
|
|
Walk:
|
|
SSPT IJI 5 A_Chase("Attack", null, CHF_NIGHTMAREFAST);
|
|
SSPT J 5 A_SerpentCheckForAttack;
|
|
Goto Dive;
|
|
Hump:
|
|
SSPT H 3 A_SerpentUnHide;
|
|
SSPT EFGEF 3 A_SerpentRaiseHump;
|
|
SSPT GEF 3;
|
|
SSPT GEFGE 3 A_SerpentLowerHump;
|
|
SSPT F 3 A_SerpentHide;
|
|
Goto See;
|
|
Attack:
|
|
SSPT K 6 A_FaceTarget;
|
|
SSPT L 5 A_SerpentChooseAttack;
|
|
Goto MeleeAttack;
|
|
MeleeAttack:
|
|
SSPT N 5 A_SerpentMeleeAttack;
|
|
Goto Dive;
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// A_SerpentUnHide
|
|
//
|
|
//============================================================================
|
|
|
|
void A_SerpentUnHide()
|
|
{
|
|
bInvisible = false;
|
|
Floorclip = 24;
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// A_SerpentHide
|
|
//
|
|
//============================================================================
|
|
|
|
void A_SerpentHide()
|
|
{
|
|
bInvisible = true;
|
|
Floorclip = 0;
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// A_SerpentRaiseHump
|
|
//
|
|
// Raises the hump above the surface by raising the floorclip level
|
|
//============================================================================
|
|
|
|
void A_SerpentRaiseHump()
|
|
{
|
|
Floorclip -= 4;
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// A_SerpentLowerHump
|
|
//
|
|
//============================================================================
|
|
|
|
void A_SerpentLowerHump()
|
|
{
|
|
Floorclip += 4;
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// A_SerpentHumpDecide
|
|
//
|
|
// Decided whether to hump up, or if the mobj is a serpent leader,
|
|
// to missile attack
|
|
//============================================================================
|
|
|
|
void A_SerpentHumpDecide()
|
|
{
|
|
if (MissileState != NULL)
|
|
{
|
|
if (random[SerpentHump]() > 30)
|
|
{
|
|
return;
|
|
}
|
|
else if (random[SerpentHump]() < 40)
|
|
{ // Missile attack
|
|
SetState (MeleeState);
|
|
return;
|
|
}
|
|
}
|
|
else if (random[SerpentHump]() > 3)
|
|
{
|
|
return;
|
|
}
|
|
if (!CheckMeleeRange ())
|
|
{ // The hump shouldn't occur when within melee range
|
|
if (MissileState != NULL && random[SerpentHump]() < 128)
|
|
{
|
|
SetState (MeleeState);
|
|
}
|
|
else
|
|
{
|
|
SetStateLabel("Hump");
|
|
A_StartSound ("SerpentActive", CHAN_BODY);
|
|
}
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// A_SerpentCheckForAttack
|
|
//
|
|
//============================================================================
|
|
|
|
void A_SerpentCheckForAttack()
|
|
{
|
|
if (!target)
|
|
{
|
|
return;
|
|
}
|
|
if (MissileState != NULL)
|
|
{
|
|
if (!CheckMeleeRange ())
|
|
{
|
|
SetStateLabel ("Attack");
|
|
return;
|
|
}
|
|
}
|
|
if (CheckMeleeRange2 ())
|
|
{
|
|
SetStateLabel ("Walk");
|
|
}
|
|
else if (CheckMeleeRange ())
|
|
{
|
|
if (random[SerpentAttack]() < 32)
|
|
{
|
|
SetStateLabel ("Walk");
|
|
}
|
|
else
|
|
{
|
|
SetStateLabel ("Attack");
|
|
}
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// A_SerpentChooseAttack
|
|
//
|
|
//============================================================================
|
|
|
|
void A_SerpentChooseAttack()
|
|
{
|
|
if (!target || CheckMeleeRange())
|
|
{
|
|
return;
|
|
}
|
|
if (MissileState != NULL)
|
|
{
|
|
SetState (MissileState);
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// A_SerpentMeleeAttack
|
|
//
|
|
//============================================================================
|
|
|
|
void A_SerpentMeleeAttack()
|
|
{
|
|
let targ = target;
|
|
if (!targ)
|
|
{
|
|
return;
|
|
}
|
|
if (CheckMeleeRange ())
|
|
{
|
|
int damage = random[SerpentAttack](1, 8) * 5;
|
|
int newdam = targ.DamageMobj (self, self, damage, 'Melee');
|
|
targ.TraceBleed (newdam > 0 ? newdam : damage, self);
|
|
A_StartSound ("SerpentMeleeHit", CHAN_BODY);
|
|
}
|
|
if (random[SerpentAttack]() < 96)
|
|
{
|
|
A_SerpentCheckForAttack();
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// A_SerpentSpawnGibs
|
|
//
|
|
//============================================================================
|
|
|
|
void A_SerpentSpawnGibs()
|
|
{
|
|
static const class<Actor> GibTypes[] =
|
|
{
|
|
"SerpentGib3",
|
|
"SerpentGib2",
|
|
"SerpentGib1"
|
|
};
|
|
|
|
for (int i = 2; i >= 0; --i)
|
|
{
|
|
double x = (random[SerpentGibs]() - 128) / 16.;
|
|
double y = (random[SerpentGibs]() - 128) / 16.;
|
|
|
|
Actor mo = Spawn (GibTypes[i], Vec2OffsetZ(x, y, floorz + 1), ALLOW_REPLACE);
|
|
if (mo)
|
|
{
|
|
mo.Vel.X = (random[SerpentGibs]() - 128) / 1024.f;
|
|
mo.Vel.Y = (random[SerpentGibs]() - 128) / 1024.f;
|
|
mo.Floorclip = 6;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
extend class Actor
|
|
{
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// FUNC P_CheckMeleeRange2
|
|
//
|
|
// This belongs to the Serpent but was initially exported on Actor
|
|
// so it needs to remain there.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
bool CheckMeleeRange2 ()
|
|
{
|
|
Actor mo;
|
|
double dist;
|
|
|
|
|
|
if (!target || (CurSector.Flags & Sector.SECF_NOATTACK))
|
|
{
|
|
return false;
|
|
}
|
|
mo = target;
|
|
dist = mo.Distance2D (self);
|
|
if (dist >= 128 || dist < meleerange + mo.radius)
|
|
{
|
|
return false;
|
|
}
|
|
if (mo.pos.Z > pos.Z + height)
|
|
{ // Target is higher than the attacker
|
|
return false;
|
|
}
|
|
else if (pos.Z > mo.pos.Z + mo.height)
|
|
{ // Attacker is higher
|
|
return false;
|
|
}
|
|
else if (IsFriend(mo))
|
|
{
|
|
// killough 7/18/98: friendly monsters don't attack other friends
|
|
return false;
|
|
}
|
|
return CheckSight(mo);
|
|
}
|
|
}
|
|
|
|
// Serpent Leader -----------------------------------------------------------
|
|
|
|
class SerpentLeader : Serpent
|
|
{
|
|
Default
|
|
{
|
|
Mass 200;
|
|
Obituary "$OB_SERPENT";
|
|
}
|
|
States
|
|
{
|
|
Missile:
|
|
SSPT N 5 A_SpawnProjectile("SerpentFX", 32, 0);
|
|
Goto Dive;
|
|
}
|
|
}
|
|
|
|
// Serpent Missile Ball -----------------------------------------------------
|
|
|
|
class SerpentFX : Actor
|
|
{
|
|
Default
|
|
{
|
|
Speed 15;
|
|
Radius 8;
|
|
Height 10;
|
|
Damage 4;
|
|
Projectile;
|
|
-ACTIVATEIMPACT -ACTIVATEPCROSS
|
|
+ZDOOMTRANS
|
|
RenderStyle "Add";
|
|
DeathSound "SerpentFXHit";
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
SSFX A 0;
|
|
SSFX A 3 Bright A_StartSound("SerpentFXContinuous", CHAN_BODY, CHANF_LOOPING);
|
|
SSFX BAB 3 Bright;
|
|
Goto Spawn+1;
|
|
Death:
|
|
SSFX C 4 Bright A_StopSound(CHAN_BODY);
|
|
SSFX DEFGH 4 Bright;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
// Serpent Head -------------------------------------------------------------
|
|
|
|
class SerpentHead : Actor
|
|
{
|
|
Default
|
|
{
|
|
Radius 5;
|
|
Height 10;
|
|
Gravity 0.125;
|
|
+NOBLOCKMAP
|
|
}
|
|
|
|
States
|
|
{
|
|
Spawn:
|
|
SSXD IJKLMNOP 4 A_SerpentHeadCheck;
|
|
Loop;
|
|
Death:
|
|
SSXD S -1;
|
|
Loop;
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// A_SerpentHeadCheck
|
|
//
|
|
//============================================================================
|
|
|
|
void A_SerpentHeadCheck()
|
|
{
|
|
if (pos.z <= floorz)
|
|
{
|
|
if (GetFloorTerrain().IsLiquid)
|
|
{
|
|
HitFloor ();
|
|
Destroy();
|
|
}
|
|
else
|
|
{
|
|
SetStateLabel ("Death");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Serpent Gib 1 ------------------------------------------------------------
|
|
|
|
class SerpentGib1 : Actor
|
|
{
|
|
Default
|
|
{
|
|
Radius 3;
|
|
Height 3;
|
|
+NOBLOCKMAP +NOGRAVITY
|
|
}
|
|
|
|
States
|
|
{
|
|
Spawn:
|
|
SSXD Q 6;
|
|
SSXD Q 6 A_FloatGib;
|
|
SSXD QQ 8 A_FloatGib;
|
|
SSXD QQ 12 A_FloatGib;
|
|
SSXD Q 232 A_DelayGib;
|
|
SSXD QQ 12 A_SinkGib;
|
|
SSXD QQQ 8 A_SinkGib;
|
|
Stop;
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// A_FloatGib
|
|
//
|
|
//============================================================================
|
|
|
|
void A_FloatGib()
|
|
{
|
|
Floorclip -= 1;
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// A_SinkGib
|
|
//
|
|
//============================================================================
|
|
|
|
void A_SinkGib()
|
|
{
|
|
Floorclip += 1;
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// A_DelayGib
|
|
//
|
|
//============================================================================
|
|
|
|
void A_DelayGib()
|
|
{
|
|
tics -= random[DelayGib]() >> 2;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
// Serpent Gib 2 ------------------------------------------------------------
|
|
|
|
class SerpentGib2 : SerpentGib1
|
|
{
|
|
States
|
|
{
|
|
Spawn:
|
|
SSXD R 6;
|
|
SSXD R 6 A_FloatGib;
|
|
SSXD RR 8 A_FloatGib;
|
|
SSXD RR 12 A_FloatGib;
|
|
SSXD R 232 A_DelayGib;
|
|
SSXD RR 12 A_SinkGib;
|
|
SSXD RRR 8 A_SinkGib;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
// Serpent Gib 3 ------------------------------------------------------------
|
|
|
|
class SerpentGib3 : SerpentGib1
|
|
{
|
|
States
|
|
{
|
|
Spawn:
|
|
SSXD T 6;
|
|
SSXD T 6 A_FloatGib;
|
|
SSXD TT 8 A_FloatGib;
|
|
SSXD TT 12 A_FloatGib;
|
|
SSXD T 232 A_DelayGib;
|
|
SSXD TT 12 A_SinkGib;
|
|
SSXD TTT 8 A_SinkGib;
|
|
Stop;
|
|
}
|
|
}
|