mirror of
https://github.com/ZDoom/qzdoom-gpl.git
synced 2024-11-27 14:12:28 +00:00
3d61d2c1f4
Nothing should ever assume that spawning an actor is unconditionally successful. There can always be some edge cases where this is not the case.
356 lines
6.2 KiB
Text
356 lines
6.2 KiB
Text
|
|
// Ironlich -----------------------------------------------------------------
|
|
|
|
class Ironlich : Actor
|
|
{
|
|
Default
|
|
{
|
|
Health 700;
|
|
Radius 40;
|
|
Height 72;
|
|
Mass 325;
|
|
Speed 6;
|
|
Painchance 32;
|
|
Monster;
|
|
+NOBLOOD
|
|
+DONTMORPH
|
|
+DONTSQUASH
|
|
+BOSSDEATH
|
|
SeeSound "ironlich/sight";
|
|
AttackSound "ironlich/attack";
|
|
PainSound "ironlich/pain";
|
|
DeathSound "ironlich/death";
|
|
ActiveSound "ironlich/active";
|
|
Obituary "$OB_IRONLICH";
|
|
HitObituary "$OB_IRONLICHHIT";
|
|
DropItem "BlasterAmmo", 84, 10;
|
|
DropItem "ArtiEgg", 51, 0;
|
|
}
|
|
|
|
|
|
States
|
|
{
|
|
Spawn:
|
|
LICH A 10 A_Look;
|
|
Loop;
|
|
See:
|
|
LICH A 4 A_Chase;
|
|
Loop;
|
|
Missile:
|
|
LICH A 5 A_FaceTarget;
|
|
LICH B 20 A_LichAttack;
|
|
Goto See;
|
|
Pain:
|
|
LICH A 4;
|
|
LICH A 4 A_Pain;
|
|
Goto See;
|
|
Death:
|
|
LICH C 7;
|
|
LICH D 7 A_Scream;
|
|
LICH EF 7;
|
|
LICH G 7 A_NoBlocking;
|
|
LICH H 7;
|
|
LICH I -1 A_BossDeath;
|
|
Stop;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_LichAttack
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void A_LichAttack ()
|
|
{
|
|
static const int atkResolve1[] = { 50, 150 };
|
|
static const int atkResolve2[] = { 150, 200 };
|
|
|
|
// Ice ball (close 20% : far 60%)
|
|
// Fire column (close 40% : far 20%)
|
|
// Whirlwind (close 40% : far 20%)
|
|
// Distance threshold = 8 cells
|
|
|
|
if (target == null)
|
|
{
|
|
return;
|
|
}
|
|
A_FaceTarget ();
|
|
if (CheckMeleeRange ())
|
|
{
|
|
int damage = random[LichAttack](1, 8) * 6;
|
|
int newdam = target.DamageMobj (self, self, damage, 'Melee');
|
|
target.TraceBleed (newdam > 0 ? newdam : damage, self);
|
|
return;
|
|
}
|
|
int dist = Distance2D(target) > 8 * 64;
|
|
int randAttack = random[LichAttack]();
|
|
if (randAttack < atkResolve1[dist])
|
|
{ // Ice ball
|
|
SpawnMissile (target, "HeadFX1");
|
|
A_PlaySound ("ironlich/attack2", CHAN_BODY);
|
|
}
|
|
else if (randAttack < atkResolve2[dist])
|
|
{ // Fire column
|
|
Actor baseFire = SpawnMissile (target, "HeadFX3");
|
|
if (baseFire != null)
|
|
{
|
|
baseFire.SetStateLabel("NoGrow");
|
|
for (int i = 0; i < 5; i++)
|
|
{
|
|
Actor fire = Spawn("HeadFX3", baseFire.Pos, ALLOW_REPLACE);
|
|
if (i == 0)
|
|
{
|
|
A_PlaySound ("ironlich/attack1", CHAN_BODY);
|
|
}
|
|
if (fire != null)
|
|
{
|
|
fire.target = baseFire.target;
|
|
fire.angle = baseFire.angle;
|
|
fire.Vel = baseFire.Vel;
|
|
fire.SetDamage(0);
|
|
fire.health = (i+1) * 2;
|
|
fire.CheckMissileSpawn (radius);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{ // Whirlwind
|
|
Actor mo = SpawnMissile (target, "Whirlwind");
|
|
if (mo != null)
|
|
{
|
|
mo.AddZ(-32);
|
|
mo.tracer = target;
|
|
mo.health = 20*TICRATE; // Duration
|
|
A_PlaySound ("ironlich/attack3", CHAN_BODY);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// Head FX 1 ----------------------------------------------------------------
|
|
|
|
class HeadFX1 : Actor
|
|
{
|
|
Default
|
|
{
|
|
Radius 12;
|
|
Height 6;
|
|
Speed 13;
|
|
FastSpeed 20;
|
|
Damage 1;
|
|
Projectile;
|
|
-ACTIVATEIMPACT
|
|
-ACTIVATEPCROSS
|
|
+THRUGHOST
|
|
RenderStyle "Add";
|
|
}
|
|
|
|
|
|
States
|
|
{
|
|
Spawn:
|
|
FX05 ABC 6 BRIGHT;
|
|
Loop;
|
|
Death:
|
|
FX05 D 5 BRIGHT A_LichIceImpact;
|
|
FX05 EFG 5 BRIGHT;
|
|
Stop;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_LichIceImpact
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void A_LichIceImpact()
|
|
{
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
Actor shard = Spawn("HeadFX2", Pos, ALLOW_REPLACE);
|
|
if (shard != null)
|
|
{
|
|
shard.target = target;
|
|
shard.angle = i*45.;
|
|
shard.VelFromAngle();
|
|
shard.Vel.Z = -.6;
|
|
shard.CheckMissileSpawn (radius);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Head FX 2 ----------------------------------------------------------------
|
|
|
|
class HeadFX2 : Actor
|
|
{
|
|
Default
|
|
{
|
|
Radius 12;
|
|
Height 6;
|
|
Speed 8;
|
|
Damage 3;
|
|
Projectile;
|
|
-ACTIVATEIMPACT
|
|
-ACTIVATEPCROSS
|
|
RenderStyle "Add";
|
|
}
|
|
|
|
States
|
|
{
|
|
Spawn:
|
|
FX05 HIJ 6 BRIGHT;
|
|
Loop;
|
|
Death:
|
|
FX05 DEFG 5 BRIGHT;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
|
|
// Head FX 3 ----------------------------------------------------------------
|
|
|
|
class HeadFX3 : Actor
|
|
{
|
|
Default
|
|
{
|
|
Radius 14;
|
|
Height 12;
|
|
Speed 10;
|
|
FastSpeed 18;
|
|
Damage 5;
|
|
Projectile;
|
|
+WINDTHRUST
|
|
-ACTIVATEIMPACT
|
|
-ACTIVATEPCROSS
|
|
-NOBLOCKMAP
|
|
RenderStyle "Add";
|
|
}
|
|
|
|
States
|
|
{
|
|
Spawn:
|
|
FX06 ABC 4 BRIGHT A_LichFireGrow;
|
|
Loop;
|
|
NoGrow:
|
|
FX06 ABC 5 BRIGHT;
|
|
Loop;
|
|
Death:
|
|
FX06 DEFG 5 BRIGHT;
|
|
Stop;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_LichFireGrow
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void A_LichFireGrow ()
|
|
{
|
|
health--;
|
|
AddZ(9.);
|
|
if (health == 0)
|
|
{
|
|
RestoreDamage();
|
|
SetStateLabel("NoGrow");
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Whirlwind ----------------------------------------------------------------
|
|
|
|
class Whirlwind : Actor
|
|
{
|
|
Default
|
|
{
|
|
Radius 16;
|
|
Height 74;
|
|
Speed 10;
|
|
Damage 1;
|
|
Projectile;
|
|
-ACTIVATEIMPACT
|
|
-ACTIVATEMCROSS
|
|
+SEEKERMISSILE
|
|
+EXPLOCOUNT
|
|
+StepMissile
|
|
RenderStyle "Translucent";
|
|
DefThreshold 60;
|
|
Threshold 50;
|
|
Alpha 0.4;
|
|
}
|
|
|
|
States
|
|
{
|
|
Spawn:
|
|
FX07 DEFG 3;
|
|
FX07 ABC 3 A_WhirlwindSeek;
|
|
Goto Spawn+4;
|
|
Death:
|
|
FX07 GFED 4;
|
|
Stop;
|
|
}
|
|
|
|
override int DoSpecialDamage (Actor target, int damage, Name damagetype)
|
|
{
|
|
int randVal;
|
|
|
|
if (!target.bDontThrust)
|
|
{
|
|
target.angle += Random2[WhirlwindDamage]() * (360 / 4096.);
|
|
target.Vel.X += Random2[WhirlwindDamage]() / 64.;
|
|
target.Vel.Y += Random2[WhirlwindDamage]() / 64.;
|
|
}
|
|
|
|
if ((level.time & 16) && !target.bBoss && !target.bDontThrust)
|
|
{
|
|
randVal = min(160, random[WhirlwindSeek]());
|
|
target.Vel.Z += randVal / 32.;
|
|
if (target.Vel.Z > 12)
|
|
{
|
|
target.Vel.Z = 12;
|
|
}
|
|
}
|
|
if (!(level.time & 7))
|
|
{
|
|
target.DamageMobj (null, target, 3, 'Melee');
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_WhirlwindSeek
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void A_WhirlwindSeek()
|
|
{
|
|
|
|
health -= 3;
|
|
if (health < 0)
|
|
{
|
|
Vel = (0,0,0);
|
|
SetStateLabel("Death");
|
|
bMissile = false;
|
|
return;
|
|
}
|
|
if ((threshold -= 3) < 0)
|
|
{
|
|
threshold = 58 + (random[WhirlwindSeek]() & 31);
|
|
A_PlaySound("ironlich/attack3", CHAN_BODY);
|
|
}
|
|
if (tracer && tracer.bShadow)
|
|
{
|
|
return;
|
|
}
|
|
A_SeekerMissile(10, 30);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|