diff --git a/OpenRA.Game/Actor.cs b/OpenRA.Game/Actor.cs index 3f8599470e..2be0d3fea9 100644 --- a/OpenRA.Game/Actor.cs +++ b/OpenRA.Game/Actor.cs @@ -41,12 +41,14 @@ namespace OpenRA public int Generation; Lazy bounds; + Lazy visualBounds; Lazy facing; Lazy health; Lazy occupySpace; Lazy effectiveOwner; public Rectangle Bounds { get { return bounds.Value; } } + public Rectangle VisualBounds { get { return visualBounds.Value; } } public IOccupySpace OccupiesSpace { get { return occupySpace.Value; } } public IEffectiveOwner EffectiveOwner { get { return effectiveOwner.Value; } } @@ -110,6 +112,19 @@ namespace OpenRA return new Rectangle(offset.X, offset.Y, size.X, size.Y); }); + visualBounds = Exts.Lazy(() => + { + var sd = Info.Traits.GetOrDefault(); + var size = (sd != null && sd.VisualBounds != null) ? new int2(sd.VisualBounds[0], sd.VisualBounds[1]) : + TraitsImplementing().Select(x => x.SelectionSize(this)).FirstOrDefault(); + + var offset = -size / 2; + if (sd != null && sd.VisualBounds != null && sd.VisualBounds.Length > 2) + offset += new int2(sd.VisualBounds[2], sd.VisualBounds[3]); + + return new Rectangle(offset.X, offset.Y, size.X, size.Y); + }); + renderModifiers = TraitsImplementing().ToArray(); renders = TraitsImplementing().ToArray(); disables = TraitsImplementing().ToArray(); diff --git a/OpenRA.Game/Graphics/SelectionBarsRenderable.cs b/OpenRA.Game/Graphics/SelectionBarsRenderable.cs index a297a14e35..45cf7b9e36 100644 --- a/OpenRA.Game/Graphics/SelectionBarsRenderable.cs +++ b/OpenRA.Game/Graphics/SelectionBarsRenderable.cs @@ -158,7 +158,7 @@ namespace OpenRA.Graphics var health = actor.TraitOrDefault(); var screenPos = wr.ScreenPxPosition(pos); - var bounds = actor.Bounds; + var bounds = actor.VisualBounds; bounds.Offset(screenPos.X, screenPos.Y); var start = new float2(bounds.Left + 1, bounds.Top); diff --git a/OpenRA.Game/Graphics/SelectionBoxRenderable.cs b/OpenRA.Game/Graphics/SelectionBoxRenderable.cs index 825314c0a5..e36eb24a59 100644 --- a/OpenRA.Game/Graphics/SelectionBoxRenderable.cs +++ b/OpenRA.Game/Graphics/SelectionBoxRenderable.cs @@ -18,16 +18,16 @@ namespace OpenRA.Graphics { readonly WPos pos; readonly float scale; - readonly Rectangle bounds; + readonly Rectangle visualBounds; readonly Color color; public SelectionBoxRenderable(Actor actor, Color color) - : this(actor.CenterPosition, actor.Bounds, 1f, color) { } + : this(actor.CenterPosition, actor.VisualBounds, 1f, color) { } - public SelectionBoxRenderable(WPos pos, Rectangle bounds, float scale, Color color) + public SelectionBoxRenderable(WPos pos, Rectangle visualBounds, float scale, Color color) { this.pos = pos; - this.bounds = bounds; + this.visualBounds = visualBounds; this.scale = scale; this.color = color; } @@ -40,15 +40,15 @@ namespace OpenRA.Graphics public IRenderable WithPalette(PaletteReference newPalette) { return this; } public IRenderable WithZOffset(int newOffset) { return this; } - public IRenderable OffsetBy(WVec vec) { return new SelectionBoxRenderable(pos + vec, bounds, scale, color); } + public IRenderable OffsetBy(WVec vec) { return new SelectionBoxRenderable(pos + vec, visualBounds, scale, color); } public IRenderable AsDecoration() { return this; } public IFinalizedRenderable PrepareRender(WorldRenderer wr) { return this; } public void Render(WorldRenderer wr) { var screenPos = wr.ScreenPxPosition(pos); - var tl = screenPos + scale * new float2(bounds.Left, bounds.Top); - var br = screenPos + scale * new float2(bounds.Right, bounds.Bottom); + var tl = screenPos + scale * new float2(visualBounds.Left, visualBounds.Top); + var br = screenPos + scale * new float2(visualBounds.Right, visualBounds.Bottom); var tr = new float2(br.X, tl.Y); var bl = new float2(tl.X, br.Y); var u = new float2(4f / wr.Viewport.Zoom, 0); diff --git a/OpenRA.Game/Traits/Selectable.cs b/OpenRA.Game/Traits/Selectable.cs index ed1df48ec1..bc069114f5 100644 --- a/OpenRA.Game/Traits/Selectable.cs +++ b/OpenRA.Game/Traits/Selectable.cs @@ -19,6 +19,7 @@ namespace OpenRA.Traits { public readonly bool Selectable = true; public readonly int Priority = 10; + [Desc("Bounds for the selectable area.")] public readonly int[] Bounds = null; [Desc("All units having the same selection class specified will be selected with select-by-type commands (e.g. double-click). " diff --git a/OpenRA.Game/Traits/SelectionDecorations.cs b/OpenRA.Game/Traits/SelectionDecorations.cs index 072a556243..4008a416ad 100644 --- a/OpenRA.Game/Traits/SelectionDecorations.cs +++ b/OpenRA.Game/Traits/SelectionDecorations.cs @@ -17,6 +17,8 @@ namespace OpenRA.Traits public class SelectionDecorationsInfo : ITraitInfo { public readonly string Palette = "chrome"; + [Desc("Visual bounds for the selection box. If null, it matches Bounds.")] + public readonly int[] VisualBounds = null; public object Create(ActorInitializer init) { return new SelectionDecorations(init.Self, this); } } @@ -41,7 +43,7 @@ namespace OpenRA.Traits if (!self.Owner.IsAlliedWith(self.World.RenderPlayer) || self.World.FogObscures(self)) yield break; - var b = self.Bounds; + var b = self.VisualBounds; var pos = wr.ScreenPxPosition(self.CenterPosition); var tl = wr.Viewport.WorldToViewPx(pos + new int2(b.Left, b.Top)); var bl = wr.Viewport.WorldToViewPx(pos + new int2(b.Left, b.Bottom)); @@ -85,7 +87,7 @@ namespace OpenRA.Traits var pipxyBase = basePosition + new int2(1 - pipSize.X / 2, -(3 + pipSize.Y / 2)); var pipxyOffset = new int2(0, 0); var pal = wr.Palette(Info.Palette); - var width = self.Bounds.Width; + var width = self.VisualBounds.Width; foreach (var pips in pipSources) { diff --git a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs index 765bb75321..37c63131d0 100644 --- a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs +++ b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs @@ -1152,6 +1152,29 @@ namespace OpenRA.Mods.Common.UtilityCommands } } + if (engineVersion < 20150603) + { + if (depth == 0 && node.Value.Nodes.Exists(n => n.Key == "Selectable")) + { + var selectable = node.Value.Nodes.FirstOrDefault(n => n.Key == "Selectable"); + var selDecor = node.Value.Nodes.FirstOrDefault(n => n.Key == "SelectionDecorations"); + var selectableNodes = selectable.Value.Nodes; + var bounds = selectableNodes.FirstOrDefault(n => n.Key == "Bounds"); + + if (bounds != null) + { + var visualBounds = FieldLoader.GetValue("Bounds", bounds.Value.Value); + if (selDecor != null) + selDecor.Value.Nodes.Add(new MiniYamlNode("VisualBounds", visualBounds.ToString())); + else + node.Value.Nodes.Add(new MiniYamlNode("SelectionDecorations", "", new List + { + new MiniYamlNode("VisualBounds", visualBounds), + })); + } + } + } + UpgradeActorRules(engineVersion, ref node.Value.Nodes, node, depth + 1); } }