diff --git a/OpenRA.Game/Map/Map.cs b/OpenRA.Game/Map/Map.cs index 484362f138..480c889fd3 100644 --- a/OpenRA.Game/Map/Map.cs +++ b/OpenRA.Game/Map/Map.cs @@ -701,9 +701,9 @@ namespace OpenRA } public const int MaxTilesInCircleRange = 50; - static List[] TilesByDistance = InitTilesByDistance(MaxTilesInCircleRange); + static CVec[][] TilesByDistance = InitTilesByDistance(MaxTilesInCircleRange); - static List[] InitTilesByDistance(int max) + static CVec[][] InitTilesByDistance(int max) { var ts = new List[max + 1]; for (var i = 0; i < max + 1; i++) @@ -714,15 +714,26 @@ namespace OpenRA if (max * max >= i * i + j * j) ts [(int)Math.Ceiling(Math.Sqrt(i * i + j * j))].Add(new CVec(i, j)); - return ts; + // Sort each integer-distance group by the actual distance + foreach (var list in ts) + list.Sort((a, b) => a.LengthSquared.CompareTo(b.LengthSquared)); + + return ts.Select(list => list.ToArray()).ToArray(); } - public IEnumerable FindTilesInCircle(CPos center, int range) + // Both ranges are inclusive because everything that calls it is designed for maxRange being inclusive: + // it rounds the actual distance up to the next integer so that this call + // will return any cells that intersect with the requested range circle. + // The returned positions are sorted by distance from the center. + public IEnumerable FindTilesInAnnulus(CPos center, int minRange, int maxRange) { - if (range >= TilesByDistance.Length) - throw new InvalidOperationException("FindTilesInCircle supports queries for only <= {0}".F(MaxTilesInCircleRange)); + if (maxRange < minRange) + throw new ArgumentOutOfRangeException("maxRange", "Maximum range is less than the minimum range."); - for (var i = 0; i <= range; i++) + if (maxRange > TilesByDistance.Length) + throw new ArgumentOutOfRangeException("maxRange", "The requested range ({0}) exceeds the maximum allowed ({1})".F(maxRange, MaxTilesInCircleRange)); + + for (var i = minRange; i <= maxRange; i++) { foreach (var offset in TilesByDistance[i]) { @@ -732,5 +743,10 @@ namespace OpenRA } } } + + public IEnumerable FindTilesInCircle(CPos center, int maxRange) + { + return FindTilesInAnnulus(center, 0, maxRange); + } } } diff --git a/OpenRA.Mods.RA/AI/HackyAI.cs b/OpenRA.Mods.RA/AI/HackyAI.cs index 0d179f8d3f..ead8a6ccb6 100644 --- a/OpenRA.Mods.RA/AI/HackyAI.cs +++ b/OpenRA.Mods.RA/AI/HackyAI.cs @@ -347,8 +347,7 @@ namespace OpenRA.Mods.RA.AI { for (var k = MaxBaseDistance; k >= 0; k--) { - var tlist = Map.FindTilesInCircle(center, k) - .OrderBy(a => (world.Map.CenterOfCell(a) - pos).LengthSquared); + var tlist = Map.FindTilesInCircle(center, k); foreach (var t in tlist) if (world.CanPlaceBuilding(actorType, bi, t, null)) diff --git a/OpenRA.Mods.RA/Activities/MoveWithinRange.cs b/OpenRA.Mods.RA/Activities/MoveWithinRange.cs index 3093f3c4e4..6775fa94ca 100755 --- a/OpenRA.Mods.RA/Activities/MoveWithinRange.cs +++ b/OpenRA.Mods.RA/Activities/MoveWithinRange.cs @@ -45,16 +45,13 @@ namespace OpenRA.Mods.RA.Activities { var map = self.World.Map; var maxCells = (maxRange.Range + 1023) / 1024; - var outerCells = map.FindTilesInCircle(targetPosition, maxCells); - var minCells = minRange.Range / 1024; - var innerCells = map.FindTilesInCircle(targetPosition, minCells); var outerSq = maxRange.Range * maxRange.Range; var innerSq = minRange.Range * minRange.Range; var center = target.CenterPosition; - return outerCells.Except(innerCells).Where(c => + return map.FindTilesInAnnulus(targetPosition, minCells + 1, maxCells).Where(c => { var dxSq = (map.CenterOfCell(c) - center).HorizontalLengthSquared; return dxSq >= innerSq && dxSq <= outerSq; diff --git a/OpenRA.Mods.RA/Activities/Teleport.cs b/OpenRA.Mods.RA/Activities/Teleport.cs index c8a0003cfd..34893e4abe 100755 --- a/OpenRA.Mods.RA/Activities/Teleport.cs +++ b/OpenRA.Mods.RA/Activities/Teleport.cs @@ -99,18 +99,13 @@ namespace OpenRA.Mods.RA.Activities if (pos.CanEnterCell(destination) && self.Owner.Shroud.IsExplored(destination)) return destination; - var searched = new List(); - for (var r = 1; r <= maxCellSearchRange || (maximumDistance != null && r <= maximumDistance); r++) + var max = maximumDistance != null ? maximumDistance.Value : maxCellSearchRange; + foreach (var tile in self.World.Map.FindTilesInCircle(destination, max)) { - foreach (var tile in self.World.Map.FindTilesInCircle(destination, r).Except(searched)) - { - if (self.Owner.Shroud.IsExplored(tile) - && (restrictTo == null || (restrictTo != null && restrictTo.Contains(tile))) - && pos.CanEnterCell(tile)) - return tile; - - searched.Add(tile); - } + if (self.Owner.Shroud.IsExplored(tile) + && (restrictTo == null || (restrictTo != null && restrictTo.Contains(tile))) + && pos.CanEnterCell(tile)) + return tile; } return null; diff --git a/OpenRA.Mods.RA/Move/Mobile.cs b/OpenRA.Mods.RA/Move/Mobile.cs index e6b6566a03..55c2307a96 100755 --- a/OpenRA.Mods.RA/Move/Mobile.cs +++ b/OpenRA.Mods.RA/Move/Mobile.cs @@ -313,6 +313,7 @@ namespace OpenRA.Mods.RA.Move public CPos NearestMoveableCell(CPos target) { + // Limit search to a radius of 10 tiles return NearestMoveableCell(target, 1, 10); } @@ -321,18 +322,9 @@ namespace OpenRA.Mods.RA.Move if (CanEnterCell(target)) return target; - var searched = new List(); - // Limit search to a radius of 10 tiles - for (var r = minRange; r < maxRange; r++) - { - foreach (var tile in self.World.Map.FindTilesInCircle(target, r).Except(searched)) - { - if (CanEnterCell(tile)) - return tile; - - searched.Add(tile); - } - } + foreach (var tile in self.World.Map.FindTilesInAnnulus(target, minRange, maxRange)) + if (CanEnterCell(tile)) + return tile; // Couldn't find a cell return target; @@ -343,17 +335,9 @@ namespace OpenRA.Mods.RA.Move if (check(target)) return target; - var searched = new List(); - for (var r = minRange; r < maxRange; r++) - { - foreach (var tile in self.World.Map.FindTilesInCircle(target, r).Except(searched)) - { - if (check(tile)) - return tile; - - searched.Add(tile); - } - } + foreach (var tile in self.World.Map.FindTilesInAnnulus(target, minRange, maxRange)) + if (check(tile)) + return tile; // Couldn't find a cell return target; diff --git a/OpenRA.Mods.RA/Player/PlayerStatistics.cs b/OpenRA.Mods.RA/Player/PlayerStatistics.cs index 3672ce4d39..066cefe41b 100644 --- a/OpenRA.Mods.RA/Player/PlayerStatistics.cs +++ b/OpenRA.Mods.RA/Player/PlayerStatistics.cs @@ -58,7 +58,9 @@ namespace OpenRA.Mods.RA var total = (double)world.Map.Bounds.Width * world.Map.Bounds.Height; MapControl = world.Actors .Where(a => !a.IsDead() && a.IsInWorld && a.Owner == player && a.HasTrait()) - .SelectMany(a => world.Map.FindTilesInCircle(a.Location, a.Trait().Range.Clamp(WRange.Zero, WRange.FromCells(50)).Range / 1024)) + .SelectMany(a => world.Map.FindTilesInCircle( + a.Location, + a.Trait().Range.Clamp(WRange.Zero, WRange.FromCells(Map.MaxTilesInCircleRange)).Range / 1024)) .Distinct() .Count() / total; } diff --git a/OpenRA.Mods.RA/SpawnMPUnits.cs b/OpenRA.Mods.RA/SpawnMPUnits.cs index 5b2e150d5a..95103168ea 100644 --- a/OpenRA.Mods.RA/SpawnMPUnits.cs +++ b/OpenRA.Mods.RA/SpawnMPUnits.cs @@ -52,8 +52,7 @@ namespace OpenRA.Mods.RA return; // Spawn support units in an annulus around the base actor - var supportSpawnCells = w.Map.FindTilesInCircle(sp, unitGroup.OuterSupportRadius) - .Except(w.Map.FindTilesInCircle(sp, unitGroup.InnerSupportRadius)); + var supportSpawnCells = w.Map.FindTilesInAnnulus(sp, unitGroup.InnerSupportRadius + 1, unitGroup.OuterSupportRadius); foreach (var s in unitGroup.SupportActors) {