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:
atlimit8
2014-08-10 19:43:45 -05:00
parent ff7ad53dee
commit b2c9064545
8 changed files with 117 additions and 64 deletions

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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
{

View File

@@ -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)
{

View File

@@ -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)

View File

@@ -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);
}

View File

@@ -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);
}