diff --git a/OpenRA.Mods.Common/Projectiles/Bullet.cs b/OpenRA.Mods.Common/Projectiles/Bullet.cs index 004ca0d585..ccc1903ef3 100644 --- a/OpenRA.Mods.Common/Projectiles/Bullet.cs +++ b/OpenRA.Mods.Common/Projectiles/Bullet.cs @@ -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(GetEffectiveFacing)); - anim.PlayRepeating(info.Sequences.Random(world.SharedRandom)); + Animation = new Animation(world, info.Image, new Func(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 Render(WorldRenderer wr) + public virtual IEnumerable 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 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)