From 6f5d035e7962297738b831da8390baa50055c818 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Thu, 7 Dec 2017 23:15:07 +0000 Subject: [PATCH] Introduce IMouseBounds and split/rework mouse rectangles. The render bounds for an actor now include the area covered by bibs, shadows, and any other widgets. In many cases this area is much larger than we really want to consider for tooltips and mouse selection. An optional Margin is added to Selectable to support cases like infantry, where we want the mouse area of the actor to be larger than the drawn selection box. --- OpenRA.Game/Actor.cs | 14 ++ OpenRA.Game/Graphics/WorldRenderer.cs | 14 +- OpenRA.Game/Orders/GenericSelectTarget.cs | 3 +- OpenRA.Game/Orders/UnitOrderGenerator.cs | 15 +- OpenRA.Game/SelectableExts.cs | 20 +- OpenRA.Game/Traits/Player/FrozenActorLayer.cs | 5 +- OpenRA.Game/Traits/Selectable.cs | 24 ++- OpenRA.Game/Traits/TraitsInterfaces.cs | 4 + OpenRA.Game/Traits/World/ScreenMap.cs | 189 +++++++++++++----- OpenRA.Mods.Cnc/Traits/Minelayer.cs | 3 +- .../Traits/Render/WithVoxelUnloadBody.cs | 18 +- .../Traits/Render/WithVoxelWalkerBody.cs | 19 +- .../Orders/GlobalButtonOrderGenerator.cs | 3 +- .../Orders/GuardOrderGenerator.cs | 3 +- .../Orders/RepairOrderGenerator.cs | 3 +- OpenRA.Mods.Common/Traits/AttackMove.cs | 3 +- .../Traits/Modifiers/FrozenUnderFog.cs | 3 + .../Traits/Render/AutoRenderSize.cs | 23 ++- .../Traits/Render/CustomRenderSize.cs | 19 +- .../Traits/Render/RenderSprites.cs | 16 +- .../Traits/Render/RenderVoxels.cs | 19 +- .../Traits/Render/WithSpriteBody.cs | 11 +- .../Traits/Render/WithVoxelBody.cs | 17 +- .../Widgets/ViewportControllerWidget.cs | 6 +- .../WorldInteractionControllerWidget.cs | 12 +- 25 files changed, 349 insertions(+), 117 deletions(-) diff --git a/OpenRA.Game/Actor.cs b/OpenRA.Game/Actor.cs index d92e2f6998..fcfe5d1530 100644 --- a/OpenRA.Game/Actor.cs +++ b/OpenRA.Game/Actor.cs @@ -77,6 +77,7 @@ namespace OpenRA readonly IHealth health; readonly IRenderModifier[] renderModifiers; readonly IRender[] renders; + readonly IMouseBounds[] mouseBounds; readonly IDisable[] disables; readonly IVisibilityModifier[] visibilityModifiers; readonly IDefaultVisibility defaultVisibility; @@ -125,6 +126,7 @@ namespace OpenRA health = TraitOrDefault(); renderModifiers = TraitsImplementing().ToArray(); renders = TraitsImplementing().ToArray(); + mouseBounds = TraitsImplementing().ToArray(); disables = TraitsImplementing().ToArray(); visibilityModifiers = TraitsImplementing().ToArray(); defaultVisibility = Trait(); @@ -221,6 +223,18 @@ namespace OpenRA yield return r; } + public Rectangle MouseBounds(WorldRenderer wr) + { + foreach (var mb in mouseBounds) + { + var bounds = mb.MouseoverBounds(this, wr); + if (!bounds.IsEmpty) + return bounds; + } + + return Rectangle.Empty; + } + public void QueueActivity(bool queued, Activity nextActivity) { if (!queued) diff --git a/OpenRA.Game/Graphics/WorldRenderer.cs b/OpenRA.Game/Graphics/WorldRenderer.cs index d88b3c78cb..36f1f2f495 100644 --- a/OpenRA.Game/Graphics/WorldRenderer.cs +++ b/OpenRA.Game/Graphics/WorldRenderer.cs @@ -102,7 +102,7 @@ namespace OpenRA.Graphics List GenerateRenderables() { - var actors = World.ScreenMap.ActorsInBox(Viewport.TopLeft, Viewport.BottomRight).Append(World.WorldActor); + var actors = World.ScreenMap.RenderableActorsInBox(Viewport.TopLeft, Viewport.BottomRight).Append(World.WorldActor); if (World.RenderPlayer != null) actors = actors.Append(World.RenderPlayer.PlayerActor); @@ -114,7 +114,7 @@ namespace OpenRA.Graphics worldRenderables = worldRenderables.Concat(World.UnpartitionedEffects.SelectMany(e => e.Render(this))); // Partitioned, currently on-screen effects - var effectRenderables = World.ScreenMap.EffectsInBox(Viewport.TopLeft, Viewport.BottomRight); + var effectRenderables = World.ScreenMap.RenderableEffectsInBox(Viewport.TopLeft, Viewport.BottomRight); worldRenderables = worldRenderables.Concat(effectRenderables.SelectMany(e => e.Render(this))); worldRenderables = worldRenderables.OrderBy(RenderableScreenZPositionComparisonKey); @@ -213,12 +213,20 @@ namespace OpenRA.Graphics } if (debugVis.Value != null && debugVis.Value.ScreenMap) - foreach (var r in World.ScreenMap.ItemBounds(World.RenderPlayer)) + { + foreach (var r in World.ScreenMap.RenderBounds(World.RenderPlayer)) Game.Renderer.WorldRgbaColorRenderer.DrawRect( new float3(r.Left, r.Top, r.Bottom), new float3(r.Right, r.Bottom, r.Bottom), 1 / Viewport.Zoom, Color.MediumSpringGreen); + foreach (var r in World.ScreenMap.MouseBounds(World.RenderPlayer)) + Game.Renderer.WorldRgbaColorRenderer.DrawRect( + new float3(r.Left, r.Top, r.Bottom), + new float3(r.Right, r.Bottom, r.Bottom), + 1 / Viewport.Zoom, Color.OrangeRed); + } + Game.Renderer.Flush(); } diff --git a/OpenRA.Game/Orders/GenericSelectTarget.cs b/OpenRA.Game/Orders/GenericSelectTarget.cs index 8d311b1f6d..1e9411d308 100644 --- a/OpenRA.Game/Orders/GenericSelectTarget.cs +++ b/OpenRA.Game/Orders/GenericSelectTarget.cs @@ -10,6 +10,7 @@ #endregion using System.Collections.Generic; +using OpenRA.Graphics; using OpenRA.Traits; namespace OpenRA.Orders @@ -62,7 +63,7 @@ namespace OpenRA.Orders return world.Map.Contains(cell) ? Cursor : "generic-blocked"; } - public override bool InputOverridesSelection(World world, int2 xy, MouseInput mi) + public override bool InputOverridesSelection(WorldRenderer wr, World world, int2 xy, MouseInput mi) { // Custom order generators always override selection return true; diff --git a/OpenRA.Game/Orders/UnitOrderGenerator.cs b/OpenRA.Game/Orders/UnitOrderGenerator.cs index e18fef5658..0390a3f67a 100644 --- a/OpenRA.Game/Orders/UnitOrderGenerator.cs +++ b/OpenRA.Game/Orders/UnitOrderGenerator.cs @@ -12,6 +12,7 @@ using System.Collections.Generic; using System.Linq; using OpenRA.Graphics; +using OpenRA.Primitives; using OpenRA.Traits; namespace OpenRA.Orders @@ -20,14 +21,14 @@ namespace OpenRA.Orders { static Target TargetForInput(World world, CPos cell, int2 worldPixel, MouseInput mi) { - var actor = world.ScreenMap.ActorsAt(mi) - .Where(a => a.Info.HasTraitInfo() && !world.FogObscures(a)) + var actor = world.ScreenMap.ActorsAtMouse(mi) + .Where(a => a.Actor.Info.HasTraitInfo() && !world.FogObscures(a.Actor)) .WithHighestSelectionPriority(worldPixel); if (actor != null) return Target.FromActor(actor); - var frozen = world.ScreenMap.FrozenActorsAt(world.RenderPlayer, mi) + var frozen = world.ScreenMap.FrozenActorsAtMouse(world.RenderPlayer, mi) .Where(a => a.Info.HasTraitInfo() && a.Visible && a.HasRenderables) .WithHighestSelectionPriority(worldPixel); @@ -78,16 +79,18 @@ namespace OpenRA.Orders } // Used for classic mouse orders, determines whether or not action at xy is move or select - public virtual bool InputOverridesSelection(World world, int2 xy, MouseInput mi) + public virtual bool InputOverridesSelection(WorldRenderer wr, World world, int2 xy, MouseInput mi) { - var actor = world.ScreenMap.ActorsAt(xy).WithHighestSelectionPriority(xy); + var actor = world.ScreenMap.ActorsAtMouse(xy).WithHighestSelectionPriority(xy); if (actor == null) return true; var target = Target.FromActor(actor); var cell = world.Map.CellContaining(target.CenterPosition); var actorsAt = world.ActorMap.GetActorsAt(cell).ToList(); - var underCursor = world.Selection.Actors.WithHighestSelectionPriority(xy); + var underCursor = world.Selection.Actors + .Select(a => new ActorBoundsPair(a, a.MouseBounds(wr))) + .WithHighestSelectionPriority(xy); var o = OrderForUnit(underCursor, target, actorsAt, cell, mi); if (o != null) diff --git a/OpenRA.Game/SelectableExts.cs b/OpenRA.Game/SelectableExts.cs index b78b5be298..172a9737b1 100644 --- a/OpenRA.Game/SelectableExts.cs +++ b/OpenRA.Game/SelectableExts.cs @@ -13,6 +13,8 @@ using System; using System.Collections.Generic; using System.Drawing; using System.Linq; +using OpenRA.Graphics; +using OpenRA.Primitives; namespace OpenRA.Traits { @@ -45,21 +47,29 @@ namespace OpenRA.Traits } } - public static Actor WithHighestSelectionPriority(this IEnumerable actors, int2 selectionPixel) + public static Actor WithHighestSelectionPriority(this IEnumerable actors, int2 selectionPixel) { - return actors.MaxByOrDefault(a => CalculateActorSelectionPriority(a.Info, a.SelectableBounds, selectionPixel)); + if (!actors.Any()) + return null; + + return actors.MaxBy(a => CalculateActorSelectionPriority(a.Actor.Info, a.Bounds, selectionPixel)).Actor; } public static FrozenActor WithHighestSelectionPriority(this IEnumerable actors, int2 selectionPixel) { - return actors.MaxByOrDefault(a => CalculateActorSelectionPriority(a.Info, a.SelectableBounds, selectionPixel)); + return actors.MaxByOrDefault(a => CalculateActorSelectionPriority(a.Info, a.MouseBounds, selectionPixel)); } static long CalculateActorSelectionPriority(ActorInfo info, Rectangle bounds, int2 selectionPixel) { - var centerPixel = new int2(bounds.X, bounds.Y); - var pixelDistance = (centerPixel - selectionPixel).Length; + if (bounds.IsEmpty) + return info.SelectionPriority(); + var centerPixel = new int2( + bounds.Left + bounds.Size.Width / 2, + bounds.Top + bounds.Size.Height / 2); + + var pixelDistance = (centerPixel - selectionPixel).Length; return ((long)-pixelDistance << 32) + info.SelectionPriority(); } diff --git a/OpenRA.Game/Traits/Player/FrozenActorLayer.cs b/OpenRA.Game/Traits/Player/FrozenActorLayer.cs index 14da553a33..8fbb7673cc 100644 --- a/OpenRA.Game/Traits/Player/FrozenActorLayer.cs +++ b/OpenRA.Game/Traits/Player/FrozenActorLayer.cs @@ -52,6 +52,9 @@ namespace OpenRA.Traits public IRenderable[] Renderables = NoRenderables; public Rectangle[] ScreenBounds = NoBounds; + // TODO: Replace this with an int2[] polygon + public Rectangle MouseBounds = Rectangle.Empty; + static readonly IRenderable[] NoRenderables = new IRenderable[0]; static readonly Rectangle[] NoBounds = new Rectangle[0]; @@ -298,7 +301,7 @@ namespace OpenRA.Traits public virtual IEnumerable Render(Actor self, WorldRenderer wr) { - return world.ScreenMap.FrozenActorsInBox(owner, wr.Viewport.TopLeft, wr.Viewport.BottomRight) + return world.ScreenMap.RenderableFrozenActorsInBox(owner, wr.Viewport.TopLeft, wr.Viewport.BottomRight) .Where(f => f.Visible) .SelectMany(ff => ff.Render(wr)); } diff --git a/OpenRA.Game/Traits/Selectable.cs b/OpenRA.Game/Traits/Selectable.cs index 42ed086a75..8b8c5f685c 100644 --- a/OpenRA.Game/Traits/Selectable.cs +++ b/OpenRA.Game/Traits/Selectable.cs @@ -9,6 +9,10 @@ */ #endregion +using System.Collections.Generic; +using System.Drawing; +using OpenRA.Graphics; + namespace OpenRA.Traits { [Desc("This actor is selectable. Defines bounds of selectable area, selection class and selection priority.")] @@ -19,6 +23,9 @@ namespace OpenRA.Traits [Desc("Bounds for the selectable area.")] public readonly int[] Bounds = null; + [Desc("Area outside the visible selection box that is enabled for selection")] + public readonly int Margin = 0; + [Desc("All units having the same selection class specified will be selected with select-by-type commands (e.g. double-click). " + "Defaults to the actor name when not defined or inherited.")] public readonly string Class = null; @@ -28,7 +35,7 @@ namespace OpenRA.Traits public object Create(ActorInitializer init) { return new Selectable(init.Self, this); } } - public class Selectable + public class Selectable : IMouseBounds { public readonly string Class = null; @@ -39,5 +46,20 @@ namespace OpenRA.Traits Class = string.IsNullOrEmpty(info.Class) ? self.Info.Name : info.Class; Info = info; } + + Rectangle IMouseBounds.MouseoverBounds(Actor self, WorldRenderer wr) + { + if (Info.Bounds == null) + return Rectangle.Empty; + + var size = new int2(Info.Bounds[0], Info.Bounds[1]); + + var offset = -size / 2 - new int2(Info.Margin, Info.Margin); + if (Info.Bounds.Length > 2) + offset += new int2(Info.Bounds[2], Info.Bounds[3]); + + var xy = wr.ScreenPxPosition(self.CenterPosition) + offset; + return new Rectangle(xy.X, xy.Y, size.X + 2 * Info.Margin, size.Y + 2 * Info.Margin); + } } } diff --git a/OpenRA.Game/Traits/TraitsInterfaces.cs b/OpenRA.Game/Traits/TraitsInterfaces.cs index f66fa09d80..32f059159b 100644 --- a/OpenRA.Game/Traits/TraitsInterfaces.cs +++ b/OpenRA.Game/Traits/TraitsInterfaces.cs @@ -105,6 +105,10 @@ namespace OpenRA.Traits public interface IAutoSelectionSizeInfo : ITraitInfoInterface { } public interface IAutoSelectionSize { int2 SelectionSize(Actor self); } + // TODO: Replace Rectangle with an int2[] polygon + public interface IMouseBounds { Rectangle MouseoverBounds(Actor self, WorldRenderer wr); } + public interface IAutoMouseBounds { Rectangle AutoMouseoverBounds(Actor self, WorldRenderer wr); } + public interface IAutoRenderSizeInfo : ITraitInfoInterface { } public interface IAutoRenderSize { int2 RenderSize(Actor self); } diff --git a/OpenRA.Game/Traits/World/ScreenMap.cs b/OpenRA.Game/Traits/World/ScreenMap.cs index dc439fe7dd..2dc7393a7e 100644 --- a/OpenRA.Game/Traits/World/ScreenMap.cs +++ b/OpenRA.Game/Traits/World/ScreenMap.cs @@ -19,6 +19,26 @@ using OpenRA.Primitives; namespace OpenRA.Traits { + public struct ActorBoundsPair : IEquatable + { + public readonly Actor Actor; + + // TODO: Replace this with an int2[] polygon + public readonly Rectangle Bounds; + + public ActorBoundsPair(Actor actor, Rectangle bounds) { Actor = actor; Bounds = bounds; } + + public static bool operator ==(ActorBoundsPair me, ActorBoundsPair other) { return me.Actor == other.Actor && Equals(me.Bounds, other.Bounds); } + public static bool operator !=(ActorBoundsPair me, ActorBoundsPair other) { return !(me == other); } + + public override int GetHashCode() { return Actor.GetHashCode() ^ Bounds.GetHashCode(); } + + public bool Equals(ActorBoundsPair other) { return this == other; } + public override bool Equals(object obj) { return obj is ActorBoundsPair && Equals((ActorBoundsPair)obj); } + + public override string ToString() { return "{0}->{1}".F(Actor.Info.Name, Bounds.GetType().Name); } + } + public class ScreenMapInfo : ITraitInfo { [Desc("Size of partition bins (world pixels)")] @@ -32,9 +52,14 @@ namespace OpenRA.Traits static readonly IEnumerable NoFrozenActors = new FrozenActor[0]; readonly Func frozenActorIsValid = fa => fa.IsValid; readonly Func actorIsInWorld = a => a.IsInWorld; - readonly Cache> partitionedFrozenActors; - readonly SpatiallyPartitioned partitionedActors; - readonly SpatiallyPartitioned partitionedEffects; + readonly Func selectActorAndBounds; + readonly Cache> partitionedMouseFrozenActors; + readonly SpatiallyPartitioned partitionedMouseActors; + readonly Dictionary partitionedMouseActorBounds = new Dictionary(); + + readonly Cache> partitionedRenderableFrozenActors; + readonly SpatiallyPartitioned partitionedRenderableActors; + readonly SpatiallyPartitioned partitionedRenderableEffects; // Updates are done in one pass to ensure all bound changes have been applied readonly HashSet addOrUpdateActors = new HashSet(); @@ -49,14 +74,19 @@ namespace OpenRA.Traits var size = world.Map.Grid.TileSize; var width = world.Map.MapSize.X * size.Width; var height = world.Map.MapSize.Y * size.Height; - partitionedFrozenActors = new Cache>( + + partitionedMouseFrozenActors = new Cache>( _ => new SpatiallyPartitioned(width, height, info.BinSize)); + partitionedMouseActors = new SpatiallyPartitioned(width, height, info.BinSize); + selectActorAndBounds = a => partitionedMouseActorBounds[a]; + + partitionedRenderableFrozenActors = new Cache>( + _ => new SpatiallyPartitioned(width, height, info.BinSize)); + partitionedRenderableActors = new SpatiallyPartitioned(width, height, info.BinSize); + partitionedRenderableEffects = new SpatiallyPartitioned(width, height, info.BinSize); addOrUpdateFrozenActors = new Cache>(_ => new HashSet()); removeFrozenActors = new Cache>(_ => new HashSet()); - - partitionedActors = new SpatiallyPartitioned(width, height, info.BinSize); - partitionedEffects = new SpatiallyPartitioned(width, height, info.BinSize); } public void WorldLoaded(World w, WorldRenderer wr) { worldRenderer = wr; } @@ -94,7 +124,7 @@ namespace OpenRA.Traits var screenHeight = Math.Abs(size.Height); var screenBounds = new Rectangle(screenPos.X - screenWidth / 2, screenPos.Y - screenHeight / 2, screenWidth, screenHeight); if (ValidBounds(screenBounds)) - partitionedEffects.Add(effect, screenBounds); + partitionedRenderableEffects.Add(effect, screenBounds); } public void Add(IEffect effect, WPos position, Sprite sprite) @@ -117,34 +147,41 @@ namespace OpenRA.Traits public void Remove(IEffect effect) { - partitionedEffects.Remove(effect); + partitionedRenderableEffects.Remove(effect); } - bool ValidBounds(Rectangle bounds) + static bool ValidBounds(Rectangle bounds) { return bounds.Width > 0 && bounds.Height > 0; } - public IEnumerable FrozenActorsAt(Player viewer, int2 worldPx) + public IEnumerable FrozenActorsAtMouse(Player viewer, int2 worldPx) { if (viewer == null) return NoFrozenActors; - return partitionedFrozenActors[viewer].At(worldPx).Where(frozenActorIsValid); + + return partitionedMouseFrozenActors[viewer] + .At(worldPx) + .Where(frozenActorIsValid) + .Where(x => x.MouseBounds.Contains(worldPx)); } - public IEnumerable FrozenActorsAt(Player viewer, MouseInput mi) + public IEnumerable FrozenActorsAtMouse(Player viewer, MouseInput mi) { - return FrozenActorsAt(viewer, worldRenderer.Viewport.ViewToWorldPx(mi.Location)); + return FrozenActorsAtMouse(viewer, worldRenderer.Viewport.ViewToWorldPx(mi.Location)); } - public IEnumerable ActorsAt(int2 worldPx) + public IEnumerable ActorsAtMouse(int2 worldPx) { - return partitionedActors.At(worldPx).Where(actorIsInWorld); + return partitionedMouseActors.At(worldPx) + .Where(actorIsInWorld) + .Select(selectActorAndBounds) + .Where(x => x.Bounds.Contains(worldPx)); } - public IEnumerable ActorsAt(MouseInput mi) + public IEnumerable ActorsAtMouse(MouseInput mi) { - return ActorsAt(worldRenderer.Viewport.ViewToWorldPx(mi.Location)); + return ActorsAtMouse(worldRenderer.Viewport.ViewToWorldPx(mi.Location)); } static Rectangle RectWithCorners(int2 a, int2 b) @@ -152,36 +189,35 @@ namespace OpenRA.Traits return Rectangle.FromLTRB(Math.Min(a.X, b.X), Math.Min(a.Y, b.Y), Math.Max(a.X, b.X), Math.Max(a.Y, b.Y)); } - public IEnumerable ActorsInBox(int2 a, int2 b) + public IEnumerable ActorsInMouseBox(int2 a, int2 b) { - return ActorsInBox(RectWithCorners(a, b)); + return ActorsInMouseBox(RectWithCorners(a, b)); } - public IEnumerable EffectsInBox(int2 a, int2 b) + public IEnumerable ActorsInMouseBox(Rectangle r) { - return partitionedEffects.InBox(RectWithCorners(a, b)); + return partitionedMouseActors.InBox(r) + .Where(actorIsInWorld) + .Select(selectActorAndBounds) + .Where(x => r.IntersectsWith(x.Bounds)); } - public IEnumerable ActorsInBox(Rectangle r) + public IEnumerable RenderableActorsInBox(int2 a, int2 b) { - return partitionedActors.InBox(r).Where(actorIsInWorld); + return partitionedRenderableActors.InBox(RectWithCorners(a, b)).Where(actorIsInWorld); } - public IEnumerable FrozenActorsInBox(Player p, int2 a, int2 b) + public IEnumerable RenderableEffectsInBox(int2 a, int2 b) { - return FrozenActorsInBox(p, RectWithCorners(a, b)); + return partitionedRenderableEffects.InBox(RectWithCorners(a, b)); } - public IEnumerable EffectsInBox(Rectangle r) - { - return partitionedEffects.InBox(r); - } - - public IEnumerable FrozenActorsInBox(Player p, Rectangle r) + public IEnumerable RenderableFrozenActorsInBox(Player p, int2 a, int2 b) { if (p == null) return NoFrozenActors; - return partitionedFrozenActors[p].InBox(r).Where(frozenActorIsValid); + + return partitionedRenderableFrozenActors[p].InBox(RectWithCorners(a, b)).Where(frozenActorIsValid); } Rectangle AggregateBounds(IEnumerable screenBounds) @@ -196,24 +232,54 @@ namespace OpenRA.Traits return bounds; } + Rectangle AggregateBounds(IEnumerable vertices) + { + if (!vertices.Any()) + return Rectangle.Empty; + + var first = vertices.First(); + var rect = new Rectangle(first.X, first.Y, 0, 0); + foreach (var v in vertices.Skip(1)) + rect = Rectangle.Union(rect, new Rectangle(v.X, v.Y, 0, 0)); + + return rect; + } + public void Tick() { foreach (var a in addOrUpdateActors) { - var bounds = AggregateBounds(a.ScreenBounds(worldRenderer)); - if (!bounds.Size.IsEmpty) + var mouseBounds = a.MouseBounds(worldRenderer); + if (!mouseBounds.Size.IsEmpty) { - if (partitionedActors.Contains(a)) - partitionedActors.Update(a, bounds); + if (partitionedMouseActors.Contains(a)) + partitionedMouseActors.Update(a, mouseBounds); else - partitionedActors.Add(a, bounds); + partitionedMouseActors.Add(a, mouseBounds); + + partitionedMouseActorBounds[a] = new ActorBoundsPair(a, mouseBounds); } else - partitionedActors.Remove(a); + partitionedMouseActors.Remove(a); + + var screenBounds = AggregateBounds(a.ScreenBounds(worldRenderer)); + if (!screenBounds.Size.IsEmpty) + { + if (partitionedRenderableActors.Contains(a)) + partitionedRenderableActors.Update(a, screenBounds); + else + partitionedRenderableActors.Add(a, screenBounds); + } + else + partitionedRenderableActors.Remove(a); } foreach (var a in removeActors) - partitionedActors.Remove(a); + { + partitionedMouseActors.Remove(a); + partitionedMouseActorBounds.Remove(a); + partitionedRenderableActors.Remove(a); + } addOrUpdateActors.Clear(); removeActors.Clear(); @@ -222,16 +288,27 @@ namespace OpenRA.Traits { foreach (var fa in kv.Value) { - var bounds = AggregateBounds(fa.ScreenBounds); - if (!bounds.Size.IsEmpty) + var mouseBounds = fa.MouseBounds; + if (!mouseBounds.Size.IsEmpty) { - if (partitionedFrozenActors[kv.Key].Contains(fa)) - partitionedFrozenActors[kv.Key].Update(fa, bounds); + if (partitionedMouseFrozenActors[kv.Key].Contains(fa)) + partitionedMouseFrozenActors[kv.Key].Update(fa, mouseBounds); else - partitionedFrozenActors[kv.Key].Add(fa, bounds); + partitionedMouseFrozenActors[kv.Key].Add(fa, mouseBounds); } else - partitionedFrozenActors[kv.Key].Remove(fa); + partitionedMouseFrozenActors[kv.Key].Remove(fa); + + var screenBounds = AggregateBounds(fa.ScreenBounds); + if (!screenBounds.Size.IsEmpty) + { + if (partitionedRenderableFrozenActors[kv.Key].Contains(fa)) + partitionedRenderableFrozenActors[kv.Key].Update(fa, screenBounds); + else + partitionedRenderableFrozenActors[kv.Key].Add(fa, screenBounds); + } + else + partitionedRenderableFrozenActors[kv.Key].Remove(fa); } kv.Value.Clear(); @@ -240,18 +317,28 @@ namespace OpenRA.Traits foreach (var kv in removeFrozenActors) { foreach (var fa in kv.Value) - partitionedFrozenActors[kv.Key].Remove(fa); + { + partitionedMouseFrozenActors[kv.Key].Remove(fa); + partitionedRenderableFrozenActors[kv.Key].Remove(fa); + } kv.Value.Clear(); } } - public IEnumerable ItemBounds(Player viewer) + public IEnumerable RenderBounds(Player viewer) { - var bounds = partitionedActors.ItemBounds - .Concat(partitionedEffects.ItemBounds); + var bounds = partitionedRenderableActors.ItemBounds + .Concat(partitionedRenderableEffects.ItemBounds); - return viewer != null ? bounds.Concat(partitionedFrozenActors[viewer].ItemBounds) : bounds; + return viewer != null ? bounds.Concat(partitionedRenderableFrozenActors[viewer].ItemBounds) : bounds; + } + + public IEnumerable MouseBounds(Player viewer) + { + var bounds = partitionedMouseActors.ItemBounds; + + return viewer != null ? bounds.Concat(partitionedMouseFrozenActors[viewer].ItemBounds) : bounds; } } } diff --git a/OpenRA.Mods.Cnc/Traits/Minelayer.cs b/OpenRA.Mods.Cnc/Traits/Minelayer.cs index 7e49e1af74..e3a98b0d77 100644 --- a/OpenRA.Mods.Cnc/Traits/Minelayer.cs +++ b/OpenRA.Mods.Cnc/Traits/Minelayer.cs @@ -186,7 +186,8 @@ namespace OpenRA.Mods.Cnc.Traits yield break; } - var underCursor = world.ScreenMap.ActorsAt(mi) + var underCursor = world.ScreenMap.ActorsAtMouse(mi) + .Select(a => a.Actor) .Where(a => !world.FogObscures(a)) .MaxByOrDefault(a => a.Info.HasTraitInfo() ? a.Info.TraitInfo().Priority : int.MinValue); diff --git a/OpenRA.Mods.Cnc/Traits/Render/WithVoxelUnloadBody.cs b/OpenRA.Mods.Cnc/Traits/Render/WithVoxelUnloadBody.cs index 5bde4b9898..9ce1ad7f69 100644 --- a/OpenRA.Mods.Cnc/Traits/Render/WithVoxelUnloadBody.cs +++ b/OpenRA.Mods.Cnc/Traits/Render/WithVoxelUnloadBody.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Drawing; using System.Linq; using OpenRA.Graphics; using OpenRA.Mods.Common.Graphics; @@ -45,22 +46,26 @@ namespace OpenRA.Mods.Cnc.Traits.Render } } - public class WithVoxelUnloadBody : IAutoSelectionSize, IAutoRenderSize + public class WithVoxelUnloadBody : IAutoSelectionSize, IAutoRenderSize, IAutoMouseBounds { public bool Docked; readonly int2 size; + readonly ModelAnimation modelAnimation; + readonly RenderVoxels rv; public WithVoxelUnloadBody(Actor self, WithVoxelUnloadBodyInfo info) { var body = self.Trait(); - var rv = self.Trait(); + rv = self.Trait(); var idleModel = self.World.ModelCache.GetModelSequence(rv.Image, info.IdleSequence); - rv.Add(new ModelAnimation(idleModel, () => WVec.Zero, + modelAnimation = new ModelAnimation(idleModel, () => WVec.Zero, () => new[] { body.QuantizeOrientation(self, self.Orientation) }, () => Docked, - () => 0, info.ShowShadow)); + () => 0, info.ShowShadow); + + rv.Add(modelAnimation); // Selection size var rvi = self.Info.TraitInfo(); @@ -76,5 +81,10 @@ namespace OpenRA.Mods.Cnc.Traits.Render int2 IAutoSelectionSize.SelectionSize(Actor self) { return size; } int2 IAutoRenderSize.RenderSize(Actor self) { return size; } + + Rectangle IAutoMouseBounds.AutoMouseoverBounds(Actor self, WorldRenderer wr) + { + return modelAnimation.ScreenBounds(self.CenterPosition, wr, rv.Info.Scale); + } } } diff --git a/OpenRA.Mods.Cnc/Traits/Render/WithVoxelWalkerBody.cs b/OpenRA.Mods.Cnc/Traits/Render/WithVoxelWalkerBody.cs index e047246778..26ccbc381c 100644 --- a/OpenRA.Mods.Cnc/Traits/Render/WithVoxelWalkerBody.cs +++ b/OpenRA.Mods.Cnc/Traits/Render/WithVoxelWalkerBody.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Drawing; using System.Linq; using OpenRA.Graphics; using OpenRA.Mods.Common; @@ -47,12 +48,15 @@ namespace OpenRA.Mods.Cnc.Traits.Render } } - public class WithVoxelWalkerBody : IAutoSelectionSize, ITick, IActorPreviewInitModifier, IAutoRenderSize + public class WithVoxelWalkerBody : IAutoSelectionSize, ITick, IActorPreviewInitModifier, IAutoRenderSize, IAutoMouseBounds { readonly WithVoxelWalkerBodyInfo info; readonly IMove movement; readonly IFacing facing; readonly int2 size; + readonly ModelAnimation modelAnimation; + readonly RenderVoxels rv; + int oldFacing; uint tick, frame, frames; @@ -63,13 +67,15 @@ namespace OpenRA.Mods.Cnc.Traits.Render facing = self.Trait(); var body = self.Trait(); - var rv = self.Trait(); + rv = self.Trait(); var model = self.World.ModelCache.GetModelSequence(rv.Image, info.Sequence); frames = model.Frames; - rv.Add(new ModelAnimation(model, () => WVec.Zero, + modelAnimation = new ModelAnimation(model, () => WVec.Zero, () => new[] { body.QuantizeOrientation(self, self.Orientation) }, - () => false, () => frame, info.ShowShadow)); + () => false, () => frame, info.ShowShadow); + + rv.Add(modelAnimation); // Selection size var rvi = self.Info.TraitInfo(); @@ -98,6 +104,11 @@ namespace OpenRA.Mods.Cnc.Traits.Render { inits.Add(new BodyAnimationFrameInit(frame)); } + + Rectangle IAutoMouseBounds.AutoMouseoverBounds(Actor self, WorldRenderer wr) + { + return modelAnimation.ScreenBounds(self.CenterPosition, wr, rv.Info.Scale); + } } public class BodyAnimationFrameInit : IActorInit diff --git a/OpenRA.Mods.Common/Orders/GlobalButtonOrderGenerator.cs b/OpenRA.Mods.Common/Orders/GlobalButtonOrderGenerator.cs index 1e782c47f1..6b8f79c4a6 100644 --- a/OpenRA.Mods.Common/Orders/GlobalButtonOrderGenerator.cs +++ b/OpenRA.Mods.Common/Orders/GlobalButtonOrderGenerator.cs @@ -37,7 +37,8 @@ namespace OpenRA.Mods.Common.Orders { if (mi.Button == MouseButton.Left) { - var underCursor = world.ScreenMap.ActorsAt(mi) + var underCursor = world.ScreenMap.ActorsAtMouse(mi) + .Select(a => a.Actor) .FirstOrDefault(a => a.Owner == world.LocalPlayer && a.TraitsImplementing() .Any(Exts.IsTraitEnabled)); diff --git a/OpenRA.Mods.Common/Orders/GuardOrderGenerator.cs b/OpenRA.Mods.Common/Orders/GuardOrderGenerator.cs index 1898f6a3f4..99ebe50273 100644 --- a/OpenRA.Mods.Common/Orders/GuardOrderGenerator.cs +++ b/OpenRA.Mods.Common/Orders/GuardOrderGenerator.cs @@ -59,7 +59,8 @@ namespace OpenRA.Mods.Common.Orders static IEnumerable FriendlyGuardableUnits(World world, MouseInput mi) { - return world.ScreenMap.ActorsAt(mi) + return world.ScreenMap.ActorsAtMouse(mi) + .Select(a => a.Actor) .Where(a => !a.IsDead && a.AppearsFriendlyTo(world.LocalPlayer.PlayerActor) && a.Info.HasTraitInfo() && diff --git a/OpenRA.Mods.Common/Orders/RepairOrderGenerator.cs b/OpenRA.Mods.Common/Orders/RepairOrderGenerator.cs index f20645ce54..98c050de66 100644 --- a/OpenRA.Mods.Common/Orders/RepairOrderGenerator.cs +++ b/OpenRA.Mods.Common/Orders/RepairOrderGenerator.cs @@ -32,7 +32,8 @@ namespace OpenRA.Mods.Common.Orders if (mi.Button != MouseButton.Left) yield break; - var underCursor = world.ScreenMap.ActorsAt(mi) + var underCursor = world.ScreenMap.ActorsAtMouse(mi) + .Select(a => a.Actor) .FirstOrDefault(a => a.AppearsFriendlyTo(world.LocalPlayer.PlayerActor) && !world.FogObscures(a)); if (underCursor == null) diff --git a/OpenRA.Mods.Common/Traits/AttackMove.cs b/OpenRA.Mods.Common/Traits/AttackMove.cs index 8330e3dc84..62a1bbb936 100644 --- a/OpenRA.Mods.Common/Traits/AttackMove.cs +++ b/OpenRA.Mods.Common/Traits/AttackMove.cs @@ -12,6 +12,7 @@ using System.Collections.Generic; using System.Drawing; using System.Linq; +using OpenRA.Graphics; using OpenRA.Mods.Common.Activities; using OpenRA.Orders; using OpenRA.Traits; @@ -178,7 +179,7 @@ namespace OpenRA.Mods.Common.Traits return prefix + "-blocked"; } - public override bool InputOverridesSelection(World world, int2 xy, MouseInput mi) + public override bool InputOverridesSelection(WorldRenderer wr, World world, int2 xy, MouseInput mi) { // Custom order generators always override selection return true; diff --git a/OpenRA.Mods.Common/Traits/Modifiers/FrozenUnderFog.cs b/OpenRA.Mods.Common/Traits/Modifiers/FrozenUnderFog.cs index 4c85716329..6998484edc 100644 --- a/OpenRA.Mods.Common/Traits/Modifiers/FrozenUnderFog.cs +++ b/OpenRA.Mods.Common/Traits/Modifiers/FrozenUnderFog.cs @@ -128,6 +128,7 @@ namespace OpenRA.Mods.Common.Traits { IRenderable[] renderables = null; Rectangle[] bounds = null; + Rectangle mouseBounds = Rectangle.Empty; for (var playerIndex = 0; playerIndex < frozenStates.Count; playerIndex++) { var frozen = frozenStates[playerIndex].FrozenActor; @@ -139,6 +140,7 @@ namespace OpenRA.Mods.Common.Traits isRendering = true; renderables = self.Render(wr).ToArray(); bounds = self.ScreenBounds(wr).ToArray(); + mouseBounds = self.MouseBounds(wr); isRendering = false; } @@ -146,6 +148,7 @@ namespace OpenRA.Mods.Common.Traits frozen.NeedRenderables = false; frozen.Renderables = renderables; frozen.ScreenBounds = bounds; + frozen.MouseBounds = mouseBounds; self.World.ScreenMap.AddOrUpdate(self.World.Players[playerIndex], frozen); } } diff --git a/OpenRA.Mods.Common/Traits/Render/AutoRenderSize.cs b/OpenRA.Mods.Common/Traits/Render/AutoRenderSize.cs index d04750fd11..6959a8016f 100644 --- a/OpenRA.Mods.Common/Traits/Render/AutoRenderSize.cs +++ b/OpenRA.Mods.Common/Traits/Render/AutoRenderSize.cs @@ -9,6 +9,10 @@ */ #endregion +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using OpenRA.Graphics; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits.Render @@ -16,17 +20,28 @@ namespace OpenRA.Mods.Common.Traits.Render [Desc("Automatically calculates the screen map boundaries from the sprite size.")] public class AutoRenderSizeInfo : ITraitInfo, Requires, IAutoRenderSizeInfo { - public object Create(ActorInitializer init) { return new AutoRenderSize(this); } + public object Create(ActorInitializer init) { return new AutoRenderSize(init.Self); } } - public class AutoRenderSize : IAutoRenderSize + public class AutoRenderSize : IAutoRenderSize, IMouseBounds { - public AutoRenderSize(AutoRenderSizeInfo info) { } + readonly RenderSprites rs; + + public AutoRenderSize(Actor self) + { + rs = self.Trait(); + } public int2 RenderSize(Actor self) { - var rs = self.Trait(); return rs.AutoRenderSize(self); } + + Rectangle IMouseBounds.MouseoverBounds(Actor self, WorldRenderer wr) + { + return self.TraitsImplementing() + .Select(s => s.AutoMouseoverBounds(self, wr)) + .FirstOrDefault(r => !r.IsEmpty); + } } } diff --git a/OpenRA.Mods.Common/Traits/Render/CustomRenderSize.cs b/OpenRA.Mods.Common/Traits/Render/CustomRenderSize.cs index 7c6ad0dd8c..af8a0d6a17 100644 --- a/OpenRA.Mods.Common/Traits/Render/CustomRenderSize.cs +++ b/OpenRA.Mods.Common/Traits/Render/CustomRenderSize.cs @@ -9,6 +9,8 @@ */ #endregion +using System.Drawing; +using OpenRA.Graphics; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits @@ -22,7 +24,7 @@ namespace OpenRA.Mods.Common.Traits public object Create(ActorInitializer init) { return new CustomRenderSize(this); } } - public class CustomRenderSize : IAutoRenderSize + public class CustomRenderSize : IAutoRenderSize, IMouseBounds { readonly CustomRenderSizeInfo info; public CustomRenderSize(CustomRenderSizeInfo info) { this.info = info; } @@ -31,5 +33,20 @@ namespace OpenRA.Mods.Common.Traits { return new int2(info.CustomBounds[0], info.CustomBounds[1]); } + + Rectangle IMouseBounds.MouseoverBounds(Actor self, WorldRenderer wr) + { + if (info.CustomBounds == null) + return Rectangle.Empty; + + var size = new int2(info.CustomBounds[0], info.CustomBounds[1]); + + var offset = -size / 2; + if (info.CustomBounds.Length > 2) + offset += new int2(info.CustomBounds[2], info.CustomBounds[3]); + + var xy = wr.ScreenPxPosition(self.CenterPosition); + return new Rectangle(xy.X, xy.Y, size.X, size.Y); + } } } diff --git a/OpenRA.Mods.Common/Traits/Render/RenderSprites.cs b/OpenRA.Mods.Common/Traits/Render/RenderSprites.cs index 4327ebbbd0..e09552c810 100644 --- a/OpenRA.Mods.Common/Traits/Render/RenderSprites.cs +++ b/OpenRA.Mods.Common/Traits/Render/RenderSprites.cs @@ -151,8 +151,8 @@ namespace OpenRA.Mods.Common.Traits.Render } } + public readonly RenderSpritesInfo Info; readonly string faction; - readonly RenderSpritesInfo info; readonly List anims = new List(); string cachedImage; @@ -165,7 +165,7 @@ namespace OpenRA.Mods.Common.Traits.Render public RenderSprites(ActorInitializer init, RenderSpritesInfo info) { - this.info = info; + Info = info; faction = init.Contains() ? init.Get() : init.Self.Owner.Faction.InternalName; } @@ -174,7 +174,7 @@ namespace OpenRA.Mods.Common.Traits.Render if (cachedImage != null) return cachedImage; - return cachedImage = info.GetImage(self.Info, self.World.Map.Rules.Sequences, faction); + return cachedImage = Info.GetImage(self.Info, self.World.Map.Rules.Sequences, faction); } public void UpdatePalette() @@ -199,7 +199,7 @@ namespace OpenRA.Mods.Common.Traits.Render a.CachePalette(wr, owner); } - foreach (var r in a.Animation.Render(self, wr, a.PaletteReference, info.Scale)) + foreach (var r in a.Animation.Render(self, wr, a.PaletteReference, Info.Scale)) yield return r; } } @@ -208,7 +208,7 @@ namespace OpenRA.Mods.Common.Traits.Render { foreach (var a in anims) if (a.IsVisible) - yield return a.Animation.ScreenBounds(self, wr, info.Scale); + yield return a.Animation.ScreenBounds(self, wr, Info.Scale); } void ITick.Tick(Actor self) @@ -231,8 +231,8 @@ namespace OpenRA.Mods.Common.Traits.Render // Use defaults if (palette == null) { - palette = info.Palette ?? info.PlayerPalette; - isPlayerPalette = info.Palette == null; + palette = Info.Palette ?? Info.PlayerPalette; + isPlayerPalette = Info.Palette == null; } anims.Add(new AnimationWrapper(anim, palette, isPlayerPalette)); @@ -281,7 +281,7 @@ namespace OpenRA.Mods.Common.Traits.Render { return anims.Where(b => b.IsVisible && b.Animation.Animation.CurrentSequence != null) - .Select(a => (a.Animation.Animation.Image.Size.XY * info.Scale).ToInt2()) + .Select(a => (a.Animation.Animation.Image.Size.XY * Info.Scale).ToInt2()) .FirstOrDefault(); } diff --git a/OpenRA.Mods.Common/Traits/Render/RenderVoxels.cs b/OpenRA.Mods.Common/Traits/Render/RenderVoxels.cs index b4916945d6..94f3b2e0ad 100644 --- a/OpenRA.Mods.Common/Traits/Render/RenderVoxels.cs +++ b/OpenRA.Mods.Common/Traits/Render/RenderVoxels.cs @@ -97,11 +97,12 @@ namespace OpenRA.Mods.Common.Traits.Render } } + public readonly RenderVoxelsInfo Info; + readonly List components = new List(); readonly Dictionary wrappers = new Dictionary(); readonly Actor self; - readonly RenderVoxelsInfo info; readonly BodyOrientation body; readonly WRot camera; readonly WRot lightSource; @@ -109,7 +110,7 @@ namespace OpenRA.Mods.Common.Traits.Render public RenderVoxels(Actor self, RenderVoxelsInfo info) { this.self = self; - this.info = info; + Info = info; body = self.Trait(); camera = new WRot(WAngle.Zero, body.CameraPitch - new WAngle(256), new WAngle(256)); lightSource = new WRot(WAngle.Zero, new WAngle(256) - info.LightPitch, info.LightYaw); @@ -133,16 +134,16 @@ namespace OpenRA.Mods.Common.Traits.Render { if (initializePalettes) { - var paletteName = info.Palette ?? info.PlayerPalette + self.Owner.InternalName; + var paletteName = Info.Palette ?? Info.PlayerPalette + self.Owner.InternalName; colorPalette = wr.Palette(paletteName); - normalsPalette = wr.Palette(info.NormalsPalette); - shadowPalette = wr.Palette(info.ShadowPalette); + normalsPalette = wr.Palette(Info.NormalsPalette); + shadowPalette = wr.Palette(Info.ShadowPalette); initializePalettes = false; } return new IRenderable[] { new ModelRenderable( - components, self.CenterPosition, 0, camera, info.Scale, - lightSource, info.LightAmbientColor, info.LightDiffuseColor, + components, self.CenterPosition, 0, camera, Info.Scale, + lightSource, Info.LightAmbientColor, Info.LightDiffuseColor, colorPalette, normalsPalette, shadowPalette) }; } @@ -151,10 +152,10 @@ namespace OpenRA.Mods.Common.Traits.Render var pos = self.CenterPosition; foreach (var c in components) if (c.IsVisible) - yield return c.ScreenBounds(pos, wr, info.Scale); + yield return c.ScreenBounds(pos, wr, Info.Scale); } - public string Image { get { return info.Image ?? self.Info.Name; } } + public string Image { get { return Info.Image ?? self.Info.Name; } } public void Add(ModelAnimation m) { components.Add(m); diff --git a/OpenRA.Mods.Common/Traits/Render/WithSpriteBody.cs b/OpenRA.Mods.Common/Traits/Render/WithSpriteBody.cs index ad2accf7aa..697ec8a1db 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithSpriteBody.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithSpriteBody.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Drawing; using OpenRA.Graphics; using OpenRA.Mods.Common.Graphics; using OpenRA.Traits; @@ -43,9 +44,10 @@ namespace OpenRA.Mods.Common.Traits.Render } } - public class WithSpriteBody : PausableConditionalTrait, INotifyDamageStateChanged, INotifyBuildComplete + public class WithSpriteBody : PausableConditionalTrait, INotifyDamageStateChanged, INotifyBuildComplete, IAutoMouseBounds { public readonly Animation DefaultAnimation; + readonly RenderSprites rs; public WithSpriteBody(ActorInitializer init, WithSpriteBodyInfo info) : this(init, info, () => 0) { } @@ -53,7 +55,7 @@ namespace OpenRA.Mods.Common.Traits.Render protected WithSpriteBody(ActorInitializer init, WithSpriteBodyInfo info, Func baseFacing) : base(info) { - var rs = init.Self.Trait(); + rs = init.Self.Trait(); Func paused = () => IsTraitPaused && DefaultAnimation.CurrentSequence.Name == NormalizeSequence(init.Self, Info.Sequence); @@ -125,5 +127,10 @@ namespace OpenRA.Mods.Common.Traits.Render { DamageStateChanged(self); } + + Rectangle IAutoMouseBounds.AutoMouseoverBounds(Actor self, WorldRenderer wr) + { + return DefaultAnimation != null ? DefaultAnimation.ScreenBounds(wr, self.CenterPosition, WVec.Zero, rs.Info.Scale) : Rectangle.Empty; + } } } diff --git a/OpenRA.Mods.Common/Traits/Render/WithVoxelBody.cs b/OpenRA.Mods.Common/Traits/Render/WithVoxelBody.cs index caf3b97b03..5ed6054ec8 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithVoxelBody.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithVoxelBody.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Drawing; using System.Linq; using OpenRA.Graphics; using OpenRA.Mods.Common.Graphics; @@ -39,21 +40,24 @@ namespace OpenRA.Mods.Common.Traits.Render } } - public class WithVoxelBody : ConditionalTrait, IAutoSelectionSize, IAutoRenderSize + public class WithVoxelBody : ConditionalTrait, IAutoSelectionSize, IAutoRenderSize, IAutoMouseBounds { readonly int2 size; + readonly ModelAnimation modelAnimation; + readonly RenderVoxels rv; public WithVoxelBody(Actor self, WithVoxelBodyInfo info) : base(info) { var body = self.Trait(); - var rv = self.Trait(); + rv = self.Trait(); var model = self.World.ModelCache.GetModelSequence(rv.Image, info.Sequence); - rv.Add(new ModelAnimation(model, () => WVec.Zero, + modelAnimation = new ModelAnimation(model, () => WVec.Zero, () => new[] { body.QuantizeOrientation(self, self.Orientation) }, - () => IsTraitDisabled, () => 0, info.ShowShadow)); + () => IsTraitDisabled, () => 0, info.ShowShadow); + rv.Add(modelAnimation); // Selection size var rvi = self.Info.TraitInfo(); var s = (int)(rvi.Scale * model.Size.Aggregate(Math.Max)); @@ -62,5 +66,10 @@ namespace OpenRA.Mods.Common.Traits.Render public int2 SelectionSize(Actor self) { return size; } public int2 RenderSize(Actor self) { return size; } + + Rectangle IAutoMouseBounds.AutoMouseoverBounds(Actor self, WorldRenderer wr) + { + return modelAnimation.ScreenBounds(self.CenterPosition, wr, rv.Info.Scale); + } } } diff --git a/OpenRA.Mods.Common/Widgets/ViewportControllerWidget.cs b/OpenRA.Mods.Common/Widgets/ViewportControllerWidget.cs index 21ae51742c..3e7109870b 100644 --- a/OpenRA.Mods.Common/Widgets/ViewportControllerWidget.cs +++ b/OpenRA.Mods.Common/Widgets/ViewportControllerWidget.cs @@ -229,8 +229,8 @@ namespace OpenRA.Mods.Common.Widgets } var worldPixel = worldRenderer.Viewport.ViewToWorldPx(Viewport.LastMousePos); - var underCursor = world.ScreenMap.ActorsAt(worldPixel) - .Where(a => a.Info.HasTraitInfo() && !world.FogObscures(a)) + var underCursor = world.ScreenMap.ActorsAtMouse(worldPixel) + .Where(a => a.Actor.Info.HasTraitInfo() && !world.FogObscures(a.Actor)) .WithHighestSelectionPriority(worldPixel); if (underCursor != null) @@ -245,7 +245,7 @@ namespace OpenRA.Mods.Common.Widgets return; } - var frozen = world.ScreenMap.FrozenActorsAt(world.RenderPlayer, worldPixel) + var frozen = world.ScreenMap.FrozenActorsAtMouse(world.RenderPlayer, worldPixel) .Where(a => a.TooltipInfo != null && a.IsValid) .WithHighestSelectionPriority(worldPixel); diff --git a/OpenRA.Mods.Common/Widgets/WorldInteractionControllerWidget.cs b/OpenRA.Mods.Common/Widgets/WorldInteractionControllerWidget.cs index 9b966bd021..abe38220ad 100644 --- a/OpenRA.Mods.Common/Widgets/WorldInteractionControllerWidget.cs +++ b/OpenRA.Mods.Common/Widgets/WorldInteractionControllerWidget.cs @@ -107,11 +107,11 @@ namespace OpenRA.Mods.Common.Widgets { if (!IsValidDragbox && World.Selection.Actors.Any() && !multiClick) { - var selectableActor = World.ScreenMap.ActorsAt(mousePos).Any(x => + var selectableActor = World.ScreenMap.ActorsAtMouse(mousePos).Select(a => a.Actor).Any(x => x.Info.HasTraitInfo() && (x.Owner.IsAlliedWith(World.RenderPlayer) || !World.FogObscures(x))); var uog = (UnitOrderGenerator)World.OrderGenerator; - if (!selectableActor || uog.InputOverridesSelection(World, mousePos, mi)) + if (!selectableActor || uog.InputOverridesSelection(worldRenderer, World, mousePos, mi)) { // Order units instead of selecting ApplyOrders(World, mi); @@ -124,7 +124,7 @@ namespace OpenRA.Mods.Common.Widgets if (multiClick) { - var unit = World.ScreenMap.ActorsAt(mousePos) + var unit = World.ScreenMap.ActorsAtMouse(mousePos) .WithHighestSelectionPriority(mousePos); if (unit != null && unit.Owner == (World.RenderPlayer ?? World.LocalPlayer)) @@ -294,7 +294,8 @@ namespace OpenRA.Mods.Common.Widgets static IEnumerable SelectActorsOnScreen(World world, WorldRenderer wr, IEnumerable selectionClasses, Player player) { - return SelectActorsByOwnerAndSelectionClass(world.ScreenMap.ActorsInBox(wr.Viewport.TopLeft, wr.Viewport.BottomRight), player, selectionClasses); + var actors = world.ScreenMap.ActorsInMouseBox(wr.Viewport.TopLeft, wr.Viewport.BottomRight).Select(a => a.Actor); + return SelectActorsByOwnerAndSelectionClass(actors, player, selectionClasses); } static IEnumerable SelectActorsInWorld(World world, IEnumerable selectionClasses, Player player) @@ -322,7 +323,8 @@ namespace OpenRA.Mods.Common.Widgets if ((a - b).Length <= Game.Settings.Game.SelectionDeadzone) a = b; - return world.ScreenMap.ActorsInBox(a, b) + return world.ScreenMap.ActorsInMouseBox(a, b) + .Select(x => x.Actor) .Where(x => x.Info.HasTraitInfo() && (x.Owner.IsAlliedWith(world.RenderPlayer) || !world.FogObscures(x))) .SubsetWithHighestSelectionPriority(); }