From dfd51c0caae05c525f71d7515ce83b3e6309d606 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sat, 21 Sep 2013 13:39:39 +1200 Subject: [PATCH] Introduce ScreenMap trait for caching screen-coord queries. --- OpenRA.Game/Graphics/WorldRenderer.cs | 9 +- OpenRA.Game/OpenRA.Game.csproj | 1 + OpenRA.Game/Orders/UnitOrderGenerator.cs | 11 +- OpenRA.Game/Traits/Player/FrozenActorLayer.cs | 40 +---- OpenRA.Game/Traits/Waypoint.cs | 12 +- OpenRA.Game/Traits/World/ScreenMap.cs | 137 ++++++++++++++++++ .../Widgets/ViewportControllerWidget.cs | 4 +- .../WorldInteractionControllerWidget.cs | 4 +- OpenRA.Game/World.cs | 4 +- OpenRA.Game/WorldUtils.cs | 22 +-- OpenRA.Mods.RA/Air/Aircraft.cs | 23 ++- OpenRA.Mods.RA/Buildings/Building.cs | 12 +- OpenRA.Mods.RA/Crate.cs | 15 +- OpenRA.Mods.RA/Guard.cs | 22 +-- OpenRA.Mods.RA/Husk.cs | 19 ++- OpenRA.Mods.RA/Mine.cs | 12 +- OpenRA.Mods.RA/Minelayer.cs | 3 +- OpenRA.Mods.RA/Move/Mobile.cs | 14 +- .../Orders/PowerDownOrderGenerator.cs | 5 +- OpenRA.Mods.RA/Orders/RepairOrderGenerator.cs | 4 +- mods/cnc/rules/system.yaml | 1 + mods/d2k/rules/system.yaml | 1 + mods/ra/rules/system.yaml | 1 + mods/ts/rules/system.yaml | 1 + 24 files changed, 281 insertions(+), 96 deletions(-) create mode 100755 OpenRA.Game/Traits/World/ScreenMap.cs diff --git a/OpenRA.Game/Graphics/WorldRenderer.cs b/OpenRA.Game/Graphics/WorldRenderer.cs index ea6b61a1d0..65c989d36a 100644 --- a/OpenRA.Game/Graphics/WorldRenderer.cs +++ b/OpenRA.Game/Graphics/WorldRenderer.cs @@ -73,12 +73,11 @@ namespace OpenRA.Graphics List GenerateRenderables() { - var bounds = Game.viewport.WorldBounds(world); var comparer = new RenderableComparer(this); - - var tl = bounds.TopLeftAsCPos(); - var br = bounds.BottomRightAsCPos(); - var actors = world.FindActorsInBox(tl, br) + var vb = Game.viewport.ViewBounds(world); + var tl = Game.viewport.ViewToWorldPx(new int2(vb.Left, vb.Top)).ToInt2(); + var br = Game.viewport.ViewToWorldPx(new int2(vb.Right, vb.Bottom)).ToInt2(); + var actors = world.ScreenMap.ActorsInBox(tl, br) .Append(world.WorldActor) .ToList(); diff --git a/OpenRA.Game/OpenRA.Game.csproj b/OpenRA.Game/OpenRA.Game.csproj index f4846fecb7..5e339a3f97 100644 --- a/OpenRA.Game/OpenRA.Game.csproj +++ b/OpenRA.Game/OpenRA.Game.csproj @@ -238,6 +238,7 @@ + diff --git a/OpenRA.Game/Orders/UnitOrderGenerator.cs b/OpenRA.Game/Orders/UnitOrderGenerator.cs index af8e2c5bad..f405c6a7f3 100644 --- a/OpenRA.Game/Orders/UnitOrderGenerator.cs +++ b/OpenRA.Game/Orders/UnitOrderGenerator.cs @@ -19,8 +19,8 @@ namespace OpenRA.Orders { public IEnumerable Order(World world, CPos xy, MouseInput mi) { - var underCursor = world.FindUnitsAtMouse(mi.Location) - .Where(a => a.HasTrait()) + var underCursor = world.ScreenMap.ActorsAt(Game.viewport.ViewToWorldPx(mi.Location)) + .Where(a => !world.FogObscures(a) && a.HasTrait()) .OrderByDescending(a => a.Info.SelectionPriority()) .FirstOrDefault(); @@ -59,10 +59,9 @@ namespace OpenRA.Orders public string GetCursor(World world, CPos xy, MouseInput mi) { - bool useSelect = false; - - var underCursor = world.FindUnitsAtMouse(mi.Location) - .Where(a => a.HasTrait()) + var useSelect = false; + var underCursor = world.ScreenMap.ActorsAt(Game.viewport.ViewToWorldPx(mi.Location)) + .Where(a => !world.FogObscures(a) && a.HasTrait()) .OrderByDescending(a => a.Info.SelectionPriority()) .FirstOrDefault(); diff --git a/OpenRA.Game/Traits/Player/FrozenActorLayer.cs b/OpenRA.Game/Traits/Player/FrozenActorLayer.cs index 20521cd096..9cb594ca35 100755 --- a/OpenRA.Game/Traits/Player/FrozenActorLayer.cs +++ b/OpenRA.Game/Traits/Player/FrozenActorLayer.cs @@ -20,10 +20,7 @@ namespace OpenRA.Traits { public class FrozenActorLayerInfo : ITraitInfo { - [Desc("Size of partition bins (screen pixels)")] - public readonly int BinSize = 250; - - public object Create(ActorInitializer init) { return new FrozenActorLayer(init.world, this); } + public object Create(ActorInitializer init) { return new FrozenActorLayer(init.self); } } public class FrozenActor @@ -98,34 +95,21 @@ namespace OpenRA.Traits [Sync] public int VisibilityHash; [Sync] public int FrozenHash; - readonly FrozenActorLayerInfo info; + readonly World world; + readonly Player owner; Dictionary frozen; - List[,] bins; - public FrozenActorLayer(World world, FrozenActorLayerInfo info) + public FrozenActorLayer(Actor self) { - this.info = info; + world = self.World; + owner = self.Owner; frozen = new Dictionary(); - bins = new List[ - world.Map.MapSize.X * Game.CellSize / info.BinSize, - world.Map.MapSize.Y * Game.CellSize / info.BinSize]; - - for (var j = 0; j <= bins.GetUpperBound(1); j++) - for (var i = 0; i <= bins.GetUpperBound(0); i++) - bins[i, j] = new List(); } public void Add(FrozenActor fa) { frozen.Add(fa.ID, fa); - - var top = (int)Math.Max(0, fa.Bounds.Top / info.BinSize); - var left = (int)Math.Max(0, fa.Bounds.Left / info.BinSize); - var bottom = (int)Math.Min(bins.GetUpperBound(1), fa.Bounds.Bottom / info.BinSize); - var right = (int)Math.Min(bins.GetUpperBound(0), fa.Bounds.Right / info.BinSize); - for (var j = top; j <= bottom; j++) - for (var i = left; i <= right; i++) - bins[i, j].Add(fa); + world.ScreenMap.Add(owner, fa); } public void Tick(Actor self) @@ -148,8 +132,7 @@ namespace OpenRA.Traits foreach (var r in remove) { - foreach (var bin in bins) - bin.Remove(frozen[r]); + world.ScreenMap.Remove(owner, frozen[r]); frozen.Remove(r); } } @@ -161,13 +144,6 @@ namespace OpenRA.Traits .SelectMany(ff => ff.Render(wr)); } - public IEnumerable FrozenActorsAt(int2 pxPos) - { - var x = (pxPos.X / info.BinSize).Clamp(0, bins.GetUpperBound(0)); - var y = (pxPos.Y / info.BinSize).Clamp(0, bins.GetUpperBound(1)); - return bins[x, y].Where(p => p.Bounds.Contains(pxPos) && p.IsValid); - } - public FrozenActor FromID(uint id) { FrozenActor ret; diff --git a/OpenRA.Game/Traits/Waypoint.cs b/OpenRA.Game/Traits/Waypoint.cs index 2ea8e2a381..2e129bbf06 100644 --- a/OpenRA.Game/Traits/Waypoint.cs +++ b/OpenRA.Game/Traits/Waypoint.cs @@ -18,7 +18,7 @@ namespace OpenRA.Traits public object Create( ActorInitializer init ) { return new Waypoint( init ); } } - class Waypoint : IOccupySpace, ISync + class Waypoint : IOccupySpace, ISync, INotifyAddedToWorld, INotifyRemovedFromWorld { [Sync] CPos location; @@ -30,5 +30,15 @@ namespace OpenRA.Traits public CPos TopLeft { get { return location; } } public IEnumerable> OccupiedCells() { yield break; } public WPos CenterPosition { get { return location.CenterPosition; } } + + public void AddedToWorld(Actor self) + { + self.World.ScreenMap.Add(self); + } + + public void RemovedFromWorld(Actor self) + { + self.World.ScreenMap.Remove(self); + } } } diff --git a/OpenRA.Game/Traits/World/ScreenMap.cs b/OpenRA.Game/Traits/World/ScreenMap.cs new file mode 100755 index 0000000000..bfbde690ff --- /dev/null +++ b/OpenRA.Game/Traits/World/ScreenMap.cs @@ -0,0 +1,137 @@ +#region Copyright & License Information +/* + * Copyright 2007-2013 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation. For more information, + * see COPYING. + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using OpenRA.FileFormats; +using OpenRA.Graphics; +using OpenRA.Traits; + +namespace OpenRA.Traits +{ + public class ScreenMapInfo : ITraitInfo + { + [Desc("Size of partition bins (world pixels)")] + public readonly int BinSize = 250; + + public object Create(ActorInitializer init) { return new ScreenMap(init.world, this); } + } + + public class ScreenMap + { + ScreenMapInfo info; + Cache[]> frozen; + List[] actors; + int rows, cols; + + public ScreenMap(World world, ScreenMapInfo info) + { + this.info = info; + 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]; + for (var j = 0; j < rows; j++) + for (var i = 0; i < cols; i++) + actors[j * cols + i] = new List(); + } + + List[] InitializeFrozenActors(Player p) + { + var f = new List[rows * cols]; + for (var j = 0; j < rows; j++) + for (var i = 0; i < cols; i++) + f[j * cols + i] = new List(); + + 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); + + for (var j = top; j <= bottom; j++) + for (var i = left; i <= right; i++) + frozen[viewer][j*cols + i].Add(fa); + } + + public void Remove(Player viewer, FrozenActor fa) + { + foreach (var bin in frozen[viewer]) + bin.Remove(fa); + } + + 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); + + for (var j = top; j <= bottom; j++) + for (var i = left; i <= right; i++) + actors[j * cols + i].Add(a); + } + + public void Remove(Actor a) + { + foreach (var bin in actors) + bin.Remove(a); + } + + public void Update(Actor a) + { + Remove(a); + Add(a); + } + + public IEnumerable FrozenActorsAt(Player viewer, int2 pxPos) + { + 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); + } + + 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); + } + + // Legacy fallback + public IEnumerable ActorsAt(PPos pxPos) { return ActorsAt(pxPos.ToInt2()); } + + public IEnumerable ActorsInBox(int2 a, int2 b) + { + return ActorsInBox(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(Rectangle r) + { + var left = (r.Left / info.BinSize).Clamp(0, cols - 1); + var right = (r.Right / info.BinSize).Clamp(0, cols - 1); + var top = (r.Top / info.BinSize).Clamp(0, rows - 1); + 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)) + yield return a; + } + } +} diff --git a/OpenRA.Game/Widgets/ViewportControllerWidget.cs b/OpenRA.Game/Widgets/ViewportControllerWidget.cs index 25b4970904..10324995b0 100755 --- a/OpenRA.Game/Widgets/ViewportControllerWidget.cs +++ b/OpenRA.Game/Widgets/ViewportControllerWidget.cs @@ -103,8 +103,8 @@ namespace OpenRA.Widgets return; } - var underCursor = world.FindUnitsAtMouse(Viewport.LastMousePos) - .Where(a => a.HasTrait()) + var underCursor = world.ScreenMap.ActorsAt(Game.viewport.ViewToWorldPx(Viewport.LastMousePos)) + .Where(a => !world.FogObscures(a) && a.HasTrait()) .OrderByDescending(a => a.Info.SelectionPriority()) .FirstOrDefault(); diff --git a/OpenRA.Game/Widgets/WorldInteractionControllerWidget.cs b/OpenRA.Game/Widgets/WorldInteractionControllerWidget.cs index 80d981b50a..d58e682a35 100644 --- a/OpenRA.Game/Widgets/WorldInteractionControllerWidget.cs +++ b/OpenRA.Game/Widgets/WorldInteractionControllerWidget.cs @@ -90,7 +90,7 @@ namespace OpenRA.Widgets { if (MultiClick) { - var unit = SelectActorsInBox(world, xy, xy, _ => true).FirstOrDefault(); + var unit = world.ScreenMap.ActorsAt(xy).FirstOrDefault(); var visibleWorld = Game.viewport.ViewBounds(world); var topLeft = Game.viewport.ViewToWorldPx(new int2(visibleWorld.Left, visibleWorld.Top)); @@ -185,7 +185,7 @@ namespace OpenRA.Widgets static readonly Actor[] NoActors = {}; IEnumerable SelectActorsInBox(World world, PPos a, PPos b, Func cond) { - return world.FindActorsInBox(a.ToWPos(0), b.ToWPos(0)) + return world.ScreenMap.ActorsInBox(a.ToInt2(), b.ToInt2()) .Where(x => x.HasTrait() && x.Trait().Info.Selectable && !world.FogObscures(x) && cond(x)) .GroupBy(x => x.GetSelectionPriority()) .OrderByDescending(g => g.Key) diff --git a/OpenRA.Game/World.cs b/OpenRA.Game/World.cs index a2288f7a3f..0d22448ebf 100644 --- a/OpenRA.Game/World.cs +++ b/OpenRA.Game/World.cs @@ -79,6 +79,7 @@ namespace OpenRA public readonly Map Map; public readonly TileSet TileSet; public readonly ActorMap ActorMap; + public readonly ScreenMap ScreenMap; public void IssueOrder( Order o ) { orderManager.IssueOrder( o ); } /* avoid exposing the OM to mod code */ @@ -121,8 +122,9 @@ namespace OpenRA TileSet = Rules.TileSets[Map.Tileset]; SharedRandom = new XRandom(orderManager.LobbyInfo.GlobalSettings.RandomSeed); - WorldActor = CreateActor( "World", new TypeDictionary() ); + WorldActor = CreateActor("World", new TypeDictionary()); ActorMap = new ActorMap(this); + ScreenMap = WorldActor.Trait(); // Add players foreach (var cmp in WorldActor.TraitsImplementing()) diff --git a/OpenRA.Game/WorldUtils.cs b/OpenRA.Game/WorldUtils.cs index 94f635ba1d..b9c0d2a4b5 100755 --- a/OpenRA.Game/WorldUtils.cs +++ b/OpenRA.Game/WorldUtils.cs @@ -21,24 +21,13 @@ namespace OpenRA { public static class WorldUtils { - public static IEnumerable FindUnitsAtMouse(this World world, int2 mouseLocation) - { - var loc = Game.viewport.ViewToWorldPx(mouseLocation).ToWPos(0); - return FindActorsInBox(world, loc, loc).Where(a => !world.FogObscures(a)); - } - public static readonly IEnumerable NoFrozenActors = new FrozenActor[0].AsEnumerable(); public static IEnumerable FindFrozenActorsAtMouse(this World world, int2 mouseLocation) { if (world.RenderPlayer == null) return NoFrozenActors; - var frozenLayer = world.RenderPlayer.PlayerActor.TraitOrDefault(); - if (frozenLayer == null) - return NoFrozenActors; - - var loc = Game.viewport.ViewToWorldPx(mouseLocation).ToInt2(); - return frozenLayer.FrozenActorsAt(loc); + return world.ScreenMap.FrozenActorsAt(world.RenderPlayer, Game.viewport.ViewToWorldPx(mouseLocation).ToInt2()); } public static IEnumerable FindActorsInBox(this World world, CPos tl, CPos br) @@ -73,13 +62,8 @@ namespace OpenRA // Target ranges are calculated in 2D, so ignore height differences var vec = new WVec(r, r, WRange.Zero); var rSq = r.Range*r.Range; - return world.FindActorsInBox(origin - vec, origin + vec).Where(a => - { - var pos = a.CenterPosition; - var dx = (long)(pos.X - origin.X); - var dy = (long)(pos.Y - origin.Y); - return dx*dx + dy*dy <= rSq; - }); + return world.FindActorsInBox(origin - vec, origin + vec).Where( + a => (a.CenterPosition - origin).HorizontalLengthSquared <= rSq); } } diff --git a/OpenRA.Mods.RA/Air/Aircraft.cs b/OpenRA.Mods.RA/Air/Aircraft.cs index 90e4285b56..ff450b021e 100755 --- a/OpenRA.Mods.RA/Air/Aircraft.cs +++ b/OpenRA.Mods.RA/Air/Aircraft.cs @@ -36,7 +36,7 @@ namespace OpenRA.Mods.RA.Air public int GetInitialFacing() { return InitialFacing; } } - public class Aircraft : IFacing, IPositionable, ISync, INotifyKilled, IIssueOrder, IOrderVoice + public class Aircraft : IFacing, IPositionable, ISync, INotifyKilled, IIssueOrder, IOrderVoice, INotifyAddedToWorld, INotifyRemovedFromWorld { static readonly Pair[] NoCells = new Pair[] { }; @@ -101,15 +101,28 @@ namespace OpenRA.Mods.RA.Air UnReserve(); } - public void SetPosition(Actor self, CPos cell) + public void SetPosition(Actor self, WPos pos) { - // Changes position, but not altitude - CenterPosition = cell.CenterPosition + new WVec(0, 0, CenterPosition.Z); + CenterPosition = pos; + + if (self.IsInWorld) + self.World.ScreenMap.Update(self); } - public void SetPosition(Actor self, WPos pos) { CenterPosition = pos; } + // Changes position, but not altitude + public void SetPosition(Actor self, CPos cell) { SetPosition(self, cell.CenterPosition + new WVec(0, 0, CenterPosition.Z)); } public void SetVisualPosition(Actor self, WPos pos) { SetPosition(self, pos); } + public void AddedToWorld(Actor self) + { + self.World.ScreenMap.Add(self); + } + + public void RemovedFromWorld(Actor self) + { + self.World.ScreenMap.Remove(self); + } + public bool AircraftCanEnter(Actor a) { if (self.AppearsHostileTo(a)) diff --git a/OpenRA.Mods.RA/Buildings/Building.cs b/OpenRA.Mods.RA/Buildings/Building.cs index be027171d7..62c64e2c72 100755 --- a/OpenRA.Mods.RA/Buildings/Building.cs +++ b/OpenRA.Mods.RA/Buildings/Building.cs @@ -97,7 +97,7 @@ namespace OpenRA.Mods.RA.Buildings } } - public class Building : INotifyDamage, IOccupySpace, INotifyCapture, ISync, ITechTreePrerequisite + public class Building : INotifyDamage, IOccupySpace, INotifyCapture, ISync, ITechTreePrerequisite, INotifyAddedToWorld, INotifyRemovedFromWorld { readonly Actor self; public readonly BuildingInfo Info; @@ -157,5 +157,15 @@ namespace OpenRA.Mods.RA.Buildings { PlayerPower = newOwner.PlayerActor.Trait(); } + + public void AddedToWorld(Actor self) + { + self.World.ScreenMap.Add(self); + } + + public void RemovedFromWorld(Actor self) + { + self.World.ScreenMap.Remove(self); + } } } diff --git a/OpenRA.Mods.RA/Crate.cs b/OpenRA.Mods.RA/Crate.cs index b1ab283250..8193cefec3 100644 --- a/OpenRA.Mods.RA/Crate.cs +++ b/OpenRA.Mods.RA/Crate.cs @@ -24,7 +24,7 @@ namespace OpenRA.Mods.RA } // ITeleportable is required for paradrop - class Crate : ITick, IPositionable, ICrushable, ISync, INotifyParachuteLanded + class Crate : ITick, IPositionable, ICrushable, ISync, INotifyParachuteLanded, INotifyAddedToWorld, INotifyRemovedFromWorld { readonly Actor self; readonly CrateInfo info; @@ -115,12 +115,25 @@ namespace OpenRA.Mods.RA rs.anim.PlayRepeating(seq); if (self.IsInWorld) + { self.World.ActorMap.Add(self, this); + self.World.ScreenMap.Update(self); + } } public bool CrushableBy(string[] crushClasses, Player owner) { return crushClasses.Contains("crate"); } + + public void AddedToWorld(Actor self) + { + self.World.ScreenMap.Add(self); + } + + public void RemovedFromWorld(Actor self) + { + self.World.ScreenMap.Remove(self); + } } } diff --git a/OpenRA.Mods.RA/Guard.cs b/OpenRA.Mods.RA/Guard.cs index 94cf1cba70..ef60634cc8 100644 --- a/OpenRA.Mods.RA/Guard.cs +++ b/OpenRA.Mods.RA/Guard.cs @@ -58,7 +58,7 @@ namespace OpenRA.Mods.RA yield break; } - var target = FriendlyGuardableUnitsAtMouse(world, mi).FirstOrDefault(); + var target = FriendlyGuardableUnits(world, mi).FirstOrDefault(); if (target == null || subjects.All(s => s.IsDead())) yield break; @@ -79,19 +79,19 @@ namespace OpenRA.Mods.RA public string GetCursor(World world, CPos xy, MouseInput mi) { - if (world.Map.IsInMap(xy)) - { - var targets = FriendlyGuardableUnitsAtMouse(world, mi); - if (targets.Any() && (subjects.Count() > 1 || (subjects.Count() == 1 && subjects.First() != targets.First()))) - return "guard"; - } - return "move-blocked"; + var multiple = subjects.Count() > 1; + var canGuard = FriendlyGuardableUnits(world, mi) + .Any(a => multiple || a != subjects.First()); + + return canGuard ? "guard" : "move-blocked"; } - static IEnumerable FriendlyGuardableUnitsAtMouse(World world, MouseInput mi) + static IEnumerable FriendlyGuardableUnits(World world, MouseInput mi) { - return world.FindUnitsAtMouse(mi.Location) - .Where(a => !a.IsDead() && a.AppearsFriendlyTo(world.LocalPlayer.PlayerActor) && a.HasTrait()); + return world.ScreenMap.ActorsAt(Game.viewport.ViewToWorldPx(mi.Location)) + .Where(a => !world.FogObscures(a) && !a.IsDead() && + a.AppearsFriendlyTo(world.LocalPlayer.PlayerActor) && + a.HasTrait()); } } diff --git a/OpenRA.Mods.RA/Husk.cs b/OpenRA.Mods.RA/Husk.cs index 8c5a4cd27e..1879b01017 100644 --- a/OpenRA.Mods.RA/Husk.cs +++ b/OpenRA.Mods.RA/Husk.cs @@ -25,7 +25,7 @@ namespace OpenRA.Mods.RA public int GetInitialFacing() { return 128; } } - class Husk : IPositionable, IFacing, ISync + class Husk : IPositionable, IFacing, ISync, INotifyAddedToWorld, INotifyRemovedFromWorld { readonly HuskInfo info; readonly Actor self; @@ -64,7 +64,11 @@ namespace OpenRA.Mods.RA } public void SetPosition(Actor self, CPos cell) { SetPosition(self, cell.CenterPosition); } - public void SetVisualPosition(Actor self, WPos pos) { CenterPosition = pos; } + public void SetVisualPosition(Actor self, WPos pos) + { + CenterPosition = pos; + self.World.ScreenMap.Update(self); + } public void SetPosition(Actor self, WPos pos) { @@ -72,6 +76,17 @@ namespace OpenRA.Mods.RA CenterPosition = pos; TopLeft = pos.ToCPos(); self.World.ActorMap.Add(self, this); + self.World.ScreenMap.Update(self); + } + + public void AddedToWorld(Actor self) + { + self.World.ScreenMap.Add(self); + } + + public void RemovedFromWorld(Actor self) + { + self.World.ScreenMap.Remove(self); } } diff --git a/OpenRA.Mods.RA/Mine.cs b/OpenRA.Mods.RA/Mine.cs index 3c3ad8df31..cab1ed9280 100644 --- a/OpenRA.Mods.RA/Mine.cs +++ b/OpenRA.Mods.RA/Mine.cs @@ -26,7 +26,7 @@ namespace OpenRA.Mods.RA public object Create(ActorInitializer init) { return new Mine(init, this); } } - class Mine : ICrushable, IOccupySpace, ISync + class Mine : ICrushable, IOccupySpace, ISync, INotifyAddedToWorld, INotifyRemovedFromWorld { readonly Actor self; readonly MineInfo info; @@ -62,6 +62,16 @@ namespace OpenRA.Mods.RA public IEnumerable> OccupiedCells() { yield return Pair.New(TopLeft, SubCell.FullCell); } public WPos CenterPosition { get { return location.CenterPosition; } } + + public void AddedToWorld(Actor self) + { + self.World.ScreenMap.Add(self); + } + + public void RemovedFromWorld(Actor self) + { + self.World.ScreenMap.Remove(self); + } } /* tag trait for stuff that shouldnt trigger mines */ diff --git a/OpenRA.Mods.RA/Minelayer.cs b/OpenRA.Mods.RA/Minelayer.cs index a8f56b9913..d52144080c 100644 --- a/OpenRA.Mods.RA/Minelayer.cs +++ b/OpenRA.Mods.RA/Minelayer.cs @@ -106,7 +106,8 @@ namespace OpenRA.Mods.RA yield break; } - var underCursor = world.FindUnitsAtMouse(mi.Location) + var underCursor = world.ScreenMap.ActorsAt(Game.viewport.ViewToWorldPx(mi.Location)) + .Where(a => !world.FogObscures(a)) .OrderByDescending(a => a.Info.Traits.Contains() ? a.Info.Traits.Get().Priority : int.MinValue) .FirstOrDefault(); diff --git a/OpenRA.Mods.RA/Move/Mobile.cs b/OpenRA.Mods.RA/Move/Mobile.cs index 5f5345e0b5..bb72477cdc 100755 --- a/OpenRA.Mods.RA/Move/Mobile.cs +++ b/OpenRA.Mods.RA/Move/Mobile.cs @@ -144,7 +144,7 @@ namespace OpenRA.Mods.RA.Move public int GetInitialFacing() { return InitialFacing; } } - public class Mobile : IIssueOrder, IResolveOrder, IOrderVoice, IPositionable, IMove, IFacing, ISync + public class Mobile : IIssueOrder, IResolveOrder, IOrderVoice, IPositionable, IMove, IFacing, ISync, INotifyAddedToWorld, INotifyRemovedFromWorld { public readonly Actor self; public readonly MobileInfo Info; @@ -231,6 +231,18 @@ namespace OpenRA.Mods.RA.Move public void SetVisualPosition(Actor self, WPos pos) { CenterPosition = pos; + if (self.IsInWorld) + self.World.ScreenMap.Update(self); + } + + public void AddedToWorld(Actor self) + { + self.World.ScreenMap.Add(self); + } + + public void RemovedFromWorld(Actor self) + { + self.World.ScreenMap.Remove(self); } public IEnumerable Orders { get { yield return new MoveOrderTargeter(Info); } } diff --git a/OpenRA.Mods.RA/Orders/PowerDownOrderGenerator.cs b/OpenRA.Mods.RA/Orders/PowerDownOrderGenerator.cs index 2a0eecc226..dc600a5407 100755 --- a/OpenRA.Mods.RA/Orders/PowerDownOrderGenerator.cs +++ b/OpenRA.Mods.RA/Orders/PowerDownOrderGenerator.cs @@ -39,9 +39,8 @@ namespace OpenRA.Mods.RA.Orders { if (mi.Button == MouseButton.Left) { - var underCursor = world.FindUnitsAtMouse(mi.Location) - .Where(a => a.Owner == world.LocalPlayer - && a.HasTrait()).FirstOrDefault(); + var underCursor = world.ScreenMap.ActorsAt(Game.viewport.ViewToWorldPx(mi.Location)) + .Where(a => a.Owner == world.LocalPlayer && a.HasTrait()).FirstOrDefault(); if (underCursor != null) yield return new Order(order, underCursor, false); diff --git a/OpenRA.Mods.RA/Orders/RepairOrderGenerator.cs b/OpenRA.Mods.RA/Orders/RepairOrderGenerator.cs index d57d624c36..c2d931ba4a 100644 --- a/OpenRA.Mods.RA/Orders/RepairOrderGenerator.cs +++ b/OpenRA.Mods.RA/Orders/RepairOrderGenerator.cs @@ -30,8 +30,8 @@ namespace OpenRA.Mods.RA.Orders { if (mi.Button == MouseButton.Left) { - var underCursor = world.FindUnitsAtMouse(mi.Location) - .Where(a => a.AppearsFriendlyTo(world.LocalPlayer.PlayerActor) && a.HasTrait()).FirstOrDefault(); + var underCursor = world.ScreenMap.ActorsAt(Game.viewport.ViewToWorldPx(mi.Location)) + .Where(a => !world.FogObscures(a) && a.AppearsFriendlyTo(world.LocalPlayer.PlayerActor) && a.HasTrait()).FirstOrDefault(); if (underCursor == null) yield break; diff --git a/mods/cnc/rules/system.yaml b/mods/cnc/rules/system.yaml index 4d1ce3e2da..2f45b021a7 100644 --- a/mods/cnc/rules/system.yaml +++ b/mods/cnc/rules/system.yaml @@ -206,6 +206,7 @@ Player: FrozenActorLayer: World: + ScreenMap: LoadWidgetAtGameStart: Widget: INGAME_ROOT CncMenuPaletteEffect: diff --git a/mods/d2k/rules/system.yaml b/mods/d2k/rules/system.yaml index 94d209e1dc..401d19e266 100644 --- a/mods/d2k/rules/system.yaml +++ b/mods/d2k/rules/system.yaml @@ -357,6 +357,7 @@ Player: PlayerStatistics: World: + ScreenMap: LoadWidgetAtGameStart: Widget: INGAME_ROOT ScreenShaker: diff --git a/mods/ra/rules/system.yaml b/mods/ra/rules/system.yaml index 14f64b79c1..49a0920f8a 100644 --- a/mods/ra/rules/system.yaml +++ b/mods/ra/rules/system.yaml @@ -529,6 +529,7 @@ Player: PlayerStatistics: World: + ScreenMap: LoadWidgetAtGameStart: Widget: INGAME_ROOT ScreenShaker: diff --git a/mods/ts/rules/system.yaml b/mods/ts/rules/system.yaml index 5a5bfdb1dd..cc885b62ed 100644 --- a/mods/ts/rules/system.yaml +++ b/mods/ts/rules/system.yaml @@ -42,6 +42,7 @@ Player: PlayerStatistics: World: + ScreenMap: LoadWidgetAtGameStart: Widget: INGAME_ROOT BuildingInfluence: