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

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