gzdoom-gles/wadsrc/static/zscript/hexen/serpent.txt
Christoph Oelckers a210aaea3e - fixed: All melee functions calling TraceBleed after DamageMobj must first copy the target member to a local variable.
DamageMobj can destroy the damaged actor if the death state sequence has zero duration. But Actor.target is a garbage collected member variable, i.e. it will be null, once the actor it points to gets destroyed.
This was originally done correctly in the C++ code but during the scriptification all those 'AActor *target = self->target' lines were removed because they looked redundant, but were not.
2017-06-06 09:12:58 +02:00

478 lines
8.9 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";
}
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_PlaySound("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_PlaySound("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_PlaySound ("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_PlaySound ("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;
}
}
}
}
// 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_PlaySound("SerpentFXContinuous", CHAN_BODY, 1.0, 1);
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;
}
}