Skip to content

Commit

Permalink
Make Bullet projectile extensible
Browse files Browse the repository at this point in the history
  • Loading branch information
michaeldgg2 committed Jul 27, 2023
1 parent c093e7c commit 34d20e8
Showing 1 changed file with 38 additions and 25 deletions.
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)));

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 (ticks >= length)
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);

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

0 comments on commit 34d20e8

Please sign in to comment.