diff --git a/OpenRA.Game/Actor.cs b/OpenRA.Game/Actor.cs index 0fcca228cd..ffc1609ad8 100644 --- a/OpenRA.Game/Actor.cs +++ b/OpenRA.Game/Actor.cs @@ -47,8 +47,9 @@ namespace OpenRA public int Generation; - public Rectangle Bounds { get; private set; } - public Rectangle VisualBounds { get; private set; } + public Rectangle RenderBounds { get; private set; } + public Rectangle SelectableBounds { get; private set; } + public Rectangle SelectionOverlayBounds { get; private set; } public IEffectiveOwner EffectiveOwner { get; private set; } public IOccupySpace OccupiesSpace { get; private set; } public ITargetable[] Targetables { get; private set; } @@ -110,8 +111,14 @@ namespace OpenRA // PERF: Cache all these traits as soon as the actor is created. This is a fairly cheap one-off cost per // actor that allows us to provide some fast implementations of commonly used methods that are relied on by // performance-sensitive parts of the core game engine, such as pathfinding, visibility and rendering. - Bounds = DetermineBounds(); - VisualBounds = DetermineVisualBounds(); + + // RenderBounds are used for ScreenMap binning + // SelectableBounds define the selectable area of the actor + // SelectionOverlayBounds are used to draw the selection box and determine offsets for other selection overlays + RenderBounds = DetermineRenderBounds(); + SelectableBounds = DetermineSelectableBounds(); + SelectionOverlayBounds = DetermineSelectionOverlayBounds(); + EffectiveOwner = TraitOrDefault(); facing = TraitOrDefault(); health = TraitOrDefault(); @@ -129,24 +136,34 @@ namespace OpenRA .Select(pair => new SyncHash(pair.First, pair.Second(pair.First))); } - Rectangle DetermineBounds() + Rectangle DetermineRenderBounds() + { + var size = TraitsImplementing().Select(x => x.SelectionSize(this)).FirstOrDefault(); + var offset = -size / 2; + + return new Rectangle(offset.X, offset.Y, size.X, size.Y); + } + + Rectangle DetermineSelectableBounds() { var si = Info.TraitInfoOrDefault(); - var size = (si != null && si.Bounds != null) ? new int2(si.Bounds[0], si.Bounds[1]) : - TraitsImplementing().Select(x => x.SelectionSize(this)).FirstOrDefault(); + if (si == null || si.Bounds == null) + return RenderBounds; + + var size = new int2(si.Bounds[0], si.Bounds[1]); var offset = -size / 2; - if (si != null && si.Bounds != null && si.Bounds.Length > 2) + if (si.Bounds.Length > 2) offset += new int2(si.Bounds[2], si.Bounds[3]); return new Rectangle(offset.X, offset.Y, size.X, size.Y); } - Rectangle DetermineVisualBounds() + Rectangle DetermineSelectionOverlayBounds() { var sd = Info.TraitInfoOrDefault(); if (sd == null || sd.SelectionBoxBounds == null) - return Bounds; + return SelectableBounds; var size = new int2(sd.SelectionBoxBounds[0], sd.SelectionBoxBounds[1]); diff --git a/OpenRA.Game/Graphics/SelectionBarsRenderable.cs b/OpenRA.Game/Graphics/SelectionBarsRenderable.cs index f0ac799d4d..ca5751b94e 100644 --- a/OpenRA.Game/Graphics/SelectionBarsRenderable.cs +++ b/OpenRA.Game/Graphics/SelectionBarsRenderable.cs @@ -149,7 +149,7 @@ namespace OpenRA.Graphics var health = actor.TraitOrDefault(); var screenPos = wr.Screen3DPxPosition(pos); - var bounds = actor.VisualBounds; + var bounds = actor.SelectionOverlayBounds; bounds.Offset((int)screenPos.X, (int)screenPos.Y); var start = new float3(bounds.Left + 1, bounds.Top, screenPos.Z); diff --git a/OpenRA.Game/SelectableExts.cs b/OpenRA.Game/SelectableExts.cs index 5087a45803..b78b5be298 100644 --- a/OpenRA.Game/SelectableExts.cs +++ b/OpenRA.Game/SelectableExts.cs @@ -47,12 +47,12 @@ namespace OpenRA.Traits public static Actor WithHighestSelectionPriority(this IEnumerable actors, int2 selectionPixel) { - return actors.MaxByOrDefault(a => CalculateActorSelectionPriority(a.Info, a.Bounds, selectionPixel)); + return actors.MaxByOrDefault(a => CalculateActorSelectionPriority(a.Info, a.SelectableBounds, selectionPixel)); } public static FrozenActor WithHighestSelectionPriority(this IEnumerable actors, int2 selectionPixel) { - return actors.MaxByOrDefault(a => CalculateActorSelectionPriority(a.Info, a.Bounds, selectionPixel)); + return actors.MaxByOrDefault(a => CalculateActorSelectionPriority(a.Info, a.SelectableBounds, selectionPixel)); } static long CalculateActorSelectionPriority(ActorInfo info, Rectangle bounds, int2 selectionPixel) diff --git a/OpenRA.Game/Traits/Player/FrozenActorLayer.cs b/OpenRA.Game/Traits/Player/FrozenActorLayer.cs index 8cbe1ed0a8..51462a940a 100644 --- a/OpenRA.Game/Traits/Player/FrozenActorLayer.cs +++ b/OpenRA.Game/Traits/Player/FrozenActorLayer.cs @@ -31,7 +31,8 @@ namespace OpenRA.Traits { public readonly PPos[] Footprint; public readonly WPos CenterPosition; - public readonly Rectangle Bounds; + public readonly Rectangle RenderBounds; + public readonly Rectangle SelectableBounds; public readonly HashSet TargetTypes; readonly Actor actor; readonly Shroud shroud; @@ -77,7 +78,8 @@ namespace OpenRA.Traits footprint.Select(p => shroud.Contains(p).ToString()).JoinWith("|"))); CenterPosition = self.CenterPosition; - Bounds = self.Bounds; + RenderBounds = self.RenderBounds; + SelectableBounds = self.SelectableBounds; TargetTypes = self.GetEnabledTargetTypes().ToHashSet(); tooltips = self.TraitsImplementing().ToArray(); diff --git a/OpenRA.Game/Traits/World/ScreenMap.cs b/OpenRA.Game/Traits/World/ScreenMap.cs index 6edce70add..ad2d2ef9af 100644 --- a/OpenRA.Game/Traits/World/ScreenMap.cs +++ b/OpenRA.Game/Traits/World/ScreenMap.cs @@ -53,7 +53,7 @@ namespace OpenRA.Traits Rectangle FrozenActorBounds(FrozenActor fa) { var pos = worldRenderer.ScreenPxPosition(fa.CenterPosition); - var bounds = fa.Bounds; + var bounds = fa.RenderBounds; bounds.Offset(pos.X, pos.Y); return bounds; } @@ -61,7 +61,7 @@ namespace OpenRA.Traits Rectangle ActorBounds(Actor a) { var pos = worldRenderer.ScreenPxPosition(a.CenterPosition); - var bounds = a.Bounds; + var bounds = a.RenderBounds; bounds.Offset(pos.X, pos.Y); return bounds; } diff --git a/OpenRA.Game/World.cs b/OpenRA.Game/World.cs index 8fb37a88ef..570a110b1f 100644 --- a/OpenRA.Game/World.cs +++ b/OpenRA.Game/World.cs @@ -192,7 +192,7 @@ namespace OpenRA ActorMap.AddInfluence(self, ios); ActorMap.AddPosition(self, ios); - if (!self.Bounds.Size.IsEmpty) + if (!self.RenderBounds.Size.IsEmpty) ScreenMap.Add(self); } @@ -201,7 +201,7 @@ namespace OpenRA if (!self.IsInWorld) return; - if (!self.Bounds.Size.IsEmpty) + if (!self.RenderBounds.Size.IsEmpty) ScreenMap.Update(self); ActorMap.UpdatePosition(self, ios); @@ -212,7 +212,7 @@ namespace OpenRA ActorMap.RemoveInfluence(self, ios); ActorMap.RemovePosition(self, ios); - if (!self.Bounds.Size.IsEmpty) + if (!self.RenderBounds.Size.IsEmpty) ScreenMap.Remove(self); } diff --git a/OpenRA.Mods.Common/Graphics/SelectionBoxRenderable.cs b/OpenRA.Mods.Common/Graphics/SelectionBoxRenderable.cs index f110b523fd..ebdfd5dae8 100644 --- a/OpenRA.Mods.Common/Graphics/SelectionBoxRenderable.cs +++ b/OpenRA.Mods.Common/Graphics/SelectionBoxRenderable.cs @@ -21,7 +21,7 @@ namespace OpenRA.Mods.Common.Graphics readonly Color color; public SelectionBoxRenderable(Actor actor, Color color) - : this(actor.CenterPosition, actor.VisualBounds, color) { } + : this(actor.CenterPosition, actor.SelectionOverlayBounds, color) { } public SelectionBoxRenderable(WPos pos, Rectangle visualBounds, Color color) { diff --git a/OpenRA.Mods.Common/Traits/Buildings/Building.cs b/OpenRA.Mods.Common/Traits/Buildings/Building.cs index cc846960d7..c20dae57aa 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/Building.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/Building.cs @@ -314,7 +314,7 @@ namespace OpenRA.Mods.Common.Traits self.World.ActorMap.AddInfluence(self, this); self.World.ActorMap.AddPosition(self, this); - if (!self.Bounds.Size.IsEmpty) + if (!self.RenderBounds.Size.IsEmpty) self.World.ScreenMap.Add(self); influence.AddInfluence(self, Info.Tiles(self.Location)); @@ -325,7 +325,7 @@ namespace OpenRA.Mods.Common.Traits self.World.ActorMap.RemoveInfluence(self, this); self.World.ActorMap.RemovePosition(self, this); - if (!self.Bounds.Size.IsEmpty) + if (!self.RenderBounds.Size.IsEmpty) self.World.ScreenMap.Remove(self); influence.RemoveInfluence(self, Info.Tiles(self.Location)); diff --git a/OpenRA.Mods.Common/Traits/Immobile.cs b/OpenRA.Mods.Common/Traits/Immobile.cs index 95c327c922..ad42cf145e 100644 --- a/OpenRA.Mods.Common/Traits/Immobile.cs +++ b/OpenRA.Mods.Common/Traits/Immobile.cs @@ -57,7 +57,7 @@ namespace OpenRA.Mods.Common.Traits self.World.ActorMap.AddInfluence(self, this); self.World.ActorMap.AddPosition(self, this); - if (!self.Bounds.Size.IsEmpty) + if (!self.RenderBounds.Size.IsEmpty) self.World.ScreenMap.Add(self); } @@ -66,7 +66,7 @@ namespace OpenRA.Mods.Common.Traits self.World.ActorMap.RemoveInfluence(self, this); self.World.ActorMap.RemovePosition(self, this); - if (!self.Bounds.Size.IsEmpty) + if (!self.RenderBounds.Size.IsEmpty) self.World.ScreenMap.Remove(self); } } diff --git a/OpenRA.Mods.Common/Traits/Render/RenderNameTag.cs b/OpenRA.Mods.Common/Traits/Render/RenderNameTag.cs index 520c44e2c1..6a2a0e99a4 100644 --- a/OpenRA.Mods.Common/Traits/Render/RenderNameTag.cs +++ b/OpenRA.Mods.Common/Traits/Render/RenderNameTag.cs @@ -47,7 +47,7 @@ namespace OpenRA.Mods.Common.Traits.Render public IEnumerable Render(Actor self, WorldRenderer wr) { var pos = wr.ScreenPxPosition(self.CenterPosition); - var bounds = self.Bounds; + var bounds = self.SelectableBounds; bounds.Offset(pos.X, pos.Y); var spaceBuffer = (int)(10 / wr.Viewport.Zoom); var effectPos = wr.ProjectedPosition(new int2(pos.X, bounds.Y - spaceBuffer)); diff --git a/OpenRA.Mods.Common/Traits/Render/SelectionDecorations.cs b/OpenRA.Mods.Common/Traits/Render/SelectionDecorations.cs index 95ae0da122..3216044ca0 100644 --- a/OpenRA.Mods.Common/Traits/Render/SelectionDecorations.cs +++ b/OpenRA.Mods.Common/Traits/Render/SelectionDecorations.cs @@ -22,7 +22,7 @@ namespace OpenRA.Mods.Common.Traits.Render { [PaletteReference] public readonly string Palette = "chrome"; - [Desc("Visual bounds for selection box. If null, it uses AutoSelectionSize.", + [Desc("Bounds for visual selection box. If null, it uses AutoSelectionSize.", "The first two values define the bounds' size, the optional third and fourth", "values specify the position relative to the actors' center. Defaults to selectable bounds.")] public readonly int[] VisualBounds = null; @@ -128,7 +128,7 @@ namespace OpenRA.Mods.Common.Traits.Render if (pipSources.Length == 0) return Enumerable.Empty(); - var b = self.VisualBounds; + var b = self.SelectionOverlayBounds; var pos = wr.ScreenPxPosition(self.CenterPosition); var bl = wr.Viewport.WorldToViewPx(pos + new int2(b.Left, b.Bottom)); var pal = wr.Palette(Info.Palette); @@ -143,7 +143,7 @@ namespace OpenRA.Mods.Common.Traits.Render var pipSize = pipImages.Image.Size.XY.ToInt2(); var pipxyBase = basePosition + new int2(1 - pipSize.X / 2, -(3 + pipSize.Y / 2)); var pipxyOffset = new int2(0, 0); - var width = self.VisualBounds.Width; + var width = self.SelectionOverlayBounds.Width; foreach (var pips in pipSources) { diff --git a/OpenRA.Mods.Common/Traits/Render/WithDecoration.cs b/OpenRA.Mods.Common/Traits/Render/WithDecoration.cs index 9676205a96..afc0ffcdcd 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithDecoration.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithDecoration.cs @@ -99,7 +99,7 @@ namespace OpenRA.Mods.Common.Traits.Render if (!ShouldRender(self) || self.World.FogObscures(self)) return Enumerable.Empty(); - var bounds = self.VisualBounds; + var bounds = self.SelectionOverlayBounds; var halfSize = (0.5f * Anim.Image.Size.XY).ToInt2(); var boundsOffset = new int2(bounds.Left + bounds.Right, bounds.Top + bounds.Bottom) / 2; diff --git a/OpenRA.Mods.Common/Traits/Render/WithSpriteControlGroupDecoration.cs b/OpenRA.Mods.Common/Traits/Render/WithSpriteControlGroupDecoration.cs index 272d6a8f7a..87b732cb45 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithSpriteControlGroupDecoration.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithSpriteControlGroupDecoration.cs @@ -68,7 +68,7 @@ namespace OpenRA.Mods.Common.Traits.Render pipImages.PlayFetchIndex(Info.GroupSequence, () => (int)group); - var bounds = self.VisualBounds; + var bounds = self.SelectionOverlayBounds; var boundsOffset = 0.5f * new float2(bounds.Left + bounds.Right, bounds.Top + bounds.Bottom); if (Info.ReferencePoint.HasFlag(ReferencePoints.Top)) boundsOffset -= new float2(0, 0.5f * bounds.Height); diff --git a/OpenRA.Mods.Common/Traits/Render/WithTextControlGroupDecoration.cs b/OpenRA.Mods.Common/Traits/Render/WithTextControlGroupDecoration.cs index 8b91cdb237..afbae1e0b6 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithTextControlGroupDecoration.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithTextControlGroupDecoration.cs @@ -82,7 +82,7 @@ namespace OpenRA.Mods.Common.Traits.Render if (group == null) yield break; - var bounds = self.VisualBounds; + var bounds = self.SelectionOverlayBounds; var number = group.Value.ToString(); var halfSize = font.Measure(number) / 2; diff --git a/OpenRA.Mods.Common/Traits/Render/WithTextDecoration.cs b/OpenRA.Mods.Common/Traits/Render/WithTextDecoration.cs index 2460378f67..9b1d1a0ff4 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithTextDecoration.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithTextDecoration.cs @@ -95,7 +95,7 @@ namespace OpenRA.Mods.Common.Traits.Render if (!ShouldRender(self) || self.World.FogObscures(self)) return Enumerable.Empty(); - var bounds = self.VisualBounds; + var bounds = self.SelectionOverlayBounds; var halfSize = font.Measure(Info.Text) / 2; var boundsOffset = new int2(bounds.Left + bounds.Right, bounds.Top + bounds.Bottom) / 2;