Use named pathfinding constants.

- Rename CostForInvalidCell to PathCostForInvalidPath
- Add MovementCostForUnreachableCell
- Update usages of int.MaxValue and short.Maxvalue to use named constants where relevant.
- Update costs on ICustomMovementLayer to return short, for consistency with costs from Locomotor.
- Rename some methods to distinguish between path/movement cost.
This commit is contained in:
RoosterDragon
2021-10-08 20:35:33 +01:00
committed by abcdefg30
parent 884e6cdb51
commit df9398a871
16 changed files with 57 additions and 54 deletions

View File

@@ -191,7 +191,7 @@ namespace OpenRA.Mods.Common.Activities
.WithCustomCost(loc => .WithCustomCost(loc =>
{ {
if ((loc - searchFromLoc).LengthSquared > searchRadiusSquared) if ((loc - searchFromLoc).LengthSquared > searchRadiusSquared)
return PathGraph.CostForInvalidCell; return PathGraph.PathCostForInvalidPath;
// Add a cost modifier to harvestable cells to prefer resources that are closer to the refinery. // Add a cost modifier to harvestable cells to prefer resources that are closer to the refinery.
// This reduces the tendancy for harvesters to move in straight lines // This reduces the tendancy for harvesters to move in straight lines

View File

@@ -25,7 +25,7 @@ namespace OpenRA.Mods.Common.Pathfinder
{ {
defaultLayer = defaultLayer =
CellLayer<CellInfo>.CreateInstance( CellLayer<CellInfo>.CreateInstance(
mpos => new CellInfo(int.MaxValue, int.MaxValue, mpos.ToCPos(map), CellStatus.Unvisited), mpos => new CellInfo(PathGraph.PathCostForInvalidPath, PathGraph.PathCostForInvalidPath, mpos.ToCPos(map), CellStatus.Unvisited),
new Size(map.MapSize.X, map.MapSize.Y), new Size(map.MapSize.X, map.MapSize.Y),
map.Grid.Type); map.Grid.Type);
} }

View File

@@ -75,7 +75,8 @@ namespace OpenRA.Mods.Common.Pathfinder
sealed class PathGraph : IGraph<CellInfo> sealed class PathGraph : IGraph<CellInfo>
{ {
public const int CostForInvalidCell = int.MaxValue; public const int PathCostForInvalidPath = int.MaxValue;
public const short MovementCostForUnreachableCell = short.MaxValue;
public Actor Actor { get; private set; } public Actor Actor { get; private set; }
public World World { get; private set; } public World World { get; private set; }
@@ -147,11 +148,11 @@ namespace OpenRA.Mods.Common.Pathfinder
{ {
var dir = directions[i]; var dir = directions[i];
var neighbor = position + dir; var neighbor = position + dir;
var movementCost = GetCostToNode(neighbor, dir); var pathCost = GetPathCostToNode(neighbor, dir);
// PERF: Skip closed cells already, 15% of all cells // PERF: Skip closed cells already, 15% of all cells
if (movementCost != CostForInvalidCell && info[neighbor].Status != CellStatus.Closed) if (pathCost != PathCostForInvalidPath && info[neighbor].Status != CellStatus.Closed)
validNeighbors.Add(new GraphConnection(neighbor, movementCost)); validNeighbors.Add(new GraphConnection(neighbor, pathCost));
} }
if (posLayer == 0) if (posLayer == 0)
@@ -160,7 +161,7 @@ namespace OpenRA.Mods.Common.Pathfinder
{ {
var layerPosition = new CPos(position.X, position.Y, cli.Layer.Index); var layerPosition = new CPos(position.X, position.Y, cli.Layer.Index);
var entryCost = cli.Layer.EntryMovementCost(locomotor.Info, layerPosition); var entryCost = cli.Layer.EntryMovementCost(locomotor.Info, layerPosition);
if (entryCost != CostForInvalidCell) if (entryCost != MovementCostForUnreachableCell)
validNeighbors.Add(new GraphConnection(layerPosition, entryCost)); validNeighbors.Add(new GraphConnection(layerPosition, entryCost));
} }
} }
@@ -168,23 +169,23 @@ namespace OpenRA.Mods.Common.Pathfinder
{ {
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 = customLayerInfo[posLayer].Layer.ExitMovementCost(locomotor.Info, layerPosition);
if (exitCost != CostForInvalidCell) if (exitCost != MovementCostForUnreachableCell)
validNeighbors.Add(new GraphConnection(layerPosition, exitCost)); validNeighbors.Add(new GraphConnection(layerPosition, exitCost));
} }
return validNeighbors; return validNeighbors;
} }
int GetCostToNode(CPos destNode, CVec direction) int GetPathCostToNode(CPos destNode, CVec direction)
{ {
var movementCost = locomotor.MovementCostToEnterCell(Actor, destNode, checkConditions, IgnoreActor); var movementCost = locomotor.MovementCostToEnterCell(Actor, destNode, checkConditions, IgnoreActor);
if (movementCost != short.MaxValue && !(CustomBlock != null && CustomBlock(destNode))) if (movementCost != MovementCostForUnreachableCell && !(CustomBlock != null && CustomBlock(destNode)))
return CalculateCellCost(destNode, direction, movementCost); return CalculateCellPathCost(destNode, direction, movementCost);
return CostForInvalidCell; return PathCostForInvalidPath;
} }
int CalculateCellCost(CPos neighborCPos, CVec direction, int movementCost) int CalculateCellPathCost(CPos neighborCPos, CVec direction, int movementCost)
{ {
var cellCost = movementCost; var cellCost = movementCost;
@@ -194,8 +195,8 @@ namespace OpenRA.Mods.Common.Pathfinder
if (CustomCost != null) if (CustomCost != null)
{ {
var customCost = CustomCost(neighborCPos); var customCost = CustomCost(neighborCPos);
if (customCost == CostForInvalidCell) if (customCost == PathCostForInvalidPath)
return CostForInvalidCell; return PathCostForInvalidPath;
cellCost += customCost; cellCost += customCost;
} }
@@ -206,7 +207,7 @@ namespace OpenRA.Mods.Common.Pathfinder
var heightLayer = World.Map.Height; var heightLayer = World.Map.Height;
var from = neighborCPos - direction; var from = neighborCPos - direction;
if (Math.Abs(heightLayer[neighborCPos] - heightLayer[from]) > 1) if (Math.Abs(heightLayer[neighborCPos] - heightLayer[from]) > 1)
return CostForInvalidCell; return PathCostForInvalidPath;
} }
// Directional bonuses for smoother flow! // Directional bonuses for smoother flow!

View File

@@ -108,7 +108,7 @@ namespace OpenRA.Mods.Common.Pathfinder
var currentCell = Graph[currentMinNode]; var currentCell = Graph[currentMinNode];
Graph[currentMinNode] = new CellInfo(currentCell.CostSoFar, currentCell.EstimatedTotal, currentCell.PreviousPos, CellStatus.Closed); Graph[currentMinNode] = new CellInfo(currentCell.CostSoFar, currentCell.EstimatedTotal, currentCell.PreviousPos, CellStatus.Closed);
if (Graph.CustomCost != null && Graph.CustomCost(currentMinNode) == PathGraph.CostForInvalidCell) if (Graph.CustomCost != null && Graph.CustomCost(currentMinNode) == PathGraph.PathCostForInvalidPath)
return currentMinNode; return currentMinNode;
foreach (var connection in Graph.GetConnections(currentMinNode)) foreach (var connection in Graph.GetConnections(currentMinNode))

View File

@@ -12,6 +12,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using OpenRA.Mods.Common.Activities; using OpenRA.Mods.Common.Activities;
using OpenRA.Mods.Common.Pathfinder;
using OpenRA.Primitives; using OpenRA.Primitives;
using OpenRA.Traits; using OpenRA.Traits;
@@ -210,7 +211,7 @@ namespace OpenRA.Mods.Common.Traits
bool CanEnterCell(Actor self, CPos cell) bool CanEnterCell(Actor self, CPos cell)
{ {
if (mobile.locomotor.MovementCostForCell(cell) == short.MaxValue) if (mobile.locomotor.MovementCostForCell(cell) == PathGraph.MovementCostForUnreachableCell)
return false; return false;
return mobile.locomotor.CanMoveFreelyInto(self, cell, BlockedByActor.All, null); return mobile.locomotor.CanMoveFreelyInto(self, cell, BlockedByActor.All, null);

View File

@@ -206,7 +206,7 @@ namespace OpenRA.Mods.Common.Traits
// Too many harvesters clogs up the refinery's delivery location: // Too many harvesters clogs up the refinery's delivery location:
if (occupancy >= Info.MaxUnloadQueue) if (occupancy >= Info.MaxUnloadQueue)
return PathGraph.CostForInvalidCell; return PathGraph.PathCostForInvalidPath;
// Prefer refineries with less occupancy (multiplier is to offset distance cost): // Prefer refineries with less occupancy (multiplier is to offset distance cost):
return occupancy * Info.UnloadQueueCostModifier; return occupancy * Info.UnloadQueueCostModifier;

View File

@@ -119,7 +119,7 @@ namespace OpenRA.Mods.Common.Traits
locomotor = world.WorldActor.TraitsImplementing<Locomotor>() locomotor = world.WorldActor.TraitsImplementing<Locomotor>()
.SingleOrDefault(l => l.Info.Name == Locomotor); .SingleOrDefault(l => l.Info.Name == Locomotor);
if (locomotor.MovementCostForCell(cell) == short.MaxValue) if (locomotor.MovementCostForCell(cell) == PathGraph.MovementCostForUnreachableCell)
return false; return false;
return locomotor.CanMoveFreelyInto(self, cell, subCell, check, ignoreActor); return locomotor.CanMoveFreelyInto(self, cell, subCell, check, ignoreActor);
@@ -520,7 +520,7 @@ namespace OpenRA.Mods.Common.Traits
public bool CanExistInCell(CPos cell) public bool CanExistInCell(CPos cell)
{ {
return Locomotor.MovementCostForCell(cell) != short.MaxValue; return Locomotor.MovementCostForCell(cell) != PathGraph.MovementCostForUnreachableCell;
} }
public bool CanEnterCell(CPos cell, Actor ignoreActor = null, BlockedByActor check = BlockedByActor.All) public bool CanEnterCell(CPos cell, Actor ignoreActor = null, BlockedByActor check = BlockedByActor.All)
@@ -1020,7 +1020,7 @@ namespace OpenRA.Mods.Common.Traits
if (mobile.IsTraitPaused if (mobile.IsTraitPaused
|| !self.World.Map.Contains(location) || !self.World.Map.Contains(location)
|| (!explored && !locomotorInfo.MoveIntoShroud) || (!explored && !locomotorInfo.MoveIntoShroud)
|| (explored && mobile.Locomotor.MovementCostForCell(location) == short.MaxValue)) || (explored && mobile.Locomotor.MovementCostForCell(location) == PathGraph.MovementCostForUnreachableCell))
cursor = mobile.Info.BlockedCursor; cursor = mobile.Info.BlockedCursor;
else if (!explored || !mobile.Info.TerrainCursors.TryGetValue(self.World.Map.GetTerrainInfo(location).Type, out cursor)) else if (!explored || !mobile.Info.TerrainCursors.TryGetValue(self.World.Map.GetTerrainInfo(location).Type, out cursor))
cursor = mobile.Info.Cursor; cursor = mobile.Info.Cursor;

View File

@@ -80,14 +80,14 @@ namespace OpenRA.Mods.Common.Traits
return cellCenters[cell]; return cellCenters[cell];
} }
int ICustomMovementLayer.EntryMovementCost(LocomotorInfo li, CPos cell) short ICustomMovementLayer.EntryMovementCost(LocomotorInfo li, CPos cell)
{ {
return ends.Contains(cell) ? 0 : PathGraph.CostForInvalidCell; return ends.Contains(cell) ? (short)0 : PathGraph.MovementCostForUnreachableCell;
} }
int ICustomMovementLayer.ExitMovementCost(LocomotorInfo li, CPos cell) short ICustomMovementLayer.ExitMovementCost(LocomotorInfo li, CPos cell)
{ {
return ends.Contains(cell) ? 0 : PathGraph.CostForInvalidCell; return ends.Contains(cell) ? (short)0 : PathGraph.MovementCostForUnreachableCell;
} }
byte ICustomMovementLayer.GetTerrainIndex(CPos cell) byte ICustomMovementLayer.GetTerrainIndex(CPos cell)

View File

@@ -87,16 +87,16 @@ namespace OpenRA.Mods.Common.Traits
return map.Ramp[cell] == 0; return map.Ramp[cell] == 0;
} }
int ICustomMovementLayer.EntryMovementCost(LocomotorInfo li, CPos cell) short ICustomMovementLayer.EntryMovementCost(LocomotorInfo li, CPos cell)
{ {
var jli = (JumpjetLocomotorInfo)li; var jli = (JumpjetLocomotorInfo)li;
return ValidTransitionCell(cell, jli) ? jli.JumpjetTransitionCost : PathGraph.CostForInvalidCell; return ValidTransitionCell(cell, jli) ? jli.JumpjetTransitionCost : PathGraph.MovementCostForUnreachableCell;
} }
int ICustomMovementLayer.ExitMovementCost(LocomotorInfo li, CPos cell) short ICustomMovementLayer.ExitMovementCost(LocomotorInfo li, CPos cell)
{ {
var jli = (JumpjetLocomotorInfo)li; var jli = (JumpjetLocomotorInfo)li;
return ValidTransitionCell(cell, jli) ? jli.JumpjetTransitionCost : PathGraph.CostForInvalidCell; return ValidTransitionCell(cell, jli) ? jli.JumpjetTransitionCost : PathGraph.MovementCostForUnreachableCell;
} }
byte ICustomMovementLayer.GetTerrainIndex(CPos cell) byte ICustomMovementLayer.GetTerrainIndex(CPos cell)

View File

@@ -17,7 +17,7 @@ namespace OpenRA.Mods.Common.Traits
public class JumpjetLocomotorInfo : LocomotorInfo public class JumpjetLocomotorInfo : LocomotorInfo
{ {
[Desc("Pathfinding cost for taking off or landing.")] [Desc("Pathfinding cost for taking off or landing.")]
public readonly int JumpjetTransitionCost = 0; public readonly short JumpjetTransitionCost = 0;
[Desc("The terrain types that this actor can transition on. Leave empty to allow any.")] [Desc("The terrain types that this actor can transition on. Leave empty to allow any.")]
public readonly HashSet<string> JumpjetTransitionTerrainTypes = new HashSet<string>(); public readonly HashSet<string> JumpjetTransitionTerrainTypes = new HashSet<string>();

View File

@@ -13,6 +13,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using OpenRA.Graphics; using OpenRA.Graphics;
using OpenRA.Mods.Common.Pathfinder;
using OpenRA.Primitives; using OpenRA.Primitives;
using OpenRA.Support; using OpenRA.Support;
using OpenRA.Traits; using OpenRA.Traits;
@@ -109,7 +110,7 @@ namespace OpenRA.Mods.Common.Traits
public TerrainInfo() public TerrainInfo()
{ {
Cost = short.MaxValue; Cost = PathGraph.MovementCostForUnreachableCell;
Speed = 0; Speed = 0;
} }
@@ -168,13 +169,13 @@ namespace OpenRA.Mods.Common.Traits
if (!info.TerrainSpeeds.TryGetValue(terrainInfo.TerrainTypes[i].Type, out terrainInfos[i])) if (!info.TerrainSpeeds.TryGetValue(terrainInfo.TerrainTypes[i].Type, out terrainInfos[i]))
terrainInfos[i] = LocomotorInfo.TerrainInfo.Impassable; terrainInfos[i] = LocomotorInfo.TerrainInfo.Impassable;
MovementClass = (uint)terrainInfos.Select(ti => ti.Cost < short.MaxValue).ToBits(); MovementClass = (uint)terrainInfos.Select(ti => ti.Cost != PathGraph.MovementCostForUnreachableCell).ToBits();
} }
public short MovementCostForCell(CPos cell) public short MovementCostForCell(CPos cell)
{ {
if (!world.Map.Contains(cell)) if (!world.Map.Contains(cell))
return short.MaxValue; return PathGraph.MovementCostForUnreachableCell;
return cell.Layer == 0 ? cellsCost[cell] : customLayerCellsCost[cell.Layer][cell]; return cell.Layer == 0 ? cellsCost[cell] : customLayerCellsCost[cell.Layer][cell];
} }
@@ -190,13 +191,13 @@ namespace OpenRA.Mods.Common.Traits
public short MovementCostToEnterCell(Actor actor, CPos destNode, BlockedByActor check, Actor ignoreActor) public short MovementCostToEnterCell(Actor actor, CPos destNode, BlockedByActor check, Actor ignoreActor)
{ {
if (!world.Map.Contains(destNode)) if (!world.Map.Contains(destNode))
return short.MaxValue; return PathGraph.MovementCostForUnreachableCell;
var cellCost = destNode.Layer == 0 ? cellsCost[destNode] : customLayerCellsCost[destNode.Layer][destNode]; var cellCost = destNode.Layer == 0 ? cellsCost[destNode] : customLayerCellsCost[destNode.Layer][destNode];
if (cellCost == short.MaxValue || if (cellCost == PathGraph.MovementCostForUnreachableCell ||
!CanMoveFreelyInto(actor, destNode, check, ignoreActor)) !CanMoveFreelyInto(actor, destNode, check, ignoreActor))
return short.MaxValue; return PathGraph.MovementCostForUnreachableCell;
return cellCost; return cellCost;
} }
@@ -276,7 +277,7 @@ namespace OpenRA.Mods.Common.Traits
public SubCell GetAvailableSubCell(Actor self, CPos cell, BlockedByActor check, SubCell preferredSubCell = SubCell.Any, Actor ignoreActor = null) public SubCell GetAvailableSubCell(Actor self, CPos cell, BlockedByActor check, SubCell preferredSubCell = SubCell.Any, Actor ignoreActor = null)
{ {
if (MovementCostForCell(cell) == short.MaxValue) if (MovementCostForCell(cell) == PathGraph.MovementCostForUnreachableCell)
return SubCell.Invalid; return SubCell.Invalid;
if (check > BlockedByActor.None) if (check > BlockedByActor.None)
@@ -381,7 +382,7 @@ namespace OpenRA.Mods.Common.Traits
{ {
var index = cml.GetTerrainIndex(cell); var index = cml.GetTerrainIndex(cell);
var cost = short.MaxValue; var cost = PathGraph.MovementCostForUnreachableCell;
if (index != byte.MaxValue) if (index != byte.MaxValue)
cost = terrainInfos[index].Cost; cost = terrainInfos[index].Cost;
@@ -416,7 +417,7 @@ namespace OpenRA.Mods.Common.Traits
? world.Map.GetTerrainIndex(cell) ? world.Map.GetTerrainIndex(cell)
: world.GetCustomMovementLayers()[cell.Layer].GetTerrainIndex(cell); : world.GetCustomMovementLayers()[cell.Layer].GetTerrainIndex(cell);
var cost = short.MaxValue; var cost = PathGraph.MovementCostForUnreachableCell;
if (index != byte.MaxValue) if (index != byte.MaxValue)
cost = terrainInfos[index].Cost; cost = terrainInfos[index].Cost;

View File

@@ -167,7 +167,7 @@ namespace OpenRA.Mods.Common.Traits
// make some progress on the first search // make some progress on the first search
var p = fromSrc.Expand(); var p = fromSrc.Expand();
if (fromDest.Graph[p].Status == CellStatus.Closed && if (fromDest.Graph[p].Status == CellStatus.Closed &&
fromDest.Graph[p].CostSoFar < int.MaxValue) fromDest.Graph[p].CostSoFar != PathGraph.PathCostForInvalidPath)
{ {
path = MakeBidiPath(fromSrc, fromDest, p); path = MakeBidiPath(fromSrc, fromDest, p);
break; break;
@@ -176,7 +176,7 @@ namespace OpenRA.Mods.Common.Traits
// make some progress on the second search // make some progress on the second search
var q = fromDest.Expand(); var q = fromDest.Expand();
if (fromSrc.Graph[q].Status == CellStatus.Closed && if (fromSrc.Graph[q].Status == CellStatus.Closed &&
fromSrc.Graph[q].CostSoFar < int.MaxValue) fromSrc.Graph[q].CostSoFar != PathGraph.PathCostForInvalidPath)
{ {
path = MakeBidiPath(fromSrc, fromDest, q); path = MakeBidiPath(fromSrc, fromDest, q);
break; break;

View File

@@ -85,16 +85,16 @@ namespace OpenRA.Mods.Common.Traits
return map.Ramp[cell] == 0; return map.Ramp[cell] == 0;
} }
int ICustomMovementLayer.EntryMovementCost(LocomotorInfo li, CPos cell) short ICustomMovementLayer.EntryMovementCost(LocomotorInfo li, CPos cell)
{ {
var sli = (SubterraneanLocomotorInfo)li; var sli = (SubterraneanLocomotorInfo)li;
return ValidTransitionCell(cell, sli) ? sli.SubterraneanTransitionCost : PathGraph.CostForInvalidCell; return ValidTransitionCell(cell, sli) ? sli.SubterraneanTransitionCost : PathGraph.MovementCostForUnreachableCell;
} }
int ICustomMovementLayer.ExitMovementCost(LocomotorInfo li, CPos cell) short ICustomMovementLayer.ExitMovementCost(LocomotorInfo li, CPos cell)
{ {
var sli = (SubterraneanLocomotorInfo)li; var sli = (SubterraneanLocomotorInfo)li;
return ValidTransitionCell(cell, sli) ? sli.SubterraneanTransitionCost : PathGraph.CostForInvalidCell; return ValidTransitionCell(cell, sli) ? sli.SubterraneanTransitionCost : PathGraph.MovementCostForUnreachableCell;
} }
byte ICustomMovementLayer.GetTerrainIndex(CPos cell) byte ICustomMovementLayer.GetTerrainIndex(CPos cell)

View File

@@ -17,7 +17,7 @@ namespace OpenRA.Mods.Common.Traits
public class SubterraneanLocomotorInfo : LocomotorInfo public class SubterraneanLocomotorInfo : LocomotorInfo
{ {
[Desc("Pathfinding cost for submerging or reemerging.")] [Desc("Pathfinding cost for submerging or reemerging.")]
public readonly int SubterraneanTransitionCost = 0; public readonly short SubterraneanTransitionCost = 0;
[Desc("The terrain types that this actor can transition on. Leave empty to allow any.")] [Desc("The terrain types that this actor can transition on. Leave empty to allow any.")]
public readonly HashSet<string> SubterraneanTransitionTerrainTypes = new HashSet<string>(); public readonly HashSet<string> SubterraneanTransitionTerrainTypes = new HashSet<string>();
@@ -25,7 +25,7 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Can this actor transition on slopes?")] [Desc("Can this actor transition on slopes?")]
public readonly bool SubterraneanTransitionOnRamps = false; public readonly bool SubterraneanTransitionOnRamps = false;
[Desc("Depth at which the subterranian condition is applied.")] [Desc("Depth at which the subterranean condition is applied.")]
public readonly WDist SubterraneanTransitionDepth = new WDist(-1024); public readonly WDist SubterraneanTransitionDepth = new WDist(-1024);
public override bool DisableDomainPassabilityCheck => true; public override bool DisableDomainPassabilityCheck => true;

View File

@@ -79,14 +79,14 @@ namespace OpenRA.Mods.Common.Traits
return cellCenters[cell]; return cellCenters[cell];
} }
int ICustomMovementLayer.EntryMovementCost(LocomotorInfo li, CPos cell) short ICustomMovementLayer.EntryMovementCost(LocomotorInfo li, CPos cell)
{ {
return portals.Contains(cell) ? 0 : PathGraph.CostForInvalidCell; return portals.Contains(cell) ? (short)0 : PathGraph.MovementCostForUnreachableCell;
} }
int ICustomMovementLayer.ExitMovementCost(LocomotorInfo li, CPos cell) short ICustomMovementLayer.ExitMovementCost(LocomotorInfo li, CPos cell)
{ {
return portals.Contains(cell) ? 0 : PathGraph.CostForInvalidCell; return portals.Contains(cell) ? (short)0 : PathGraph.MovementCostForUnreachableCell;
} }
byte ICustomMovementLayer.GetTerrainIndex(CPos cell) byte ICustomMovementLayer.GetTerrainIndex(CPos cell)

View File

@@ -404,8 +404,8 @@ namespace OpenRA.Mods.Common.Traits
bool ReturnToGroundLayerOnIdle { get; } bool ReturnToGroundLayerOnIdle { get; }
bool EnabledForLocomotor(LocomotorInfo li); bool EnabledForLocomotor(LocomotorInfo li);
int EntryMovementCost(LocomotorInfo li, CPos cell); short EntryMovementCost(LocomotorInfo li, CPos cell);
int ExitMovementCost(LocomotorInfo li, CPos cell); short ExitMovementCost(LocomotorInfo li, CPos cell);
byte GetTerrainIndex(CPos cell); byte GetTerrainIndex(CPos cell);
WPos CenterOfCell(CPos cell); WPos CenterOfCell(CPos cell);