Allow units to give way when path is blocked by oncoming unit.
This commit is contained in:
@@ -158,7 +158,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
bool IOccupySpaceInfo.SharesCell { get { return false; } }
|
||||
|
||||
// Used to determine if an aircraft can spawn landed
|
||||
public bool CanEnterCell(World world, Actor self, CPos cell, Actor ignoreActor = null, bool checkTransientActors = true)
|
||||
public bool CanEnterCell(World world, Actor self, CPos cell, Actor ignoreActor = null, BlockedByActor check = BlockedByActor.All)
|
||||
{
|
||||
if (!world.Map.Contains(cell))
|
||||
return false;
|
||||
@@ -170,7 +170,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
if (world.WorldActor.Trait<BuildingInfluence>().GetBuildingAt(cell) != null)
|
||||
return false;
|
||||
|
||||
if (!checkTransientActors)
|
||||
if (check == BlockedByActor.None)
|
||||
return true;
|
||||
|
||||
return !world.ActorMap.GetActorsAt(cell).Any(x => x != ignoreActor);
|
||||
@@ -721,9 +721,9 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
public bool CanExistInCell(CPos cell) { return true; }
|
||||
public bool IsLeavingCell(CPos location, SubCell subCell = SubCell.Any) { return false; } // TODO: Handle landing
|
||||
public bool CanEnterCell(CPos cell, Actor ignoreActor = null, bool checkTransientActors = true) { return true; }
|
||||
public bool CanEnterCell(CPos cell, Actor ignoreActor = null, BlockedByActor check = BlockedByActor.All) { return true; }
|
||||
public SubCell GetValidSubCell(SubCell preferred) { return SubCell.Invalid; }
|
||||
public SubCell GetAvailableSubCell(CPos a, SubCell preferredSubCell = SubCell.Any, Actor ignoreActor = null, bool checkTransientActors = true)
|
||||
public SubCell GetAvailableSubCell(CPos a, SubCell preferredSubCell = SubCell.Any, Actor ignoreActor = null, BlockedByActor check = BlockedByActor.All)
|
||||
{
|
||||
// Does not use any subcell
|
||||
return SubCell.Invalid;
|
||||
|
||||
@@ -231,7 +231,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
if (IsTraitDisabled)
|
||||
return false;
|
||||
|
||||
if (Info.AttackRequiresEnteringCell && (positionable == null || !positionable.CanEnterCell(t.Actor.Location, null, false)))
|
||||
if (Info.AttackRequiresEnteringCell && (positionable == null || !positionable.CanEnterCell(t.Actor.Location, null, BlockedByActor.None)))
|
||||
return false;
|
||||
|
||||
// PERF: Avoid LINQ.
|
||||
|
||||
@@ -146,7 +146,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
claimLayer.CanClaimCell(actor, cell);
|
||||
|
||||
var path = pathfinder.FindPath(
|
||||
PathSearch.Search(world, harv.Locomotor, actor, true, isValidResource)
|
||||
PathSearch.Search(world, harv.Locomotor, actor, BlockedByActor.Stationary, isValidResource)
|
||||
.WithCustomCost(loc => world.FindActorsInCircle(world.Map.CenterOfCell(loc), Info.HarvesterEnemyAvoidanceRadius)
|
||||
.Where(u => !u.IsDead && actor.Owner.Stances[u.Owner] == Stance.Enemy)
|
||||
.Sum(u => Math.Max(WDist.Zero.Length, Info.HarvesterEnemyAvoidanceRadius.Length - (world.Map.CenterOfCell(loc) - u.CenterPosition).Length)))
|
||||
|
||||
@@ -198,7 +198,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
if (mobile.locomotor.MovementCostForCell(cell) == short.MaxValue)
|
||||
return false;
|
||||
|
||||
return mobile.locomotor.CanMoveFreelyInto(self, cell, null, CellConditions.BlockedByMovers);
|
||||
return mobile.locomotor.CanMoveFreelyInto(self, cell, BlockedByActor.All, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,7 +218,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
return Util.AdjacentCells(self.World, Target.FromActor(self)).Where(c => self.Location != c);
|
||||
}
|
||||
|
||||
public bool CanUnload(bool immediate = false)
|
||||
public bool CanUnload(BlockedByActor check = BlockedByActor.None)
|
||||
{
|
||||
if (checkTerrainType)
|
||||
{
|
||||
@@ -229,7 +229,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
}
|
||||
|
||||
return !IsEmpty(self) && (aircraft == null || aircraft.CanLand(self.Location, blockedByMobile: false))
|
||||
&& CurrentAdjacentCells != null && CurrentAdjacentCells.Any(c => Passengers.Any(p => !p.IsDead && p.Trait<IPositionable>().CanEnterCell(c, null, immediate)));
|
||||
&& CurrentAdjacentCells != null && CurrentAdjacentCells.Any(c => Passengers.Any(p => !p.IsDead && p.Trait<IPositionable>().CanEnterCell(c, null, check)));
|
||||
}
|
||||
|
||||
public bool CanLoad(Actor self, Actor a)
|
||||
@@ -430,7 +430,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
void INotifyKilled.Killed(Actor self, AttackInfo e)
|
||||
{
|
||||
if (Info.EjectOnDeath)
|
||||
while (!IsEmpty(self) && CanUnload(true))
|
||||
while (!IsEmpty(self) && CanUnload(BlockedByActor.All))
|
||||
{
|
||||
var passenger = Unload(self);
|
||||
var cp = self.CenterPosition;
|
||||
@@ -438,7 +438,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
var positionable = passenger.Trait<IPositionable>();
|
||||
positionable.SetPosition(passenger, self.Location);
|
||||
|
||||
if (!inAir && positionable.CanEnterCell(self.Location, self, false))
|
||||
if (!inAir && positionable.CanEnterCell(self.Location, self, BlockedByActor.None))
|
||||
{
|
||||
self.World.AddFrameEndTask(w => w.Add(passenger));
|
||||
var nbms = passenger.TraitsImplementing<INotifyBlockingMove>();
|
||||
|
||||
@@ -40,9 +40,9 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
bool IOccupySpaceInfo.SharesCell { get { return false; } }
|
||||
|
||||
public bool CanEnterCell(World world, Actor self, CPos cell, Actor ignoreActor = null, bool checkTransientActors = true)
|
||||
public bool CanEnterCell(World world, Actor self, CPos cell, Actor ignoreActor = null, BlockedByActor check = BlockedByActor.All)
|
||||
{
|
||||
return GetAvailableSubCell(world, cell, ignoreActor, checkTransientActors) != SubCell.Invalid;
|
||||
return GetAvailableSubCell(world, cell, ignoreActor, check) != SubCell.Invalid;
|
||||
}
|
||||
|
||||
public bool CanExistInCell(World world, CPos cell)
|
||||
@@ -57,7 +57,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
return true;
|
||||
}
|
||||
|
||||
public SubCell GetAvailableSubCell(World world, CPos cell, Actor ignoreActor = null, bool checkTransientActors = true)
|
||||
public SubCell GetAvailableSubCell(World world, CPos cell, Actor ignoreActor = null, BlockedByActor check = BlockedByActor.All)
|
||||
{
|
||||
if (!CanExistInCell(world, cell))
|
||||
return SubCell.Invalid;
|
||||
@@ -65,7 +65,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
if (world.WorldActor.Trait<BuildingInfluence>().GetBuildingAt(cell) != null)
|
||||
return SubCell.Invalid;
|
||||
|
||||
if (!checkTransientActors)
|
||||
if (check == BlockedByActor.None)
|
||||
return SubCell.FullCell;
|
||||
|
||||
return !world.ActorMap.GetActorsAt(cell).Any(x => x != ignoreActor)
|
||||
@@ -208,16 +208,16 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
public bool IsLeavingCell(CPos location, SubCell subCell = SubCell.Any) { return self.Location == location && ticks + 1 == info.Lifetime * 25; }
|
||||
public SubCell GetValidSubCell(SubCell preferred = SubCell.Any) { return SubCell.FullCell; }
|
||||
public SubCell GetAvailableSubCell(CPos cell, SubCell preferredSubCell = SubCell.Any, Actor ignoreActor = null, bool checkTransientActors = true)
|
||||
public SubCell GetAvailableSubCell(CPos cell, SubCell preferredSubCell = SubCell.Any, Actor ignoreActor = null, BlockedByActor check = BlockedByActor.All)
|
||||
{
|
||||
return info.GetAvailableSubCell(self.World, cell, ignoreActor, checkTransientActors);
|
||||
return info.GetAvailableSubCell(self.World, cell, ignoreActor, check);
|
||||
}
|
||||
|
||||
public bool CanExistInCell(CPos cell) { return info.CanExistInCell(self.World, cell); }
|
||||
|
||||
public bool CanEnterCell(CPos a, Actor ignoreActor = null, bool checkTransientActors = true)
|
||||
public bool CanEnterCell(CPos a, Actor ignoreActor = null, BlockedByActor check = BlockedByActor.All)
|
||||
{
|
||||
return GetAvailableSubCell(a, SubCell.Any, ignoreActor, checkTransientActors) != SubCell.Invalid;
|
||||
return GetAvailableSubCell(a, SubCell.Any, ignoreActor, check) != SubCell.Invalid;
|
||||
}
|
||||
|
||||
bool ICrushable.CrushableBy(Actor self, Actor crusher, BitSet<CrushClass> crushClasses)
|
||||
|
||||
@@ -184,7 +184,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
// Start a search from each refinery's delivery location:
|
||||
List<CPos> path;
|
||||
|
||||
using (var search = PathSearch.FromPoints(self.World, mobile.Locomotor, self, refs.Values.Select(r => r.Location), self.Location, false)
|
||||
using (var search = PathSearch.FromPoints(self.World, mobile.Locomotor, self, refs.Values.Select(r => r.Location), self.Location, BlockedByActor.None)
|
||||
.WithCustomCost(loc =>
|
||||
{
|
||||
if (!refs.ContainsKey(loc))
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
bool IOccupySpaceInfo.SharesCell { get { return false; } }
|
||||
|
||||
public bool CanEnterCell(World world, Actor self, CPos cell, Actor ignoreActor = null, bool checkTransientActors = true)
|
||||
public bool CanEnterCell(World world, Actor self, CPos cell, Actor ignoreActor = null, BlockedByActor check = BlockedByActor.All)
|
||||
{
|
||||
// IPositionable*Info*.CanEnterCell is only ever used for things like exiting production facilities,
|
||||
// all places relevant for husks check IPositionable.CanEnterCell instead, so we can safely set this to true.
|
||||
@@ -107,21 +107,21 @@ namespace OpenRA.Mods.Common.Traits
|
||||
public Pair<CPos, SubCell>[] OccupiedCells() { return new[] { Pair.New(TopLeft, SubCell.FullCell) }; }
|
||||
public bool IsLeavingCell(CPos location, SubCell subCell = SubCell.Any) { return false; }
|
||||
public SubCell GetValidSubCell(SubCell preferred = SubCell.Any) { return SubCell.FullCell; }
|
||||
public SubCell GetAvailableSubCell(CPos cell, SubCell preferredSubCell = SubCell.Any, Actor ignoreActor = null, bool checkTransientActors = true)
|
||||
public SubCell GetAvailableSubCell(CPos cell, SubCell preferredSubCell = SubCell.Any, Actor ignoreActor = null, BlockedByActor check = BlockedByActor.All)
|
||||
{
|
||||
if (!CanExistInCell(cell))
|
||||
return SubCell.Invalid;
|
||||
|
||||
if (!checkTransientActors)
|
||||
if (check == BlockedByActor.None)
|
||||
return SubCell.FullCell;
|
||||
|
||||
return self.World.ActorMap.GetActorsAt(cell)
|
||||
.All(x => x == ignoreActor) ? SubCell.FullCell : SubCell.Invalid;
|
||||
}
|
||||
|
||||
public bool CanEnterCell(CPos a, Actor ignoreActor = null, bool checkTransientActors = true)
|
||||
public bool CanEnterCell(CPos a, Actor ignoreActor = null, BlockedByActor check = BlockedByActor.All)
|
||||
{
|
||||
return GetAvailableSubCell(a, SubCell.Any, ignoreActor, checkTransientActors) != SubCell.Invalid;
|
||||
return GetAvailableSubCell(a, SubCell.Any, ignoreActor, check) != SubCell.Invalid;
|
||||
}
|
||||
|
||||
public void SetPosition(Actor self, CPos cell, SubCell subCell = SubCell.Any) { SetPosition(self, self.World.Map.CenterOfCell(cell)); }
|
||||
|
||||
@@ -82,7 +82,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
// initialized and used by CanEnterCell
|
||||
Locomotor locomotor;
|
||||
|
||||
public bool CanEnterCell(World world, Actor self, CPos cell, Actor ignoreActor = null, bool checkTransientActors = true)
|
||||
public bool CanEnterCell(World world, Actor self, CPos cell, Actor ignoreActor = null, BlockedByActor check = BlockedByActor.All)
|
||||
{
|
||||
// PERF: Avoid repeated trait queries on the hot path
|
||||
if (locomotor == null)
|
||||
@@ -92,8 +92,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
if (locomotor.MovementCostForCell(cell) == short.MaxValue)
|
||||
return false;
|
||||
|
||||
var check = checkTransientActors ? CellConditions.All : CellConditions.BlockedByMovers;
|
||||
return locomotor.CanMoveFreelyInto(self, cell, ignoreActor, check);
|
||||
return locomotor.CanMoveFreelyInto(self, cell, check, ignoreActor);
|
||||
}
|
||||
|
||||
public IReadOnlyDictionary<CPos, SubCell> OccupiedCells(ActorInfo info, CPos location, SubCell subCell = SubCell.Any)
|
||||
@@ -176,6 +175,8 @@ namespace OpenRA.Mods.Common.Traits
|
||||
INotifyFinishedMoving[] notifyFinishedMoving;
|
||||
IWrapMove[] moveWrappers;
|
||||
bool requireForceMove;
|
||||
public bool TurnToMove;
|
||||
public bool IsBlocking { get; private set; }
|
||||
|
||||
#region IFacing
|
||||
[Sync]
|
||||
@@ -298,15 +299,6 @@ namespace OpenRA.Mods.Common.Traits
|
||||
if (IsTraitDisabled || IsTraitPaused)
|
||||
return;
|
||||
|
||||
// Initial fairly braindead implementation.
|
||||
// don't allow ourselves to be pushed around by the enemy!
|
||||
if (!force && self.Owner.Stances[nudger.Owner] != Stance.Ally)
|
||||
return;
|
||||
|
||||
// Don't nudge if we're busy doing something!
|
||||
if (!force && !self.IsIdle)
|
||||
return;
|
||||
|
||||
// Pick an adjacent available cell.
|
||||
var availCells = new List<CPos>();
|
||||
var notStupidCells = new List<CPos>();
|
||||
@@ -358,6 +350,17 @@ namespace OpenRA.Mods.Common.Traits
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsLeaving()
|
||||
{
|
||||
if (CurrentMovementTypes.HasFlag(MovementType.Horizontal))
|
||||
return true;
|
||||
|
||||
if (CurrentMovementTypes.HasFlag(MovementType.Turn))
|
||||
return TurnToMove;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool CanInteractWithGroundLayer(Actor self)
|
||||
{
|
||||
// TODO: Think about extending this to support arbitrary layer-layer checks
|
||||
@@ -442,10 +445,9 @@ namespace OpenRA.Mods.Common.Traits
|
||||
&& (subCell == SubCell.Any || FromSubCell == subCell || subCell == SubCell.FullCell || FromSubCell == SubCell.FullCell);
|
||||
}
|
||||
|
||||
public SubCell GetAvailableSubCell(CPos a, SubCell preferredSubCell = SubCell.Any, Actor ignoreActor = null, bool checkTransientActors = true)
|
||||
public SubCell GetAvailableSubCell(CPos a, SubCell preferredSubCell = SubCell.Any, Actor ignoreActor = null, BlockedByActor check = BlockedByActor.All)
|
||||
{
|
||||
var cellConditions = checkTransientActors ? CellConditions.All : CellConditions.None;
|
||||
return Locomotor.GetAvailableSubCell(self, a, preferredSubCell, ignoreActor, cellConditions);
|
||||
return Locomotor.GetAvailableSubCell(self, a, check, preferredSubCell, ignoreActor);
|
||||
}
|
||||
|
||||
public bool CanExistInCell(CPos cell)
|
||||
@@ -453,9 +455,9 @@ namespace OpenRA.Mods.Common.Traits
|
||||
return Locomotor.MovementCostForCell(cell) != short.MaxValue;
|
||||
}
|
||||
|
||||
public bool CanEnterCell(CPos cell, Actor ignoreActor = null, bool checkTransientActors = true)
|
||||
public bool CanEnterCell(CPos cell, Actor ignoreActor = null, BlockedByActor check = BlockedByActor.All)
|
||||
{
|
||||
return Info.CanEnterCell(self.World, self, cell, ignoreActor, checkTransientActors);
|
||||
return Info.CanEnterCell(self.World, self, cell, ignoreActor, check);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -474,6 +476,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
FromSubCell = fromSub;
|
||||
ToSubCell = toSub;
|
||||
AddInfluence();
|
||||
IsBlocking = false;
|
||||
|
||||
// Most custom layer conditions are added/removed when starting the transition between layers.
|
||||
if (toCell.Layer != fromCell.Layer)
|
||||
@@ -737,7 +740,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
}
|
||||
|
||||
public Activity ScriptedMove(CPos cell) { return new Move(self, cell); }
|
||||
public Activity MoveTo(Func<List<CPos>> pathFunc) { return new Move(self, pathFunc); }
|
||||
public Activity MoveTo(Func<BlockedByActor, List<CPos>> pathFunc) { return new Move(self, pathFunc); }
|
||||
|
||||
Activity VisualMove(Actor self, WPos fromPos, WPos toPos, CPos cell)
|
||||
{
|
||||
@@ -758,7 +761,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
var pathFinder = self.World.WorldActor.Trait<IPathFinder>();
|
||||
List<CPos> path;
|
||||
using (var search = PathSearch.Search(self.World, Locomotor, self, true,
|
||||
using (var search = PathSearch.Search(self.World, Locomotor, self, BlockedByActor.All,
|
||||
loc => loc.Layer == 0 && CanEnterCell(loc))
|
||||
.FromPoint(self.Location))
|
||||
path = pathFinder.FindPath(search);
|
||||
@@ -782,7 +785,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
init.Add(new FacingInit(facing));
|
||||
|
||||
// Allows the husk to drag to its final position
|
||||
if (CanEnterCell(self.Location, self, false))
|
||||
if (CanEnterCell(self.Location, self, BlockedByActor.Stationary))
|
||||
init.Add(new HuskSpeedInit(MovementSpeedForCell(self, self.Location)));
|
||||
}
|
||||
|
||||
@@ -804,8 +807,16 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
void INotifyBlockingMove.OnNotifyBlockingMove(Actor self, Actor blocking)
|
||||
{
|
||||
if (self.IsIdle && self.AppearsFriendlyTo(blocking))
|
||||
if (!self.AppearsFriendlyTo(blocking))
|
||||
return;
|
||||
|
||||
if (self.IsIdle)
|
||||
{
|
||||
Nudge(self, blocking, true);
|
||||
return;
|
||||
}
|
||||
|
||||
IsBlocking = true;
|
||||
}
|
||||
|
||||
public override IEnumerable<VariableObserver> GetVariableObservers()
|
||||
|
||||
@@ -19,33 +19,19 @@ using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
[Flags]
|
||||
public enum CellConditions
|
||||
{
|
||||
None = 0,
|
||||
TransientActors,
|
||||
BlockedByMovers,
|
||||
All = TransientActors | BlockedByMovers
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum CellFlag : byte
|
||||
{
|
||||
HasFreeSpace = 0,
|
||||
HasActor = 1,
|
||||
HasMovingActor = 2,
|
||||
HasCrushableActor = 4,
|
||||
HasTemporaryBlocker = 8
|
||||
HasMovingActor = 1,
|
||||
HasStationaryActor = 2,
|
||||
HasMovableActor = 4,
|
||||
HasCrushableActor = 8,
|
||||
HasTemporaryBlocker = 16
|
||||
}
|
||||
|
||||
public static class LocomoterExts
|
||||
{
|
||||
public static bool HasCellCondition(this CellConditions c, CellConditions cellCondition)
|
||||
{
|
||||
// PERF: Enum.HasFlag is slower and requires allocations.
|
||||
return (c & cellCondition) == cellCondition;
|
||||
}
|
||||
|
||||
public static bool HasCellFlag(this CellFlag c, CellFlag cellFlag)
|
||||
{
|
||||
// PERF: Enum.HasFlag is slower and requires allocations.
|
||||
@@ -204,13 +190,13 @@ namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
struct CellCache
|
||||
{
|
||||
public readonly LongBitSet<PlayerBitMask> Blocking;
|
||||
public readonly LongBitSet<PlayerBitMask> Immovable;
|
||||
public readonly LongBitSet<PlayerBitMask> Crushable;
|
||||
public readonly CellFlag CellFlag;
|
||||
|
||||
public CellCache(LongBitSet<PlayerBitMask> blocking, CellFlag cellFlag, LongBitSet<PlayerBitMask> crushable = default(LongBitSet<PlayerBitMask>))
|
||||
public CellCache(LongBitSet<PlayerBitMask> immovable, CellFlag cellFlag, LongBitSet<PlayerBitMask> crushable = default(LongBitSet<PlayerBitMask>))
|
||||
{
|
||||
Blocking = blocking;
|
||||
Immovable = immovable;
|
||||
Crushable = crushable;
|
||||
CellFlag = cellFlag;
|
||||
}
|
||||
@@ -244,7 +230,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
return cell.Layer == 0 ? cellsCost[cell] : customLayerCellsCost[cell.Layer][cell];
|
||||
}
|
||||
|
||||
public short MovementCostToEnterCell(Actor actor, CPos destNode, Actor ignoreActor, CellConditions check)
|
||||
public short MovementCostToEnterCell(Actor actor, CPos destNode, BlockedByActor check, Actor ignoreActor)
|
||||
{
|
||||
if (!world.Map.Contains(destNode))
|
||||
return short.MaxValue;
|
||||
@@ -252,19 +238,20 @@ namespace OpenRA.Mods.Common.Traits
|
||||
var cellCost = destNode.Layer == 0 ? cellsCost[destNode] : customLayerCellsCost[destNode.Layer][destNode];
|
||||
|
||||
if (cellCost == short.MaxValue ||
|
||||
!CanMoveFreelyInto(actor, destNode, ignoreActor, check))
|
||||
!CanMoveFreelyInto(actor, destNode, check, ignoreActor))
|
||||
return short.MaxValue;
|
||||
|
||||
return cellCost;
|
||||
}
|
||||
|
||||
// Determines whether the actor is blocked by other Actors
|
||||
public bool CanMoveFreelyInto(Actor actor, CPos cell, Actor ignoreActor, CellConditions check)
|
||||
public bool CanMoveFreelyInto(Actor actor, CPos cell, BlockedByActor check, Actor ignoreActor)
|
||||
{
|
||||
var cellCache = GetCache(cell);
|
||||
var cellFlag = cellCache.CellFlag;
|
||||
|
||||
if (!check.HasCellCondition(CellConditions.TransientActors))
|
||||
// If the check allows: We are not blocked by transient actors.
|
||||
if (check == BlockedByActor.None)
|
||||
return true;
|
||||
|
||||
// No actor in the cell or free SubCell.
|
||||
@@ -281,14 +268,31 @@ namespace OpenRA.Mods.Common.Traits
|
||||
if (cellCache.Crushable.Overlaps(actor.Owner.PlayerMask))
|
||||
return true;
|
||||
|
||||
// If the check allows: We are not blocked by moving units.
|
||||
if (check <= BlockedByActor.Stationary && !cellFlag.HasCellFlag(CellFlag.HasStationaryActor))
|
||||
return true;
|
||||
|
||||
// If the check allows: We are not blocked by units that we can force to move out of the way.
|
||||
if (check <= BlockedByActor.Immovable && !cellCache.Immovable.Overlaps(actor.Owner.PlayerMask))
|
||||
return true;
|
||||
|
||||
// Cache doesn't account for ignored actors or temporary blockers - these must use the slow path.
|
||||
if (ignoreActor == null && !cellFlag.HasCellFlag(CellFlag.HasTemporaryBlocker))
|
||||
{
|
||||
// We are blocked by another actor in the cell.
|
||||
if (cellCache.Blocking.Overlaps(actor.Owner.PlayerMask))
|
||||
// We already know there are uncrushable actors in the cell so we are always blocked.
|
||||
if (check == BlockedByActor.All)
|
||||
return false;
|
||||
|
||||
if (check == CellConditions.BlockedByMovers && cellFlag < CellFlag.HasCrushableActor)
|
||||
// We already know there are either immovable or stationary actors which the check does not allow.
|
||||
if (!cellFlag.HasCellFlag(CellFlag.HasCrushableActor))
|
||||
return false;
|
||||
|
||||
// All actors in the cell are immovable and some cannot be crushed.
|
||||
if (!cellFlag.HasCellFlag(CellFlag.HasMovableActor))
|
||||
return false;
|
||||
|
||||
// All actors in the cell are stationary and some cannot be crushed.
|
||||
if (check == BlockedByActor.Stationary && !cellFlag.HasCellFlag(CellFlag.HasMovingActor))
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -299,12 +303,12 @@ namespace OpenRA.Mods.Common.Traits
|
||||
return true;
|
||||
}
|
||||
|
||||
public SubCell GetAvailableSubCell(Actor self, CPos cell, SubCell preferredSubCell = SubCell.Any, Actor ignoreActor = null, CellConditions check = CellConditions.All)
|
||||
public SubCell GetAvailableSubCell(Actor self, CPos cell, BlockedByActor check, SubCell preferredSubCell = SubCell.Any, Actor ignoreActor = null)
|
||||
{
|
||||
if (MovementCostForCell(cell) == short.MaxValue)
|
||||
return SubCell.Invalid;
|
||||
|
||||
if (check.HasCellCondition(CellConditions.TransientActors))
|
||||
if (check > BlockedByActor.None)
|
||||
{
|
||||
Func<Actor, bool> checkTransient = otherActor => IsBlockedBy(self, otherActor, ignoreActor, check, GetCache(cell).CellFlag);
|
||||
|
||||
@@ -320,15 +324,20 @@ namespace OpenRA.Mods.Common.Traits
|
||||
return world.ActorMap.FreeSubCell(cell, preferredSubCell);
|
||||
}
|
||||
|
||||
bool IsBlockedBy(Actor self, Actor otherActor, Actor ignoreActor, CellConditions check, CellFlag cellFlag)
|
||||
bool IsBlockedBy(Actor self, Actor otherActor, Actor ignoreActor, BlockedByActor check, CellFlag cellFlag)
|
||||
{
|
||||
if (otherActor == ignoreActor)
|
||||
return false;
|
||||
|
||||
// If the check allows: we are not blocked by allied units moving in our direction.
|
||||
if (!check.HasCellCondition(CellConditions.BlockedByMovers) && cellFlag.HasCellFlag(CellFlag.HasMovingActor) &&
|
||||
// If the check allows: We are not blocked by units that we can force to move out of the way.
|
||||
if (check <= BlockedByActor.Immovable && cellFlag.HasCellFlag(CellFlag.HasMovableActor) &&
|
||||
self.Owner.Stances[otherActor.Owner] == Stance.Ally &&
|
||||
IsMovingInMyDirection(self, otherActor))
|
||||
otherActor.TraitOrDefault<IMove>() != null)
|
||||
return false;
|
||||
|
||||
// If the check allows: we are not blocked by moving units.
|
||||
if (check <= BlockedByActor.Stationary && cellFlag.HasCellFlag(CellFlag.HasMovingActor) &&
|
||||
IsMoving(self, otherActor))
|
||||
return false;
|
||||
|
||||
if (cellFlag.HasCellFlag(CellFlag.HasTemporaryBlocker))
|
||||
@@ -339,11 +348,8 @@ namespace OpenRA.Mods.Common.Traits
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!cellFlag.HasCellFlag(CellFlag.HasCrushableActor))
|
||||
return true;
|
||||
|
||||
// If we cannot crush the other actor in our way, we are blocked.
|
||||
if (Info.Crushes.IsEmpty)
|
||||
if (!cellFlag.HasCellFlag(CellFlag.HasCrushableActor) || Info.Crushes.IsEmpty)
|
||||
return true;
|
||||
|
||||
// If the other actor in our way cannot be crushed, we are blocked.
|
||||
@@ -356,7 +362,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool IsMovingInMyDirection(Actor self, Actor other)
|
||||
static bool IsMoving(Actor self, Actor other)
|
||||
{
|
||||
// PERF: Because we can be sure that OccupiesSpace is Mobile here we can save some performance by avoiding querying for the trait.
|
||||
var otherMobile = other.OccupiesSpace as Mobile;
|
||||
@@ -368,9 +374,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
if (selfMobile == null)
|
||||
return false;
|
||||
|
||||
// Moving in the same direction if the facing delta is between +/- 90 degrees
|
||||
var delta = Util.NormalizeFacing(otherMobile.Facing - selfMobile.Facing);
|
||||
return delta < 64 || delta > 192;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void WorldLoaded(World w, WorldRenderer wr)
|
||||
@@ -456,31 +460,32 @@ namespace OpenRA.Mods.Common.Traits
|
||||
var cache = cell.Layer == 0 ? blockingCache : customLayerBlockingCache[cell.Layer];
|
||||
|
||||
var actors = actorMap.GetActorsAt(cell);
|
||||
var cellFlag = CellFlag.HasFreeSpace;
|
||||
|
||||
if (!actors.Any())
|
||||
{
|
||||
cache[cell] = new CellCache(default(LongBitSet<PlayerBitMask>), CellFlag.HasFreeSpace);
|
||||
cache[cell] = new CellCache(default(LongBitSet<PlayerBitMask>), cellFlag);
|
||||
return;
|
||||
}
|
||||
|
||||
if (sharesCell && actorMap.HasFreeSubCell(cell))
|
||||
{
|
||||
cache[cell] = new CellCache(default(LongBitSet<PlayerBitMask>), CellFlag.HasFreeSpace);
|
||||
cache[cell] = new CellCache(default(LongBitSet<PlayerBitMask>), cellFlag);
|
||||
return;
|
||||
}
|
||||
|
||||
var cellFlag = CellFlag.HasActor;
|
||||
var cellBlockedPlayers = default(LongBitSet<PlayerBitMask>);
|
||||
var cellImmovablePlayers = default(LongBitSet<PlayerBitMask>);
|
||||
var cellCrushablePlayers = world.AllPlayersMask;
|
||||
|
||||
foreach (var actor in actors)
|
||||
{
|
||||
var actorBlocksPlayers = world.AllPlayersMask;
|
||||
var actorImmovablePlayers = world.AllPlayersMask;
|
||||
var actorCrushablePlayers = world.NoPlayersMask;
|
||||
|
||||
var crushables = actor.TraitsImplementing<ICrushable>();
|
||||
var mobile = actor.OccupiesSpace as Mobile;
|
||||
var isMoving = mobile != null && mobile.CurrentMovementTypes.HasMovementType(MovementType.Horizontal);
|
||||
var isMovable = mobile != null;
|
||||
var isMoving = isMovable && mobile.CurrentMovementTypes.HasMovementType(MovementType.Horizontal);
|
||||
|
||||
if (crushables.Any())
|
||||
{
|
||||
@@ -490,9 +495,14 @@ namespace OpenRA.Mods.Common.Traits
|
||||
}
|
||||
|
||||
if (isMoving)
|
||||
{
|
||||
actorBlocksPlayers = actorBlocksPlayers.Except(actor.Owner.AlliedPlayersMask);
|
||||
cellFlag |= CellFlag.HasMovingActor;
|
||||
else
|
||||
cellFlag |= CellFlag.HasStationaryActor;
|
||||
|
||||
if (isMovable)
|
||||
{
|
||||
cellFlag |= CellFlag.HasMovableActor;
|
||||
actorImmovablePlayers = actorImmovablePlayers.Except(actor.Owner.AlliedPlayersMask);
|
||||
}
|
||||
|
||||
// PERF: Only perform ITemporaryBlocker trait look-up if mod/map rules contain any actors that are temporary blockers
|
||||
@@ -504,10 +514,10 @@ namespace OpenRA.Mods.Common.Traits
|
||||
}
|
||||
|
||||
cellCrushablePlayers = cellCrushablePlayers.Intersect(actorCrushablePlayers);
|
||||
cellBlockedPlayers = cellBlockedPlayers.Union(actorBlocksPlayers);
|
||||
cellImmovablePlayers = cellImmovablePlayers.Union(actorImmovablePlayers);
|
||||
}
|
||||
|
||||
cache[cell] = new CellCache(cellBlockedPlayers, cellFlag, cellCrushablePlayers);
|
||||
cache[cell] = new CellCache(cellImmovablePlayers, cellFlag, cellCrushablePlayers);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,9 +33,9 @@ namespace OpenRA.Mods.Common.Traits
|
||||
/// Calculates a path for the actor from source to destination
|
||||
/// </summary>
|
||||
/// <returns>A path from start to target</returns>
|
||||
List<CPos> FindUnitPath(CPos source, CPos target, Actor self, Actor ignoreActor);
|
||||
List<CPos> FindUnitPath(CPos source, CPos target, Actor self, Actor ignoreActor, BlockedByActor check);
|
||||
|
||||
List<CPos> FindUnitPathToRange(CPos source, SubCell srcSub, WPos target, WDist range, Actor self);
|
||||
List<CPos> FindUnitPathToRange(CPos source, SubCell srcSub, WPos target, WDist range, Actor self, BlockedByActor check);
|
||||
|
||||
/// <summary>
|
||||
/// Calculates a path given a search specification
|
||||
@@ -62,7 +62,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
this.world = world;
|
||||
}
|
||||
|
||||
public List<CPos> FindUnitPath(CPos source, CPos target, Actor self, Actor ignoreActor)
|
||||
public List<CPos> FindUnitPath(CPos source, CPos target, Actor self, Actor ignoreActor, BlockedByActor check)
|
||||
{
|
||||
var mobile = self.Trait<Mobile>();
|
||||
var locomotor = mobile.Locomotor;
|
||||
@@ -78,19 +78,23 @@ namespace OpenRA.Mods.Common.Traits
|
||||
return EmptyPath;
|
||||
|
||||
var distance = source - target;
|
||||
if (source.Layer == target.Layer && distance.LengthSquared < 3 && locomotor.CanMoveFreelyInto(self, target, null, CellConditions.All))
|
||||
var canMoveFreely = locomotor.CanMoveFreelyInto(self, target, check, null);
|
||||
if (distance.LengthSquared < 3 && !canMoveFreely)
|
||||
return new List<CPos> { };
|
||||
|
||||
if (source.Layer == target.Layer && distance.LengthSquared < 3 && canMoveFreely)
|
||||
return new List<CPos> { target };
|
||||
|
||||
List<CPos> pb;
|
||||
|
||||
using (var fromSrc = PathSearch.FromPoint(world, locomotor, self, target, source, true).WithIgnoredActor(ignoreActor))
|
||||
using (var fromDest = PathSearch.FromPoint(world, locomotor, self, source, target, true).WithIgnoredActor(ignoreActor).Reverse())
|
||||
using (var fromSrc = PathSearch.FromPoint(world, locomotor, self, target, source, check).WithIgnoredActor(ignoreActor))
|
||||
using (var fromDest = PathSearch.FromPoint(world, locomotor, self, source, target, check).WithIgnoredActor(ignoreActor).Reverse())
|
||||
pb = FindBidiPath(fromSrc, fromDest);
|
||||
|
||||
return pb;
|
||||
}
|
||||
|
||||
public List<CPos> FindUnitPathToRange(CPos source, SubCell srcSub, WPos target, WDist range, Actor self)
|
||||
public List<CPos> FindUnitPathToRange(CPos source, SubCell srcSub, WPos target, WDist range, Actor self, BlockedByActor check)
|
||||
{
|
||||
if (!cached)
|
||||
{
|
||||
@@ -123,8 +127,8 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
var locomotor = mobile.Locomotor;
|
||||
|
||||
using (var fromSrc = PathSearch.FromPoints(world, locomotor, self, tilesInRange, source, true))
|
||||
using (var fromDest = PathSearch.FromPoint(world, locomotor, self, source, targetCell, true).Reverse())
|
||||
using (var fromSrc = PathSearch.FromPoints(world, locomotor, self, tilesInRange, source, check))
|
||||
using (var fromDest = PathSearch.FromPoint(world, locomotor, self, source, targetCell, check).Reverse())
|
||||
return FindBidiPath(fromSrc, fromDest);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user