Merge pull request #12571 from reaperrr/bullet-bounce
Implement bullet bouncing logic
This commit is contained in:
@@ -63,6 +63,16 @@ namespace OpenRA.Mods.Common.Projectiles
|
|||||||
[Desc("Arc in WAngles, two values indicate variable arc.")]
|
[Desc("Arc in WAngles, two values indicate variable arc.")]
|
||||||
public readonly WAngle[] LaunchAngle = { WAngle.Zero };
|
public readonly WAngle[] LaunchAngle = { WAngle.Zero };
|
||||||
|
|
||||||
|
[Desc("Up to how many times does this bullet bounce when touching ground without hitting a target.",
|
||||||
|
"0 implies exploding on contact with the originally targeted position.")]
|
||||||
|
public readonly int BounceCount = 0;
|
||||||
|
|
||||||
|
[Desc("Modify distance of each bounce by this percentage of previous distance.")]
|
||||||
|
public readonly int BounceRangeModifier = 60;
|
||||||
|
|
||||||
|
[Desc("If projectile touches an actor with one of these stances during or after the first bounce, trigger explosion.")]
|
||||||
|
public readonly Stance ValidBounceBlockerStances = Stance.Enemy | Stance.Neutral;
|
||||||
|
|
||||||
[Desc("Interval in ticks between each spawned Trail animation.")]
|
[Desc("Interval in ticks between each spawned Trail animation.")]
|
||||||
public readonly int TrailInterval = 2;
|
public readonly int TrailInterval = 2;
|
||||||
|
|
||||||
@@ -96,10 +106,11 @@ namespace OpenRA.Mods.Common.Projectiles
|
|||||||
ContrailRenderable contrail;
|
ContrailRenderable contrail;
|
||||||
string trailPalette;
|
string trailPalette;
|
||||||
|
|
||||||
[Sync] WPos pos, target;
|
[Sync] WPos pos, target, source;
|
||||||
int length;
|
int length;
|
||||||
[Sync] int facing;
|
[Sync] int facing;
|
||||||
int ticks, smokeTicks;
|
int ticks, smokeTicks;
|
||||||
|
int remainingBounces;
|
||||||
|
|
||||||
public Actor SourceActor { get { return args.SourceActor; } }
|
public Actor SourceActor { get { return args.SourceActor; } }
|
||||||
|
|
||||||
@@ -108,6 +119,7 @@ namespace OpenRA.Mods.Common.Projectiles
|
|||||||
this.info = info;
|
this.info = info;
|
||||||
this.args = args;
|
this.args = args;
|
||||||
pos = args.Source;
|
pos = args.Source;
|
||||||
|
source = args.Source;
|
||||||
|
|
||||||
var world = args.SourceActor.World;
|
var world = args.SourceActor.World;
|
||||||
|
|
||||||
@@ -150,6 +162,7 @@ namespace OpenRA.Mods.Common.Projectiles
|
|||||||
trailPalette += args.SourceActor.Owner.InternalName;
|
trailPalette += args.SourceActor.Owner.InternalName;
|
||||||
|
|
||||||
smokeTicks = info.TrailDelay;
|
smokeTicks = info.TrailDelay;
|
||||||
|
remainingBounces = info.BounceCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
int GetEffectiveFacing()
|
int GetEffectiveFacing()
|
||||||
@@ -171,7 +184,7 @@ namespace OpenRA.Mods.Common.Projectiles
|
|||||||
anim.Tick();
|
anim.Tick();
|
||||||
|
|
||||||
var lastPos = pos;
|
var lastPos = pos;
|
||||||
pos = WPos.LerpQuadratic(args.Source, target, angle, ticks, length);
|
pos = WPos.LerpQuadratic(source, target, angle, ticks, length);
|
||||||
|
|
||||||
// Check for walls or other blocking obstacles
|
// Check for walls or other blocking obstacles
|
||||||
var shouldExplode = false;
|
var shouldExplode = false;
|
||||||
@@ -185,7 +198,7 @@ namespace OpenRA.Mods.Common.Projectiles
|
|||||||
|
|
||||||
if (!string.IsNullOrEmpty(info.TrailImage) && --smokeTicks < 0)
|
if (!string.IsNullOrEmpty(info.TrailImage) && --smokeTicks < 0)
|
||||||
{
|
{
|
||||||
var delayedPos = WPos.LerpQuadratic(args.Source, target, angle, ticks - info.TrailDelay, length);
|
var delayedPos = WPos.LerpQuadratic(source, target, angle, ticks - info.TrailDelay, length);
|
||||||
world.AddFrameEndTask(w => w.Add(new SpriteEffect(delayedPos, w, info.TrailImage, info.TrailSequences.Random(world.SharedRandom),
|
world.AddFrameEndTask(w => w.Add(new SpriteEffect(delayedPos, w, info.TrailImage, info.TrailSequences.Random(world.SharedRandom),
|
||||||
trailPalette, false, false, GetEffectiveFacing())));
|
trailPalette, false, false, GetEffectiveFacing())));
|
||||||
|
|
||||||
@@ -195,8 +208,30 @@ namespace OpenRA.Mods.Common.Projectiles
|
|||||||
if (info.ContrailLength > 0)
|
if (info.ContrailLength > 0)
|
||||||
contrail.Update(pos);
|
contrail.Update(pos);
|
||||||
|
|
||||||
|
var flightLengthReached = ticks++ >= length;
|
||||||
|
var shouldBounce = remainingBounces > 0;
|
||||||
|
|
||||||
|
if (flightLengthReached && shouldBounce)
|
||||||
|
{
|
||||||
|
shouldExplode |= AnyValidTargetsInRadius(world, pos, info.Width + info.TargetExtraSearchRadius, args.SourceActor, true);
|
||||||
|
target += (pos - source) * info.BounceRangeModifier / 100;
|
||||||
|
var dat = world.Map.DistanceAboveTerrain(target);
|
||||||
|
target += new WVec(0, 0, -dat.Length);
|
||||||
|
length = Math.Max((target - pos).Length / speed.Length, 1);
|
||||||
|
ticks = 0;
|
||||||
|
source = pos;
|
||||||
|
remainingBounces--;
|
||||||
|
}
|
||||||
|
|
||||||
// Flight length reached / exceeded
|
// Flight length reached / exceeded
|
||||||
shouldExplode |= ticks++ >= length;
|
shouldExplode |= flightLengthReached && !shouldBounce;
|
||||||
|
|
||||||
|
// Driving into cell with higher height level
|
||||||
|
shouldExplode |= world.Map.DistanceAboveTerrain(pos).Length < 0;
|
||||||
|
|
||||||
|
// After first bounce, check for targets each tick
|
||||||
|
if (remainingBounces < info.BounceCount)
|
||||||
|
shouldExplode |= AnyValidTargetsInRadius(world, pos, info.Width + info.TargetExtraSearchRadius, args.SourceActor, true);
|
||||||
|
|
||||||
if (shouldExplode)
|
if (shouldExplode)
|
||||||
Explode(world);
|
Explode(world);
|
||||||
@@ -236,5 +271,27 @@ namespace OpenRA.Mods.Common.Projectiles
|
|||||||
|
|
||||||
args.Weapon.Impact(Target.FromPos(pos), args.SourceActor, args.DamageModifiers);
|
args.Weapon.Impact(Target.FromPos(pos), args.SourceActor, args.DamageModifiers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AnyValidTargetsInRadius(World world, WPos pos, WDist radius, Actor firedBy, bool checkTargetType)
|
||||||
|
{
|
||||||
|
foreach (var victim in world.FindActorsInCircle(pos, radius))
|
||||||
|
{
|
||||||
|
if (checkTargetType && !Target.FromActor(victim).IsValidFor(firedBy))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!info.ValidBounceBlockerStances.HasStance(victim.Owner.Stances[firedBy.Owner]))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var healthInfo = victim.Info.TraitInfoOrDefault<HealthInfo>();
|
||||||
|
if (healthInfo == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// If the impact position is within any actor's HitShape, we have a direct hit
|
||||||
|
if (healthInfo.Shape.DistanceFromEdge(pos, victim).Length <= 0)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
Grenade:
|
Grenade:
|
||||||
ReloadDelay: 60
|
ReloadDelay: 60
|
||||||
Range: 4c512
|
Range: 4c512
|
||||||
Projectile: Bullet # TODO: Add bounce effect
|
Projectile: Bullet
|
||||||
Speed: 85
|
Speed: 160
|
||||||
Blockable: false
|
Blockable: true
|
||||||
Shadow: true
|
Shadow: true
|
||||||
LaunchAngle: 45
|
LaunchAngle: 60
|
||||||
Inaccuracy: 384
|
Inaccuracy: 0
|
||||||
Image: DISCUS
|
Image: DISCUS
|
||||||
|
BounceCount: 2
|
||||||
Warhead@1Dam: SpreadDamage
|
Warhead@1Dam: SpreadDamage
|
||||||
Spread: 171
|
Spread: 192
|
||||||
Damage: 40
|
Damage: 40
|
||||||
Versus:
|
Versus:
|
||||||
None: 100
|
None: 100
|
||||||
|
|||||||
Reference in New Issue
Block a user