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
|
||||
{
|
||||
bool CanEnterCell(CPos location);
|
||||
bool CanEnterCell(CPos location, Actor ignoreActor, bool checkTransientActors);
|
||||
int GetDesiredSubcell(CPos a, Actor ignoreActor);
|
||||
bool IsMovingFrom(CPos location, int subCell = -1);
|
||||
bool CanEnterCell(CPos location, Actor ignoreActor = null, bool checkTransientActors = true);
|
||||
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, WPos pos);
|
||||
void SetVisualPosition(Actor self, WPos pos);
|
||||
|
||||
@@ -83,35 +83,68 @@ namespace OpenRA.Traits
|
||||
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;
|
||||
|
||||
if (!AnyUnitsAt(a))
|
||||
return map.SubCellDefaultIndex;
|
||||
|
||||
for (var i = 1; i < map.SubCellOffsets.Length; i++)
|
||||
if (!AnyUnitsAt(a, i))
|
||||
if (i != preferredSubCell && !AnyUnitsAt(a, i, checkTransient))
|
||||
return i;
|
||||
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)
|
||||
{
|
||||
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)
|
||||
if (i.SubCell == sub || i.SubCell == 0)
|
||||
return true;
|
||||
if (sub <= 0 || i.SubCell == sub || i.SubCell == 0)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace OpenRA.Mods.RA.Activities
|
||||
|
||||
return cargo.CurrentAdjacentCells
|
||||
.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>?>()
|
||||
.FirstOrDefault(s => s.Value.Second >= 0);
|
||||
}
|
||||
|
||||
@@ -199,9 +199,9 @@ namespace OpenRA.Mods.RA.Air
|
||||
|| info.RepairBuildings.Contains(a.Info.Name);
|
||||
}
|
||||
|
||||
public int GetDesiredSubcell(CPos a, Actor ignoreActor) { return -1; } // does not use any subcell
|
||||
public bool CanEnterCell(CPos location) { return true; }
|
||||
public bool CanEnterCell(CPos cell, Actor ignoreActor, bool checkTransientActors) { return true; }
|
||||
public bool IsMovingFrom(CPos location, int subCell = -1) { return false; } // TODO: handle landing
|
||||
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 = null, bool checkTransientActors = true) { return true; }
|
||||
|
||||
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 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;
|
||||
if (!info.TerrainTypes.Contains(type))
|
||||
return false;
|
||||
return -1;
|
||||
|
||||
if (self.World.WorldActor.Trait<BuildingInfluence>().GetBuildingAt(cell) != null)
|
||||
return false;
|
||||
return -1;
|
||||
|
||||
if (!checkTransientActors)
|
||||
return true;
|
||||
return 0;
|
||||
|
||||
return !self.World.ActorMap.GetUnitsAt(cell)
|
||||
.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 cell) { return CanEnterCell(cell, null, true); }
|
||||
public bool CanEnterCell(CPos a, Actor ignoreActor = null, bool checkTransientActors = true)
|
||||
{
|
||||
return GetAvailableSubcell(a, -1, ignoreActor, checkTransientActors) >= 0;
|
||||
}
|
||||
|
||||
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 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;
|
||||
return -1;
|
||||
|
||||
if (!info.AllowedTerrain.Contains(self.World.Map.GetTerrainInfo(cell).Type))
|
||||
return false;
|
||||
return -1;
|
||||
|
||||
if (!checkTransientActors)
|
||||
return true;
|
||||
return 0;
|
||||
|
||||
return !self.World.ActorMap.GetUnitsAt(cell)
|
||||
.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 SetVisualPosition(Actor self, WPos pos)
|
||||
|
||||
@@ -206,6 +206,45 @@ namespace OpenRA.Mods.RA.Move
|
||||
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; }
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
if (blockingActors.Any())
|
||||
{
|
||||
// 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;
|
||||
return toCell != location && __fromCell == location
|
||||
&& (subCell == -1 || fromSubCell == subCell || subCell == 0 || fromSubCell == 0);
|
||||
}
|
||||
|
||||
public int GetDesiredSubcell(CPos a, Actor ignoreActor)
|
||||
public int GetAvailableSubcell(CPos a, int preferredSubCell, Actor ignoreActor = null, bool checkTransientActors = true)
|
||||
{
|
||||
if (!Info.SharesCell)
|
||||
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;
|
||||
return Info.GetAvailableSubCell(self.World, self, a, preferredSubCell, ignoreActor, checkTransientActors? CellConditions.All : CellConditions.None);
|
||||
}
|
||||
|
||||
public bool CanEnterCell(CPos p)
|
||||
{
|
||||
return CanEnterCell(p, null, true);
|
||||
}
|
||||
|
||||
public bool CanEnterCell(CPos cell, Actor ignoreActor, bool checkTransientActors)
|
||||
public bool CanEnterCell(CPos cell, Actor ignoreActor = null, bool checkTransientActors = true)
|
||||
{
|
||||
return Info.CanEnterCell(self.World, self, cell, ignoreActor, checkTransientActors ? CellConditions.All : CellConditions.BlockedByMovers);
|
||||
}
|
||||
|
||||
@@ -245,7 +245,7 @@ namespace OpenRA.Mods.RA.Move
|
||||
hasWaited = false;
|
||||
path.RemoveAt(path.Count - 1);
|
||||
|
||||
var subCell = mobile.GetDesiredSubcell(nextCell, ignoreBuilding);
|
||||
var subCell = mobile.GetAvailableSubcell(nextCell, -1, ignoreBuilding);
|
||||
return Pair.New(nextCell, subCell);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user