Change GetCustomMovementLayers to expose an array, not a dictionary.

As there are few custom movement layers, using an array is good for improving lookup speed. Additionally, we can simplify some code by reserving index 0 of the array for the ground layer. Code that needs to maintain a state for the ground layer and every custom movement layer can now maintain a flat array of state using index 0 for the ground layer, and the the ICustomMovementLayer.Index for the custom movement layer. This removes a lot of ternary statements checking for the ground layer special case.
This commit is contained in:
RoosterDragon
2021-10-08 21:11:14 +01:00
committed by Paul Chote
parent 3310f14dea
commit dd9d600ef9
4 changed files with 105 additions and 86 deletions

View File

@@ -88,22 +88,23 @@ namespace OpenRA.Mods.Common.Pathfinder
readonly Locomotor locomotor; readonly Locomotor locomotor;
readonly CellInfoLayerPool.PooledCellInfoLayer pooledLayer; readonly CellInfoLayerPool.PooledCellInfoLayer pooledLayer;
readonly bool checkTerrainHeight; readonly bool checkTerrainHeight;
CellLayer<CellInfo> groundInfo; readonly CellLayer<CellInfo>[] cellInfoForLayer;
readonly Dictionary<byte, (ICustomMovementLayer Layer, CellLayer<CellInfo> Info)> customLayerInfo =
new Dictionary<byte, (ICustomMovementLayer, CellLayer<CellInfo>)>();
public PathGraph(CellInfoLayerPool layerPool, Locomotor locomotor, Actor actor, World world, BlockedByActor check) public PathGraph(CellInfoLayerPool layerPool, Locomotor locomotor, Actor actor, World world, BlockedByActor check)
{ {
pooledLayer = layerPool.Get();
groundInfo = pooledLayer.GetLayer();
var locomotorInfo = locomotor.Info;
this.locomotor = locomotor; this.locomotor = locomotor;
// As we support a search over the whole map area,
// use the pool to grab the CellInfos we need to track the graph state.
// This allows us to avoid the cost of allocating large arrays constantly.
// PERF: Avoid LINQ // PERF: Avoid LINQ
foreach (var cml in world.GetCustomMovementLayers().Values) var cmls = world.GetCustomMovementLayers();
if (cml.EnabledForLocomotor(locomotorInfo)) pooledLayer = layerPool.Get();
customLayerInfo[cml.Index] = (cml, pooledLayer.GetLayer()); cellInfoForLayer = new CellLayer<CellInfo>[cmls.Length];
cellInfoForLayer[0] = pooledLayer.GetLayer();
foreach (var cml in cmls)
if (cml != null && cml.EnabledForLocomotor(locomotor.Info))
cellInfoForLayer[cml.Index] = pooledLayer.GetLayer();
World = world; World = world;
Actor = actor; Actor = actor;
@@ -132,8 +133,8 @@ namespace OpenRA.Mods.Common.Pathfinder
public List<GraphConnection> GetConnections(CPos position) public List<GraphConnection> GetConnections(CPos position)
{ {
var posLayer = position.Layer; var layer = position.Layer;
var info = posLayer == 0 ? groundInfo : customLayerInfo[posLayer].Info; var info = cellInfoForLayer[layer];
var previousPos = info[position].PreviousPos; var previousPos = info[position].PreviousPos;
var dx = position.X - previousPos.X; var dx = position.X - previousPos.X;
@@ -141,7 +142,7 @@ namespace OpenRA.Mods.Common.Pathfinder
var index = dy * 3 + dx + 4; var index = dy * 3 + dx + 4;
var directions = DirectedNeighbors[index]; var directions = DirectedNeighbors[index];
var validNeighbors = new List<GraphConnection>(directions.Length + (posLayer == 0 ? customLayerInfo.Count : 1)); var validNeighbors = new List<GraphConnection>(directions.Length + (layer == 0 ? cellInfoForLayer.Length : 1));
for (var i = 0; i < directions.Length; i++) for (var i = 0; i < directions.Length; i++)
{ {
var dir = directions[i]; var dir = directions[i];
@@ -153,12 +154,16 @@ namespace OpenRA.Mods.Common.Pathfinder
validNeighbors.Add(new GraphConnection(neighbor, pathCost)); validNeighbors.Add(new GraphConnection(neighbor, pathCost));
} }
if (posLayer == 0) var cmls = World.GetCustomMovementLayers();
if (layer == 0)
{ {
foreach (var cli in customLayerInfo.Values) foreach (var cml in cmls)
{ {
var layerPosition = new CPos(position.X, position.Y, cli.Layer.Index); if (cml == null || !cml.EnabledForLocomotor(locomotor.Info))
var entryCost = cli.Layer.EntryMovementCost(locomotor.Info, layerPosition); continue;
var layerPosition = new CPos(position.X, position.Y, cml.Index);
var entryCost = cml.EntryMovementCost(locomotor.Info, layerPosition);
if (entryCost != MovementCostForUnreachableCell) if (entryCost != MovementCostForUnreachableCell)
validNeighbors.Add(new GraphConnection(layerPosition, entryCost)); validNeighbors.Add(new GraphConnection(layerPosition, entryCost));
} }
@@ -166,7 +171,7 @@ namespace OpenRA.Mods.Common.Pathfinder
else else
{ {
var layerPosition = new CPos(position.X, position.Y, 0); var layerPosition = new CPos(position.X, position.Y, 0);
var exitCost = customLayerInfo[posLayer].Layer.ExitMovementCost(locomotor.Info, layerPosition); var exitCost = cmls[layer].ExitMovementCost(locomotor.Info, layerPosition);
if (exitCost != MovementCostForUnreachableCell) if (exitCost != MovementCostForUnreachableCell)
validNeighbors.Add(new GraphConnection(layerPosition, exitCost)); validNeighbors.Add(new GraphConnection(layerPosition, exitCost));
} }
@@ -226,14 +231,12 @@ namespace OpenRA.Mods.Common.Pathfinder
public CellInfo this[CPos pos] public CellInfo this[CPos pos]
{ {
get => (pos.Layer == 0 ? groundInfo : customLayerInfo[pos.Layer].Info)[pos]; get => cellInfoForLayer[pos.Layer][pos];
set => (pos.Layer == 0 ? groundInfo : customLayerInfo[pos.Layer].Info)[pos] = value; set => cellInfoForLayer[pos.Layer][pos] = value;
} }
public void Dispose() public void Dispose()
{ {
groundInfo = null;
customLayerInfo.Clear();
pooledLayer.Dispose(); pooledLayer.Dispose();
} }
} }

View File

@@ -428,10 +428,8 @@ namespace OpenRA.Mods.Common.Traits
if (ToCell.Layer == 0) if (ToCell.Layer == 0)
return true; return true;
if (self.World.GetCustomMovementLayers().TryGetValue(ToCell.Layer, out var layer)) var layer = self.World.GetCustomMovementLayers()[ToCell.Layer];
return layer.InteractsWithDefaultLayer; return layer == null || layer.InteractsWithDefaultLayer;
return true;
} }
#endregion #endregion
@@ -862,9 +860,7 @@ namespace OpenRA.Mods.Common.Traits
return; return;
} }
var cml = self.World.WorldActor.TraitsImplementing<ICustomMovementLayer>() var cml = self.World.GetCustomMovementLayers()[self.Location.Layer];
.First(l => l.Index == self.Location.Layer);
if (!cml.ReturnToGroundLayerOnIdle) if (!cml.ReturnToGroundLayerOnIdle)
return; return;

View File

@@ -171,9 +171,10 @@ namespace OpenRA.Mods.Common.Traits
readonly Dictionary<int, ProximityTrigger> proximityTriggers = new Dictionary<int, ProximityTrigger>(); readonly Dictionary<int, ProximityTrigger> proximityTriggers = new Dictionary<int, ProximityTrigger>();
int nextTriggerId; int nextTriggerId;
readonly CellLayer<InfluenceNode> influence; CellLayer<InfluenceNode>[] influence;
readonly Dictionary<int, CellLayer<InfluenceNode>> customInfluence = new Dictionary<int, CellLayer<InfluenceNode>>();
public readonly Dictionary<int, ICustomMovementLayer> CustomMovementLayers = new Dictionary<int, ICustomMovementLayer>(); // Index 0 is kept null as layer 0 is used for the ground layer.
public ICustomMovementLayer[] CustomMovementLayers = new ICustomMovementLayer[] { null };
public event Action<CPos> CellUpdated; public event Action<CPos> CellUpdated;
readonly Bin[] bins; readonly Bin[] bins;
readonly int rows, cols; readonly int rows, cols;
@@ -191,7 +192,7 @@ namespace OpenRA.Mods.Common.Traits
{ {
this.info = info; this.info = info;
map = world.Map; map = world.Map;
influence = new CellLayer<InfluenceNode>(world.Map); influence = new[] { new CellLayer<InfluenceNode>(world.Map) };
cols = CellCoordToBinIndex(world.Map.MapSize.X) + 1; cols = CellCoordToBinIndex(world.Map.MapSize.X) + 1;
rows = CellCoordToBinIndex(world.Map.MapSize.Y) + 1; rows = CellCoordToBinIndex(world.Map.MapSize.Y) + 1;
@@ -210,10 +211,18 @@ namespace OpenRA.Mods.Common.Traits
void INotifyCreated.Created(Actor self) void INotifyCreated.Created(Actor self)
{ {
foreach (var cml in self.TraitsImplementing<ICustomMovementLayer>()) var cmls = self.TraitsImplementing<ICustomMovementLayer>().ToList();
if (cmls.Count == 0)
return;
var length = cmls.Max(cml => cml.Index) + 1;
Array.Resize(ref CustomMovementLayers, length);
Array.Resize(ref influence, length);
foreach (var cml in cmls)
{ {
CustomMovementLayers[cml.Index] = cml; CustomMovementLayers[cml.Index] = cml;
customInfluence.Add(cml.Index, new CellLayer<InfluenceNode>(self.World.Map)); influence[cml.Index] = new CellLayer<InfluenceNode>(self.World.Map);
} }
} }
@@ -259,21 +268,21 @@ namespace OpenRA.Mods.Common.Traits
{ {
// PERF: Custom enumerator for efficiency - using `yield` is slower. // PERF: Custom enumerator for efficiency - using `yield` is slower.
var uv = a.ToMPos(map); var uv = a.ToMPos(map);
if (!influence.Contains(uv)) var layer = influence[a.Layer];
if (!layer.Contains(uv))
return Enumerable.Empty<Actor>(); return Enumerable.Empty<Actor>();
var layer = a.Layer == 0 ? influence : customInfluence[a.Layer];
return new ActorsAtEnumerable(layer[uv]); return new ActorsAtEnumerable(layer[uv]);
} }
public IEnumerable<Actor> GetActorsAt(CPos a, SubCell sub) public IEnumerable<Actor> GetActorsAt(CPos a, SubCell sub)
{ {
var uv = a.ToMPos(map); var uv = a.ToMPos(map);
if (!influence.Contains(uv)) var layer = influence[a.Layer];
if (!layer.Contains(uv))
yield break; yield break;
var always = sub == SubCell.FullCell || sub == SubCell.Any; var always = sub == SubCell.FullCell || sub == SubCell.Any;
var layer = a.Layer == 0 ? influence : customInfluence[a.Layer];
for (var i = layer[uv]; i != null; i = i.Next) for (var i = layer[uv]; i != null; i = i.Next)
if (!i.Actor.Disposed && (i.SubCell == sub || i.SubCell == SubCell.FullCell || always)) if (!i.Actor.Disposed && (i.SubCell == sub || i.SubCell == SubCell.FullCell || always))
yield return i.Actor; yield return i.Actor;
@@ -287,17 +296,18 @@ namespace OpenRA.Mods.Common.Traits
public SubCell FreeSubCell(CPos cell, SubCell preferredSubCell = SubCell.Any, bool checkTransient = true) public SubCell FreeSubCell(CPos cell, SubCell preferredSubCell = SubCell.Any, bool checkTransient = true)
{ {
var uv = cell.ToMPos(map); var uv = cell.ToMPos(map);
if (!influence.Contains(uv)) var layer = influence[cell.Layer];
if (!layer.Contains(uv))
return preferredSubCell != SubCell.Any ? preferredSubCell : SubCell.First; return preferredSubCell != SubCell.Any ? preferredSubCell : SubCell.First;
if (preferredSubCell != SubCell.Any && !AnyActorsAt(uv, cell, preferredSubCell, checkTransient)) if (preferredSubCell != SubCell.Any && !AnyActorsAt(uv, cell, layer, preferredSubCell, checkTransient))
return preferredSubCell; return preferredSubCell;
if (!AnyActorsAt(uv, cell.Layer)) if (!AnyActorsAt(uv, layer))
return map.Grid.DefaultSubCell; return map.Grid.DefaultSubCell;
for (var i = (int)SubCell.First; i < map.Grid.SubCellOffsets.Length; i++) for (var i = (int)SubCell.First; i < map.Grid.SubCellOffsets.Length; i++)
if (i != (int)preferredSubCell && !AnyActorsAt(uv, cell, (SubCell)i, checkTransient)) if (i != (int)preferredSubCell && !AnyActorsAt(uv, cell, layer, (SubCell)i, checkTransient))
return (SubCell)i; return (SubCell)i;
return SubCell.Invalid; return SubCell.Invalid;
@@ -306,26 +316,26 @@ namespace OpenRA.Mods.Common.Traits
public SubCell FreeSubCell(CPos cell, SubCell preferredSubCell, Func<Actor, bool> checkIfBlocker) public SubCell FreeSubCell(CPos cell, SubCell preferredSubCell, Func<Actor, bool> checkIfBlocker)
{ {
var uv = cell.ToMPos(map); var uv = cell.ToMPos(map);
if (!influence.Contains(uv)) var layer = influence[cell.Layer];
if (!layer.Contains(uv))
return preferredSubCell != SubCell.Any ? preferredSubCell : SubCell.First; return preferredSubCell != SubCell.Any ? preferredSubCell : SubCell.First;
if (preferredSubCell != SubCell.Any && !AnyActorsAt(uv, cell, preferredSubCell, checkIfBlocker)) if (preferredSubCell != SubCell.Any && !AnyActorsAt(uv, layer, preferredSubCell, checkIfBlocker))
return preferredSubCell; return preferredSubCell;
if (!AnyActorsAt(uv, cell.Layer)) if (!AnyActorsAt(uv, layer))
return map.Grid.DefaultSubCell; return map.Grid.DefaultSubCell;
for (var i = (byte)SubCell.First; i < map.Grid.SubCellOffsets.Length; i++) for (var i = (byte)SubCell.First; i < map.Grid.SubCellOffsets.Length; i++)
if (i != (byte)preferredSubCell && !AnyActorsAt(uv, cell, (SubCell)i, checkIfBlocker)) if (i != (byte)preferredSubCell && !AnyActorsAt(uv, layer, (SubCell)i, checkIfBlocker))
return (SubCell)i; return (SubCell)i;
return SubCell.Invalid; return SubCell.Invalid;
} }
// NOTE: pos required to be in map bounds // NOTE: pos required to be in map bounds
bool AnyActorsAt(MPos uv, int layerIndex) bool AnyActorsAt(MPos uv, CellLayer<InfluenceNode> layer)
{ {
var layer = layerIndex == 0 ? influence : customInfluence[layerIndex];
return layer[uv] != null; return layer[uv] != null;
} }
@@ -333,17 +343,17 @@ namespace OpenRA.Mods.Common.Traits
public bool AnyActorsAt(CPos a) public bool AnyActorsAt(CPos a)
{ {
var uv = a.ToMPos(map); var uv = a.ToMPos(map);
if (!influence.Contains(uv)) var layer = influence[a.Layer];
if (!layer.Contains(uv))
return false; return false;
return AnyActorsAt(uv, a.Layer); return AnyActorsAt(uv, layer);
} }
// NOTE: pos required to be in map bounds // NOTE: pos required to be in map bounds
bool AnyActorsAt(MPos uv, CPos a, SubCell sub, bool checkTransient) bool AnyActorsAt(MPos uv, CPos a, CellLayer<InfluenceNode> layer, SubCell sub, bool checkTransient)
{ {
var always = sub == SubCell.FullCell || sub == SubCell.Any; var always = sub == SubCell.FullCell || sub == SubCell.Any;
var layer = a.Layer == 0 ? influence : customInfluence[a.Layer];
for (var i = layer[uv]; i != null; i = i.Next) for (var i = layer[uv]; i != null; i = i.Next)
{ {
if (always || i.SubCell == sub || i.SubCell == SubCell.FullCell) if (always || i.SubCell == sub || i.SubCell == SubCell.FullCell)
@@ -364,17 +374,17 @@ namespace OpenRA.Mods.Common.Traits
public bool AnyActorsAt(CPos a, SubCell sub, bool checkTransient = true) public bool AnyActorsAt(CPos a, SubCell sub, bool checkTransient = true)
{ {
var uv = a.ToMPos(map); var uv = a.ToMPos(map);
if (!influence.Contains(uv)) var layer = influence[a.Layer];
if (!layer.Contains(uv))
return false; return false;
return AnyActorsAt(uv, a, sub, checkTransient); return AnyActorsAt(uv, a, layer, sub, checkTransient);
} }
// NOTE: can not check aircraft // NOTE: can not check aircraft
bool AnyActorsAt(MPos uv, CPos a, SubCell sub, Func<Actor, bool> withCondition) bool AnyActorsAt(MPos uv, CellLayer<InfluenceNode> layer, SubCell sub, Func<Actor, bool> withCondition)
{ {
var always = sub == SubCell.FullCell || sub == SubCell.Any; var always = sub == SubCell.FullCell || sub == SubCell.Any;
var layer = a.Layer == 0 ? influence : customInfluence[a.Layer];
for (var i = layer[uv]; i != null; i = i.Next) for (var i = layer[uv]; i != null; i = i.Next)
if ((always || i.SubCell == sub || i.SubCell == SubCell.FullCell) && !i.Actor.Disposed && withCondition(i.Actor)) if ((always || i.SubCell == sub || i.SubCell == SubCell.FullCell) && !i.Actor.Disposed && withCondition(i.Actor))
return true; return true;
@@ -386,10 +396,11 @@ namespace OpenRA.Mods.Common.Traits
public bool AnyActorsAt(CPos a, SubCell sub, Func<Actor, bool> withCondition) public bool AnyActorsAt(CPos a, SubCell sub, Func<Actor, bool> withCondition)
{ {
var uv = a.ToMPos(map); var uv = a.ToMPos(map);
if (!influence.Contains(uv)) var layer = influence[a.Layer];
if (!layer.Contains(uv))
return false; return false;
return AnyActorsAt(uv, a, sub, withCondition); return AnyActorsAt(uv, layer, sub, withCondition);
} }
public void AddInfluence(Actor self, IOccupySpace ios) public void AddInfluence(Actor self, IOccupySpace ios)
@@ -397,10 +408,10 @@ namespace OpenRA.Mods.Common.Traits
foreach (var c in ios.OccupiedCells()) foreach (var c in ios.OccupiedCells())
{ {
var uv = c.Cell.ToMPos(map); var uv = c.Cell.ToMPos(map);
if (!influence.Contains(uv)) var layer = influence[c.Cell.Layer];
if (!layer.Contains(uv))
continue; continue;
var layer = c.Cell.Layer == 0 ? influence : customInfluence[c.Cell.Layer];
layer[uv] = new InfluenceNode { Next = layer[uv], SubCell = c.SubCell, Actor = self }; layer[uv] = new InfluenceNode { Next = layer[uv], SubCell = c.SubCell, Actor = self };
if (cellTriggerInfluence.TryGetValue(c.Cell, out var triggers)) if (cellTriggerInfluence.TryGetValue(c.Cell, out var triggers))
@@ -416,10 +427,10 @@ namespace OpenRA.Mods.Common.Traits
foreach (var c in ios.OccupiedCells()) foreach (var c in ios.OccupiedCells())
{ {
var uv = c.Cell.ToMPos(map); var uv = c.Cell.ToMPos(map);
if (!influence.Contains(uv)) var layer = influence[c.Cell.Layer];
if (!layer.Contains(uv))
continue; continue;
var layer = c.Cell.Layer == 0 ? influence : customInfluence[c.Cell.Layer];
var temp = layer[uv]; var temp = layer[uv];
RemoveInfluenceInner(ref temp, self); RemoveInfluenceInner(ref temp, self);
layer[uv] = temp; layer[uv] = temp;
@@ -496,9 +507,10 @@ namespace OpenRA.Mods.Common.Traits
var t = new CellTrigger(cells, onEntry, onExit); var t = new CellTrigger(cells, onEntry, onExit);
cellTriggers.Add(id, t); cellTriggers.Add(id, t);
var layer = influence[0];
foreach (var c in cells) foreach (var c in cells)
{ {
if (!influence.Contains(c)) if (!layer.Contains(c))
continue; continue;
if (!cellTriggerInfluence.ContainsKey(c)) if (!cellTriggerInfluence.ContainsKey(c))
@@ -649,7 +661,15 @@ namespace OpenRA.Mods.Common.Traits
public static class ActorMapWorldExts public static class ActorMapWorldExts
{ {
public static Dictionary<int, ICustomMovementLayer> GetCustomMovementLayers(this World world) /// <summary>
/// Returns an array of custom movement layers.
/// The <see cref="ICustomMovementLayer.Index"/> of a layer is used to index into this array.
/// This array may contain null entries for layers which are not present in the world.
/// This array is guaranteed to have a length of at least one. Index 0 is always null.
/// Index 0 is kept null as layer 0 is used for the ground layer, consumers can combine
/// the ground layer and custom layers into a single array for easy indexing.
/// </summary>
public static ICustomMovementLayer[] GetCustomMovementLayers(this World world)
{ {
return ((ActorMap)world.ActorMap).CustomMovementLayers; return ((ActorMap)world.ActorMap).CustomMovementLayers;
} }

View File

@@ -144,18 +144,16 @@ namespace OpenRA.Mods.Common.Traits
public readonly LocomotorInfo Info; public readonly LocomotorInfo Info;
public readonly uint MovementClass; public readonly uint MovementClass;
CellLayer<short> cellsCost;
CellLayer<CellCache> blockingCache;
readonly Dictionary<byte, CellLayer<short>> customLayerCellsCost = new Dictionary<byte, CellLayer<short>>();
readonly Dictionary<byte, CellLayer<CellCache>> customLayerBlockingCache = new Dictionary<byte, CellLayer<CellCache>>();
readonly LocomotorInfo.TerrainInfo[] terrainInfos; readonly LocomotorInfo.TerrainInfo[] terrainInfos;
readonly World world; readonly World world;
readonly HashSet<CPos> dirtyCells = new HashSet<CPos>(); readonly HashSet<CPos> dirtyCells = new HashSet<CPos>();
readonly bool sharesCell;
CellLayer<short>[] cellsCost;
CellLayer<CellCache>[] blockingCache;
IActorMap actorMap; IActorMap actorMap;
bool sharesCell;
public Locomotor(Actor self, LocomotorInfo info) public Locomotor(Actor self, LocomotorInfo info)
{ {
@@ -177,7 +175,7 @@ namespace OpenRA.Mods.Common.Traits
if (!world.Map.Contains(cell)) if (!world.Map.Contains(cell))
return PathGraph.MovementCostForUnreachableCell; return PathGraph.MovementCostForUnreachableCell;
return cell.Layer == 0 ? cellsCost[cell] : customLayerCellsCost[cell.Layer][cell]; return cellsCost[cell.Layer][cell];
} }
public int MovementSpeedForCell(CPos cell) public int MovementSpeedForCell(CPos cell)
@@ -193,7 +191,7 @@ namespace OpenRA.Mods.Common.Traits
if (!world.Map.Contains(destNode)) if (!world.Map.Contains(destNode))
return PathGraph.MovementCostForUnreachableCell; return PathGraph.MovementCostForUnreachableCell;
var cellCost = destNode.Layer == 0 ? cellsCost[destNode] : customLayerCellsCost[destNode.Layer][destNode]; var cellCost = cellsCost[destNode.Layer][destNode];
if (cellCost == PathGraph.MovementCostForUnreachableCell || if (cellCost == PathGraph.MovementCostForUnreachableCell ||
!CanMoveFreelyInto(actor, destNode, check, ignoreActor)) !CanMoveFreelyInto(actor, destNode, check, ignoreActor))
@@ -359,8 +357,8 @@ namespace OpenRA.Mods.Common.Traits
actorMap = w.ActorMap; actorMap = w.ActorMap;
actorMap.CellUpdated += CellUpdated; actorMap.CellUpdated += CellUpdated;
blockingCache = new CellLayer<CellCache>(map); cellsCost = new[] { new CellLayer<short>(map) };
cellsCost = new CellLayer<short>(map); blockingCache = new[] { new CellLayer<CellCache>(map) };
foreach (var cell in map.AllCells) foreach (var cell in map.AllCells)
UpdateCellCost(cell); UpdateCellCost(cell);
@@ -371,12 +369,17 @@ namespace OpenRA.Mods.Common.Traits
// This section needs to run after WorldLoaded() because we need to be sure that all types of ICustomMovementLayer have been initialized. // This section needs to run after WorldLoaded() because we need to be sure that all types of ICustomMovementLayer have been initialized.
w.AddFrameEndTask(_ => w.AddFrameEndTask(_ =>
{ {
var customMovementLayers = w.WorldActor.TraitsImplementing<ICustomMovementLayer>(); var cmls = world.GetCustomMovementLayers();
foreach (var cml in customMovementLayers) Array.Resize(ref cellsCost, cmls.Length);
Array.Resize(ref blockingCache, cmls.Length);
foreach (var cml in cmls)
{ {
if (cml == null)
continue;
var cellLayer = new CellLayer<short>(map); var cellLayer = new CellLayer<short>(map);
customLayerCellsCost[cml.Index] = cellLayer; cellsCost[cml.Index] = cellLayer;
customLayerBlockingCache[cml.Index] = new CellLayer<CellCache>(map); blockingCache[cml.Index] = new CellLayer<CellCache>(map);
foreach (var cell in map.AllCells) foreach (var cell in map.AllCells)
{ {
@@ -395,13 +398,10 @@ namespace OpenRA.Mods.Common.Traits
CellCache GetCache(CPos cell) CellCache GetCache(CPos cell)
{ {
if (dirtyCells.Contains(cell)) if (dirtyCells.Remove(cell))
{
UpdateCellBlocking(cell); UpdateCellBlocking(cell);
dirtyCells.Remove(cell);
}
var cache = cell.Layer == 0 ? blockingCache : customLayerBlockingCache[cell.Layer]; var cache = blockingCache[cell.Layer];
return cache[cell]; return cache[cell];
} }
@@ -422,7 +422,7 @@ namespace OpenRA.Mods.Common.Traits
if (index != byte.MaxValue) if (index != byte.MaxValue)
cost = terrainInfos[index].Cost; cost = terrainInfos[index].Cost;
var cache = cell.Layer == 0 ? cellsCost : customLayerCellsCost[cell.Layer]; var cache = cellsCost[cell.Layer];
cache[cell] = cost; cache[cell] = cost;
} }
@@ -431,7 +431,7 @@ namespace OpenRA.Mods.Common.Traits
{ {
using (new PerfSample("locomotor_cache")) using (new PerfSample("locomotor_cache"))
{ {
var cache = cell.Layer == 0 ? blockingCache : customLayerBlockingCache[cell.Layer]; var cache = blockingCache[cell.Layer];
var actors = actorMap.GetActorsAt(cell); var actors = actorMap.GetActorsAt(cell);
var cellFlag = CellFlag.HasFreeSpace; var cellFlag = CellFlag.HasFreeSpace;