Add plumbing for custom movement layers.
This commit is contained in:
@@ -19,23 +19,25 @@ namespace OpenRA
|
|||||||
public struct CPos : IScriptBindable, ILuaAdditionBinding, ILuaSubtractionBinding, ILuaEqualityBinding, ILuaTableBinding, IEquatable<CPos>
|
public struct CPos : IScriptBindable, ILuaAdditionBinding, ILuaSubtractionBinding, ILuaEqualityBinding, ILuaTableBinding, IEquatable<CPos>
|
||||||
{
|
{
|
||||||
public readonly int X, Y;
|
public readonly int X, Y;
|
||||||
|
public readonly byte Layer;
|
||||||
|
|
||||||
public CPos(int x, int y) { X = x; Y = y; }
|
public CPos(int x, int y) { X = x; Y = y; Layer = 0; }
|
||||||
public static readonly CPos Zero = new CPos(0, 0);
|
public CPos(int x, int y, byte layer) { X = x; Y = y; Layer = layer; }
|
||||||
|
public static readonly CPos Zero = new CPos(0, 0, 0);
|
||||||
|
|
||||||
public static explicit operator CPos(int2 a) { return new CPos(a.X, a.Y); }
|
public static explicit operator CPos(int2 a) { return new CPos(a.X, a.Y); }
|
||||||
|
|
||||||
public static CPos operator +(CVec a, CPos b) { return new CPos(a.X + b.X, a.Y + b.Y); }
|
public static CPos operator +(CVec a, CPos b) { return new CPos(a.X + b.X, a.Y + b.Y, b.Layer); }
|
||||||
public static CPos operator +(CPos a, CVec b) { return new CPos(a.X + b.X, a.Y + b.Y); }
|
public static CPos operator +(CPos a, CVec b) { return new CPos(a.X + b.X, a.Y + b.Y, a.Layer); }
|
||||||
public static CPos operator -(CPos a, CVec b) { return new CPos(a.X - b.X, a.Y - b.Y); }
|
public static CPos operator -(CPos a, CVec b) { return new CPos(a.X - b.X, a.Y - b.Y, a.Layer); }
|
||||||
public static CVec operator -(CPos a, CPos b) { return new CVec(a.X - b.X, a.Y - b.Y); }
|
public static CVec operator -(CPos a, CPos b) { return new CVec(a.X - b.X, a.Y - b.Y); }
|
||||||
|
|
||||||
public static bool operator ==(CPos me, CPos other) { return me.X == other.X && me.Y == other.Y; }
|
public static bool operator ==(CPos me, CPos other) { return me.X == other.X && me.Y == other.Y && me.Layer == other.Layer; }
|
||||||
public static bool operator !=(CPos me, CPos other) { return !(me == other); }
|
public static bool operator !=(CPos me, CPos other) { return !(me == other); }
|
||||||
|
|
||||||
public override int GetHashCode() { return X.GetHashCode() ^ Y.GetHashCode(); }
|
public override int GetHashCode() { return X.GetHashCode() ^ Y.GetHashCode() ^ Layer.GetHashCode(); }
|
||||||
|
|
||||||
public bool Equals(CPos other) { return X == other.X && Y == other.Y; }
|
public bool Equals(CPos other) { return X == other.X && Y == other.Y && Layer == other.Layer; }
|
||||||
public override bool Equals(object obj) { return obj is CPos && Equals((CPos)obj); }
|
public override bool Equals(object obj) { return obj is CPos && Equals((CPos)obj); }
|
||||||
|
|
||||||
public override string ToString() { return X + "," + Y; }
|
public override string ToString() { return X + "," + Y; }
|
||||||
@@ -117,6 +119,7 @@ namespace OpenRA
|
|||||||
{
|
{
|
||||||
case "X": return X;
|
case "X": return X;
|
||||||
case "Y": return Y;
|
case "Y": return Y;
|
||||||
|
case "Layer": return Layer;
|
||||||
default: throw new LuaException("CPos does not define a member '{0}'".F(key));
|
default: throw new LuaException("CPos does not define a member '{0}'".F(key));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,7 +100,9 @@ namespace OpenRA.Mods.Common.Activities
|
|||||||
minRange = armaments.Max(a => a.Weapon.MinRange);
|
minRange = armaments.Max(a => a.Weapon.MinRange);
|
||||||
maxRange = armaments.Min(a => a.MaxRange());
|
maxRange = armaments.Min(a => a.MaxRange());
|
||||||
|
|
||||||
if (!Target.IsInRange(self.CenterPosition, maxRange) || Target.IsInRange(self.CenterPosition, minRange))
|
var mobile = move as Mobile;
|
||||||
|
if (!Target.IsInRange(self.CenterPosition, maxRange) || Target.IsInRange(self.CenterPosition, minRange)
|
||||||
|
|| (mobile != null && !mobile.CanInteractWithGroundLayer(self)))
|
||||||
{
|
{
|
||||||
// Try to move within range, drop the target otherwise
|
// Try to move within range, drop the target otherwise
|
||||||
if (move == null)
|
if (move == null)
|
||||||
|
|||||||
@@ -184,10 +184,15 @@ namespace OpenRA.Mods.Common.Activities
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
mobile.SetLocation(mobile.FromCell, mobile.FromSubCell, nextCell.Value.First, nextCell.Value.Second);
|
mobile.SetLocation(mobile.FromCell, mobile.FromSubCell, nextCell.Value.First, nextCell.Value.Second);
|
||||||
var from = self.World.Map.CenterOfSubCell(mobile.FromCell, mobile.FromSubCell);
|
|
||||||
|
var map = self.World.Map;
|
||||||
|
var from = (mobile.FromCell.Layer == 0 ? map.CenterOfCell(mobile.FromCell) :
|
||||||
|
self.World.GetCustomMovementLayers()[mobile.FromCell.Layer].CenterOfCell(mobile.FromCell)) +
|
||||||
|
map.Grid.OffsetOfSubCell(mobile.FromSubCell);
|
||||||
|
|
||||||
var to = Util.BetweenCells(self.World, mobile.FromCell, mobile.ToCell) +
|
var to = Util.BetweenCells(self.World, mobile.FromCell, mobile.ToCell) +
|
||||||
(self.World.Map.Grid.OffsetOfSubCell(mobile.FromSubCell) +
|
(map.Grid.OffsetOfSubCell(mobile.FromSubCell) + map.Grid.OffsetOfSubCell(mobile.ToSubCell)) / 2;
|
||||||
self.World.Map.Grid.OffsetOfSubCell(mobile.ToSubCell)) / 2;
|
|
||||||
var move = new MoveFirstHalf(
|
var move = new MoveFirstHalf(
|
||||||
this,
|
this,
|
||||||
from,
|
from,
|
||||||
@@ -415,21 +420,22 @@ namespace OpenRA.Mods.Common.Activities
|
|||||||
|
|
||||||
protected override MovePart OnComplete(Actor self, Mobile mobile, Move parent)
|
protected override MovePart OnComplete(Actor self, Mobile mobile, Move parent)
|
||||||
{
|
{
|
||||||
var fromSubcellOffset = self.World.Map.Grid.OffsetOfSubCell(mobile.FromSubCell);
|
var map = self.World.Map;
|
||||||
var toSubcellOffset = self.World.Map.Grid.OffsetOfSubCell(mobile.ToSubCell);
|
var fromSubcellOffset = map.Grid.OffsetOfSubCell(mobile.FromSubCell);
|
||||||
|
var toSubcellOffset = map.Grid.OffsetOfSubCell(mobile.ToSubCell);
|
||||||
|
|
||||||
var nextCell = parent.PopPath(self);
|
var nextCell = parent.PopPath(self);
|
||||||
if (nextCell != null)
|
if (nextCell != null)
|
||||||
{
|
{
|
||||||
if (IsTurn(mobile, nextCell.Value.First))
|
if (IsTurn(mobile, nextCell.Value.First))
|
||||||
{
|
{
|
||||||
var nextSubcellOffset = self.World.Map.Grid.OffsetOfSubCell(nextCell.Value.Second);
|
var nextSubcellOffset = map.Grid.OffsetOfSubCell(nextCell.Value.Second);
|
||||||
var ret = new MoveFirstHalf(
|
var ret = new MoveFirstHalf(
|
||||||
Move,
|
Move,
|
||||||
Util.BetweenCells(self.World, mobile.FromCell, mobile.ToCell) + (fromSubcellOffset + toSubcellOffset) / 2,
|
Util.BetweenCells(self.World, mobile.FromCell, mobile.ToCell) + (fromSubcellOffset + toSubcellOffset) / 2,
|
||||||
Util.BetweenCells(self.World, mobile.ToCell, nextCell.Value.First) + (toSubcellOffset + nextSubcellOffset) / 2,
|
Util.BetweenCells(self.World, mobile.ToCell, nextCell.Value.First) + (toSubcellOffset + nextSubcellOffset) / 2,
|
||||||
mobile.Facing,
|
mobile.Facing,
|
||||||
Util.GetNearestFacing(mobile.Facing, self.World.Map.FacingBetween(mobile.ToCell, nextCell.Value.First, mobile.Facing)),
|
Util.GetNearestFacing(mobile.Facing, map.FacingBetween(mobile.ToCell, nextCell.Value.First, mobile.Facing)),
|
||||||
moveFraction - MoveFractionTotal);
|
moveFraction - MoveFractionTotal);
|
||||||
|
|
||||||
mobile.FinishedMoving(self);
|
mobile.FinishedMoving(self);
|
||||||
@@ -440,10 +446,13 @@ namespace OpenRA.Mods.Common.Activities
|
|||||||
parent.path.Add(nextCell.Value.First);
|
parent.path.Add(nextCell.Value.First);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var toPos = mobile.ToCell.Layer == 0 ? map.CenterOfCell(mobile.ToCell) :
|
||||||
|
self.World.GetCustomMovementLayers()[mobile.ToCell.Layer].CenterOfCell(mobile.ToCell);
|
||||||
|
|
||||||
var ret2 = new MoveSecondHalf(
|
var ret2 = new MoveSecondHalf(
|
||||||
Move,
|
Move,
|
||||||
Util.BetweenCells(self.World, mobile.FromCell, mobile.ToCell) + (fromSubcellOffset + toSubcellOffset) / 2,
|
Util.BetweenCells(self.World, mobile.FromCell, mobile.ToCell) + (fromSubcellOffset + toSubcellOffset) / 2,
|
||||||
self.World.Map.CenterOfCell(mobile.ToCell) + toSubcellOffset,
|
toPos + toSubcellOffset,
|
||||||
mobile.Facing,
|
mobile.Facing,
|
||||||
mobile.Facing,
|
mobile.Facing,
|
||||||
moveFraction - MoveFractionTotal);
|
moveFraction - MoveFractionTotal);
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ namespace OpenRA.Mods.Common.Activities
|
|||||||
{
|
{
|
||||||
static readonly List<CPos> NoPath = new List<CPos>();
|
static readonly List<CPos> NoPath = new List<CPos>();
|
||||||
|
|
||||||
readonly Mobile mobile;
|
protected readonly Mobile Mobile;
|
||||||
readonly IPathFinder pathFinder;
|
readonly IPathFinder pathFinder;
|
||||||
readonly DomainIndex domainIndex;
|
readonly DomainIndex domainIndex;
|
||||||
readonly uint movementClass;
|
readonly uint movementClass;
|
||||||
@@ -53,10 +53,10 @@ namespace OpenRA.Mods.Common.Activities
|
|||||||
{
|
{
|
||||||
Target = target;
|
Target = target;
|
||||||
|
|
||||||
mobile = self.Trait<Mobile>();
|
Mobile = self.Trait<Mobile>();
|
||||||
pathFinder = self.World.WorldActor.Trait<IPathFinder>();
|
pathFinder = self.World.WorldActor.Trait<IPathFinder>();
|
||||||
domainIndex = self.World.WorldActor.Trait<DomainIndex>();
|
domainIndex = self.World.WorldActor.Trait<DomainIndex>();
|
||||||
movementClass = (uint)mobile.Info.GetMovementClass(self.World.Map.Rules.TileSet);
|
movementClass = (uint)Mobile.Info.GetMovementClass(self.World.Map.Rules.TileSet);
|
||||||
|
|
||||||
if (target.IsValidFor(self))
|
if (target.IsValidFor(self))
|
||||||
targetPosition = self.World.Map.CellContaining(target.CenterPosition);
|
targetPosition = self.World.Map.CellContaining(target.CenterPosition);
|
||||||
@@ -91,7 +91,7 @@ namespace OpenRA.Mods.Common.Activities
|
|||||||
inner.Cancel(self);
|
inner.Cancel(self);
|
||||||
|
|
||||||
self.SetTargetLine(Target.FromCell(self.World, targetPosition), Color.Green);
|
self.SetTargetLine(Target.FromCell(self.World, targetPosition), Color.Green);
|
||||||
return ActivityUtils.RunActivity(self, new AttackMoveActivity(self, mobile.MoveTo(targetPosition, 0)));
|
return ActivityUtils.RunActivity(self, new AttackMoveActivity(self, Mobile.MoveTo(targetPosition, 0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inner move order has completed.
|
// Inner move order has completed.
|
||||||
@@ -103,7 +103,7 @@ namespace OpenRA.Mods.Common.Activities
|
|||||||
return NextActivity;
|
return NextActivity;
|
||||||
|
|
||||||
// Target has moved, and MoveAdjacentTo is still valid.
|
// Target has moved, and MoveAdjacentTo is still valid.
|
||||||
inner = mobile.MoveTo(() => CalculatePathToTarget(self));
|
inner = Mobile.MoveTo(() => CalculatePathToTarget(self));
|
||||||
repath = false;
|
repath = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,14 +142,14 @@ namespace OpenRA.Mods.Common.Activities
|
|||||||
var loc = self.Location;
|
var loc = self.Location;
|
||||||
|
|
||||||
foreach (var cell in targetCells)
|
foreach (var cell in targetCells)
|
||||||
if (domainIndex.IsPassable(loc, cell, movementClass) && mobile.CanEnterCell(cell))
|
if (domainIndex.IsPassable(loc, cell, movementClass) && Mobile.CanEnterCell(cell))
|
||||||
searchCells.Add(cell);
|
searchCells.Add(cell);
|
||||||
|
|
||||||
if (!searchCells.Any())
|
if (!searchCells.Any())
|
||||||
return NoPath;
|
return NoPath;
|
||||||
|
|
||||||
using (var fromSrc = PathSearch.FromPoints(self.World, mobile.Info, self, searchCells, loc, true))
|
using (var fromSrc = PathSearch.FromPoints(self.World, Mobile.Info, self, searchCells, loc, true))
|
||||||
using (var fromDest = PathSearch.FromPoint(self.World, mobile.Info, self, loc, targetPosition, true).Reverse())
|
using (var fromDest = PathSearch.FromPoint(self.World, Mobile.Info, self, loc, targetPosition, true).Reverse())
|
||||||
return pathFinder.FindBidiPath(fromSrc, fromDest);
|
return pathFinder.FindBidiPath(fromSrc, fromDest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using OpenRA.Mods.Common.Traits;
|
||||||
using OpenRA.Traits;
|
using OpenRA.Traits;
|
||||||
|
|
||||||
namespace OpenRA.Mods.Common.Activities
|
namespace OpenRA.Mods.Common.Activities
|
||||||
@@ -31,12 +32,13 @@ namespace OpenRA.Mods.Common.Activities
|
|||||||
{
|
{
|
||||||
// We are now in range. Don't move any further!
|
// We are now in range. Don't move any further!
|
||||||
// HACK: This works around the pathfinder not returning the shortest path
|
// HACK: This works around the pathfinder not returning the shortest path
|
||||||
return AtCorrectRange(self.CenterPosition);
|
return AtCorrectRange(self.CenterPosition) && Mobile.CanInteractWithGroundLayer(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool ShouldRepath(Actor self, CPos oldTargetPosition)
|
protected override bool ShouldRepath(Actor self, CPos oldTargetPosition)
|
||||||
{
|
{
|
||||||
return targetPosition != oldTargetPosition && !AtCorrectRange(self.CenterPosition);
|
return targetPosition != oldTargetPosition && (!AtCorrectRange(self.CenterPosition)
|
||||||
|
|| !Mobile.CanInteractWithGroundLayer(self));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override IEnumerable<CPos> CandidateMovementCells(Actor self)
|
protected override IEnumerable<CPos> CandidateMovementCells(Actor self)
|
||||||
|
|||||||
@@ -57,21 +57,28 @@ namespace OpenRA.Mods.Common.Pathfinder
|
|||||||
|
|
||||||
public class PooledCellInfoLayer : IDisposable
|
public class PooledCellInfoLayer : IDisposable
|
||||||
{
|
{
|
||||||
public CellLayer<CellInfo> Layer { get; private set; }
|
|
||||||
CellInfoLayerPool layerPool;
|
CellInfoLayerPool layerPool;
|
||||||
|
List<CellLayer<CellInfo>> layers = new List<CellLayer<CellInfo>>();
|
||||||
|
|
||||||
public PooledCellInfoLayer(CellInfoLayerPool layerPool)
|
public PooledCellInfoLayer(CellInfoLayerPool layerPool)
|
||||||
{
|
{
|
||||||
this.layerPool = layerPool;
|
this.layerPool = layerPool;
|
||||||
Layer = layerPool.GetLayer();
|
}
|
||||||
|
|
||||||
|
public CellLayer<CellInfo> GetLayer()
|
||||||
|
{
|
||||||
|
var layer = layerPool.GetLayer();
|
||||||
|
layers.Add(layer);
|
||||||
|
return layer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
if (Layer == null)
|
if (layerPool != null)
|
||||||
return;
|
foreach (var layer in layers)
|
||||||
layerPool.ReturnLayer(Layer);
|
layerPool.ReturnLayer(layer);
|
||||||
Layer = null;
|
|
||||||
|
layers = null;
|
||||||
layerPool = null;
|
layerPool = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,10 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using OpenRA.Mods.Common.Traits;
|
using OpenRA.Mods.Common.Traits;
|
||||||
|
using OpenRA.Primitives;
|
||||||
|
using OpenRA.Traits;
|
||||||
|
|
||||||
namespace OpenRA.Mods.Common.Pathfinder
|
namespace OpenRA.Mods.Common.Pathfinder
|
||||||
{
|
{
|
||||||
@@ -84,12 +87,21 @@ namespace OpenRA.Mods.Common.Pathfinder
|
|||||||
readonly MobileInfo mobileInfo;
|
readonly MobileInfo mobileInfo;
|
||||||
readonly MobileInfo.WorldMovementInfo worldMovementInfo;
|
readonly MobileInfo.WorldMovementInfo worldMovementInfo;
|
||||||
readonly CellInfoLayerPool.PooledCellInfoLayer pooledLayer;
|
readonly CellInfoLayerPool.PooledCellInfoLayer pooledLayer;
|
||||||
CellLayer<CellInfo> cellInfo;
|
CellLayer<CellInfo> groundInfo;
|
||||||
|
|
||||||
|
readonly Dictionary<byte, Pair<ICustomMovementLayer, CellLayer<CellInfo>>> customLayerInfo =
|
||||||
|
new Dictionary<byte, Pair<ICustomMovementLayer, CellLayer<CellInfo>>>();
|
||||||
|
|
||||||
public PathGraph(CellInfoLayerPool layerPool, MobileInfo mobileInfo, Actor actor, World world, bool checkForBlocked)
|
public PathGraph(CellInfoLayerPool layerPool, MobileInfo mobileInfo, Actor actor, World world, bool checkForBlocked)
|
||||||
{
|
{
|
||||||
pooledLayer = layerPool.Get();
|
pooledLayer = layerPool.Get();
|
||||||
cellInfo = pooledLayer.Layer;
|
groundInfo = pooledLayer.GetLayer();
|
||||||
|
var layers = world.GetCustomMovementLayers().Values
|
||||||
|
.Where(cml => cml.EnabledForActor(actor.Info, mobileInfo));
|
||||||
|
|
||||||
|
foreach (var cml in layers)
|
||||||
|
customLayerInfo[cml.Index] = Pair.New(cml, pooledLayer.GetLayer());
|
||||||
|
|
||||||
World = world;
|
World = world;
|
||||||
this.mobileInfo = mobileInfo;
|
this.mobileInfo = mobileInfo;
|
||||||
worldMovementInfo = mobileInfo.GetWorldMovementInfo(world);
|
worldMovementInfo = mobileInfo.GetWorldMovementInfo(world);
|
||||||
@@ -117,7 +129,8 @@ namespace OpenRA.Mods.Common.Pathfinder
|
|||||||
|
|
||||||
public List<GraphConnection> GetConnections(CPos position)
|
public List<GraphConnection> GetConnections(CPos position)
|
||||||
{
|
{
|
||||||
var previousPos = cellInfo[position].PreviousPos;
|
var info = position.Layer == 0 ? groundInfo : customLayerInfo[position.Layer].Second;
|
||||||
|
var previousPos = info[position].PreviousPos;
|
||||||
|
|
||||||
var dx = position.X - previousPos.X;
|
var dx = position.X - previousPos.X;
|
||||||
var dy = position.Y - previousPos.Y;
|
var dy = position.Y - previousPos.Y;
|
||||||
@@ -133,6 +146,24 @@ namespace OpenRA.Mods.Common.Pathfinder
|
|||||||
validNeighbors.Add(new GraphConnection(neighbor, movementCost));
|
validNeighbors.Add(new GraphConnection(neighbor, movementCost));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (position.Layer == 0)
|
||||||
|
{
|
||||||
|
foreach (var cli in customLayerInfo.Values)
|
||||||
|
{
|
||||||
|
var layerPosition = new CPos(position.X, position.Y, cli.First.Index);
|
||||||
|
var entryCost = cli.First.EntryMovementCost(Actor.Info, mobileInfo, layerPosition);
|
||||||
|
if (entryCost != Constants.InvalidNode)
|
||||||
|
validNeighbors.Add(new GraphConnection(layerPosition, entryCost));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var layerPosition = new CPos(position.X, position.Y, 0);
|
||||||
|
var exitCost = customLayerInfo[position.Layer].First.ExitMovementCost(Actor.Info, mobileInfo, layerPosition);
|
||||||
|
if (exitCost != Constants.InvalidNode)
|
||||||
|
validNeighbors.Add(new GraphConnection(layerPosition, exitCost));
|
||||||
|
}
|
||||||
|
|
||||||
return validNeighbors;
|
return validNeighbors;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,14 +210,15 @@ namespace OpenRA.Mods.Common.Pathfinder
|
|||||||
|
|
||||||
public CellInfo this[CPos pos]
|
public CellInfo this[CPos pos]
|
||||||
{
|
{
|
||||||
get { return cellInfo[pos]; }
|
get { return (pos.Layer == 0 ? groundInfo : customLayerInfo[pos.Layer].Second)[pos]; }
|
||||||
set { cellInfo[pos] = value; }
|
set { (pos.Layer == 0 ? groundInfo : customLayerInfo[pos.Layer].Second)[pos] = value; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
groundInfo = null;
|
||||||
|
customLayerInfo.Clear();
|
||||||
pooledLayer.Dispose();
|
pooledLayer.Dispose();
|
||||||
cellInfo = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -151,15 +151,17 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
|
|
||||||
public int MovementCostForCell(World world, CPos cell)
|
public int MovementCostForCell(World world, CPos cell)
|
||||||
{
|
{
|
||||||
return MovementCostForCell(world.Map, TilesetTerrainInfo[world.Map.Rules.TileSet], cell);
|
return MovementCostForCell(world, TilesetTerrainInfo[world.Map.Rules.TileSet], cell);
|
||||||
}
|
}
|
||||||
|
|
||||||
int MovementCostForCell(Map map, TerrainInfo[] terrainInfos, CPos cell)
|
int MovementCostForCell(World world, TerrainInfo[] terrainInfos, CPos cell)
|
||||||
{
|
{
|
||||||
if (!map.Contains(cell))
|
if (!world.Map.Contains(cell))
|
||||||
return int.MaxValue;
|
return int.MaxValue;
|
||||||
|
|
||||||
var index = map.GetTerrainIndex(cell);
|
var index = cell.Layer == 0 ? world.Map.GetTerrainIndex(cell) :
|
||||||
|
world.GetCustomMovementLayers()[cell.Layer].GetTerrainIndex(cell);
|
||||||
|
|
||||||
if (index == byte.MaxValue)
|
if (index == byte.MaxValue)
|
||||||
return int.MaxValue;
|
return int.MaxValue;
|
||||||
|
|
||||||
@@ -279,7 +281,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
|
|
||||||
public int MovementCostToEnterCell(WorldMovementInfo worldMovementInfo, Actor self, CPos cell, Actor ignoreActor = null, CellConditions check = CellConditions.All)
|
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);
|
var cost = MovementCostForCell(worldMovementInfo.World, worldMovementInfo.TerrainInfos, cell);
|
||||||
if (cost == int.MaxValue || !CanMoveFreelyInto(worldMovementInfo.World, self, cell, ignoreActor, check))
|
if (cost == int.MaxValue || !CanMoveFreelyInto(worldMovementInfo.World, self, cell, ignoreActor, check))
|
||||||
return int.MaxValue;
|
return int.MaxValue;
|
||||||
return cost;
|
return cost;
|
||||||
@@ -413,7 +415,12 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
{
|
{
|
||||||
subCell = GetValidSubCell(subCell);
|
subCell = GetValidSubCell(subCell);
|
||||||
SetLocation(cell, subCell, cell, subCell);
|
SetLocation(cell, subCell, cell, subCell);
|
||||||
SetVisualPosition(self, self.World.Map.CenterOfSubCell(cell, subCell));
|
|
||||||
|
var position = cell.Layer == 0 ? self.World.Map.CenterOfCell(cell) :
|
||||||
|
self.World.GetCustomMovementLayers()[cell.Layer].CenterOfCell(cell);
|
||||||
|
|
||||||
|
var subcellOffset = self.World.Map.Grid.OffsetOfSubCell(subCell);
|
||||||
|
SetVisualPosition(self, position + subcellOffset);
|
||||||
FinishedMoving(self);
|
FinishedMoving(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -626,7 +633,9 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
|
|
||||||
public int MovementSpeedForCell(Actor self, CPos cell)
|
public int MovementSpeedForCell(Actor self, CPos cell)
|
||||||
{
|
{
|
||||||
var index = self.World.Map.GetTerrainIndex(cell);
|
var index = cell.Layer == 0 ? self.World.Map.GetTerrainIndex(cell) :
|
||||||
|
self.World.GetCustomMovementLayers()[cell.Layer].GetTerrainIndex(cell);
|
||||||
|
|
||||||
if (index == byte.MaxValue)
|
if (index == byte.MaxValue)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@@ -716,6 +725,21 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool CanInteractWithGroundLayer(Actor self)
|
||||||
|
{
|
||||||
|
// TODO: Think about extending this to support arbitrary layer-layer checks
|
||||||
|
// in a way that is compatible with the other IMove types.
|
||||||
|
// This would then allow us to e.g. have units attack other units inside tunnels.
|
||||||
|
if (ToCell.Layer == 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
ICustomMovementLayer layer;
|
||||||
|
if (self.World.GetCustomMovementLayers().TryGetValue(ToCell.Layer, out layer))
|
||||||
|
return layer.InteractsWithDefaultLayer;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void IActorPreviewInitModifier.ModifyActorPreviewInit(Actor self, TypeDictionary inits)
|
void IActorPreviewInitModifier.ModifyActorPreviewInit(Actor self, TypeDictionary inits)
|
||||||
{
|
{
|
||||||
if (!inits.Contains<DynamicFacingInit>() && !inits.Contains<FacingInit>())
|
if (!inits.Contains<DynamicFacingInit>() && !inits.Contains<FacingInit>())
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
public object Create(ActorInitializer init) { return new ActorMap(init.World, this); }
|
public object Create(ActorInitializer init) { return new ActorMap(init.World, this); }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ActorMap : IActorMap, ITick
|
public class ActorMap : IActorMap, ITick, INotifyCreated
|
||||||
{
|
{
|
||||||
class InfluenceNode
|
class InfluenceNode
|
||||||
{
|
{
|
||||||
@@ -165,6 +165,8 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
int nextTriggerId;
|
int nextTriggerId;
|
||||||
|
|
||||||
readonly CellLayer<InfluenceNode> influence;
|
readonly CellLayer<InfluenceNode> influence;
|
||||||
|
readonly Dictionary<int, CellLayer<InfluenceNode>> customInfluence = new Dictionary<int, CellLayer<InfluenceNode>>();
|
||||||
|
public readonly Dictionary<int, ICustomMovementLayer> CustomMovementLayers = new Dictionary<int, ICustomMovementLayer>();
|
||||||
|
|
||||||
readonly Bin[] bins;
|
readonly Bin[] bins;
|
||||||
readonly int rows, cols;
|
readonly int rows, cols;
|
||||||
@@ -192,6 +194,15 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
actorShouldBeRemoved = removeActorPosition.Contains;
|
actorShouldBeRemoved = removeActorPosition.Contains;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void INotifyCreated.Created(Actor self)
|
||||||
|
{
|
||||||
|
foreach (var cml in self.TraitsImplementing<ICustomMovementLayer>())
|
||||||
|
{
|
||||||
|
CustomMovementLayers[cml.Index] = cml;
|
||||||
|
customInfluence.Add(cml.Index, new CellLayer<InfluenceNode>(self.World.Map));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sealed class ActorsAtEnumerator : IEnumerator<Actor>
|
sealed class ActorsAtEnumerator : IEnumerator<Actor>
|
||||||
{
|
{
|
||||||
InfluenceNode node;
|
InfluenceNode node;
|
||||||
@@ -228,7 +239,9 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
var uv = a.ToMPos(map);
|
var uv = a.ToMPos(map);
|
||||||
if (!influence.Contains(uv))
|
if (!influence.Contains(uv))
|
||||||
return Enumerable.Empty<Actor>();
|
return Enumerable.Empty<Actor>();
|
||||||
return new ActorsAtEnumerable(influence[uv]);
|
|
||||||
|
var layer = a.Layer == 0 ? influence : customInfluence[a.Layer];
|
||||||
|
return new ActorsAtEnumerable(layer[uv]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<Actor> GetActorsAt(CPos a, SubCell sub)
|
public IEnumerable<Actor> GetActorsAt(CPos a, SubCell sub)
|
||||||
@@ -237,7 +250,8 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
if (!influence.Contains(uv))
|
if (!influence.Contains(uv))
|
||||||
yield break;
|
yield break;
|
||||||
|
|
||||||
for (var i = influence[uv]; i != null; i = i.Next)
|
var layer = a.Layer == 0 ? influence : customInfluence[a.Layer];
|
||||||
|
for (var i = layer[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;
|
||||||
}
|
}
|
||||||
@@ -283,7 +297,8 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
if (!influence.Contains(uv))
|
if (!influence.Contains(uv))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return influence[uv] != null;
|
var layer = a.Layer == 0 ? influence : customInfluence[a.Layer];
|
||||||
|
return layer[uv] != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: can not check aircraft
|
// NOTE: can not check aircraft
|
||||||
@@ -294,7 +309,8 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
var always = sub == SubCell.FullCell || sub == SubCell.Any;
|
var always = sub == SubCell.FullCell || sub == SubCell.Any;
|
||||||
for (var i = influence[uv]; i != null; i = i.Next)
|
var layer = a.Layer == 0 ? influence : customInfluence[a.Layer];
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
@@ -318,7 +334,8 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
var always = sub == SubCell.FullCell || sub == SubCell.Any;
|
var always = sub == SubCell.FullCell || sub == SubCell.Any;
|
||||||
for (var i = influence[uv]; i != null; i = i.Next)
|
var layer = a.Layer == 0 ? influence : customInfluence[a.Layer];
|
||||||
|
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;
|
||||||
|
|
||||||
@@ -333,7 +350,8 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
if (!influence.Contains(uv))
|
if (!influence.Contains(uv))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
influence[uv] = new InfluenceNode { Next = influence[uv], SubCell = c.Second, Actor = self };
|
var layer = c.First.Layer == 0 ? influence : customInfluence[c.First.Layer];
|
||||||
|
layer[uv] = new InfluenceNode { Next = layer[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))
|
||||||
@@ -350,9 +368,10 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
if (!influence.Contains(uv))
|
if (!influence.Contains(uv))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var temp = influence[uv];
|
var layer = c.First.Layer == 0 ? influence : customInfluence[c.First.Layer];
|
||||||
|
var temp = layer[uv];
|
||||||
RemoveInfluenceInner(ref temp, self);
|
RemoveInfluenceInner(ref temp, self);
|
||||||
influence[uv] = temp;
|
layer[uv] = temp;
|
||||||
|
|
||||||
List<CellTrigger> triggers;
|
List<CellTrigger> triggers;
|
||||||
if (cellTriggerInfluence.TryGetValue(c.First, out triggers))
|
if (cellTriggerInfluence.TryGetValue(c.First, out triggers))
|
||||||
@@ -567,4 +586,12 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class ActorMapWorldExts
|
||||||
|
{
|
||||||
|
public static Dictionary<int, ICustomMovementLayer> GetCustomMovementLayers(this World world)
|
||||||
|
{
|
||||||
|
return ((ActorMap)world.ActorMap).CustomMovementLayers;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,11 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
|
|
||||||
public bool IsPassable(CPos p1, CPos p2, uint movementClass)
|
public bool IsPassable(CPos p1, CPos p2, uint movementClass)
|
||||||
{
|
{
|
||||||
|
// HACK: Work around units in other movement layers from being blocked
|
||||||
|
// when the point in the main layer is not pathable
|
||||||
|
if (p1.Layer != 0 || p2.Layer != 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
return domainIndexes[movementClass].IsPassable(p1, p2);
|
return domainIndexes[movementClass].IsPassable(p1, p2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,6 +54,12 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
foreach (var index in domainIndexes)
|
foreach (var index in domainIndexes)
|
||||||
index.Value.UpdateCells(world, dirty);
|
index.Value.UpdateCells(world, dirty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void AddFixedConnection(IEnumerable<CPos> cells)
|
||||||
|
{
|
||||||
|
foreach (var index in domainIndexes)
|
||||||
|
index.Value.AddFixedConnection(cells);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MovementClassDomainIndex
|
class MovementClassDomainIndex
|
||||||
@@ -119,6 +130,19 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
CreateConnection(c1, c2);
|
CreateConnection(c1, c2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void AddFixedConnection(IEnumerable<CPos> cells)
|
||||||
|
{
|
||||||
|
// HACK: this is a temporary workaround to add a permanent connection between the domains of the listed cells.
|
||||||
|
// This is sufficient for fixed point-to-point tunnels, but not for dynamically updating custom layers
|
||||||
|
// such as destroyable elevated bridges.
|
||||||
|
// To support those the domain index will need to learn about custom movement layers, but that then requires
|
||||||
|
// a complete refactor of the domain code to deal with MobileInfo or better a shared pathfinder class type.
|
||||||
|
var cellDomains = cells.Select(c => domains[c]).ToHashSet();
|
||||||
|
foreach (var c1 in cellDomains)
|
||||||
|
foreach (var c2 in cellDomains.Where(c => c != c1))
|
||||||
|
CreateConnection(c1, c2);
|
||||||
|
}
|
||||||
|
|
||||||
bool HasConnection(int d1, int d2)
|
bool HasConnection(int d1, int d2)
|
||||||
{
|
{
|
||||||
// Search our connections graph for a possible route
|
// Search our connections graph for a possible route
|
||||||
|
|||||||
@@ -233,4 +233,18 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
|
|
||||||
[RequireExplicitImplementation]
|
[RequireExplicitImplementation]
|
||||||
public interface IGainsExperienceModifier { int GetGainsExperienceModifier(); }
|
public interface IGainsExperienceModifier { int GetGainsExperienceModifier(); }
|
||||||
|
|
||||||
|
[RequireExplicitImplementation]
|
||||||
|
public interface ICustomMovementLayer
|
||||||
|
{
|
||||||
|
byte Index { get; }
|
||||||
|
bool InteractsWithDefaultLayer { get; }
|
||||||
|
|
||||||
|
bool EnabledForActor(ActorInfo a, MobileInfo mi);
|
||||||
|
int EntryMovementCost(ActorInfo a, MobileInfo mi, CPos cell);
|
||||||
|
int ExitMovementCost(ActorInfo a, MobileInfo mi, CPos cell);
|
||||||
|
|
||||||
|
byte GetTerrainIndex(CPos cell);
|
||||||
|
WPos CenterOfCell(CPos cell);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using OpenRA.Mods.Common.Traits;
|
||||||
using OpenRA.Support;
|
using OpenRA.Support;
|
||||||
using OpenRA.Traits;
|
using OpenRA.Traits;
|
||||||
|
|
||||||
@@ -85,7 +86,13 @@ namespace OpenRA.Mods.Common
|
|||||||
|
|
||||||
public static WPos BetweenCells(World w, CPos from, CPos to)
|
public static WPos BetweenCells(World w, CPos from, CPos to)
|
||||||
{
|
{
|
||||||
return WPos.Lerp(w.Map.CenterOfCell(from), w.Map.CenterOfCell(to), 1, 2);
|
var fromPos = from.Layer == 0 ? w.Map.CenterOfCell(from) :
|
||||||
|
w.GetCustomMovementLayers()[from.Layer].CenterOfCell(from);
|
||||||
|
|
||||||
|
var toPos = to.Layer == 0 ? w.Map.CenterOfCell(to) :
|
||||||
|
w.GetCustomMovementLayers()[to.Layer].CenterOfCell(to);
|
||||||
|
|
||||||
|
return WPos.Lerp(fromPos, toPos, 1, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> ts, MersenneTwister random)
|
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> ts, MersenneTwister random)
|
||||||
|
|||||||
Reference in New Issue
Block a user