I might be able to chime in with something. What you're trying to achieve here is quite a bit similar to something I have done recently and I figured I'd share this.
Most of this code is not mine and came from help in another post viewtopic.php?f=122&t=70826
The Revenant fires an invisible projectile that fires the two missiles instead. The purpose of the invisible projectile is to keep both missiles in sync with each other. I found through play that this code made the Revenant way too aggressive and instead of going back to the vanilla A_Tracer function, I'd just make sure only one instance of seeker missiles was ever spawned at once. I modified the code to choose between a seeking and non-seeking variant. The seeking variant will tell the Revenant that it exists or has been destroyed. If a seeker missile exists, the Revenant will fire non-seeking missiles, if it has been destroyed, the Revenant will be free to fire seeking missiles again.
This code could be modified to allow a more chance-based method instead by using jump functions if desired.
Code: Select all
Class XtRevenant : Actor Replaces Revenant
{
Default
{
Health 300;
Radius 20;
Height 56;
Mass 200;
Speed 10;
PainChance 100;
MeleeThreshold 196;
BloodColor "820000";
Species "Revenant";
SeeSound "skeleton/sight";
PainSound "skeleton/pain";
DeathSound "skeleton/death";
ActiveSound "skeleton/active";
HitObituary "$OB_UNDEADHIT";
Obituary "$OB_UNDEAD";
Tag "$FN_REVEN";
+FLOORCLIP
}
int missile_count;
States
{
Spawn:
SKEL AB 10 NoDelay A_Look ();
Loop;
See:
SKEL AABBCCDDEEFF 2 A_Chase ();
Loop;
Melee:
SKEL G 5
{
A_FaceTarget ();
A_StartSound ("skeleton/swing", CHAN_WEAPON);
}
SKEL H 5 A_FaceTarget ();
SKEL I 10 A_CustomMeleeAttack (Random (1, 10)*5, "skeleton/melee", "", "Melee", 1);
Goto See;
Missile:
SKEL J 10 A_FaceTarget ();
SKEL J 10 Bright
{
if (missile_count == 0)
{
A_SpawnProjectile ('XtRevenantTracerTracker', 60);
}
else if (missile_count >= 1)
{
A_SpawnProjectile ('XtRevenantTracerTrackerNoSeek', 60);
}
}
SKEL K 10 A_FaceTarget ();
Goto See;
Pain:
SKEL L 3;
SKEL L 3 A_Pain;
Goto See;
Death:
SKEL LM 5;
SKEL N 5 A_Scream;
SKEL O 5 A_NoBlocking;
SKEL P 5;
SKEL Q -1;
Stop;
Raise:
SKEL QPONML 5;
Goto See;
}
}
Class XtRevenantTracerTracker : Actor
{
XtRevenantTracer t1, t2;
Default
{
Radius 1;
Height 1;
Projectile;
Speed 10;
+NOINTERACTION;
+SEEKERMISSILE;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
t1 = XtRevenantTracer (A_SpawnProjectile ('XtRevenantTracer', -1, -19, flags: CMF_TRACKOWNER));
t2 = XtRevenantTracer (A_SpawnProjectile ('XtRevenantTracer', 1, 14, flags: CMF_TRACKOWNER));
bool ok = Tracer != null && t1 != null && t2 != null;
// Set initial velocities
double ang = Tracer != null ? AngleTo(Tracer, true) : Angle;
if (t1 != null)
{
t1.Angle = ang;
t1.Vel.Z = Vel.Z;
t1.VelFromAngle();
}
if (t2 != null)
{
t2.Angle = ang;
t2.Vel.Z = Vel.Z;
t2.VelFromAngle();
}
if (ok) // both missiles present and have tracer
{
t1.tracker = t2.tracker = self;
}
else
{
Destroy();
return;
}
}
override void Tick()
{
Super.Tick();
if (t1 != null && t1.Tracer == Tracer && t2 != null && t2.Tracer == Tracer)
{
// Adjust the velocities of the tracked missiles
// to match the velocity of this actor.
t1.Angle = t2.Angle = Angle;
t1.Vel = t2.Vel = Vel;
}
else
{
Destroy();
return;
}
}
States
{
Spawn:
TNT1 A 4
{
XtRevenantTracer.DoSeek(self);
}
Loop;
}
}
Class XtRevenantTracer : Actor
{
XtRevenantTracerTracker tracker;
Default
{
Radius 11;
Height 8;
Scale 0.5;
Speed 10;
MaxTargetRange 16;
DamageFunction (Random(1, 8)*5);
DamageType "Ballistic";
Projectile;
SeeSound "skeleton/attack";
DeathSound "skeleton/tracex";
RenderStyle "Add";
Decal "RevenantScorch";
+FORCEXYBILLBOARD
+NOFORWARDFALL
+SEEKERMISSILE
+RANDOMIZE
+ZDOOMTRANS
}
static void DoSeek(Actor source)
{
source.A_SeekerMissile (10, 30, SMF_LOOK | SMF_PRECISE, 64, 16);
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
let revmaster = XtRevenant(target);
if (revmaster)
{
revmaster.missile_count += 1;
}
}
override void OnDestroy()
{
let revmaster = XtRevenant(target);
if (revmaster)
{
revmaster.missile_count -= 1;
}
}
void A_XtRevenantTracerInFlight()
{
A_SpawnItemEx ('BulletPuff', 0, 0, 0, 0, 0, 0, 0, SXF_ORIGINATOR, 0, 0);
if (GetAge() > 0 && (tracker == null || Tracer != tracker.Tracer))
{
DoSeek(self);
}
}
States
{
Spawn:
FATB AB 4 NoDelay Bright A_XtRevenantTracerInFlight ();
Loop;
Death:
FBXP ABC 4 Bright;
Stop;
}
}
Class XtRevenantTracerTrackerNoSeek : Actor
{
XtRevenantTracerNoSeek t1, t2;
Default
{
Radius 1;
Height 1;
Projectile;
Speed 10;
+NOINTERACTION;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
t1 = XtRevenantTracerNoSeek (A_SpawnProjectile ('XtRevenantTracerNoSeek', -1, -19, flags: CMF_TRACKOWNER));
t2 = XtRevenantTracerNoSeek (A_SpawnProjectile ('XtRevenantTracerNoSeek', 1, 14, flags: CMF_TRACKOWNER));
bool ok = Tracer != null && t1 != null && t2 != null;
// Set initial velocities
double ang = Tracer != null ? AngleTo(Tracer, true) : Angle;
if (t1 != null)
{
t1.Angle = ang;
t1.Vel.Z = Vel.Z;
t1.VelFromAngle();
}
if (t2 != null)
{
t2.Angle = ang;
t2.Vel.Z = Vel.Z;
t2.VelFromAngle();
}
if (ok) // both missiles present and have tracer
{
t1.tracker = t2.tracker = self;
}
else
{
Destroy();
return;
}
}
override void Tick()
{
Super.Tick();
if (t1 != null && t1.Tracer == Tracer && t2 != null && t2.Tracer == Tracer)
{
// Adjust the velocities of the tracked missiles
// to match the velocity of this actor.
t1.Angle = t2.Angle = Angle;
t1.Vel = t2.Vel = Vel;
}
else
{
Destroy();
return;
}
}
States
{
Spawn:
TNT1 A 4;
Loop;
}
}
Class XtRevenantTracerNoSeek : Actor
{
XtRevenantTracerTrackerNoSeek tracker;
Default
{
Radius 11;
Height 8;
Scale 0.5;
Speed 10;
MaxTargetRange 16;
DamageFunction (Random(1, 8)*5);
DamageType "Ballistic";
Projectile;
SeeSound "skeleton/attack";
DeathSound "skeleton/tracex";
RenderStyle "Add";
Decal "RevenantScorch";
+FORCEXYBILLBOARD
+NOFORWARDFALL
+RANDOMIZE
+ZDOOMTRANS
}
States
{
Spawn:
FATB AB 4 NoDelay Bright A_SpawnItemEx ('BulletPuff', 0, 0, 0, 0, 0, 0, 0, SXF_ORIGINATOR, 0, 0);
Loop;
Death:
FBXP ABC 4 Bright;
Stop;
}
}