diff --git a/OpenRA.Mods.Common/Effects/Bullet.cs b/OpenRA.Mods.Common/Effects/Bullet.cs index 77efeafb56..57bdf48d5d 100644 --- a/OpenRA.Mods.Common/Effects/Bullet.cs +++ b/OpenRA.Mods.Common/Effects/Bullet.cs @@ -50,6 +50,12 @@ namespace OpenRA.Mods.Common.Effects [Desc("Is this blocked by actors with BlocksProjectiles trait.")] public readonly bool Blockable = true; + [Desc("Width of projectile (used for finding blocking actors).")] + public readonly WDist Width = new WDist(1); + + [Desc("Extra search radius beyond path for blocking actors.")] + public readonly WDist TargetExtraSearchRadius = new WDist(2048); + [Desc("Arc in WAngles, two values indicate variable arc.")] public readonly WAngle[] Angle = { WAngle.Zero }; @@ -156,8 +162,19 @@ namespace OpenRA.Mods.Common.Effects if (anim != null) anim.Tick(); + var lastPos = pos; pos = WPos.LerpQuadratic(args.Source, target, angle, ticks, length); + // Check for walls or other blocking obstacles + var shouldExplode = false; + WPos blockedPos; + if (info.Blockable && BlocksProjectiles.AnyBlockingActorsBetween(world, lastPos, pos, info.Width, + info.TargetExtraSearchRadius, out blockedPos)) + { + pos = blockedPos; + shouldExplode = true; + } + if (!string.IsNullOrEmpty(info.Trail) && --smokeTicks < 0) { var delayedPos = WPos.LerpQuadratic(args.Source, target, angle, ticks - info.TrailDelay, length); @@ -168,8 +185,8 @@ namespace OpenRA.Mods.Common.Effects if (info.ContrailLength > 0) contrail.Update(pos); - var shouldExplode = ticks++ >= length // Flight length reached/exceeded - || (info.Blockable && BlocksProjectiles.AnyBlockingActorAt(world, pos)); // Hit a wall or other blocking obstacle + // Flight length reached / exceeded + shouldExplode |= ticks++ >= length; if (shouldExplode) Explode(world); diff --git a/OpenRA.Mods.Common/Effects/Missile.cs b/OpenRA.Mods.Common/Effects/Missile.cs index 262e5e100a..430803ea93 100644 --- a/OpenRA.Mods.Common/Effects/Missile.cs +++ b/OpenRA.Mods.Common/Effects/Missile.cs @@ -58,6 +58,12 @@ namespace OpenRA.Mods.Common.Effects [Desc("Is the missile blocked by actors with BlocksProjectiles: trait.")] public readonly bool Blockable = true; + [Desc("Width of projectile (used for finding blocking actors).")] + public readonly WDist Width = new WDist(1); + + [Desc("Extra search radius beyond path for blocking actors.")] + public readonly WDist TargetExtraSearchRadius = new WDist(2048); + [Desc("Maximum offset at the maximum range")] public readonly WDist Inaccuracy = WDist.Zero; @@ -773,8 +779,19 @@ namespace OpenRA.Mods.Common.Effects renderFacing = WAngle.ArcTan(move.Z - move.Y, move.X).Angle / 4 - 64; // Move the missile + var lastPos = pos; pos += move; + // Check for walls or other blocking obstacles + var shouldExplode = false; + WPos blockedPos; + if (info.Blockable && BlocksProjectiles.AnyBlockingActorsBetween(world, lastPos, pos, info.Width, + info.TargetExtraSearchRadius, out blockedPos)) + { + pos = blockedPos; + shouldExplode = true; + } + // Create the smoke trail effect if (!string.IsNullOrEmpty(info.TrailImage) && --ticksToNextSmoke < 0 && (state != States.Freefall || info.TrailWhenDeactivated)) { @@ -786,14 +803,10 @@ namespace OpenRA.Mods.Common.Effects contrail.Update(pos); var cell = world.Map.CellContaining(pos); - - // NOTE: High speeds might cause the missile to miss the target or fly through obstacles - // In that case, big moves should probably be decomposed into multiple smaller ones with hit checks var height = world.Map.DistanceAboveTerrain(pos); - var shouldExplode = (height.Length < 0) // Hit the ground - || (relTarDist < info.CloseEnough.Length) // Within range + shouldExplode |= height.Length < 0 // Hit the ground + || relTarDist < info.CloseEnough.Length // Within range || (info.ExplodeWhenEmpty && info.RangeLimit != 0 && ticks > info.RangeLimit) // Ran out of fuel - || (info.Blockable && BlocksProjectiles.AnyBlockingActorAt(world, pos)) // Hit a wall or other blocking obstacle || !world.Map.Contains(cell) // This also avoids an IndexOutOfRangeException in GetTerrainInfo below. || (!string.IsNullOrEmpty(info.BoundToTerrainType) && world.Map.GetTerrainInfo(cell).Type != info.BoundToTerrainType) // Hit incompatible terrain || (height.Length < info.AirburstAltitude.Length && relTarHorDist < info.CloseEnough.Length); // Airburst