Refactored [sub-]cell availability logic in IPositionable & ActorMap
Moved the logic from IPositionable.CanEnterCell & integrated sub-cell selection.
Added IPositionable.IsMovingFrom(CPos location, int subCell = -1) - to detect transient actors
Renamed IPositionable.{GetDesiredSubcell => GetAvailableSubcell} - since it checks for available sub-cells
Reduced IPositionable.CanEnterCell to one method that usually uses IPositionable.GetAvailableSubcell
Added actor checking to ActorMap.{HasFreeSubCell, FreeSubCell, AnyUnitsAt} - used by [sub-]cell availability logic
This commit is contained in:
@@ -180,9 +180,9 @@ namespace OpenRA.Traits
|
|||||||
|
|
||||||
public interface IPositionable : IOccupySpace
|
public interface IPositionable : IOccupySpace
|
||||||
{
|
{
|
||||||
bool CanEnterCell(CPos location);
|
bool IsMovingFrom(CPos location, int subCell = -1);
|
||||||
bool CanEnterCell(CPos location, Actor ignoreActor, bool checkTransientActors);
|
bool CanEnterCell(CPos location, Actor ignoreActor = null, bool checkTransientActors = true);
|
||||||
int GetDesiredSubcell(CPos a, Actor ignoreActor);
|
int GetAvailableSubcell(CPos location, int preferredSubCell = -1, Actor ignoreActor = null, bool checkTransientActors = true);
|
||||||
void SetPosition(Actor self, CPos cell, int subCell = -1);
|
void SetPosition(Actor self, CPos cell, int subCell = -1);
|
||||||
void SetPosition(Actor self, WPos pos);
|
void SetPosition(Actor self, WPos pos);
|
||||||
void SetVisualPosition(Actor self, WPos pos);
|
void SetVisualPosition(Actor self, WPos pos);
|
||||||
|
|||||||
@@ -83,35 +83,68 @@ namespace OpenRA.Traits
|
|||||||
yield return i.Actor;
|
yield return i.Actor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool HasFreeSubCell(CPos a)
|
public bool HasFreeSubCell(CPos a, bool checkTransient = true)
|
||||||
{
|
{
|
||||||
return FreeSubCell(a) >= 0;
|
return FreeSubCell(a, -1, checkTransient) >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int FreeSubCell(CPos a, int preferredSubCell = -1)
|
public int FreeSubCell(CPos a, int preferredSubCell = -1, bool checkTransient = true)
|
||||||
{
|
{
|
||||||
if (preferredSubCell >= 0 && !AnyUnitsAt(a, preferredSubCell))
|
if (preferredSubCell >= 0 && !AnyUnitsAt(a, preferredSubCell, checkTransient))
|
||||||
return preferredSubCell;
|
return preferredSubCell;
|
||||||
|
|
||||||
if (!AnyUnitsAt(a))
|
if (!AnyUnitsAt(a))
|
||||||
return map.SubCellDefaultIndex;
|
return map.SubCellDefaultIndex;
|
||||||
|
|
||||||
for (var i = 1; i < map.SubCellOffsets.Length; i++)
|
for (var i = 1; i < map.SubCellOffsets.Length; i++)
|
||||||
if (!AnyUnitsAt(a, i))
|
if (i != preferredSubCell && !AnyUnitsAt(a, i, checkTransient))
|
||||||
return i;
|
return i;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int FreeSubCell(CPos a, int preferredSubCell, Func<Actor, bool> checkIfBlocker)
|
||||||
|
{
|
||||||
|
if (preferredSubCell >= 0 && !AnyUnitsAt(a, preferredSubCell, checkIfBlocker))
|
||||||
|
return preferredSubCell;
|
||||||
|
|
||||||
|
if (!AnyUnitsAt(a))
|
||||||
|
return map.SubCellDefaultIndex;
|
||||||
|
|
||||||
|
for (var i = 1; i < map.SubCellOffsets.Length; i++)
|
||||||
|
if (i != preferredSubCell && !AnyUnitsAt(a, i, checkIfBlocker))
|
||||||
|
return i;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: does not check transients, but checks aircraft
|
||||||
public bool AnyUnitsAt(CPos a)
|
public bool AnyUnitsAt(CPos a)
|
||||||
{
|
{
|
||||||
return influence[a] != null;
|
return influence[a] != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool AnyUnitsAt(CPos a, int sub)
|
// NOTE: can not check aircraft
|
||||||
|
public bool AnyUnitsAt(CPos a, int sub, bool checkTransient = true)
|
||||||
{
|
{
|
||||||
for (var i = influence[a]; i != null; i = i.Next)
|
for (var i = influence[a]; i != null; i = i.Next)
|
||||||
if (i.SubCell == sub || i.SubCell == 0)
|
if (sub <= 0 || i.SubCell == sub || i.SubCell == 0)
|
||||||
return true;
|
{
|
||||||
|
if (checkTransient)
|
||||||
|
return true;
|
||||||
|
var pos = i.Actor.TraitOrDefault<IPositionable>();
|
||||||
|
if (pos == null || !pos.IsMovingFrom(a, i.SubCell))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: can not check aircraft
|
||||||
|
public bool AnyUnitsAt(CPos a, int sub, Func<Actor, bool> withCondition)
|
||||||
|
{
|
||||||
|
for (var i = influence[a]; i != null; i = i.Next)
|
||||||
|
if (sub <= 0 || i.SubCell == sub || i.SubCell == 0)
|
||||||
|
if (withCondition(i.Actor))
|
||||||
|
return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ namespace OpenRA.Mods.RA.Activities
|
|||||||
|
|
||||||
return cargo.CurrentAdjacentCells
|
return cargo.CurrentAdjacentCells
|
||||||
.Shuffle(self.World.SharedRandom)
|
.Shuffle(self.World.SharedRandom)
|
||||||
.Select(c => Pair.New(c, pos.GetDesiredSubcell(c, null)))
|
.Select(c => Pair.New(c, pos.GetAvailableSubcell(c, -1, null)))
|
||||||
.Cast<Pair<CPos, int>?>()
|
.Cast<Pair<CPos, int>?>()
|
||||||
.FirstOrDefault(s => s.Value.Second >= 0);
|
.FirstOrDefault(s => s.Value.Second >= 0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -198,10 +198,10 @@ namespace OpenRA.Mods.RA.Air
|
|||||||
return info.RearmBuildings.Contains(a.Info.Name)
|
return info.RearmBuildings.Contains(a.Info.Name)
|
||||||
|| info.RepairBuildings.Contains(a.Info.Name);
|
|| info.RepairBuildings.Contains(a.Info.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int GetDesiredSubcell(CPos a, Actor ignoreActor) { return -1; } // does not use any subcell
|
public bool IsMovingFrom(CPos location, int subCell = -1) { return false; } // TODO: handle landing
|
||||||
public bool CanEnterCell(CPos location) { return true; }
|
public int GetAvailableSubcell(CPos a, int preferredSubCell = -1, Actor ignoreActor = null, bool checkTransientActors = true) { return -1; } // does not use any subcell
|
||||||
public bool CanEnterCell(CPos cell, Actor ignoreActor, bool checkTransientActors) { return true; }
|
public bool CanEnterCell(CPos cell, Actor ignoreActor = null, bool checkTransientActors = true) { return true; }
|
||||||
|
|
||||||
public int MovementSpeed
|
public int MovementSpeed
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -95,27 +95,30 @@ namespace OpenRA.Mods.RA
|
|||||||
public void SetPosition(Actor self, WPos pos) { SetPosition(self, self.World.Map.CellContaining(pos)); }
|
public void SetPosition(Actor self, WPos pos) { SetPosition(self, self.World.Map.CellContaining(pos)); }
|
||||||
public void SetVisualPosition(Actor self, WPos pos) { SetPosition(self, self.World.Map.CellContaining(pos)); }
|
public void SetVisualPosition(Actor self, WPos pos) { SetPosition(self, self.World.Map.CellContaining(pos)); }
|
||||||
|
|
||||||
public bool CanEnterCell(CPos cell, Actor ignoreActor, bool checkTransientActors)
|
public bool IsMovingFrom(CPos location, int subCell = -1) { return false; }
|
||||||
|
public int GetAvailableSubcell(CPos cell, int preferredSubCell = -1, Actor ignoreActor = null, bool checkTransientActors = true)
|
||||||
{
|
{
|
||||||
if (!self.World.Map.Contains(cell)) return false;
|
if (!self.World.Map.Contains(cell)) return -1;
|
||||||
|
|
||||||
var type = self.World.Map.GetTerrainInfo(cell).Type;
|
var type = self.World.Map.GetTerrainInfo(cell).Type;
|
||||||
if (!info.TerrainTypes.Contains(type))
|
if (!info.TerrainTypes.Contains(type))
|
||||||
return false;
|
return -1;
|
||||||
|
|
||||||
if (self.World.WorldActor.Trait<BuildingInfluence>().GetBuildingAt(cell) != null)
|
if (self.World.WorldActor.Trait<BuildingInfluence>().GetBuildingAt(cell) != null)
|
||||||
return false;
|
return -1;
|
||||||
|
|
||||||
if (!checkTransientActors)
|
if (!checkTransientActors)
|
||||||
return true;
|
return 0;
|
||||||
|
|
||||||
return !self.World.ActorMap.GetUnitsAt(cell)
|
return !self.World.ActorMap.GetUnitsAt(cell)
|
||||||
.Where(x => x != ignoreActor)
|
.Where(x => x != ignoreActor)
|
||||||
.Any();
|
.Any() ? 0 : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int GetDesiredSubcell(CPos a, Actor ignoreActor) { return CanEnterCell(a, ignoreActor, true) ? 0 : -1; }
|
public bool CanEnterCell(CPos a, Actor ignoreActor = null, bool checkTransientActors = true)
|
||||||
public bool CanEnterCell(CPos cell) { return CanEnterCell(cell, null, true); }
|
{
|
||||||
|
return GetAvailableSubcell(a, -1, ignoreActor, checkTransientActors) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
public void SetPosition(Actor self, CPos cell, int subCell = -1)
|
public void SetPosition(Actor self, CPos cell, int subCell = -1)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -53,24 +53,28 @@ namespace OpenRA.Mods.RA
|
|||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<Pair<CPos, int>> OccupiedCells() { yield return Pair.New(TopLeft, 0); }
|
public IEnumerable<Pair<CPos, int>> OccupiedCells() { yield return Pair.New(TopLeft, 0); }
|
||||||
public bool CanEnterCell(CPos cell, Actor ignoreActor, bool checkTransientActors)
|
public bool IsMovingFrom(CPos location, int subCell = -1) { return false; }
|
||||||
|
public int GetAvailableSubcell(CPos cell, int preferredSubCell = -1, Actor ignoreActor = null, bool checkTransientActors = true)
|
||||||
{
|
{
|
||||||
if (!self.World.Map.Contains(cell))
|
if (!self.World.Map.Contains(cell))
|
||||||
return false;
|
return -1;
|
||||||
|
|
||||||
if (!info.AllowedTerrain.Contains(self.World.Map.GetTerrainInfo(cell).Type))
|
if (!info.AllowedTerrain.Contains(self.World.Map.GetTerrainInfo(cell).Type))
|
||||||
return false;
|
return -1;
|
||||||
|
|
||||||
if (!checkTransientActors)
|
if (!checkTransientActors)
|
||||||
return true;
|
return 0;
|
||||||
|
|
||||||
return !self.World.ActorMap.GetUnitsAt(cell)
|
return !self.World.ActorMap.GetUnitsAt(cell)
|
||||||
.Where(x => x != ignoreActor)
|
.Where(x => x != ignoreActor)
|
||||||
.Any();
|
.Any() ? 0 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CanEnterCell(CPos a, Actor ignoreActor = null, bool checkTransientActors = true)
|
||||||
|
{
|
||||||
|
return GetAvailableSubcell(a, -1, ignoreActor, checkTransientActors) >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int GetDesiredSubcell(CPos a, Actor ignoreActor) { return CanEnterCell(a, ignoreActor, true) ? 0 : -1; }
|
|
||||||
public bool CanEnterCell(CPos cell) { return CanEnterCell(cell, null, true); }
|
|
||||||
public void SetPosition(Actor self, CPos cell, int subCell = -1) { SetPosition(self, self.World.Map.CenterOfCell(cell)); }
|
public void SetPosition(Actor self, CPos cell, int subCell = -1) { SetPosition(self, self.World.Map.CenterOfCell(cell)); }
|
||||||
|
|
||||||
public void SetVisualPosition(Actor self, WPos pos)
|
public void SetVisualPosition(Actor self, WPos pos)
|
||||||
|
|||||||
@@ -206,6 +206,45 @@ namespace OpenRA.Mods.RA.Move
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int GetAvailableSubCell(World world, Actor self, CPos cell, int preferredSubCell = -1, Actor ignoreActor = null, CellConditions check = CellConditions.All)
|
||||||
|
{
|
||||||
|
if (MovementCostForCell(world, cell) == int.MaxValue)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (check.HasFlag(CellConditions.TransientActors))
|
||||||
|
{
|
||||||
|
var canIgnoreMovingAllies = self != null && !check.HasFlag(CellConditions.BlockedByMovers);
|
||||||
|
var needsCellExclusively = self == null || Crushes == null;
|
||||||
|
|
||||||
|
Func<Actor, bool> checkTransient = a =>
|
||||||
|
{
|
||||||
|
if (a == ignoreActor) return false;
|
||||||
|
|
||||||
|
// Neutral/enemy units are blockers. Allied units that are moving are not blockers.
|
||||||
|
if (canIgnoreMovingAllies && self.Owner.Stances[a.Owner] == Stance.Ally && IsMovingInMyDirection(self, a)) return false;
|
||||||
|
|
||||||
|
// Non-sharable unit can enter a cell with shareable units only if it can crush all of them.
|
||||||
|
if (needsCellExclusively) return true;
|
||||||
|
if (!a.HasTrait<ICrushable>()) return true;
|
||||||
|
foreach (var crushable in a.TraitsImplementing<ICrushable>())
|
||||||
|
if (!crushable.CrushableBy(Crushes, self.Owner))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!SharesCell)
|
||||||
|
return world.ActorMap.AnyUnitsAt(cell, 0, checkTransient)? -1 : 0;
|
||||||
|
|
||||||
|
return world.ActorMap.FreeSubCell(cell, preferredSubCell, checkTransient);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SharesCell)
|
||||||
|
return world.ActorMap.AnyUnitsAt(cell, 0)? -1 : 0;
|
||||||
|
|
||||||
|
return world.ActorMap.FreeSubCell(cell, preferredSubCell);
|
||||||
|
}
|
||||||
|
|
||||||
public int GetInitialFacing() { return InitialFacing; }
|
public int GetInitialFacing() { return InitialFacing; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -437,44 +476,18 @@ namespace OpenRA.Mods.RA.Move
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsDesiredSubcellNotBlocked(CPos a, int b, Actor ignoreActor)
|
public bool IsMovingFrom(CPos location, int subCell = -1)
|
||||||
{
|
{
|
||||||
var blockingActors = self.World.ActorMap.GetUnitsAt(a, b).Where(c => c != ignoreActor);
|
return toCell != location && __fromCell == location
|
||||||
if (blockingActors.Any())
|
&& (subCell == -1 || fromSubCell == subCell || subCell == 0 || fromSubCell == 0);
|
||||||
{
|
|
||||||
// Non-sharable unit can enter a cell with shareable units only if it can crush all of them
|
|
||||||
if (Info.Crushes == null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (blockingActors.Any(c => !(c.HasTrait<ICrushable>() &&
|
|
||||||
c.TraitsImplementing<ICrushable>().Any(d => d.CrushableBy(Info.Crushes, self.Owner)))))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int GetDesiredSubcell(CPos a, Actor ignoreActor)
|
public int GetAvailableSubcell(CPos a, int preferredSubCell, Actor ignoreActor = null, bool checkTransientActors = true)
|
||||||
{
|
{
|
||||||
if (!Info.SharesCell)
|
return Info.GetAvailableSubCell(self.World, self, a, preferredSubCell, ignoreActor, checkTransientActors? CellConditions.All : CellConditions.None);
|
||||||
return 0;
|
|
||||||
|
|
||||||
// Prioritise the current subcell
|
|
||||||
if (IsDesiredSubcellNotBlocked(a, fromSubCell, ignoreActor))
|
|
||||||
return fromSubCell;
|
|
||||||
|
|
||||||
for (var i = 1; i < self.World.Map.SubCellOffsets.Length; i++)
|
|
||||||
if (IsDesiredSubcellNotBlocked(a, i, ignoreActor))
|
|
||||||
return i;
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool CanEnterCell(CPos p)
|
public bool CanEnterCell(CPos cell, Actor ignoreActor = null, bool checkTransientActors = true)
|
||||||
{
|
|
||||||
return CanEnterCell(p, null, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool CanEnterCell(CPos cell, Actor ignoreActor, bool checkTransientActors)
|
|
||||||
{
|
{
|
||||||
return Info.CanEnterCell(self.World, self, cell, ignoreActor, checkTransientActors ? CellConditions.All : CellConditions.BlockedByMovers);
|
return Info.CanEnterCell(self.World, self, cell, ignoreActor, checkTransientActors ? CellConditions.All : CellConditions.BlockedByMovers);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -245,7 +245,7 @@ namespace OpenRA.Mods.RA.Move
|
|||||||
hasWaited = false;
|
hasWaited = false;
|
||||||
path.RemoveAt(path.Count - 1);
|
path.RemoveAt(path.Count - 1);
|
||||||
|
|
||||||
var subCell = mobile.GetDesiredSubcell(nextCell, ignoreBuilding);
|
var subCell = mobile.GetAvailableSubcell(nextCell, -1, ignoreBuilding);
|
||||||
return Pair.New(nextCell, subCell);
|
return Pair.New(nextCell, subCell);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user