diff --git a/OpenRA.Mods.Common/HitShapes/Capsule.cs b/OpenRA.Mods.Common/HitShapes/Capsule.cs index 69ac0ed944..640a715c79 100644 --- a/OpenRA.Mods.Common/HitShapes/Capsule.cs +++ b/OpenRA.Mods.Common/HitShapes/Capsule.cs @@ -81,27 +81,23 @@ namespace OpenRA.Mods.Common.HitShapes return new WDist(Math.Max(0, distance - Radius.Length)); } - public WDist DistanceFromEdge(WPos pos, Actor actor) + public WDist DistanceFromEdge(WPos pos, WPos origin, WRot orientation) { - var actorPos = actor.CenterPosition; + if (pos.Z > origin.Z + VerticalTopOffset) + return DistanceFromEdge((pos - (origin + new WVec(0, 0, VerticalTopOffset))).Rotate(-orientation)); - if (pos.Z > actorPos.Z + VerticalTopOffset) - return DistanceFromEdge((pos - (actorPos + new WVec(0, 0, VerticalTopOffset))).Rotate(-actor.Orientation)); + if (pos.Z < origin.Z + VerticalBottomOffset) + return DistanceFromEdge((pos - (origin + new WVec(0, 0, VerticalBottomOffset))).Rotate(-orientation)); - if (pos.Z < actorPos.Z + VerticalBottomOffset) - return DistanceFromEdge((pos - (actorPos + new WVec(0, 0, VerticalBottomOffset))).Rotate(-actor.Orientation)); - - return DistanceFromEdge((pos - new WPos(actorPos.X, actorPos.Y, pos.Z)).Rotate(-actor.Orientation)); + return DistanceFromEdge((pos - new WPos(origin.X, origin.Y, pos.Z)).Rotate(-orientation)); } - IEnumerable IHitShape.RenderDebugOverlay(WorldRenderer wr, Actor actor) + IEnumerable IHitShape.RenderDebugOverlay(WorldRenderer wr, WPos origin, WRot orientation) { - var actorPos = actor.CenterPosition; - - var a = actorPos + new WVec(PointA.X, PointA.Y, VerticalTopOffset).Rotate(actor.Orientation); - var b = actorPos + new WVec(PointB.X, PointB.Y, VerticalTopOffset).Rotate(actor.Orientation); - var aa = actorPos + new WVec(PointA.X, PointA.Y, VerticalBottomOffset).Rotate(actor.Orientation); - var bb = actorPos + new WVec(PointB.X, PointB.Y, VerticalBottomOffset).Rotate(actor.Orientation); + var a = origin + new WVec(PointA.X, PointA.Y, VerticalTopOffset).Rotate(orientation); + var b = origin + new WVec(PointB.X, PointB.Y, VerticalTopOffset).Rotate(orientation); + var aa = origin + new WVec(PointA.X, PointA.Y, VerticalBottomOffset).Rotate(orientation); + var bb = origin + new WVec(PointB.X, PointB.Y, VerticalBottomOffset).Rotate(orientation); var offset1 = new WVec(a.Y - b.Y, b.X - a.X, 0); offset1 = offset1 * Radius.Length / offset1.Length; @@ -112,7 +108,7 @@ namespace OpenRA.Mods.Common.HitShapes yield return new CircleAnnotationRenderable(b, Radius, 1, Color.Yellow); yield return new CircleAnnotationRenderable(aa, Radius, 1, Color.Yellow); yield return new CircleAnnotationRenderable(bb, Radius, 1, Color.Yellow); - yield return new CircleAnnotationRenderable(actorPos, OuterRadius, 1, Color.LimeGreen); + yield return new CircleAnnotationRenderable(origin, OuterRadius, 1, Color.LimeGreen); yield return new LineAnnotationRenderable(a - offset1, b - offset1, 1, Color.Yellow); yield return new LineAnnotationRenderable(a + offset1, b + offset1, 1, Color.Yellow); yield return new LineAnnotationRenderable(aa - offset2, bb - offset2, 1, Color.Yellow); diff --git a/OpenRA.Mods.Common/HitShapes/Circle.cs b/OpenRA.Mods.Common/HitShapes/Circle.cs index 198a02f988..25fa6de0fa 100644 --- a/OpenRA.Mods.Common/HitShapes/Circle.cs +++ b/OpenRA.Mods.Common/HitShapes/Circle.cs @@ -45,24 +45,21 @@ namespace OpenRA.Mods.Common.HitShapes return new WDist(Math.Max(0, v.Length - Radius.Length)); } - public WDist DistanceFromEdge(WPos pos, Actor actor) + public WDist DistanceFromEdge(WPos pos, WPos origin, WRot orientation) { - var actorPos = actor.CenterPosition; + if (pos.Z > origin.Z + VerticalTopOffset) + return DistanceFromEdge(pos - (origin + new WVec(0, 0, VerticalTopOffset))); - if (pos.Z > actorPos.Z + VerticalTopOffset) - return DistanceFromEdge(pos - (actorPos + new WVec(0, 0, VerticalTopOffset))); + if (pos.Z < origin.Z + VerticalBottomOffset) + return DistanceFromEdge(pos - (origin + new WVec(0, 0, VerticalBottomOffset))); - if (pos.Z < actorPos.Z + VerticalBottomOffset) - return DistanceFromEdge(pos - (actorPos + new WVec(0, 0, VerticalBottomOffset))); - - return DistanceFromEdge(pos - new WPos(actorPos.X, actorPos.Y, pos.Z)); + return DistanceFromEdge(pos - new WPos(origin.X, origin.Y, pos.Z)); } - IEnumerable IHitShape.RenderDebugOverlay(WorldRenderer wr, Actor actor) + IEnumerable IHitShape.RenderDebugOverlay(WorldRenderer wr, WPos origin, WRot orientation) { - var actorPos = actor.CenterPosition; - yield return new CircleAnnotationRenderable(actorPos + new WVec(0, 0, VerticalTopOffset), Radius, 1, Color.Yellow); - yield return new CircleAnnotationRenderable(actorPos + new WVec(0, 0, VerticalBottomOffset), Radius, 1, Color.Yellow); + yield return new CircleAnnotationRenderable(origin + new WVec(0, 0, VerticalTopOffset), Radius, 1, Color.Yellow); + yield return new CircleAnnotationRenderable(origin + new WVec(0, 0, VerticalBottomOffset), Radius, 1, Color.Yellow); } } } diff --git a/OpenRA.Mods.Common/HitShapes/IHitShape.cs b/OpenRA.Mods.Common/HitShapes/IHitShape.cs index 14efdc4975..34ee80ecde 100644 --- a/OpenRA.Mods.Common/HitShapes/IHitShape.cs +++ b/OpenRA.Mods.Common/HitShapes/IHitShape.cs @@ -19,9 +19,9 @@ namespace OpenRA.Mods.Common.HitShapes WDist OuterRadius { get; } WDist DistanceFromEdge(WVec v); - WDist DistanceFromEdge(WPos pos, Actor actor); + WDist DistanceFromEdge(WPos pos, WPos origin, WRot orientation); void Initialize(); - IEnumerable RenderDebugOverlay(WorldRenderer wr, Actor actor); + IEnumerable RenderDebugOverlay(WorldRenderer wr, WPos origin, WRot orientation); } } diff --git a/OpenRA.Mods.Common/HitShapes/Polygon.cs b/OpenRA.Mods.Common/HitShapes/Polygon.cs index 1880c86bfd..edbef8d67f 100644 --- a/OpenRA.Mods.Common/HitShapes/Polygon.cs +++ b/OpenRA.Mods.Common/HitShapes/Polygon.cs @@ -99,25 +99,22 @@ namespace OpenRA.Mods.Common.HitShapes return new WDist(Exts.ISqrt(min2 + z * z)); } - public WDist DistanceFromEdge(WPos pos, Actor actor) + public WDist DistanceFromEdge(WPos pos, WPos origin, WRot orientation) { - var actorPos = actor.CenterPosition; - var orientation = actor.Orientation + WRot.FromYaw(LocalYaw); + orientation += WRot.FromYaw(LocalYaw); - if (pos.Z > actorPos.Z + VerticalTopOffset) - return DistanceFromEdge((pos - (actorPos + new WVec(0, 0, VerticalTopOffset))).Rotate(-orientation)); + if (pos.Z > origin.Z + VerticalTopOffset) + return DistanceFromEdge((pos - (origin + new WVec(0, 0, VerticalTopOffset))).Rotate(-orientation)); - if (pos.Z < actorPos.Z + VerticalBottomOffset) - return DistanceFromEdge((pos - (actorPos + new WVec(0, 0, VerticalBottomOffset))).Rotate(-orientation)); + if (pos.Z < origin.Z + VerticalBottomOffset) + return DistanceFromEdge((pos - (origin + new WVec(0, 0, VerticalBottomOffset))).Rotate(-orientation)); - return DistanceFromEdge((pos - new WPos(actorPos.X, actorPos.Y, pos.Z)).Rotate(-orientation)); + return DistanceFromEdge((pos - new WPos(origin.X, origin.Y, pos.Z)).Rotate(-orientation)); } - IEnumerable IHitShape.RenderDebugOverlay(WorldRenderer wr, Actor actor) + IEnumerable IHitShape.RenderDebugOverlay(WorldRenderer wr, WPos actorPos, WRot orientation) { - var actorPos = actor.CenterPosition; - var orientation = actor.Orientation + WRot.FromYaw(LocalYaw); - + orientation += WRot.FromYaw(LocalYaw); var vertsTop = combatOverlayVertsTop.Select(v => actorPos + v.Rotate(orientation)).ToArray(); var vertsBottom = combatOverlayVertsBottom.Select(v => actorPos + v.Rotate(orientation)).ToArray(); diff --git a/OpenRA.Mods.Common/HitShapes/Rectangle.cs b/OpenRA.Mods.Common/HitShapes/Rectangle.cs index 3a60da4e58..a89a1e018b 100644 --- a/OpenRA.Mods.Common/HitShapes/Rectangle.cs +++ b/OpenRA.Mods.Common/HitShapes/Rectangle.cs @@ -94,31 +94,29 @@ namespace OpenRA.Mods.Common.HitShapes return new WDist(r.HorizontalLength); } - public WDist DistanceFromEdge(WPos pos, Actor actor) + public WDist DistanceFromEdge(WPos pos, WPos origin, WRot orientation) { - var actorPos = actor.CenterPosition; - var orientation = actor.Orientation + WRot.FromYaw(LocalYaw); + orientation += WRot.FromYaw(LocalYaw); - if (pos.Z > actorPos.Z + VerticalTopOffset) - return DistanceFromEdge((pos - (actorPos + new WVec(0, 0, VerticalTopOffset))).Rotate(-orientation)); + if (pos.Z > origin.Z + VerticalTopOffset) + return DistanceFromEdge((pos - (origin + new WVec(0, 0, VerticalTopOffset))).Rotate(-orientation)); - if (pos.Z < actorPos.Z + VerticalBottomOffset) - return DistanceFromEdge((pos - (actorPos + new WVec(0, 0, VerticalBottomOffset))).Rotate(-orientation)); + if (pos.Z < origin.Z + VerticalBottomOffset) + return DistanceFromEdge((pos - (origin + new WVec(0, 0, VerticalBottomOffset))).Rotate(-orientation)); - return DistanceFromEdge((pos - new WPos(actorPos.X, actorPos.Y, pos.Z)).Rotate(-orientation)); + return DistanceFromEdge((pos - new WPos(origin.X, origin.Y, pos.Z)).Rotate(-orientation)); } - IEnumerable IHitShape.RenderDebugOverlay(WorldRenderer wr, Actor actor) + IEnumerable IHitShape.RenderDebugOverlay(WorldRenderer wr, WPos origin, WRot orientation) { - var actorPos = actor.CenterPosition; - var orientation = actor.Orientation + WRot.FromYaw(LocalYaw); + orientation += WRot.FromYaw(LocalYaw); - var vertsTop = combatOverlayVertsTop.Select(v => actorPos + v.Rotate(orientation)).ToArray(); - var vertsBottom = combatOverlayVertsBottom.Select(v => actorPos + v.Rotate(orientation)).ToArray(); + var vertsTop = combatOverlayVertsTop.Select(v => origin + v.Rotate(orientation)).ToArray(); + var vertsBottom = combatOverlayVertsBottom.Select(v => origin + v.Rotate(orientation)).ToArray(); - yield return new PolygonAnnotationRenderable(vertsTop, actorPos, 1, Color.Yellow); - yield return new PolygonAnnotationRenderable(vertsBottom, actorPos, 1, Color.Yellow); - yield return new CircleAnnotationRenderable(actorPos, OuterRadius, 1, Color.LimeGreen); + yield return new PolygonAnnotationRenderable(vertsTop, origin, 1, Color.Yellow); + yield return new PolygonAnnotationRenderable(vertsBottom, origin, 1, Color.Yellow); + yield return new CircleAnnotationRenderable(origin, OuterRadius, 1, Color.LimeGreen); } } } diff --git a/OpenRA.Mods.Common/Projectiles/Bullet.cs b/OpenRA.Mods.Common/Projectiles/Bullet.cs index f7975d6486..2c72aac97c 100644 --- a/OpenRA.Mods.Common/Projectiles/Bullet.cs +++ b/OpenRA.Mods.Common/Projectiles/Bullet.cs @@ -295,7 +295,7 @@ namespace OpenRA.Mods.Common.Projectiles // If the impact position is within any actor's HitShape, we have a direct hit var activeShapes = victim.TraitsImplementing().Where(Exts.IsTraitEnabled); - if (activeShapes.Any(i => i.Info.Type.DistanceFromEdge(pos, victim).Length <= 0)) + if (activeShapes.Any(i => i.DistanceFromEdge(victim, pos).Length <= 0)) return true; } diff --git a/OpenRA.Mods.Common/Traits/CombatDebugOverlay.cs b/OpenRA.Mods.Common/Traits/CombatDebugOverlay.cs index f3d4747e87..2f1d3e2ae9 100644 --- a/OpenRA.Mods.Common/Traits/CombatDebugOverlay.cs +++ b/OpenRA.Mods.Common/Traits/CombatDebugOverlay.cs @@ -66,7 +66,7 @@ namespace OpenRA.Mods.Common.Traits var activeShapes = shapes.Where(Exts.IsTraitEnabled); foreach (var s in activeShapes) - foreach (var r in s.Info.Type.RenderDebugOverlay(wr, self)) + foreach (var r in s.RenderDebugOverlay(self, wr)) yield return r; var positions = Target.FromActor(self).Positions; diff --git a/OpenRA.Mods.Common/Traits/HitShape.cs b/OpenRA.Mods.Common/Traits/HitShape.cs index 59e7ca77e3..b9c3318f4c 100644 --- a/OpenRA.Mods.Common/Traits/HitShape.cs +++ b/OpenRA.Mods.Common/Traits/HitShape.cs @@ -11,6 +11,7 @@ using System.Collections.Generic; using System.Linq; +using OpenRA.Graphics; using OpenRA.Mods.Common.HitShapes; using OpenRA.Primitives; using OpenRA.Traits; @@ -20,6 +21,9 @@ namespace OpenRA.Mods.Common.Traits [Desc("Shape of actor for targeting and damage calculations.")] public class HitShapeInfo : ConditionalTraitInfo, Requires { + [Desc("Name of turret this shape is linked to. Leave empty to link shape to body.")] + public readonly string Turret = null; + [Desc("Create a targetable position for each offset listed here (relative to CenterPosition).")] public readonly WVec[] TargetableOffsets = { WVec.Zero }; @@ -66,6 +70,7 @@ namespace OpenRA.Mods.Common.Traits { BodyOrientation orientation; ITargetableCells targetableCells; + Turreted turret; public HitShape(Actor self, HitShapeInfo info) : base(info) { } @@ -74,6 +79,7 @@ namespace OpenRA.Mods.Common.Traits { orientation = self.Trait(); targetableCells = self.TraitOrDefault(); + turret = self.TraitsImplementing().FirstOrDefault(t => t.Name == Info.Turret); base.Created(self); } @@ -91,9 +97,53 @@ namespace OpenRA.Mods.Common.Traits foreach (var o in Info.TargetableOffsets) { - var offset = orientation.LocalToWorld(o.Rotate(orientation.QuantizeOrientation(self, self.Orientation))); + var offset = CalculateTargetableOffset(self, o); yield return self.CenterPosition + offset; } } + + WVec CalculateTargetableOffset(Actor self, WVec offset) + { + var localOffset = offset; + var quantizedBodyOrientation = orientation.QuantizeOrientation(self, self.Orientation); + + if (turret != null) + { + // WorldOrientation is quantized to satisfy the *Fudges. + // Need to then convert back to a pseudo-local coordinate space, apply offsets, + // then rotate back at the end + var turretOrientation = turret.WorldOrientation(self) - quantizedBodyOrientation; + localOffset = localOffset.Rotate(turretOrientation); + localOffset += turret.Offset; + } + + return orientation.LocalToWorld(localOffset.Rotate(quantizedBodyOrientation)); + } + + public WDist DistanceFromEdge(Actor self, WPos pos) + { + var origin = self.CenterPosition; + var orientation = self.Orientation; + if (turret != null) + { + origin += turret.Position(self); + orientation = turret.WorldOrientation(self); + } + + return Info.Type.DistanceFromEdge(pos, origin, orientation); + } + + public IEnumerable RenderDebugOverlay(Actor self, WorldRenderer wr) + { + var origin = self.CenterPosition; + var orientation = self.Orientation; + if (turret != null) + { + origin += turret.Position(self); + orientation = turret.WorldOrientation(self); + } + + return Info.Type.RenderDebugOverlay(wr, origin, orientation); + } } } diff --git a/OpenRA.Mods.Common/Warheads/CreateEffectWarhead.cs b/OpenRA.Mods.Common/Warheads/CreateEffectWarhead.cs index 90a7ba2e40..687da8461d 100644 --- a/OpenRA.Mods.Common/Warheads/CreateEffectWarhead.cs +++ b/OpenRA.Mods.Common/Warheads/CreateEffectWarhead.cs @@ -87,7 +87,7 @@ namespace OpenRA.Mods.Common.Warheads // If the impact position is within any HitShape, we have a direct hit var activeShapes = victim.TraitsImplementing().Where(Exts.IsTraitEnabled); - var directHit = activeShapes.Any(i => i.Info.Type.DistanceFromEdge(pos, victim).Length <= 0); + var directHit = activeShapes.Any(i => i.DistanceFromEdge(victim, pos).Length <= 0); // If the warhead landed outside the actor's hit-shape(s), we need to skip the rest so it won't be considered an invalidHit if (!directHit) diff --git a/OpenRA.Mods.Common/Warheads/DamageWarhead.cs b/OpenRA.Mods.Common/Warheads/DamageWarhead.cs index 9795fa1ef1..b94823702c 100644 --- a/OpenRA.Mods.Common/Warheads/DamageWarhead.cs +++ b/OpenRA.Mods.Common/Warheads/DamageWarhead.cs @@ -71,7 +71,7 @@ namespace OpenRA.Mods.Common.Warheads return; var closestActiveShape = victim.TraitsImplementing().Where(Exts.IsTraitEnabled) - .MinByOrDefault(t => t.Info.Type.DistanceFromEdge(victim.CenterPosition, victim)); + .MinByOrDefault(t => t.DistanceFromEdge(victim, victim.CenterPosition)); // Cannot be damaged without an active HitShape if (closestActiveShape == null) diff --git a/OpenRA.Mods.Common/Warheads/SpreadDamageWarhead.cs b/OpenRA.Mods.Common/Warheads/SpreadDamageWarhead.cs index a0558d4b3a..8dc00702c4 100644 --- a/OpenRA.Mods.Common/Warheads/SpreadDamageWarhead.cs +++ b/OpenRA.Mods.Common/Warheads/SpreadDamageWarhead.cs @@ -57,7 +57,7 @@ namespace OpenRA.Mods.Common.Warheads var closestActiveShape = victim.TraitsImplementing() .Where(Exts.IsTraitEnabled) - .Select(s => Pair.New(s, s.Info.Type.DistanceFromEdge(pos, victim))) + .Select(s => Pair.New(s, s.DistanceFromEdge(victim, pos))) .MinByOrDefault(s => s.Second); // Cannot be damaged without an active HitShape diff --git a/OpenRA.Mods.Common/Warheads/TargetDamageWarhead.cs b/OpenRA.Mods.Common/Warheads/TargetDamageWarhead.cs index 479855596e..a4c5d31489 100644 --- a/OpenRA.Mods.Common/Warheads/TargetDamageWarhead.cs +++ b/OpenRA.Mods.Common/Warheads/TargetDamageWarhead.cs @@ -38,7 +38,7 @@ namespace OpenRA.Mods.Common.Warheads var closestActiveShape = victim.TraitsImplementing() .Where(Exts.IsTraitEnabled) - .Select(s => Pair.New(s, s.Info.Type.DistanceFromEdge(pos, victim))) + .Select(s => Pair.New(s, s.DistanceFromEdge(victim, pos))) .MinByOrDefault(s => s.Second); // Cannot be damaged without an active HitShape or if HitShape is outside Spread