Make Bullet projectile extensible

This commit is contained in:
michaeldgg2
2023-06-23 18:52:59 +02:00
committed by Gustas
parent e1940eec77
commit a14cc8cc4d

View File

@@ -134,14 +134,14 @@ namespace OpenRA.Mods.Common.Projectiles
[Desc("The alpha value [from 0 to 255] of color at the contrail end.")]
public readonly int ContrailEndColorAlpha = 0;
public IProjectile Create(ProjectileArgs args) { return new Bullet(this, args); }
public virtual IProjectile Create(ProjectileArgs args) { return new Bullet(this, args); }
}
public class Bullet : IProjectile, ISync
{
readonly BulletInfo info;
readonly ProjectileArgs args;
readonly Animation anim;
protected readonly ProjectileArgs Args;
protected readonly Animation Animation;
readonly WAngle facing;
readonly WAngle angle;
readonly WDist speed;
@@ -153,16 +153,18 @@ namespace OpenRA.Mods.Common.Projectiles
readonly ContrailRenderable contrail;
[Sync]
WPos pos, lastPos, target, source;
protected WPos pos, lastPos, target, source;
int length;
int ticks, smokeTicks;
int remainingBounces;
protected bool FlightLengthReached => ticks >= length;
public Bullet(BulletInfo info, ProjectileArgs args)
{
this.info = info;
this.args = args;
Args = args;
pos = args.Source;
source = args.Source;
@@ -193,8 +195,8 @@ namespace OpenRA.Mods.Common.Projectiles
if (!string.IsNullOrEmpty(info.Image))
{
anim = new Animation(world, info.Image, new Func<WAngle>(GetEffectiveFacing));
anim.PlayRepeating(info.Sequences.Random(world.SharedRandom));
Animation = new Animation(world, info.Image, new Func<WAngle>(GetEffectiveFacing));
Animation.PlayRepeating(info.Sequences.Random(world.SharedRandom));
}
if (info.ContrailLength > 0)
@@ -230,21 +232,26 @@ namespace OpenRA.Mods.Common.Projectiles
return new WAngle(effective);
}
public void Tick(World world)
public virtual void Tick(World world)
{
anim?.Tick();
Animation?.Tick();
lastPos = pos;
pos = WPos.LerpQuadratic(source, target, angle, ticks, length);
if (ShouldExplode(world))
{
if (info.ContrailLength > 0)
world.AddFrameEndTask(w => w.Add(new ContrailFader(pos, contrail)));
Explode(world);
}
}
bool ShouldExplode(World world)
{
// Check for walls or other blocking obstacles
if (info.Blockable && BlocksProjectiles.AnyBlockingActorsBetween(world, args.SourceActor.Owner, lastPos, pos, info.Width, out var blockedPos))
if (info.Blockable && BlocksProjectiles.AnyBlockingActorsBetween(world, Args.SourceActor.Owner, lastPos, pos, info.Width, out var blockedPos))
{
pos = blockedPos;
return true;
@@ -274,7 +281,7 @@ namespace OpenRA.Mods.Common.Projectiles
if (info.InvalidBounceTerrain.Contains(world.Map.GetTerrainInfo(cell).Type))
return true;
if (AnyValidTargetsInRadius(world, pos, info.Width, args.SourceActor, true))
if (AnyValidTargetsInRadius(world, pos, info.Width, Args.SourceActor, true))
return true;
target += (pos - source) * info.BounceRangeModifier / 100;
@@ -297,26 +304,35 @@ namespace OpenRA.Mods.Common.Projectiles
return true;
// After first bounce, check for targets each tick
if (remainingBounces < info.BounceCount && AnyValidTargetsInRadius(world, pos, info.Width, args.SourceActor, true))
if (remainingBounces < info.BounceCount && AnyValidTargetsInRadius(world, pos, info.Width, Args.SourceActor, true))
return true;
return false;
}
public IEnumerable<IRenderable> Render(WorldRenderer wr)
public virtual IEnumerable<IRenderable> Render(WorldRenderer wr)
{
if (info.ContrailLength > 0)
yield return contrail;
if (anim == null || ticks >= length)
if (FlightLengthReached)
yield break;
var world = args.SourceActor.World;
foreach (var r in RenderAnimation(wr))
yield return r;
}
protected IEnumerable<IRenderable> RenderAnimation(WorldRenderer wr)
{
if (Animation == null)
yield break;
var world = Args.SourceActor.World;
if (!world.FogObscures(pos))
{
var paletteName = info.Palette;
if (paletteName != null && info.IsPlayerPalette)
paletteName += args.SourceActor.Owner.InternalName;
paletteName += Args.SourceActor.Owner.InternalName;
var palette = wr.Palette(paletteName);
@@ -324,31 +340,28 @@ namespace OpenRA.Mods.Common.Projectiles
{
var dat = world.Map.DistanceAboveTerrain(pos);
var shadowPos = pos - new WVec(0, 0, dat.Length);
foreach (var r in anim.Render(shadowPos, palette))
foreach (var r in Animation.Render(shadowPos, palette))
yield return ((IModifyableRenderable)r)
.WithTint(shadowColor, ((IModifyableRenderable)r).TintModifiers | TintModifiers.ReplaceColor)
.WithAlpha(shadowAlpha);
}
foreach (var r in anim.Render(pos, palette))
foreach (var r in Animation.Render(pos, palette))
yield return r;
}
}
void Explode(World world)
protected virtual void Explode(World world)
{
if (info.ContrailLength > 0)
world.AddFrameEndTask(w => w.Add(new ContrailFader(pos, contrail)));
world.AddFrameEndTask(w => w.Remove(this));
var warheadArgs = new WarheadArgs(args)
var warheadArgs = new WarheadArgs(Args)
{
ImpactOrientation = new WRot(WAngle.Zero, Util.GetVerticalAngle(lastPos, pos), args.Facing),
ImpactOrientation = new WRot(WAngle.Zero, Util.GetVerticalAngle(lastPos, pos), Args.Facing),
ImpactPosition = pos,
};
args.Weapon.Impact(Target.FromPos(pos), warheadArgs);
Args.Weapon.Impact(Target.FromPos(pos), warheadArgs);
}
bool AnyValidTargetsInRadius(World world, WPos pos, WDist radius, Actor firedBy, bool checkTargetType)