Fix and optimize FindTilesInCircle
This commit is contained in:
@@ -630,9 +630,9 @@ namespace OpenRA
|
||||
}
|
||||
|
||||
public const int MaxTilesInCircleRange = 50;
|
||||
static List<CVec>[] TilesByDistance = InitTilesByDistance(MaxTilesInCircleRange);
|
||||
static CVec[][] TilesByDistance = InitTilesByDistance(MaxTilesInCircleRange);
|
||||
|
||||
static List<CVec>[] InitTilesByDistance(int max)
|
||||
static CVec[][] InitTilesByDistance(int max)
|
||||
{
|
||||
var ts = new List<CVec>[max + 1];
|
||||
for (var i = 0; i < max + 1; i++)
|
||||
@@ -643,15 +643,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<CPos> 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<CPos> 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])
|
||||
{
|
||||
@@ -661,5 +672,10 @@ namespace OpenRA
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<CPos> FindTilesInCircle(CPos center, int maxRange)
|
||||
{
|
||||
return FindTilesInAnnulus(center, 0, maxRange);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -99,18 +99,13 @@ namespace OpenRA.Mods.RA.Activities
|
||||
if (pos.CanEnterCell(destination) && self.Owner.Shroud.IsExplored(destination))
|
||||
return destination;
|
||||
|
||||
var searched = new List<CPos>();
|
||||
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;
|
||||
|
||||
@@ -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<CPos>();
|
||||
// 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<CPos>();
|
||||
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;
|
||||
|
||||
@@ -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<RevealsShroud>())
|
||||
.SelectMany(a => world.Map.FindTilesInCircle(a.Location, a.Trait<RevealsShroud>().Range.Clamp(WRange.Zero, WRange.FromCells(50)).Range / 1024))
|
||||
.SelectMany(a => world.Map.FindTilesInCircle(
|
||||
a.Location,
|
||||
a.Trait<RevealsShroud>().Range.Clamp(WRange.Zero, WRange.FromCells(Map.MaxTilesInCircleRange)).Range / 1024))
|
||||
.Distinct()
|
||||
.Count() / total;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user