Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make Bullet projectile extensible #20931

Merged
merged 1 commit into from
Aug 13, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
63 changes: 38 additions & 25 deletions OpenRA.Mods.Common/Projectiles/Bullet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -134,14 +134,14 @@ public class BulletInfo : IProjectileInfo
[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;
Expand All @@ -153,16 +153,18 @@ public class Bullet : IProjectile, ISync
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;

Expand Down Expand Up @@ -193,8 +195,8 @@ public Bullet(BulletInfo info, ProjectileArgs args)

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)
Expand Down Expand Up @@ -230,21 +232,26 @@ WAngle GetEffectiveFacing()
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)));
PunkPun marked this conversation as resolved.
Show resolved Hide resolved

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;
Expand Down Expand Up @@ -274,7 +281,7 @@ bool ShouldExplode(World world)
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;
Expand All @@ -297,58 +304,64 @@ bool ShouldExplode(World world)
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))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will likely also have quite a negative peformance overhad with many bullets flying around.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did take inspiration from Actor.Renderables(), but I can quickly turn it into standard (non-iterator) method.

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);

if (info.Shadow)
{
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)
Expand Down