Note: The following code is ZScript. I'm really sorry if you don't know how to work with it, but I cannot recommend using deprecated DECORATE anymore, especially for stuff like this, as trying to implement it in a way you are suggesting constitutes a hack (at least in my opinion), and I do not approve of them.
Here's some example code of a projectile that orbits around its target. Note that it isn't actually a
projectile per se - more like a special effect, as it does not collide with anything (not even walls), courtesy of the +NOINTERACTION flag. It is not a requirement that it must work like that, of course - you are free to alter the code in any way you want and see what happens. The example actor's code is not restricted to orbiting players, only the method that spawns it does. You can make it orbit around anything you want by setting its
target field accordingly immediately after you've spawned it.
The orbiting code has been adapted from SorcFX2, the Heresiarch's invulnerability effect from Hexen. In addition to orbiting in the XY plane, SorcFX2 also features some periodic vertical movement. This part is not included in the example code, you can look it up youself in
gzdoom.pk3/zscript/hexen/heresiarch.txt.
Code: Select all
//===========================================================================
//
// OrbitingProjectile
//
// DO NOT SPAWN DIRECTLY. Use "give TestInv" instead. See below.
//
//===========================================================================
class OrbitingProjectile : Actor
{
Default
{
// Make sure we aren't colliding with anything
+NOINTERACTION;
}
States
{
Spawn:
BAL1 AB 4 BRIGHT A_Orbit(32, 32, 20);
Loop;
}
//===========================================================================
//
// OrbitingProjectile::A_Orbit
//
// Orbits around this actor's target. Destroys itself without a target.
// Parameters:
// - orbitDist: distance from the target actor's center;
// - orbitHeight: distance from the target actor's feet;
// - angleDelta: amount to increase the angle with each call.
//
// To make the projectile orbit faster:
// 1) increase angleDelta (but at the cost of losing precision) OR
// 2) call this function more often. In this example it is called
// every 4 ticks as defined in the Spawn state. Try calling it more often
// and see what happens.
//
//===========================================================================
void A_Orbit(double orbitDist, double orbitHeight, double angleDelta)
{
// Disappear without a target.
if (!target)
{
Destroy();
}
else
{
// Calculate the new position.
let newPos = target.Vec3Angle(orbitDist, angle, target.Floorclip + orbitHeight);
// Move to the newly calculated position.
SetOrigin(newPos, true);
// Increase the angle.
angle += angleDelta;
}
}
}
//===========================================================================
//
// TestInv
//
// Type "give TestInv" in the console to spawn an orbiting projectile.
//
//===========================================================================
class TestInv : Inventory
{
override bool TryPickup(out Actor toucher)
{
// Spawn an orbiting projectile at the toucher's position.
let proj = Spawn('OrbitingProjectile', toucher.pos);
if (proj)
{
// Assign the orbiting projectile's target
// so that it won't disapepar.
proj.target = toucher;
}
// Remove this item from play.
GoAwayAndDie();
return true;
}
}