gzdoom/wadsrc/static/zscript/actors/heretic/dsparil.zs

555 lines
10 KiB
Text

// Boss spot ----------------------------------------------------------------
class BossSpot : SpecialSpot
{
Default
{
+INVISIBLE
}
}
// Sorcerer (D'Sparil on his serpent) ---------------------------------------
class Sorcerer1 : Actor
{
Default
{
Health 2000;
Radius 28;
Height 100;
Mass 800;
Speed 16;
PainChance 56;
Monster;
+BOSS
+DONTMORPH
+NORADIUSDMG
+NOTARGET
+NOICEDEATH
+FLOORCLIP
+DONTGIB
SeeSound "dsparilserpent/sight";
AttackSound "dsparilserpent/attack";
PainSound "dsparilserpent/pain";
DeathSound "dsparilserpent/death";
ActiveSound "dsparilserpent/active";
Obituary "$OB_DSPARIL1";
HitObituary "$OB_DSPARIL1HIT";
Tag "$FN_DSPARIL";
}
States
{
Spawn:
SRCR AB 10 A_Look;
Loop;
See:
SRCR ABCD 5 A_Sor1Chase;
Loop;
Pain:
SRCR Q 6 A_Sor1Pain;
Goto See;
Missile:
SRCR Q 7 A_FaceTarget;
SRCR R 6 A_FaceTarget;
SRCR S 10 A_Srcr1Attack;
Goto See;
Missile2:
SRCR S 10 A_FaceTarget;
SRCR Q 7 A_FaceTarget;
SRCR R 6 A_FaceTarget;
SRCR S 10 A_Srcr1Attack;
Goto See;
Death:
SRCR E 7;
SRCR F 7 A_Scream;
SRCR G 7;
SRCR HIJK 6;
SRCR L 25 A_StartSound("dsparil/zap", CHAN_BODY, CHANF_DEFAULT, 1., ATTN_NONE);
SRCR MN 5;
SRCR O 4;
SRCR L 20 A_StartSound("dsparil/zap", CHAN_BODY, CHANF_DEFAULT, 1., ATTN_NONE);
SRCR MN 5;
SRCR O 4;
SRCR L 12;
SRCR P -1 A_SorcererRise;
}
//----------------------------------------------------------------------------
//
// PROC A_Sor1Pain
//
//----------------------------------------------------------------------------
void A_Sor1Pain ()
{
special1 = 20; // Number of steps to walk fast
A_Pain();
}
//----------------------------------------------------------------------------
//
// PROC A_Sor1Chase
//
//----------------------------------------------------------------------------
void A_Sor1Chase ()
{
if (special1)
{
special1--;
tics -= 3;
}
A_Chase();
}
//----------------------------------------------------------------------------
//
// PROC A_Srcr1Attack
//
// Sorcerer demon attack.
//
//----------------------------------------------------------------------------
void A_Srcr1Attack ()
{
let targ = target;
if (!targ)
{
return;
}
A_StartSound (AttackSound, CHAN_BODY);
if (CheckMeleeRange ())
{
int damage = random[Srcr1Attack](1,8) * 8;
int newdam = targ.DamageMobj (self, self, damage, 'Melee');
targ.TraceBleed (newdam > 0 ? newdam : damage, self);
return;
}
if (health > (SpawnHealth()/3)*2)
{ // Spit one fireball
SpawnMissileZ (pos.z + 48, targ, "SorcererFX1");
}
else
{ // Spit three fireballs
Actor mo = SpawnMissileZ (pos.z + 48, targ, "SorcererFX1");
if (mo != null)
{
double ang = mo.angle;
SpawnMissileAngleZ(pos.z + 48, "SorcererFX1", ang - 3, mo.Vel.Z);
SpawnMissileAngleZ(pos.z + 48, "SorcererFX1", ang + 3, mo.Vel.Z);
}
if (health < SpawnHealth()/3)
{ // Maybe attack again
if (special1)
{ // Just attacked, so don't attack again
special1 = 0;
}
else
{ // Set state to attack again
special1 = 1;
SetStateLabel("Missile2");
}
}
}
}
//----------------------------------------------------------------------------
//
// PROC A_SorcererRise
//
//----------------------------------------------------------------------------
void A_SorcererRise ()
{
bSolid = false;
Actor mo = Spawn("Sorcerer2", Pos, ALLOW_REPLACE);
if (mo != null)
{
mo.Translation = Translation;
mo.SetStateLabel("Rise");
mo.angle = angle;
mo.CopyFriendliness (self, true);
}
}
}
// Sorcerer FX 1 ------------------------------------------------------------
class SorcererFX1 : Actor
{
Default
{
Radius 10;
Height 10;
Speed 20;
FastSpeed 28;
Damage 10;
DamageType "Fire";
Projectile;
-ACTIVATEIMPACT
-ACTIVATEPCROSS
+ZDOOMTRANS
RenderStyle "Add";
}
States
{
Spawn:
FX14 ABC 6 BRIGHT;
Loop;
Death:
FX14 DEFGH 5 BRIGHT;
Stop;
}
}
// Sorcerer 2 (D'Sparil without his serpent) --------------------------------
class Sorcerer2 : Actor
{
Default
{
Health 3500;
Radius 16;
Height 70;
Mass 300;
Speed 14;
Painchance 32;
Monster;
+DROPOFF
+BOSS
+DONTMORPH
+FULLVOLACTIVE
+NORADIUSDMG
+NOTARGET
+NOICEDEATH
+FLOORCLIP
+BOSSDEATH
SeeSound "dsparil/sight";
AttackSound "dsparil/attack";
PainSound "dsparil/pain";
ActiveSound "dsparil/active";
Obituary "$OB_DSPARIL2";
HitObituary "$OB_DSPARIL2HIT";
Tag "$FN_DSPARIL";
}
States
{
Spawn:
SOR2 MN 10 A_Look;
Loop;
See:
SOR2 MNOP 4 A_Chase;
Loop;
Rise:
SOR2 AB 4;
SOR2 C 4 A_StartSound("dsparil/rise", CHAN_BODY, CHANF_DEFAULT, 1., ATTN_NONE);
SOR2 DEF 4;
SOR2 G 12 A_StartSound("dsparil/sight", CHAN_BODY, CHANF_DEFAULT, 1., ATTN_NONE);
Goto See;
Pain:
SOR2 Q 3;
SOR2 Q 6 A_Pain;
Goto See;
Missile:
SOR2 R 9 A_Srcr2Decide;
SOR2 S 9 A_FaceTarget;
SOR2 T 20 A_Srcr2Attack;
Goto See;
Teleport:
SOR2 LKJIHG 6;
Goto See;
Death:
SDTH A 8 A_Sor2DthInit;
SDTH B 8;
SDTH C 8 A_StartSound("dsparil/scream", CHAN_BODY, CHANF_DEFAULT, 1., ATTN_NONE);
DeathLoop:
SDTH DE 7;
SDTH F 7 A_Sor2DthLoop;
SDTH G 6 A_StartSound("dsparil/explode", CHAN_BODY, CHANF_DEFAULT, 1., ATTN_NONE);
SDTH H 6;
SDTH I 18;
SDTH J 6 A_NoBlocking;
SDTH K 6 A_StartSound("dsparil/bones", CHAN_BODY, CHANF_DEFAULT, 1., ATTN_NONE);
SDTH LMN 6;
SDTH O -1 A_BossDeath;
Stop;
}
//----------------------------------------------------------------------------
//
// PROC P_DSparilTeleport
//
//----------------------------------------------------------------------------
void DSparilTeleport ()
{
SpotState state = Level.GetSpotState();
if (state == null) return;
Actor spot = state.GetSpotWithMinMaxDistance("BossSpot", pos.x, pos.y, 128, 0);
if (spot == null) return;
Vector3 prev = Pos;
if (TeleportMove (spot.Pos, false))
{
Actor mo = Spawn("Sorcerer2Telefade", prev, ALLOW_REPLACE);
if (mo)
{
mo.Translation = Translation;
mo.A_StartSound("misc/teleport", CHAN_BODY);
}
SetStateLabel ("Teleport");
A_StartSound ("misc/teleport", CHAN_BODY);
SetZ(floorz);
angle = spot.angle;
vel = (0,0,0);
}
}
//----------------------------------------------------------------------------
//
// PROC A_Srcr2Decide
//
//----------------------------------------------------------------------------
void A_Srcr2Decide ()
{
static const int chance[] =
{
192, 120, 120, 120, 64, 64, 32, 16, 0
};
int health8 = max(1, SpawnHealth() / 8);
int chanceindex = min(8, health / health8);
if (random[Srcr2Decide]() < chance[chanceindex])
{
DSparilTeleport ();
}
}
//----------------------------------------------------------------------------
//
// PROC A_Srcr2Attack
//
//----------------------------------------------------------------------------
void A_Srcr2Attack ()
{
let targ = target;
if (!targ)
{
return;
}
A_StartSound (AttackSound, CHAN_BODY, CHANF_DEFAULT, 1., ATTN_NONE);
if (CheckMeleeRange())
{
int damage = random[Srcr2Atk](1, 8) * 20;
int newdam = targ.DamageMobj (self, self, damage, 'Melee');
targ.TraceBleed (newdam > 0 ? newdam : damage, self);
return;
}
int chance = health < SpawnHealth()/2 ? 96 : 48;
if (random[Srcr2Atk]() < chance)
{ // Wizard spawners
SpawnMissileAngle("Sorcerer2FX2", Angle - 45, 0.5);
SpawnMissileAngle("Sorcerer2FX2", Angle + 45, 0.5);
}
else
{ // Blue bolt
SpawnMissile (targ, "Sorcerer2FX1");
}
}
//----------------------------------------------------------------------------
//
// PROC A_Sor2DthInit
//
//----------------------------------------------------------------------------
void A_Sor2DthInit ()
{
special1 = 7; // Animation loop counter
Thing_Destroy(0); // Kill monsters early
}
//----------------------------------------------------------------------------
//
// PROC A_Sor2DthLoop
//
//----------------------------------------------------------------------------
void A_Sor2DthLoop ()
{
if (--special1)
{ // Need to loop
SetStateLabel("DeathLoop");
}
}
}
// Sorcerer 2 FX 1 ----------------------------------------------------------
class Sorcerer2FX1 : Actor
{
Default
{
Radius 10;
Height 6;
Speed 20;
FastSpeed 28;
Damage 1;
Projectile;
-ACTIVATEIMPACT
-ACTIVATEPCROSS
+ZDOOMTRANS
RenderStyle "Add";
}
States
{
Spawn:
FX16 ABC 3 BRIGHT A_BlueSpark;
Loop;
Death:
FX16 G 5 BRIGHT A_Explode(random[S2FX1](80,111));
FX16 HIJKL 5 BRIGHT;
Stop;
}
//----------------------------------------------------------------------------
//
// PROC A_BlueSpark
//
//----------------------------------------------------------------------------
void A_BlueSpark ()
{
for (int i = 0; i < 2; i++)
{
Actor mo = Spawn("Sorcerer2FXSpark", pos, ALLOW_REPLACE);
if (mo != null)
{
mo.Vel.X = Random2[BlueSpark]() / 128.;
mo.Vel.Y = Random2[BlueSpark]() / 128.;
mo.Vel.Z = 1. + Random[BlueSpark]() / 256.;
}
}
}
}
// Sorcerer 2 FX Spark ------------------------------------------------------
class Sorcerer2FXSpark : Actor
{
Default
{
Radius 20;
Height 16;
+NOBLOCKMAP
+NOGRAVITY
+NOTELEPORT
+CANNOTPUSH
+ZDOOMTRANS
RenderStyle "Add";
}
States
{
Spawn:
FX16 DEF 12 BRIGHT;
Stop;
}
}
// Sorcerer 2 FX 2 ----------------------------------------------------------
class Sorcerer2FX2 : Actor
{
Default
{
Radius 10;
Height 6;
Speed 6;
Damage 10;
Projectile;
-ACTIVATEIMPACT
-ACTIVATEPCROSS
+ZDOOMTRANS
RenderStyle "Add";
}
States
{
Spawn:
FX11 A 35 BRIGHT;
FX11 A 5 BRIGHT A_GenWizard;
FX11 B 5 BRIGHT;
Goto Spawn+1;
Death:
FX11 CDEFG 5 BRIGHT;
Stop;
}
//----------------------------------------------------------------------------
//
// PROC A_GenWizard
//
//----------------------------------------------------------------------------
void A_GenWizard ()
{
Actor mo = Spawn("Wizard", pos, ALLOW_REPLACE);
if (mo != null)
{
mo.AddZ(-mo.Default.Height / 2, false);
if (!mo.TestMobjLocation ())
{ // Didn't fit
mo.ClearCounters();
mo.Destroy ();
}
else
{ // [RH] Make the new wizards inherit D'Sparil's target
mo.CopyFriendliness (self.target, true);
Vel = (0,0,0);
SetStateLabel('Death');
bMissile = false;
mo.master = target;
SpawnTeleportFog(pos, false, true);
}
}
}
}
// Sorcerer 2 Telefade ------------------------------------------------------
class Sorcerer2Telefade : Actor
{
Default
{
+NOBLOCKMAP
}
States
{
Spawn:
SOR2 GHIJKL 6;
Stop;
}
}