mirror of
https://github.com/ZDoom/gzdoom.git
synced 2024-11-30 15:52:09 +00:00
a210aaea3e
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.
251 lines
4.7 KiB
Text
251 lines
4.7 KiB
Text
//===========================================================================
|
|
//
|
|
// Revenant
|
|
//
|
|
//===========================================================================
|
|
class Revenant : Actor
|
|
{
|
|
Default
|
|
{
|
|
Health 300;
|
|
Radius 20;
|
|
Height 56;
|
|
Mass 500;
|
|
Speed 10;
|
|
PainChance 100;
|
|
Monster;
|
|
MeleeThreshold 196;
|
|
+MISSILEMORE
|
|
+FLOORCLIP
|
|
SeeSound "skeleton/sight";
|
|
PainSound "skeleton/pain";
|
|
DeathSound "skeleton/death";
|
|
ActiveSound "skeleton/active";
|
|
MeleeSound "skeleton/melee";
|
|
HitObituary "$OB_UNDEADHIT";
|
|
Obituary "$OB_UNDEAD";
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
SKEL AB 10 A_Look;
|
|
Loop;
|
|
See:
|
|
SKEL AABBCCDDEEFF 2 A_Chase;
|
|
Loop;
|
|
Melee:
|
|
SKEL G 0 A_FaceTarget;
|
|
SKEL G 6 A_SkelWhoosh;
|
|
SKEL H 6 A_FaceTarget;
|
|
SKEL I 6 A_SkelFist;
|
|
Goto See;
|
|
Missile:
|
|
SKEL J 0 BRIGHT A_FaceTarget;
|
|
SKEL J 10 BRIGHT A_FaceTarget;
|
|
SKEL K 10 A_SkelMissile;
|
|
SKEL K 10 A_FaceTarget;
|
|
Goto See;
|
|
Pain:
|
|
SKEL L 5;
|
|
SKEL L 5 A_Pain;
|
|
Goto See;
|
|
Death:
|
|
SKEL LM 7;
|
|
SKEL N 7 A_Scream;
|
|
SKEL O 7 A_NoBlocking;
|
|
SKEL P 7;
|
|
SKEL Q -1;
|
|
Stop;
|
|
Raise:
|
|
SKEL Q 5;
|
|
SKEL PONML 5;
|
|
Goto See;
|
|
}
|
|
}
|
|
|
|
|
|
//===========================================================================
|
|
//
|
|
// Revenant Tracer
|
|
//
|
|
//===========================================================================
|
|
class RevenantTracer : Actor
|
|
{
|
|
Default
|
|
{
|
|
Radius 11;
|
|
Height 8;
|
|
Speed 10;
|
|
Damage 10;
|
|
Projectile;
|
|
+SEEKERMISSILE
|
|
+RANDOMIZE
|
|
+ZDOOMTRANS
|
|
SeeSound "skeleton/attack";
|
|
DeathSound "skeleton/tracex";
|
|
RenderStyle "Add";
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
FATB AB 2 BRIGHT A_Tracer;
|
|
Loop;
|
|
Death:
|
|
FBXP A 8 BRIGHT;
|
|
FBXP B 6 BRIGHT;
|
|
FBXP C 4 BRIGHT;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
|
|
//===========================================================================
|
|
//
|
|
// Revenant Tracer Smoke
|
|
//
|
|
//===========================================================================
|
|
class RevenantTracerSmoke : Actor
|
|
{
|
|
Default
|
|
{
|
|
+NOBLOCKMAP
|
|
+NOGRAVITY
|
|
+NOTELEPORT
|
|
+ZDOOMTRANS
|
|
RenderStyle "Translucent";
|
|
Alpha 0.5;
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
PUFF ABABC 4;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
//===========================================================================
|
|
//
|
|
// Code (must be attached to Actor)
|
|
//
|
|
//===========================================================================
|
|
|
|
extend class Actor
|
|
{
|
|
void A_SkelMissile()
|
|
{
|
|
if (target == null) return;
|
|
A_FaceTarget();
|
|
AddZ(16);
|
|
Actor missile = SpawnMissile(target, "RevenantTracer");
|
|
AddZ(-16);
|
|
if (missile != null)
|
|
{
|
|
missile.SetOrigin(missile.Vec3Offset(missile.Vel.X, missile.Vel.Y, 0.), false);
|
|
missile.tracer = target;
|
|
}
|
|
}
|
|
|
|
void A_SkelWhoosh()
|
|
{
|
|
if (target == null) return;
|
|
A_FaceTarget();
|
|
A_PlaySound("skeleton/swing", CHAN_WEAPON);
|
|
}
|
|
|
|
void A_SkelFist()
|
|
{
|
|
let targ = target;
|
|
if (targ == null) return;
|
|
A_FaceTarget();
|
|
|
|
if (CheckMeleeRange ())
|
|
{
|
|
int damage = random[SkelFist](1, 10) * 6;
|
|
A_PlaySound("skeleton/melee", CHAN_WEAPON);
|
|
int newdam = targ.DamageMobj (self, self, damage, 'Melee');
|
|
targ.TraceBleed (newdam > 0 ? newdam : damage, self);
|
|
}
|
|
}
|
|
|
|
void A_Tracer2(double traceang = 19.6875)
|
|
{
|
|
double dist;
|
|
double slope;
|
|
Actor dest;
|
|
|
|
// adjust direction
|
|
dest = tracer;
|
|
|
|
if (!dest || dest.health <= 0 || Speed == 0 || !CanSeek(dest))
|
|
return;
|
|
|
|
// change angle
|
|
double exact = AngleTo(dest);
|
|
double diff = deltaangle(angle, exact);
|
|
|
|
if (diff < 0)
|
|
{
|
|
angle -= traceang;
|
|
if (deltaangle(angle, exact) > 0)
|
|
angle = exact;
|
|
}
|
|
else if (diff > 0)
|
|
{
|
|
angle += traceang;
|
|
if (deltaangle(angle, exact) < 0.)
|
|
angle = exact;
|
|
}
|
|
|
|
VelFromAngle();
|
|
|
|
if (!bFloorHugger && !bCeilingHugger)
|
|
{
|
|
// change slope
|
|
dist = DistanceBySpeed(dest, Speed);
|
|
|
|
if (dest.Height >= 56.)
|
|
{
|
|
slope = (dest.pos.z + 40. - pos.z) / dist;
|
|
}
|
|
else
|
|
{
|
|
slope = (dest.pos.z + Height*(2./3) - pos.z) / dist;
|
|
}
|
|
|
|
if (slope < Vel.Z)
|
|
Vel.Z -= 1. / 8;
|
|
else
|
|
Vel.Z += 1. / 8;
|
|
}
|
|
}
|
|
|
|
void A_Tracer()
|
|
{
|
|
// killough 1/18/98: this is why some missiles do not have smoke
|
|
// and some do. Also, internal demos start at random gametics, thus
|
|
// the bug in which revenants cause internal demos to go out of sync.
|
|
//
|
|
// killough 3/6/98: fix revenant internal demo bug by subtracting
|
|
// levelstarttic from gametic:
|
|
//
|
|
// [RH] level.time is always 0-based, so nothing special to do here.
|
|
|
|
if (level.time & 3) return;
|
|
|
|
// spawn a puff of smoke behind the rocket
|
|
SpawnPuff ("BulletPuff", pos, angle, angle, 3);
|
|
Actor smoke = Spawn ("RevenantTracerSmoke", Vec3Offset(-Vel.X, -Vel.Y, 0.), ALLOW_REPLACE);
|
|
|
|
if (smoke != null)
|
|
{
|
|
smoke.Vel.Z = 1.;
|
|
smoke.tics -= random[Tracer](0, 3);
|
|
if (smoke.tics < 1)
|
|
smoke.tics = 1;
|
|
}
|
|
|
|
// The rest of this function was identical with Strife's version, except for the angle being used.
|
|
A_Tracer2(16.875);
|
|
}
|
|
}
|
|
|