diff --git a/OpenRA.Game/Actor.cs b/OpenRA.Game/Actor.cs index e9adebf8dd..4fcd7f5f5e 100755 --- a/OpenRA.Game/Actor.cs +++ b/OpenRA.Game/Actor.cs @@ -24,13 +24,11 @@ namespace OpenRA public readonly World World; public readonly uint ActorID; + public Lazy Bounds; Lazy occupySpace; Lazy facing; - public Cached Bounds; - public Cached ExtendedBounds; - public IOccupySpace OccupiesSpace { get { return occupySpace.Value; } } public CPos Location { get { return occupySpace.Value.TopLeft; } } @@ -76,27 +74,25 @@ namespace OpenRA facing = Lazy.New(() => TraitOrDefault()); - size = Lazy.New(() => - { - var si = Info.Traits.GetOrDefault(); - if (si != null && si.Bounds != null) - return new int2(si.Bounds[0], si.Bounds[1]); - - return TraitsImplementing().Select(x => x.SelectionSize(this)).FirstOrDefault(); - }); - applyIRender = (x, wr) => x.Render(this, wr); applyRenderModifier = (m, p, wr) => p.ModifyRender(this, wr, m); - Bounds = Cached.New(() => CalculateBounds(false)); - ExtendedBounds = Cached.New(() => CalculateBounds(true)); + Bounds = Lazy.New(() => + { + var si = Info.Traits.GetOrDefault(); + var size = (si != null && si.Bounds != null) ? new int2(si.Bounds[0], si.Bounds[1]) : + TraitsImplementing().Select(x => x.SelectionSize(this)).FirstOrDefault(); + + var offset = -size / 2; + if (si != null && si.Bounds != null && si.Bounds.Length > 2) + offset += new int2(si.Bounds[2], si.Bounds[3]); + + return new Rectangle(offset.X, offset.Y, size.X, size.Y); + }); } public void Tick() { - Bounds.Invalidate(); - ExtendedBounds.Invalidate(); - currentActivity = Traits.Util.RunActivity(this, currentActivity); } @@ -105,8 +101,6 @@ namespace OpenRA get { return currentActivity == null; } } - OpenRA.FileFormats.Lazy size; - // note: these delegates are cached to avoid massive allocation. Func> applyIRender; Func, IRenderModifier, WorldRenderer, IEnumerable> applyRenderModifier; @@ -117,34 +111,6 @@ namespace OpenRA return mods.Aggregate(sprites, (m, p) => applyRenderModifier(m, p, wr)); } - // When useAltitude = true, the bounding box is extended - // vertically to altitude = 0 to support FindUnitsInCircle queries - // When false, the bounding box is given for the actor - // at its current altitude - Rectangle CalculateBounds(bool useAltitude) - { - var sizeVector = (PVecInt)size.Value; - var loc = CenterLocation - sizeVector / 2; - - var si = Info.Traits.GetOrDefault(); - if (si != null && si.Bounds != null && si.Bounds.Length > 2) - { - loc += new PVecInt(si.Bounds[2], si.Bounds[3]); - } - - var ios = occupySpace.Value; - if (ios != null) - { - var altitude = ios.CenterPosition.Z * Game.CellSize / 1024; - loc -= new PVecInt(0, altitude); - - if (useAltitude) - sizeVector = new PVecInt(sizeVector.X, sizeVector.Y + altitude); - } - - return new Rectangle(loc.X, loc.Y, sizeVector.X, sizeVector.Y); - } - public bool IsInWorld { get; internal set; } public void QueueActivity(bool queued, Activity nextActivity) diff --git a/OpenRA.Game/Graphics/WorldRenderer.cs b/OpenRA.Game/Graphics/WorldRenderer.cs index 65c989d36a..56c2f10c9b 100644 --- a/OpenRA.Game/Graphics/WorldRenderer.cs +++ b/OpenRA.Game/Graphics/WorldRenderer.cs @@ -150,14 +150,15 @@ namespace OpenRA.Graphics Game.Renderer.Flush(); } - public void DrawSelectionBox(Actor selectedUnit, Color c) + public void DrawSelectionBox(Actor a, Color c) { - var bounds = selectedUnit.Bounds.Value; + var pos = ScreenPxPosition(a.CenterPosition); + var bounds = a.Bounds.Value; - var xy = new float2(bounds.Left, bounds.Top); - var Xy = new float2(bounds.Right, bounds.Top); - var xY = new float2(bounds.Left, bounds.Bottom); - var XY = new float2(bounds.Right, bounds.Bottom); + var xy = pos + new float2(bounds.Left, bounds.Top); + var Xy = pos + new float2(bounds.Right, bounds.Top); + var xY = pos + new float2(bounds.Left, bounds.Bottom); + var XY = pos + new float2(bounds.Right, bounds.Bottom); var wlr = Game.Renderer.WorldLineRenderer; wlr.DrawLine(xy, xy + new float2(4, 0), c, c); diff --git a/OpenRA.Game/Traits/Selectable.cs b/OpenRA.Game/Traits/Selectable.cs index 0a5d6d9609..b01cc222c8 100644 --- a/OpenRA.Game/Traits/Selectable.cs +++ b/OpenRA.Game/Traits/Selectable.cs @@ -40,7 +40,9 @@ namespace OpenRA.Traits if (!Info.Selectable) return; + var pos = wr.ScreenPxPosition(self.CenterPosition); var bounds = self.Bounds.Value; + bounds.Offset(pos.X, pos.Y); var xy = new float2(bounds.Left, bounds.Top); var Xy = new float2(bounds.Right, bounds.Top); @@ -56,7 +58,9 @@ namespace OpenRA.Traits if (!Info.Selectable) return; + var pos = wr.ScreenPxPosition(self.CenterPosition); var bounds = self.Bounds.Value; + bounds.Offset(pos.X, pos.Y); var xy = new float2(bounds.Left, bounds.Top); var Xy = new float2(bounds.Right, bounds.Top); diff --git a/OpenRA.Game/Traits/SelectionDecorations.cs b/OpenRA.Game/Traits/SelectionDecorations.cs index d9981c52b5..be999bd9b9 100644 --- a/OpenRA.Game/Traits/SelectionDecorations.cs +++ b/OpenRA.Game/Traits/SelectionDecorations.cs @@ -34,7 +34,10 @@ namespace OpenRA.Traits if (self.World.FogObscures(self)) return; + var pos = wr.ScreenPxPosition(self.CenterPosition); var bounds = self.Bounds.Value; + bounds.Offset(pos.X, pos.Y); + var xy = new float2(bounds.Left, bounds.Top); var xY = new float2(bounds.Left, bounds.Bottom); diff --git a/OpenRA.Game/Traits/World/ScreenMap.cs b/OpenRA.Game/Traits/World/ScreenMap.cs index bfbde690ff..684a60c365 100755 --- a/OpenRA.Game/Traits/World/ScreenMap.cs +++ b/OpenRA.Game/Traits/World/ScreenMap.cs @@ -26,11 +26,12 @@ namespace OpenRA.Traits public object Create(ActorInitializer init) { return new ScreenMap(init.world, this); } } - public class ScreenMap + public class ScreenMap : IWorldLoaded { ScreenMapInfo info; - Cache[]> frozen; - List[] actors; + WorldRenderer worldRenderer; + Cache[]> frozen; + Dictionary[] actors; int rows, cols; public ScreenMap(World world, ScreenMapInfo info) @@ -39,33 +40,39 @@ namespace OpenRA.Traits cols = world.Map.MapSize.X * Game.CellSize / info.BinSize + 1; rows = world.Map.MapSize.Y * Game.CellSize / info.BinSize + 1; - frozen = new Cache[]>(InitializeFrozenActors); - actors = new List[rows * cols]; + frozen = new Cache[]>(InitializeFrozenActors); + actors = new Dictionary[rows * cols]; for (var j = 0; j < rows; j++) for (var i = 0; i < cols; i++) - actors[j * cols + i] = new List(); + actors[j * cols + i] = new Dictionary(); } - List[] InitializeFrozenActors(Player p) + public void WorldLoaded(World w, WorldRenderer wr) { worldRenderer = wr; } + + Dictionary[] InitializeFrozenActors(Player p) { - var f = new List[rows * cols]; + var f = new Dictionary[rows * cols]; for (var j = 0; j < rows; j++) for (var i = 0; i < cols; i++) - f[j * cols + i] = new List(); + f[j * cols + i] = new Dictionary(); return f; } public void Add(Player viewer, FrozenActor fa) { - var top = Math.Max(0, fa.Bounds.Top / info.BinSize); - var left = Math.Max(0, fa.Bounds.Left / info.BinSize); - var bottom = Math.Min(rows - 1, fa.Bounds.Bottom / info.BinSize); - var right = Math.Min(cols - 1, fa.Bounds.Right / info.BinSize); + var pos = worldRenderer.ScreenPxPosition(fa.CenterPosition); + var bounds = fa.Bounds; + bounds.Offset(pos.X, pos.Y); + + var top = Math.Max(0, bounds.Top / info.BinSize); + var left = Math.Max(0, bounds.Left / info.BinSize); + var bottom = Math.Min(rows - 1, bounds.Bottom / info.BinSize); + var right = Math.Min(cols - 1, bounds.Right / info.BinSize); for (var j = top; j <= bottom; j++) for (var i = left; i <= right; i++) - frozen[viewer][j*cols + i].Add(fa); + frozen[viewer][j*cols + i].Add(fa, bounds); } public void Remove(Player viewer, FrozenActor fa) @@ -76,15 +83,18 @@ namespace OpenRA.Traits public void Add(Actor a) { - var b = a.Bounds.Value; - var top = Math.Max(0, b.Top / info.BinSize); - var left = Math.Max(0, b.Left / info.BinSize); - var bottom = Math.Min(rows - 1, b.Bottom / info.BinSize); - var right = Math.Min(cols - 1, b.Right / info.BinSize); + var pos = worldRenderer.ScreenPxPosition(a.CenterPosition); + var bounds = a.Bounds.Value; + bounds.Offset(pos.X, pos.Y); + + var top = Math.Max(0, bounds.Top / info.BinSize); + var left = Math.Max(0, bounds.Left / info.BinSize); + var bottom = Math.Min(rows - 1, bounds.Bottom / info.BinSize); + var right = Math.Min(cols - 1, bounds.Right / info.BinSize); for (var j = top; j <= bottom; j++) for (var i = left; i <= right; i++) - actors[j * cols + i].Add(a); + actors[j * cols + i].Add(a, bounds); } public void Remove(Actor a) @@ -103,14 +113,18 @@ namespace OpenRA.Traits { var i = (pxPos.X / info.BinSize).Clamp(0, cols - 1); var j = (pxPos.Y / info.BinSize).Clamp(0, rows - 1); - return frozen[viewer][j*cols + i].Where(fa => fa.Bounds.Contains(pxPos) && fa.IsValid); + return frozen[viewer][j*cols + i] + .Where(kv => kv.Key.IsValid && kv.Value.Contains(pxPos)) + .Select(kv => kv.Key); } public IEnumerable ActorsAt(int2 pxPos) { var i = (pxPos.X / info.BinSize).Clamp(0, cols - 1); var j = (pxPos.Y / info.BinSize).Clamp(0, rows - 1); - return actors[j*cols + i].Where(a => a.Bounds.Value.Contains(pxPos) && a.IsInWorld); + return actors[j * cols + i] + .Where(kv => kv.Key.IsInWorld && kv.Value.Contains(pxPos)) + .Select(kv => kv.Key); } // Legacy fallback @@ -129,9 +143,17 @@ namespace OpenRA.Traits var bottom = (r.Bottom / info.BinSize).Clamp(0, rows - 1); for (var j = top; j <= bottom; j++) + { for (var i = left; i <= right; i++) - foreach (var a in actors[j*cols + i].Where(b => b.Bounds.Value.IntersectsWith(r) && b.IsInWorld)) + { + var ret = actors[j * cols + i] + .Where(kv => kv.Key.IsInWorld && kv.Value.IntersectsWith(r)) + .Select(kv => kv.Key); + + foreach (var a in ret) yield return a; + } + } } } } diff --git a/OpenRA.Mods.RA/GainsExperience.cs b/OpenRA.Mods.RA/GainsExperience.cs index 135e4ba4b1..5c9edd51e0 100644 --- a/OpenRA.Mods.RA/GainsExperience.cs +++ b/OpenRA.Mods.RA/GainsExperience.cs @@ -117,9 +117,12 @@ namespace OpenRA.Mods.RA if (self.World.FogObscures(self)) yield break; + var pos = wr.ScreenPxPosition(self.CenterPosition); var bounds = self.Bounds.Value; - var pos = new PPos(bounds.Right, bounds.Bottom - 2).ToWPos(0); - yield return new SpriteRenderable(RankAnim.Image, pos, WVec.Zero, 0, wr.Palette("effect"), 1f, true); + bounds.Offset(pos.X, pos.Y); + + var effectPos = new PPos(bounds.Right, bounds.Bottom - 2).ToWPos(0); + yield return new SpriteRenderable(RankAnim.Image, effectPos, WVec.Zero, 0, wr.Palette("effect"), 1f, true); } }