Merge pull request #9140 from RoosterDragon/safe-pathfinder-perf
Improve pathfinder performance
This commit is contained in:
@@ -246,6 +246,7 @@ namespace OpenRA
|
|||||||
[FieldLoader.Ignore] public Lazy<CellLayer<byte>> MapHeight;
|
[FieldLoader.Ignore] public Lazy<CellLayer<byte>> MapHeight;
|
||||||
|
|
||||||
[FieldLoader.Ignore] public CellLayer<byte> CustomTerrain;
|
[FieldLoader.Ignore] public CellLayer<byte> CustomTerrain;
|
||||||
|
[FieldLoader.Ignore] CellLayer<short> cachedTerrainIndexes;
|
||||||
|
|
||||||
[FieldLoader.Ignore] bool initializedCellProjection;
|
[FieldLoader.Ignore] bool initializedCellProjection;
|
||||||
[FieldLoader.Ignore] CellLayer<PPos[]> cellProjection;
|
[FieldLoader.Ignore] CellLayer<PPos[]> cellProjection;
|
||||||
@@ -292,6 +293,8 @@ namespace OpenRA
|
|||||||
{
|
{
|
||||||
var ret = new CellLayer<TerrainTile>(tileShape, size);
|
var ret = new CellLayer<TerrainTile>(tileShape, size);
|
||||||
ret.Clear(tileRef);
|
ret.Clear(tileRef);
|
||||||
|
if (MaximumTerrainHeight > 0)
|
||||||
|
ret.CellEntryChanged += UpdateProjection;
|
||||||
return ret;
|
return ret;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -299,6 +302,8 @@ namespace OpenRA
|
|||||||
{
|
{
|
||||||
var ret = new CellLayer<byte>(tileShape, size);
|
var ret = new CellLayer<byte>(tileShape, size);
|
||||||
ret.Clear(0);
|
ret.Clear(0);
|
||||||
|
if (MaximumTerrainHeight > 0)
|
||||||
|
ret.CellEntryChanged += UpdateProjection;
|
||||||
return ret;
|
return ret;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -762,6 +767,9 @@ namespace OpenRA
|
|||||||
|
|
||||||
bool ContainsAllProjectedCellsCovering(MPos uv)
|
bool ContainsAllProjectedCellsCovering(MPos uv)
|
||||||
{
|
{
|
||||||
|
if (MaximumTerrainHeight == 0)
|
||||||
|
return Contains((PPos)uv);
|
||||||
|
|
||||||
foreach (var puv in ProjectedCellsCovering(uv))
|
foreach (var puv in ProjectedCellsCovering(uv))
|
||||||
if (!Contains(puv))
|
if (!Contains(puv))
|
||||||
return false;
|
return false;
|
||||||
@@ -947,9 +955,32 @@ namespace OpenRA
|
|||||||
|
|
||||||
public byte GetTerrainIndex(CPos cell)
|
public byte GetTerrainIndex(CPos cell)
|
||||||
{
|
{
|
||||||
|
const short InvalidCachedTerrainIndex = -1;
|
||||||
|
|
||||||
|
// Lazily initialize a cache for terrain indexes.
|
||||||
|
if (cachedTerrainIndexes == null)
|
||||||
|
{
|
||||||
|
cachedTerrainIndexes = new CellLayer<short>(this);
|
||||||
|
cachedTerrainIndexes.Clear(InvalidCachedTerrainIndex);
|
||||||
|
|
||||||
|
// Invalidate the entry for a cell if anything could cause the terrain index to change.
|
||||||
|
Action<CPos> invalidateTerrainIndex = c => cachedTerrainIndexes[c] = InvalidCachedTerrainIndex;
|
||||||
|
CustomTerrain.CellEntryChanged += invalidateTerrainIndex;
|
||||||
|
MapTiles.Value.CellEntryChanged += invalidateTerrainIndex;
|
||||||
|
}
|
||||||
|
|
||||||
var uv = cell.ToMPos(this);
|
var uv = cell.ToMPos(this);
|
||||||
|
var terrainIndex = cachedTerrainIndexes[uv];
|
||||||
|
|
||||||
|
// Cache terrain indexes per cell on demand.
|
||||||
|
if (terrainIndex == InvalidCachedTerrainIndex)
|
||||||
|
{
|
||||||
var custom = CustomTerrain[uv];
|
var custom = CustomTerrain[uv];
|
||||||
return custom != byte.MaxValue ? custom : cachedTileSet.Value.GetTerrainIndex(MapTiles.Value[uv]);
|
terrainIndex = cachedTerrainIndexes[uv] =
|
||||||
|
custom != byte.MaxValue ? custom : cachedTileSet.Value.GetTerrainIndex(MapTiles.Value[uv]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (byte)terrainIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TerrainTypeInfo GetTerrainInfo(CPos cell)
|
public TerrainTypeInfo GetTerrainInfo(CPos cell)
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ namespace OpenRA
|
|||||||
public readonly bool PickAny;
|
public readonly bool PickAny;
|
||||||
public readonly string Category;
|
public readonly string Category;
|
||||||
|
|
||||||
TerrainTileInfo[] tileInfo;
|
readonly TerrainTileInfo[] tileInfo;
|
||||||
|
|
||||||
public TerrainTemplateInfo(ushort id, string[] images, int2 size, byte[] tiles)
|
public TerrainTemplateInfo(ushort id, string[] images, int2 size, byte[] tiles)
|
||||||
{
|
{
|
||||||
@@ -177,7 +177,7 @@ namespace OpenRA
|
|||||||
public readonly bool IgnoreTileSpriteOffsets;
|
public readonly bool IgnoreTileSpriteOffsets;
|
||||||
|
|
||||||
[FieldLoader.Ignore]
|
[FieldLoader.Ignore]
|
||||||
public readonly Dictionary<ushort, TerrainTemplateInfo> Templates = new Dictionary<ushort, TerrainTemplateInfo>();
|
public readonly IReadOnlyDictionary<ushort, TerrainTemplateInfo> Templates;
|
||||||
|
|
||||||
[FieldLoader.Ignore]
|
[FieldLoader.Ignore]
|
||||||
public readonly TerrainTypeInfo[] TerrainInfo;
|
public readonly TerrainTypeInfo[] TerrainInfo;
|
||||||
@@ -217,7 +217,7 @@ namespace OpenRA
|
|||||||
|
|
||||||
// Templates
|
// Templates
|
||||||
Templates = yaml["Templates"].ToDictionary().Values
|
Templates = yaml["Templates"].ToDictionary().Values
|
||||||
.Select(y => new TerrainTemplateInfo(this, y)).ToDictionary(t => t.Id);
|
.Select(y => new TerrainTemplateInfo(this, y)).ToDictionary(t => t.Id).AsReadOnly();
|
||||||
}
|
}
|
||||||
|
|
||||||
public TileSet(string name, string id, string palette, TerrainTypeInfo[] terrainInfo)
|
public TileSet(string name, string id, string palette, TerrainTypeInfo[] terrainInfo)
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -186,22 +187,51 @@ namespace OpenRA.Traits
|
|||||||
actorShouldBeRemoved = removeActorPosition.Contains;
|
actorShouldBeRemoved = removeActorPosition.Contains;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sealed class UnitsAtEnumerator : IEnumerator<Actor>
|
||||||
|
{
|
||||||
|
InfluenceNode node;
|
||||||
|
public UnitsAtEnumerator(InfluenceNode node) { this.node = node; }
|
||||||
|
public void Reset() { throw new NotSupportedException(); }
|
||||||
|
public Actor Current { get; private set; }
|
||||||
|
object IEnumerator.Current { get { return Current; } }
|
||||||
|
public void Dispose() { }
|
||||||
|
public bool MoveNext()
|
||||||
|
{
|
||||||
|
while (node != null)
|
||||||
|
{
|
||||||
|
Current = node.Actor;
|
||||||
|
node = node.Next;
|
||||||
|
if (!Current.Disposed)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class UnitsAtEnumerable : IEnumerable<Actor>
|
||||||
|
{
|
||||||
|
readonly InfluenceNode node;
|
||||||
|
public UnitsAtEnumerable(InfluenceNode node) { this.node = node; }
|
||||||
|
public IEnumerator<Actor> GetEnumerator() { return new UnitsAtEnumerator(node); }
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
|
||||||
|
}
|
||||||
|
|
||||||
public IEnumerable<Actor> GetUnitsAt(CPos a)
|
public IEnumerable<Actor> GetUnitsAt(CPos a)
|
||||||
{
|
{
|
||||||
if (!influence.Contains(a))
|
var uv = a.ToMPos(map);
|
||||||
yield break;
|
if (!influence.Contains(uv))
|
||||||
|
return Enumerable.Empty<Actor>();
|
||||||
for (var i = influence[a]; i != null; i = i.Next)
|
return new UnitsAtEnumerable(influence[uv]);
|
||||||
if (!i.Actor.Disposed)
|
|
||||||
yield return i.Actor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<Actor> GetUnitsAt(CPos a, SubCell sub)
|
public IEnumerable<Actor> GetUnitsAt(CPos a, SubCell sub)
|
||||||
{
|
{
|
||||||
if (!influence.Contains(a))
|
var uv = a.ToMPos(map);
|
||||||
|
if (!influence.Contains(uv))
|
||||||
yield break;
|
yield break;
|
||||||
|
|
||||||
for (var i = influence[a]; i != null; i = i.Next)
|
for (var i = influence[uv]; i != null; i = i.Next)
|
||||||
if (!i.Actor.Disposed && (i.SubCell == sub || i.SubCell == SubCell.FullCell))
|
if (!i.Actor.Disposed && (i.SubCell == sub || i.SubCell == SubCell.FullCell))
|
||||||
yield return i.Actor;
|
yield return i.Actor;
|
||||||
}
|
}
|
||||||
@@ -243,20 +273,22 @@ namespace OpenRA.Traits
|
|||||||
// NOTE: always includes transients with influence
|
// NOTE: always includes transients with influence
|
||||||
public bool AnyUnitsAt(CPos a)
|
public bool AnyUnitsAt(CPos a)
|
||||||
{
|
{
|
||||||
if (!influence.Contains(a))
|
var uv = a.ToMPos(map);
|
||||||
|
if (!influence.Contains(uv))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return influence[a] != null;
|
return influence[uv] != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: can not check aircraft
|
// NOTE: can not check aircraft
|
||||||
public bool AnyUnitsAt(CPos a, SubCell sub, bool checkTransient = true)
|
public bool AnyUnitsAt(CPos a, SubCell sub, bool checkTransient = true)
|
||||||
{
|
{
|
||||||
if (!influence.Contains(a))
|
var uv = a.ToMPos(map);
|
||||||
|
if (!influence.Contains(uv))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var always = sub == SubCell.FullCell || sub == SubCell.Any;
|
var always = sub == SubCell.FullCell || sub == SubCell.Any;
|
||||||
for (var i = influence[a]; i != null; i = i.Next)
|
for (var i = influence[uv]; i != null; i = i.Next)
|
||||||
{
|
{
|
||||||
if (always || i.SubCell == sub || i.SubCell == SubCell.FullCell)
|
if (always || i.SubCell == sub || i.SubCell == SubCell.FullCell)
|
||||||
{
|
{
|
||||||
@@ -275,11 +307,12 @@ namespace OpenRA.Traits
|
|||||||
// NOTE: can not check aircraft
|
// NOTE: can not check aircraft
|
||||||
public bool AnyUnitsAt(CPos a, SubCell sub, Func<Actor, bool> withCondition)
|
public bool AnyUnitsAt(CPos a, SubCell sub, Func<Actor, bool> withCondition)
|
||||||
{
|
{
|
||||||
if (!influence.Contains(a))
|
var uv = a.ToMPos(map);
|
||||||
|
if (!influence.Contains(uv))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var always = sub == SubCell.FullCell || sub == SubCell.Any;
|
var always = sub == SubCell.FullCell || sub == SubCell.Any;
|
||||||
for (var i = influence[a]; i != null; i = i.Next)
|
for (var i = influence[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;
|
||||||
|
|
||||||
@@ -290,10 +323,11 @@ namespace OpenRA.Traits
|
|||||||
{
|
{
|
||||||
foreach (var c in ios.OccupiedCells())
|
foreach (var c in ios.OccupiedCells())
|
||||||
{
|
{
|
||||||
if (!influence.Contains(c.First))
|
var uv = c.First.ToMPos(map);
|
||||||
|
if (!influence.Contains(uv))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
influence[c.First] = new InfluenceNode { Next = influence[c.First], SubCell = c.Second, Actor = self };
|
influence[uv] = new InfluenceNode { Next = influence[uv], SubCell = c.Second, Actor = self };
|
||||||
|
|
||||||
List<CellTrigger> triggers;
|
List<CellTrigger> triggers;
|
||||||
if (cellTriggerInfluence.TryGetValue(c.First, out triggers))
|
if (cellTriggerInfluence.TryGetValue(c.First, out triggers))
|
||||||
@@ -306,12 +340,13 @@ namespace OpenRA.Traits
|
|||||||
{
|
{
|
||||||
foreach (var c in ios.OccupiedCells())
|
foreach (var c in ios.OccupiedCells())
|
||||||
{
|
{
|
||||||
if (!influence.Contains(c.First))
|
var uv = c.First.ToMPos(map);
|
||||||
|
if (!influence.Contains(uv))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var temp = influence[c.First];
|
var temp = influence[uv];
|
||||||
RemoveInfluenceInner(ref temp, self);
|
RemoveInfluenceInner(ref temp, self);
|
||||||
influence[c.First] = temp;
|
influence[uv] = temp;
|
||||||
|
|
||||||
List<CellTrigger> triggers;
|
List<CellTrigger> triggers;
|
||||||
if (cellTriggerInfluence.TryGetValue(c.First, out triggers))
|
if (cellTriggerInfluence.TryGetValue(c.First, out triggers))
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ namespace OpenRA.Mods.Common.Pathfinder
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets all the Connections for a given node in the graph
|
/// Gets all the Connections for a given node in the graph
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IEnumerable<GraphConnection> GetConnections(CPos position);
|
List<GraphConnection> GetConnections(CPos position);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieves an object given a node in the graph
|
/// Retrieves an object given a node in the graph
|
||||||
@@ -47,13 +47,11 @@ namespace OpenRA.Mods.Common.Pathfinder
|
|||||||
|
|
||||||
public struct GraphConnection
|
public struct GraphConnection
|
||||||
{
|
{
|
||||||
public readonly CPos Source;
|
|
||||||
public readonly CPos Destination;
|
public readonly CPos Destination;
|
||||||
public readonly int Cost;
|
public readonly int Cost;
|
||||||
|
|
||||||
public GraphConnection(CPos source, CPos destination, int cost)
|
public GraphConnection(CPos destination, int cost)
|
||||||
{
|
{
|
||||||
Source = source;
|
|
||||||
Destination = destination;
|
Destination = destination;
|
||||||
Cost = cost;
|
Cost = cost;
|
||||||
}
|
}
|
||||||
@@ -71,6 +69,7 @@ namespace OpenRA.Mods.Common.Pathfinder
|
|||||||
|
|
||||||
readonly CellConditions checkConditions;
|
readonly CellConditions checkConditions;
|
||||||
readonly MobileInfo mobileInfo;
|
readonly MobileInfo mobileInfo;
|
||||||
|
readonly MobileInfo.WorldMovementInfo worldMovementInfo;
|
||||||
CellLayer<CellInfo> cellInfo;
|
CellLayer<CellInfo> cellInfo;
|
||||||
|
|
||||||
public PathGraph(CellLayer<CellInfo> cellInfo, MobileInfo mobileInfo, Actor actor, World world, bool checkForBlocked)
|
public PathGraph(CellLayer<CellInfo> cellInfo, MobileInfo mobileInfo, Actor actor, World world, bool checkForBlocked)
|
||||||
@@ -78,6 +77,7 @@ namespace OpenRA.Mods.Common.Pathfinder
|
|||||||
this.cellInfo = cellInfo;
|
this.cellInfo = cellInfo;
|
||||||
World = world;
|
World = world;
|
||||||
this.mobileInfo = mobileInfo;
|
this.mobileInfo = mobileInfo;
|
||||||
|
worldMovementInfo = mobileInfo.GetWorldMovementInfo(world);
|
||||||
Actor = actor;
|
Actor = actor;
|
||||||
LaneBias = 1;
|
LaneBias = 1;
|
||||||
checkConditions = checkForBlocked ? CellConditions.TransientActors : CellConditions.None;
|
checkConditions = checkForBlocked ? CellConditions.TransientActors : CellConditions.None;
|
||||||
@@ -100,7 +100,7 @@ namespace OpenRA.Mods.Common.Pathfinder
|
|||||||
new[] { new CVec(1, -1), new CVec(1, 0), new CVec(-1, 1), new CVec(0, 1), new CVec(1, 1) },
|
new[] { new CVec(1, -1), new CVec(1, 0), new CVec(-1, 1), new CVec(0, 1), new CVec(1, 1) },
|
||||||
};
|
};
|
||||||
|
|
||||||
public IEnumerable<GraphConnection> GetConnections(CPos position)
|
public List<GraphConnection> GetConnections(CPos position)
|
||||||
{
|
{
|
||||||
var previousPos = cellInfo[position].PreviousPos;
|
var previousPos = cellInfo[position].PreviousPos;
|
||||||
|
|
||||||
@@ -108,14 +108,14 @@ namespace OpenRA.Mods.Common.Pathfinder
|
|||||||
var dy = position.Y - previousPos.Y;
|
var dy = position.Y - previousPos.Y;
|
||||||
var index = dy * 3 + dx + 4;
|
var index = dy * 3 + dx + 4;
|
||||||
|
|
||||||
var validNeighbors = new LinkedList<GraphConnection>();
|
|
||||||
var directions = DirectedNeighbors[index];
|
var directions = DirectedNeighbors[index];
|
||||||
|
var validNeighbors = new List<GraphConnection>(directions.Length);
|
||||||
for (var i = 0; i < directions.Length; i++)
|
for (var i = 0; i < directions.Length; i++)
|
||||||
{
|
{
|
||||||
var neighbor = position + directions[i];
|
var neighbor = position + directions[i];
|
||||||
var movementCost = GetCostToNode(neighbor, directions[i]);
|
var movementCost = GetCostToNode(neighbor, directions[i]);
|
||||||
if (movementCost != Constants.InvalidNode)
|
if (movementCost != Constants.InvalidNode)
|
||||||
validNeighbors.AddLast(new GraphConnection(position, neighbor, movementCost));
|
validNeighbors.Add(new GraphConnection(neighbor, movementCost));
|
||||||
}
|
}
|
||||||
|
|
||||||
return validNeighbors;
|
return validNeighbors;
|
||||||
@@ -123,17 +123,9 @@ namespace OpenRA.Mods.Common.Pathfinder
|
|||||||
|
|
||||||
int GetCostToNode(CPos destNode, CVec direction)
|
int GetCostToNode(CPos destNode, CVec direction)
|
||||||
{
|
{
|
||||||
int movementCost;
|
var movementCost = mobileInfo.MovementCostToEnterCell(worldMovementInfo, Actor, destNode, IgnoredActor, checkConditions);
|
||||||
if (mobileInfo.CanEnterCell(
|
if (movementCost != int.MaxValue && !(CustomBlock != null && CustomBlock(destNode)))
|
||||||
World,
|
|
||||||
Actor,
|
|
||||||
destNode,
|
|
||||||
out movementCost,
|
|
||||||
IgnoredActor,
|
|
||||||
checkConditions) && !(CustomBlock != null && CustomBlock(destNode)))
|
|
||||||
{
|
|
||||||
return CalculateCellCost(destNode, direction, movementCost);
|
return CalculateCellCost(destNode, direction, movementCost);
|
||||||
}
|
|
||||||
|
|
||||||
return Constants.InvalidNode;
|
return Constants.InvalidNode;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -125,6 +125,17 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct WorldMovementInfo
|
||||||
|
{
|
||||||
|
internal readonly World World;
|
||||||
|
internal readonly TerrainInfo[] TerrainInfos;
|
||||||
|
internal WorldMovementInfo(World world, MobileInfo info)
|
||||||
|
{
|
||||||
|
World = world;
|
||||||
|
TerrainInfos = info.TilesetTerrainInfo[world.TileSet];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public readonly Cache<TileSet, TerrainInfo[]> TilesetTerrainInfo;
|
public readonly Cache<TileSet, TerrainInfo[]> TilesetTerrainInfo;
|
||||||
public readonly Cache<TileSet, int> TilesetMovementClass;
|
public readonly Cache<TileSet, int> TilesetMovementClass;
|
||||||
|
|
||||||
@@ -136,14 +147,19 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
|
|
||||||
public int MovementCostForCell(World world, CPos cell)
|
public int MovementCostForCell(World world, CPos cell)
|
||||||
{
|
{
|
||||||
if (!world.Map.Contains(cell))
|
return MovementCostForCell(world.Map, TilesetTerrainInfo[world.TileSet], cell);
|
||||||
|
}
|
||||||
|
|
||||||
|
int MovementCostForCell(Map map, TerrainInfo[] terrainInfos, CPos cell)
|
||||||
|
{
|
||||||
|
if (!map.Contains(cell))
|
||||||
return int.MaxValue;
|
return int.MaxValue;
|
||||||
|
|
||||||
var index = world.Map.GetTerrainIndex(cell);
|
var index = map.GetTerrainIndex(cell);
|
||||||
if (index == byte.MaxValue)
|
if (index == byte.MaxValue)
|
||||||
return int.MaxValue;
|
return int.MaxValue;
|
||||||
|
|
||||||
return TilesetTerrainInfo[world.TileSet][index].Cost;
|
return terrainInfos[index].Cost;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int CalculateTilesetMovementClass(TileSet tileset)
|
public int CalculateTilesetMovementClass(TileSet tileset)
|
||||||
@@ -226,22 +242,33 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
|
|
||||||
// If the other actor in our way cannot be crushed, we are blocked.
|
// If the other actor in our way cannot be crushed, we are blocked.
|
||||||
var crushables = otherActor.TraitsImplementing<ICrushable>();
|
var crushables = otherActor.TraitsImplementing<ICrushable>();
|
||||||
if (!crushables.Any())
|
var lacksCrushability = true;
|
||||||
return true;
|
|
||||||
foreach (var crushable in crushables)
|
foreach (var crushable in crushables)
|
||||||
|
{
|
||||||
|
lacksCrushability = false;
|
||||||
if (!crushable.CrushableBy(Crushes, self.Owner))
|
if (!crushable.CrushableBy(Crushes, self.Owner))
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are no crushable traits at all, this means the other actor cannot be crushed - we are blocked.
|
||||||
|
if (lacksCrushability)
|
||||||
|
return true;
|
||||||
|
|
||||||
// We are not blocked by the other actor.
|
// We are not blocked by the other actor.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool CanEnterCell(World world, Actor self, CPos cell, out int movementCost, Actor ignoreActor = null, CellConditions check = CellConditions.All)
|
public WorldMovementInfo GetWorldMovementInfo(World world)
|
||||||
{
|
{
|
||||||
if ((movementCost = MovementCostForCell(world, cell)) == int.MaxValue)
|
return new WorldMovementInfo(world, this);
|
||||||
return false;
|
}
|
||||||
|
|
||||||
return CanMoveFreelyInto(world, self, cell, ignoreActor, check);
|
public int MovementCostToEnterCell(WorldMovementInfo worldMovementInfo, Actor self, CPos cell, Actor ignoreActor = null, CellConditions check = CellConditions.All)
|
||||||
|
{
|
||||||
|
var cost = MovementCostForCell(worldMovementInfo.World.Map, worldMovementInfo.TerrainInfos, cell);
|
||||||
|
if (cost == int.MaxValue || !CanMoveFreelyInto(worldMovementInfo.World, self, cell, ignoreActor, check))
|
||||||
|
return int.MaxValue;
|
||||||
|
return cost;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SubCell GetAvailableSubCell(
|
public SubCell GetAvailableSubCell(
|
||||||
@@ -283,7 +310,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
internal int TicksBeforePathing = 0;
|
internal int TicksBeforePathing = 0;
|
||||||
|
|
||||||
readonly Actor self;
|
readonly Actor self;
|
||||||
readonly Lazy<ISpeedModifier[]> speedModifiers;
|
readonly Lazy<IEnumerable<int>> speedModifiers;
|
||||||
public readonly MobileInfo Info;
|
public readonly MobileInfo Info;
|
||||||
public bool IsMoving { get; set; }
|
public bool IsMoving { get; set; }
|
||||||
|
|
||||||
@@ -324,7 +351,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
self = init.Self;
|
self = init.Self;
|
||||||
Info = info;
|
Info = info;
|
||||||
|
|
||||||
speedModifiers = Exts.Lazy(() => self.TraitsImplementing<ISpeedModifier>().ToArray());
|
speedModifiers = Exts.Lazy(() => self.TraitsImplementing<ISpeedModifier>().ToArray().Select(x => x.GetSpeedModifier()));
|
||||||
|
|
||||||
ToSubCell = FromSubCell = info.SharesCell ? init.World.Map.DefaultSubCell : SubCell.FullCell;
|
ToSubCell = FromSubCell = info.SharesCell ? init.World.Map.DefaultSubCell : SubCell.FullCell;
|
||||||
if (init.Contains<SubCellInit>())
|
if (init.Contains<SubCellInit>())
|
||||||
@@ -581,7 +608,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
if (terrainSpeed == 0)
|
if (terrainSpeed == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
var modifiers = speedModifiers.Value.Select(x => x.GetSpeedModifier()).Append(terrainSpeed);
|
var modifiers = speedModifiers.Value.Append(terrainSpeed);
|
||||||
|
|
||||||
return Util.ApplyPercentageModifiers(Info.Speed, modifiers);
|
return Util.ApplyPercentageModifiers(Info.Speed, modifiers);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user