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 readonly int X, Y;
|
||||
public readonly byte Layer;
|
||||
|
||||
public CPos(int x, int y) { X = x; Y = y; }
|
||||
public static readonly CPos Zero = new CPos(0, 0);
|
||||
public CPos(int x, int y) { X = x; Y = y; Layer = 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 CPos operator +(CVec a, CPos 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); }
|
||||
public static CPos operator -(CPos a, CVec 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, a.Layer); }
|
||||
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 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 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 string ToString() { return X + "," + Y; }
|
||||
@@ -117,6 +119,7 @@ namespace OpenRA
|
||||
{
|
||||
case "X": return X;
|
||||
case "Y": return Y;
|
||||
case "Layer": return Layer;
|
||||
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);
|
||||
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
|
||||
if (move == null)
|
||||
|
||||
@@ -184,10 +184,15 @@ namespace OpenRA.Mods.Common.Activities
|
||||
else
|
||||
{
|
||||
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) +
|
||||
(self.World.Map.Grid.OffsetOfSubCell(mobile.FromSubCell) +
|
||||
self.World.Map.Grid.OffsetOfSubCell(mobile.ToSubCell)) / 2;
|
||||
(map.Grid.OffsetOfSubCell(mobile.FromSubCell) + map.Grid.OffsetOfSubCell(mobile.ToSubCell)) / 2;
|
||||
|
||||
var move = new MoveFirstHalf(
|
||||
this,
|
||||
from,
|
||||
@@ -415,21 +420,22 @@ namespace OpenRA.Mods.Common.Activities
|
||||
|
||||
protected override MovePart OnComplete(Actor self, Mobile mobile, Move parent)
|
||||
{
|
||||
var fromSubcellOffset = self.World.Map.Grid.OffsetOfSubCell(mobile.FromSubCell);
|
||||
var toSubcellOffset = self.World.Map.Grid.OffsetOfSubCell(mobile.ToSubCell);
|
||||
var map = self.World.Map;
|
||||
var fromSubcellOffset = map.Grid.OffsetOfSubCell(mobile.FromSubCell);
|
||||
var toSubcellOffset = map.Grid.OffsetOfSubCell(mobile.ToSubCell);
|
||||
|
||||
var nextCell = parent.PopPath(self);
|
||||
if (nextCell != null)
|
||||
{
|
||||
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(
|
||||
Move,
|
||||
Util.BetweenCells(self.World, mobile.FromCell, mobile.ToCell) + (fromSubcellOffset + toSubcellOffset) / 2,
|
||||
Util.BetweenCells(self.World, mobile.ToCell, nextCell.Value.First) + (toSubcellOffset + nextSubcellOffset) / 2,
|
||||
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);
|
||||
|
||||
mobile.FinishedMoving(self);
|
||||
@@ -440,10 +446,13 @@ namespace OpenRA.Mods.Common.Activities
|
||||
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(
|
||||
Move,
|
||||
Util.BetweenCells(self.World, mobile.FromCell, mobile.ToCell) + (fromSubcellOffset + toSubcellOffset) / 2,
|
||||
self.World.Map.CenterOfCell(mobile.ToCell) + toSubcellOffset,
|
||||
toPos + toSubcellOffset,
|
||||
mobile.Facing,
|
||||
mobile.Facing,
|
||||
moveFraction - MoveFractionTotal);
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace OpenRA.Mods.Common.Activities
|
||||
{
|
||||
static readonly List<CPos> NoPath = new List<CPos>();
|
||||
|
||||
readonly Mobile mobile;
|
||||
protected readonly Mobile Mobile;
|
||||
readonly IPathFinder pathFinder;
|
||||
readonly DomainIndex domainIndex;
|
||||
readonly uint movementClass;
|
||||
@@ -53,10 +53,10 @@ namespace OpenRA.Mods.Common.Activities
|
||||
{
|
||||
Target = target;
|
||||
|
||||
mobile = self.Trait<Mobile>();
|
||||
Mobile = self.Trait<Mobile>();
|
||||
pathFinder = self.World.WorldActor.Trait<IPathFinder>();
|
||||
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))
|
||||
targetPosition = self.World.Map.CellContaining(target.CenterPosition);
|
||||
@@ -91,7 +91,7 @@ namespace OpenRA.Mods.Common.Activities
|
||||
inner.Cancel(self);
|
||||
|
||||
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.
|
||||
@@ -103,7 +103,7 @@ namespace OpenRA.Mods.Common.Activities
|
||||
return NextActivity;
|
||||
|
||||
// Target has moved, and MoveAdjacentTo is still valid.
|
||||
inner = mobile.MoveTo(() => CalculatePathToTarget(self));
|
||||
inner = Mobile.MoveTo(() => CalculatePathToTarget(self));
|
||||
repath = false;
|
||||
}
|
||||
|
||||
@@ -142,14 +142,14 @@ namespace OpenRA.Mods.Common.Activities
|
||||
var loc = self.Location;
|
||||
|
||||
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);
|
||||
|
||||
if (!searchCells.Any())
|
||||
return NoPath;
|
||||
|
||||
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 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())
|
||||
return pathFinder.FindBidiPath(fromSrc, fromDest);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.Activities
|
||||
@@ -31,12 +32,13 @@ namespace OpenRA.Mods.Common.Activities
|
||||
{
|
||||
// We are now in range. Don't move any further!
|
||||
// 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)
|
||||
{
|
||||
return targetPosition != oldTargetPosition && !AtCorrectRange(self.CenterPosition);
|
||||
return targetPosition != oldTargetPosition && (!AtCorrectRange(self.CenterPosition)
|
||||
|| !Mobile.CanInteractWithGroundLayer(self));
|
||||
}
|
||||
|
||||
protected override IEnumerable<CPos> CandidateMovementCells(Actor self)
|
||||
|
||||
@@ -57,21 +57,28 @@ namespace OpenRA.Mods.Common.Pathfinder
|
||||
|
||||
public class PooledCellInfoLayer : IDisposable
|
||||
{
|
||||
public CellLayer<CellInfo> Layer { get; private set; }
|
||||
CellInfoLayerPool layerPool;
|
||||
List<CellLayer<CellInfo>> layers = new List<CellLayer<CellInfo>>();
|
||||
|
||||
public PooledCellInfoLayer(CellInfoLayerPool layerPool)
|
||||
{
|
||||
this.layerPool = layerPool;
|
||||
Layer = layerPool.GetLayer();
|
||||
}
|
||||
|
||||
public CellLayer<CellInfo> GetLayer()
|
||||
{
|
||||
var layer = layerPool.GetLayer();
|
||||
layers.Add(layer);
|
||||
return layer;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (Layer == null)
|
||||
return;
|
||||
layerPool.ReturnLayer(Layer);
|
||||
Layer = null;
|
||||
if (layerPool != null)
|
||||
foreach (var layer in layers)
|
||||
layerPool.ReturnLayer(layer);
|
||||
|
||||
layers = null;
|
||||
layerPool = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,10 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.Pathfinder
|
||||
{
|
||||
@@ -84,12 +87,21 @@ namespace OpenRA.Mods.Common.Pathfinder
|
||||
readonly MobileInfo mobileInfo;
|
||||
readonly MobileInfo.WorldMovementInfo worldMovementInfo;
|
||||
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)
|
||||
{
|
||||
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;
|
||||
this.mobileInfo = mobileInfo;
|
||||
worldMovementInfo = mobileInfo.GetWorldMovementInfo(world);
|
||||
@@ -117,7 +129,8 @@ namespace OpenRA.Mods.Common.Pathfinder
|
||||
|
||||
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 dy = position.Y - previousPos.Y;
|
||||
@@ -133,6 +146,24 @@ namespace OpenRA.Mods.Common.Pathfinder
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -179,14 +210,15 @@ namespace OpenRA.Mods.Common.Pathfinder
|
||||
|
||||
public CellInfo this[CPos pos]
|
||||
{
|
||||
get { return cellInfo[pos]; }
|
||||
set { cellInfo[pos] = value; }
|
||||
get { return (pos.Layer == 0 ? groundInfo : customLayerInfo[pos.Layer].Second)[pos]; }
|
||||
set { (pos.Layer == 0 ? groundInfo : customLayerInfo[pos.Layer].Second)[pos] = value; }
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
groundInfo = null;
|
||||
customLayerInfo.Clear();
|
||||
pooledLayer.Dispose();
|
||||
cellInfo = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,15 +151,17 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
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;
|
||||
|
||||
var index = map.GetTerrainIndex(cell);
|
||||
var index = cell.Layer == 0 ? world.Map.GetTerrainIndex(cell) :
|
||||
world.GetCustomMovementLayers()[cell.Layer].GetTerrainIndex(cell);
|
||||
|
||||
if (index == byte.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)
|
||||
{
|
||||
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))
|
||||
return int.MaxValue;
|
||||
return cost;
|
||||
@@ -413,7 +415,12 @@ namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
subCell = GetValidSubCell(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);
|
||||
}
|
||||
|
||||
@@ -626,7 +633,9 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
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)
|
||||
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)
|
||||
{
|
||||
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 class ActorMap : IActorMap, ITick
|
||||
public class ActorMap : IActorMap, ITick, INotifyCreated
|
||||
{
|
||||
class InfluenceNode
|
||||
{
|
||||
@@ -165,6 +165,8 @@ namespace OpenRA.Mods.Common.Traits
|
||||
int nextTriggerId;
|
||||
|
||||
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 int rows, cols;
|
||||
@@ -192,6 +194,15 @@ namespace OpenRA.Mods.Common.Traits
|
||||
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>
|
||||
{
|
||||
InfluenceNode node;
|
||||
@@ -228,7 +239,9 @@ namespace OpenRA.Mods.Common.Traits
|
||||
var uv = a.ToMPos(map);
|
||||
if (!influence.Contains(uv))
|
||||
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)
|
||||
@@ -237,7 +250,8 @@ namespace OpenRA.Mods.Common.Traits
|
||||
if (!influence.Contains(uv))
|
||||
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))
|
||||
yield return i.Actor;
|
||||
}
|
||||
@@ -283,7 +297,8 @@ namespace OpenRA.Mods.Common.Traits
|
||||
if (!influence.Contains(uv))
|
||||
return false;
|
||||
|
||||
return influence[uv] != null;
|
||||
var layer = a.Layer == 0 ? influence : customInfluence[a.Layer];
|
||||
return layer[uv] != null;
|
||||
}
|
||||
|
||||
// NOTE: can not check aircraft
|
||||
@@ -294,7 +309,8 @@ namespace OpenRA.Mods.Common.Traits
|
||||
return false;
|
||||
|
||||
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)
|
||||
{
|
||||
@@ -318,7 +334,8 @@ namespace OpenRA.Mods.Common.Traits
|
||||
return false;
|
||||
|
||||
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))
|
||||
return true;
|
||||
|
||||
@@ -333,7 +350,8 @@ namespace OpenRA.Mods.Common.Traits
|
||||
if (!influence.Contains(uv))
|
||||
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;
|
||||
if (cellTriggerInfluence.TryGetValue(c.First, out triggers))
|
||||
@@ -350,9 +368,10 @@ namespace OpenRA.Mods.Common.Traits
|
||||
if (!influence.Contains(uv))
|
||||
continue;
|
||||
|
||||
var temp = influence[uv];
|
||||
var layer = c.First.Layer == 0 ? influence : customInfluence[c.First.Layer];
|
||||
var temp = layer[uv];
|
||||
RemoveInfluenceInner(ref temp, self);
|
||||
influence[uv] = temp;
|
||||
layer[uv] = temp;
|
||||
|
||||
List<CellTrigger> 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)
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
|
||||
@@ -49,6 +54,12 @@ namespace OpenRA.Mods.Common.Traits
|
||||
foreach (var index in domainIndexes)
|
||||
index.Value.UpdateCells(world, dirty);
|
||||
}
|
||||
|
||||
public void AddFixedConnection(IEnumerable<CPos> cells)
|
||||
{
|
||||
foreach (var index in domainIndexes)
|
||||
index.Value.AddFixedConnection(cells);
|
||||
}
|
||||
}
|
||||
|
||||
class MovementClassDomainIndex
|
||||
@@ -119,6 +130,19 @@ namespace OpenRA.Mods.Common.Traits
|
||||
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)
|
||||
{
|
||||
// Search our connections graph for a possible route
|
||||
|
||||
@@ -233,4 +233,18 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
[RequireExplicitImplementation]
|
||||
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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
using OpenRA.Support;
|
||||
using OpenRA.Traits;
|
||||
|
||||
@@ -85,7 +86,13 @@ namespace OpenRA.Mods.Common
|
||||
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user