Allow units to give way when path is blocked by oncoming unit.
This commit is contained in:
@@ -32,6 +32,16 @@ namespace OpenRA.Traits
|
|||||||
Dead = 32
|
Dead = 32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE: Each subsequent category is a superset of the previous categories
|
||||||
|
// and categories are mutually exclusive.
|
||||||
|
public enum BlockedByActor
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Immovable,
|
||||||
|
Stationary,
|
||||||
|
All
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Type tag for DamageTypes <see cref="Primitives.BitSet{T}"/>.
|
/// Type tag for DamageTypes <see cref="Primitives.BitSet{T}"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -312,16 +322,16 @@ namespace OpenRA.Traits
|
|||||||
|
|
||||||
public interface IPositionableInfo : IOccupySpaceInfo
|
public interface IPositionableInfo : IOccupySpaceInfo
|
||||||
{
|
{
|
||||||
bool CanEnterCell(World world, Actor self, CPos cell, Actor ignoreActor = null, bool checkTransientActors = true);
|
bool CanEnterCell(World world, Actor self, CPos cell, Actor ignoreActor = null, BlockedByActor check = BlockedByActor.All);
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface IPositionable : IOccupySpace
|
public interface IPositionable : IOccupySpace
|
||||||
{
|
{
|
||||||
bool CanExistInCell(CPos location);
|
bool CanExistInCell(CPos location);
|
||||||
bool IsLeavingCell(CPos location, SubCell subCell = SubCell.Any);
|
bool IsLeavingCell(CPos location, SubCell subCell = SubCell.Any);
|
||||||
bool CanEnterCell(CPos location, Actor ignoreActor = null, bool checkTransientActors = true);
|
bool CanEnterCell(CPos location, Actor ignoreActor = null, BlockedByActor check = BlockedByActor.All);
|
||||||
SubCell GetValidSubCell(SubCell preferred = SubCell.Any);
|
SubCell GetValidSubCell(SubCell preferred = SubCell.Any);
|
||||||
SubCell GetAvailableSubCell(CPos location, SubCell preferredSubCell = SubCell.Any, Actor ignoreActor = null, bool checkTransientActors = true);
|
SubCell GetAvailableSubCell(CPos location, SubCell preferredSubCell = SubCell.Any, Actor ignoreActor = null, BlockedByActor check = BlockedByActor.All);
|
||||||
void SetPosition(Actor self, CPos cell, SubCell subCell = SubCell.Any);
|
void SetPosition(Actor self, CPos cell, SubCell subCell = SubCell.Any);
|
||||||
void SetPosition(Actor self, WPos pos);
|
void SetPosition(Actor self, WPos pos);
|
||||||
void SetVisualPosition(Actor self, WPos pos);
|
void SetVisualPosition(Actor self, WPos pos);
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ namespace OpenRA.Mods.Cnc.Traits
|
|||||||
var movement = self.Trait<IPositionable>();
|
var movement = self.Trait<IPositionable>();
|
||||||
|
|
||||||
var minefield = GetMinefieldCells(minefieldStart, cell, Info.MinefieldDepth)
|
var minefield = GetMinefieldCells(minefieldStart, cell, Info.MinefieldDepth)
|
||||||
.Where(c => movement.CanEnterCell(c, null, false))
|
.Where(c => movement.CanEnterCell(c, null, BlockedByActor.None))
|
||||||
.OrderBy(c => (c - minefieldStart).LengthSquared).ToList();
|
.OrderBy(c => (c - minefieldStart).LengthSquared).ToList();
|
||||||
|
|
||||||
self.QueueActivity(order.Queued, new LayMines(self, minefield));
|
self.QueueActivity(order.Queued, new LayMines(self, minefield));
|
||||||
@@ -216,7 +216,7 @@ namespace OpenRA.Mods.Cnc.Traits
|
|||||||
var pal = wr.Palette(TileSet.TerrainPaletteInternalName);
|
var pal = wr.Palette(TileSet.TerrainPaletteInternalName);
|
||||||
foreach (var c in minefield)
|
foreach (var c in minefield)
|
||||||
{
|
{
|
||||||
var tile = movement.CanEnterCell(c, null, false) && !world.ShroudObscures(c) ? tileOk : tileBlocked;
|
var tile = movement.CanEnterCell(c, null, BlockedByActor.None) && !world.ShroudObscures(c) ? tileOk : tileBlocked;
|
||||||
yield return new SpriteRenderable(tile, world.Map.CenterOfCell(c),
|
yield return new SpriteRenderable(tile, world.Map.CenterOfCell(c),
|
||||||
WVec.Zero, -511, pal, 1f, true);
|
WVec.Zero, -511, pal, 1f, true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ namespace OpenRA.Mods.Cnc.Traits
|
|||||||
bool IOccupySpaceInfo.SharesCell { get { return false; } }
|
bool IOccupySpaceInfo.SharesCell { get { return false; } }
|
||||||
|
|
||||||
// Used to determine if actor can spawn
|
// Used to determine if actor can spawn
|
||||||
public bool CanEnterCell(World world, Actor self, CPos cell, Actor ignoreActor = null, bool checkTransientActors = false)
|
public bool CanEnterCell(World world, Actor self, CPos cell, Actor ignoreActor = null, BlockedByActor check = BlockedByActor.All)
|
||||||
{
|
{
|
||||||
if (!world.Map.Contains(cell))
|
if (!world.Map.Contains(cell))
|
||||||
return false;
|
return false;
|
||||||
@@ -158,9 +158,9 @@ namespace OpenRA.Mods.Cnc.Traits
|
|||||||
|
|
||||||
public bool CanExistInCell(CPos cell) { return true; }
|
public bool CanExistInCell(CPos cell) { return true; }
|
||||||
public bool IsLeavingCell(CPos location, SubCell subCell = SubCell.Any) { return false; }
|
public bool IsLeavingCell(CPos location, SubCell subCell = SubCell.Any) { return false; }
|
||||||
public bool CanEnterCell(CPos cell, Actor ignoreActor = null, bool checkTransientActors = false) { 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 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
|
// Does not use any subcell
|
||||||
return SubCell.Invalid;
|
return SubCell.Invalid;
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ namespace OpenRA.Mods.Common.Activities
|
|||||||
if (!target.IsValidFor(self))
|
if (!target.IsValidFor(self))
|
||||||
return AttackStatus.UnableToAttack;
|
return AttackStatus.UnableToAttack;
|
||||||
|
|
||||||
if (attack.Info.AttackRequiresEnteringCell && !positionable.CanEnterCell(target.Actor.Location, null, false))
|
if (attack.Info.AttackRequiresEnteringCell && !positionable.CanEnterCell(target.Actor.Location, null, BlockedByActor.None))
|
||||||
return AttackStatus.UnableToAttack;
|
return AttackStatus.UnableToAttack;
|
||||||
|
|
||||||
if (!attack.Info.TargetFrozenActors && !forceAttack && target.Type == TargetType.FrozenActor)
|
if (!attack.Info.TargetFrozenActors && !forceAttack && target.Type == TargetType.FrozenActor)
|
||||||
|
|||||||
@@ -194,7 +194,7 @@ namespace OpenRA.Mods.Common.Activities
|
|||||||
|
|
||||||
// Find any harvestable resources:
|
// Find any harvestable resources:
|
||||||
List<CPos> path;
|
List<CPos> path;
|
||||||
using (var search = PathSearch.Search(self.World, mobile.Locomotor, self, true, loc =>
|
using (var search = PathSearch.Search(self.World, mobile.Locomotor, self, BlockedByActor.Stationary, loc =>
|
||||||
domainIndex.IsPassable(self.Location, loc, locomotorInfo) && harv.CanHarvestCell(self, loc) && claimLayer.CanClaimCell(self, loc))
|
domainIndex.IsPassable(self.Location, loc, locomotorInfo) && harv.CanHarvestCell(self, loc) && claimLayer.CanClaimCell(self, loc))
|
||||||
.WithCustomCost(loc =>
|
.WithCustomCost(loc =>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ namespace OpenRA.Mods.Common.Activities
|
|||||||
foreach (var n in notifyHarvesterActions)
|
foreach (var n in notifyHarvesterActions)
|
||||||
n.MovingToResources(self, targetCell);
|
n.MovingToResources(self, targetCell);
|
||||||
|
|
||||||
QueueChild(move.MoveTo(targetCell, 2));
|
QueueChild(move.MoveTo(targetCell, 0));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using OpenRA.Activities;
|
using OpenRA.Activities;
|
||||||
using OpenRA.Mods.Common.Pathfinder;
|
using OpenRA.Mods.Common.Pathfinder;
|
||||||
@@ -27,7 +26,7 @@ namespace OpenRA.Mods.Common.Activities
|
|||||||
|
|
||||||
readonly Mobile mobile;
|
readonly Mobile mobile;
|
||||||
readonly WDist nearEnough;
|
readonly WDist nearEnough;
|
||||||
readonly Func<List<CPos>> getPath;
|
readonly Func<BlockedByActor, List<CPos>> getPath;
|
||||||
readonly Actor ignoreActor;
|
readonly Actor ignoreActor;
|
||||||
readonly Color? targetLineColor;
|
readonly Color? targetLineColor;
|
||||||
|
|
||||||
@@ -36,7 +35,6 @@ namespace OpenRA.Mods.Common.Activities
|
|||||||
|
|
||||||
// For dealing with blockers
|
// For dealing with blockers
|
||||||
bool hasWaited;
|
bool hasWaited;
|
||||||
bool hasNotifiedBlocker;
|
|
||||||
int waitTicksRemaining;
|
int waitTicksRemaining;
|
||||||
|
|
||||||
// To work around queued activity issues while minimizing changes to legacy behaviour
|
// To work around queued activity issues while minimizing changes to legacy behaviour
|
||||||
@@ -48,11 +46,11 @@ namespace OpenRA.Mods.Common.Activities
|
|||||||
{
|
{
|
||||||
mobile = self.Trait<Mobile>();
|
mobile = self.Trait<Mobile>();
|
||||||
|
|
||||||
getPath = () =>
|
getPath = check =>
|
||||||
{
|
{
|
||||||
List<CPos> path;
|
List<CPos> path;
|
||||||
using (var search =
|
using (var search =
|
||||||
PathSearch.FromPoint(self.World, mobile.Locomotor, self, mobile.ToCell, destination, false)
|
PathSearch.FromPoint(self.World, mobile.Locomotor, self, mobile.ToCell, destination, check)
|
||||||
.WithoutLaneBias())
|
.WithoutLaneBias())
|
||||||
path = self.World.WorldActor.Trait<IPathFinder>().FindPath(search);
|
path = self.World.WorldActor.Trait<IPathFinder>().FindPath(search);
|
||||||
return path;
|
return path;
|
||||||
@@ -67,13 +65,13 @@ namespace OpenRA.Mods.Common.Activities
|
|||||||
{
|
{
|
||||||
mobile = self.Trait<Mobile>();
|
mobile = self.Trait<Mobile>();
|
||||||
|
|
||||||
getPath = () =>
|
getPath = check =>
|
||||||
{
|
{
|
||||||
if (!this.destination.HasValue)
|
if (!this.destination.HasValue)
|
||||||
return NoPath;
|
return NoPath;
|
||||||
|
|
||||||
return self.World.WorldActor.Trait<IPathFinder>()
|
return self.World.WorldActor.Trait<IPathFinder>()
|
||||||
.FindUnitPath(mobile.ToCell, this.destination.Value, self, ignoreActor);
|
.FindUnitPath(mobile.ToCell, this.destination.Value, self, ignoreActor, check);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Note: Will be recalculated from OnFirstRun if evaluateNearestMovableCell is true
|
// Note: Will be recalculated from OnFirstRun if evaluateNearestMovableCell is true
|
||||||
@@ -89,8 +87,8 @@ namespace OpenRA.Mods.Common.Activities
|
|||||||
{
|
{
|
||||||
mobile = self.Trait<Mobile>();
|
mobile = self.Trait<Mobile>();
|
||||||
|
|
||||||
getPath = () => self.World.WorldActor.Trait<IPathFinder>()
|
getPath = check => self.World.WorldActor.Trait<IPathFinder>()
|
||||||
.FindUnitPathToRange(mobile.FromCell, subCell, self.World.Map.CenterOfSubCell(destination, subCell), nearEnough, self);
|
.FindUnitPathToRange(mobile.FromCell, subCell, self.World.Map.CenterOfSubCell(destination, subCell), nearEnough, self, check);
|
||||||
this.destination = destination;
|
this.destination = destination;
|
||||||
this.nearEnough = nearEnough;
|
this.nearEnough = nearEnough;
|
||||||
this.targetLineColor = targetLineColor;
|
this.targetLineColor = targetLineColor;
|
||||||
@@ -100,13 +98,13 @@ namespace OpenRA.Mods.Common.Activities
|
|||||||
{
|
{
|
||||||
mobile = self.Trait<Mobile>();
|
mobile = self.Trait<Mobile>();
|
||||||
|
|
||||||
getPath = () =>
|
getPath = check =>
|
||||||
{
|
{
|
||||||
if (!target.IsValidFor(self))
|
if (!target.IsValidFor(self))
|
||||||
return NoPath;
|
return NoPath;
|
||||||
|
|
||||||
return self.World.WorldActor.Trait<IPathFinder>().FindUnitPathToRange(
|
return self.World.WorldActor.Trait<IPathFinder>().FindUnitPathToRange(
|
||||||
mobile.ToCell, mobile.ToSubCell, target.CenterPosition, range, self);
|
mobile.ToCell, mobile.ToSubCell, target.CenterPosition, range, self, check);
|
||||||
};
|
};
|
||||||
|
|
||||||
destination = null;
|
destination = null;
|
||||||
@@ -114,7 +112,7 @@ namespace OpenRA.Mods.Common.Activities
|
|||||||
this.targetLineColor = targetLineColor;
|
this.targetLineColor = targetLineColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Move(Actor self, Func<List<CPos>> getPath, Color? targetLineColor = null)
|
public Move(Actor self, Func<BlockedByActor, List<CPos>> getPath, Color? targetLineColor = null)
|
||||||
{
|
{
|
||||||
mobile = self.Trait<Mobile>();
|
mobile = self.Trait<Mobile>();
|
||||||
|
|
||||||
@@ -135,9 +133,9 @@ namespace OpenRA.Mods.Common.Activities
|
|||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<CPos> EvalPath()
|
List<CPos> EvalPath(BlockedByActor check)
|
||||||
{
|
{
|
||||||
var path = getPath().TakeWhile(a => a != mobile.ToCell).ToList();
|
var path = getPath(check).TakeWhile(a => a != mobile.ToCell).ToList();
|
||||||
mobile.PathHash = HashList(path);
|
mobile.PathHash = HashList(path);
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
@@ -149,10 +147,16 @@ namespace OpenRA.Mods.Common.Activities
|
|||||||
var movableDestination = mobile.NearestMoveableCell(destination.Value);
|
var movableDestination = mobile.NearestMoveableCell(destination.Value);
|
||||||
destination = mobile.CanEnterCell(movableDestination) ? movableDestination : (CPos?)null;
|
destination = mobile.CanEnterCell(movableDestination) ? movableDestination : (CPos?)null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
path = EvalPath(BlockedByActor.Stationary);
|
||||||
|
if (path.Count == 0)
|
||||||
|
path = EvalPath(BlockedByActor.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool Tick(Actor self)
|
public override bool Tick(Actor self)
|
||||||
{
|
{
|
||||||
|
mobile.TurnToMove = false;
|
||||||
|
|
||||||
// If the actor is inside a tunnel then we must let them move
|
// If the actor is inside a tunnel then we must let them move
|
||||||
// all the way through before moving to the next activity
|
// all the way through before moving to the next activity
|
||||||
if (IsCanceling && self.Location.Layer != CustomMovementLayerType.Tunnel)
|
if (IsCanceling && self.Location.Layer != CustomMovementLayerType.Tunnel)
|
||||||
@@ -164,9 +168,6 @@ namespace OpenRA.Mods.Common.Activities
|
|||||||
if (destination == mobile.ToCell)
|
if (destination == mobile.ToCell)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (path == null)
|
|
||||||
path = EvalPath();
|
|
||||||
|
|
||||||
if (path.Count == 0)
|
if (path.Count == 0)
|
||||||
{
|
{
|
||||||
destination = mobile.ToCell;
|
destination = mobile.ToCell;
|
||||||
@@ -184,6 +185,7 @@ namespace OpenRA.Mods.Common.Activities
|
|||||||
{
|
{
|
||||||
path.Add(nextCell.Value.First);
|
path.Add(nextCell.Value.First);
|
||||||
QueueChild(new Turn(self, firstFacing));
|
QueueChild(new Turn(self, firstFacing));
|
||||||
|
mobile.TurnToMove = true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,7 +213,7 @@ namespace OpenRA.Mods.Common.Activities
|
|||||||
var containsTemporaryBlocker = WorldUtils.ContainsTemporaryBlocker(self.World, nextCell, self);
|
var containsTemporaryBlocker = WorldUtils.ContainsTemporaryBlocker(self.World, nextCell, self);
|
||||||
|
|
||||||
// Next cell in the move is blocked by another actor
|
// Next cell in the move is blocked by another actor
|
||||||
if (containsTemporaryBlocker || !mobile.CanEnterCell(nextCell, ignoreActor, true))
|
if (containsTemporaryBlocker || !mobile.CanEnterCell(nextCell, ignoreActor))
|
||||||
{
|
{
|
||||||
// Are we close enough?
|
// Are we close enough?
|
||||||
var cellRange = nearEnough.Length / 1024;
|
var cellRange = nearEnough.Length / 1024;
|
||||||
@@ -221,42 +223,116 @@ namespace OpenRA.Mods.Common.Activities
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// See if they will move
|
// There is no point in waiting for the other actor to move if it is incapable of moving.
|
||||||
if (!hasNotifiedBlocker)
|
if (!mobile.CanEnterCell(nextCell, ignoreActor, BlockedByActor.Immovable))
|
||||||
{
|
{
|
||||||
self.NotifyBlocker(nextCell);
|
path = EvalPath(BlockedByActor.Immovable);
|
||||||
hasNotifiedBlocker = true;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// See if they will move
|
||||||
|
self.NotifyBlocker(nextCell);
|
||||||
|
|
||||||
// Wait a bit to see if they leave
|
// Wait a bit to see if they leave
|
||||||
if (!hasWaited)
|
if (!hasWaited)
|
||||||
{
|
{
|
||||||
waitTicksRemaining = mobile.Info.LocomotorInfo.WaitAverage
|
waitTicksRemaining = mobile.Info.LocomotorInfo.WaitAverage;
|
||||||
+ self.World.SharedRandom.Next(-mobile.Info.LocomotorInfo.WaitSpread, mobile.Info.LocomotorInfo.WaitSpread);
|
|
||||||
|
|
||||||
hasWaited = true;
|
hasWaited = true;
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (--waitTicksRemaining >= 0)
|
if (--waitTicksRemaining >= 0)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
hasWaited = false;
|
||||||
|
|
||||||
|
// If the blocking actors are already leaving, wait a little longer instead of repathing
|
||||||
|
if (CellIsEvacuating(self, nextCell))
|
||||||
|
return null;
|
||||||
|
|
||||||
// Calculate a new path
|
// Calculate a new path
|
||||||
mobile.RemoveInfluence();
|
mobile.RemoveInfluence();
|
||||||
var newPath = EvalPath();
|
var newPath = EvalPath(BlockedByActor.All);
|
||||||
mobile.AddInfluence();
|
mobile.AddInfluence();
|
||||||
|
|
||||||
if (newPath.Count != 0)
|
if (newPath.Count != 0)
|
||||||
|
{
|
||||||
path = newPath;
|
path = newPath;
|
||||||
|
var newCell = path[path.Count - 1];
|
||||||
|
path.RemoveAt(path.Count - 1);
|
||||||
|
|
||||||
|
return Pair.New(newCell, mobile.GetAvailableSubCell(nextCell, SubCell.Any, ignoreActor));
|
||||||
|
}
|
||||||
|
else if (mobile.IsBlocking)
|
||||||
|
{
|
||||||
|
// If there is no way around the blocker and blocker will not move and we are blocking others, back up to let others pass.
|
||||||
|
var newCell = GetAdjacentCell(self, nextCell);
|
||||||
|
if (newCell != null)
|
||||||
|
{
|
||||||
|
if ((nextCell - newCell).Value.LengthSquared > 2)
|
||||||
|
path.Add(mobile.ToCell);
|
||||||
|
|
||||||
|
return Pair.New(newCell.Value, mobile.GetAvailableSubCell(newCell.Value, SubCell.Any, ignoreActor));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
hasNotifiedBlocker = false;
|
|
||||||
hasWaited = false;
|
hasWaited = false;
|
||||||
path.RemoveAt(path.Count - 1);
|
path.RemoveAt(path.Count - 1);
|
||||||
|
|
||||||
var subCell = mobile.GetAvailableSubCell(nextCell, SubCell.Any, ignoreActor);
|
return Pair.New(nextCell, mobile.GetAvailableSubCell(nextCell, SubCell.Any, ignoreActor));
|
||||||
return Pair.New(nextCell, subCell);
|
}
|
||||||
|
|
||||||
|
protected override void OnLastRun(Actor self)
|
||||||
|
{
|
||||||
|
path = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CellIsEvacuating(Actor self, CPos cell)
|
||||||
|
{
|
||||||
|
foreach (var actor in self.World.ActorMap.GetActorsAt(cell))
|
||||||
|
{
|
||||||
|
var move = actor.TraitOrDefault<Mobile>();
|
||||||
|
if (move == null || !move.IsTraitEnabled() || !move.IsLeaving())
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
CPos? GetAdjacentCell(Actor self, CPos nextCell)
|
||||||
|
{
|
||||||
|
var availCells = new List<CPos>();
|
||||||
|
var notStupidCells = new List<CPos>();
|
||||||
|
for (var i = -1; i <= 1; i++)
|
||||||
|
{
|
||||||
|
for (var j = -1; j <= 1; j++)
|
||||||
|
{
|
||||||
|
var p = mobile.ToCell + new CVec(i, j);
|
||||||
|
if (mobile.CanEnterCell(p))
|
||||||
|
availCells.Add(p);
|
||||||
|
else if (p != nextCell && p != mobile.ToCell)
|
||||||
|
notStupidCells.Add(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CPos? newCell = null;
|
||||||
|
if (availCells.Count > 0)
|
||||||
|
newCell = availCells.Random(self.World.SharedRandom);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var cellInfo = notStupidCells
|
||||||
|
.SelectMany(c => self.World.ActorMap.GetActorsAt(c)
|
||||||
|
.Where(a => a.IsIdle && a.Info.HasTraitInfo<MobileInfo>()),
|
||||||
|
(c, a) => new { Cell = c, Actor = a })
|
||||||
|
.RandomOrDefault(self.World.SharedRandom);
|
||||||
|
if (cellInfo != null)
|
||||||
|
newCell = cellInfo.Cell;
|
||||||
|
}
|
||||||
|
|
||||||
|
return newCell;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Cancel(Actor self, bool keepQueue = false)
|
public override void Cancel(Actor self, bool keepQueue = false)
|
||||||
@@ -397,10 +473,13 @@ namespace OpenRA.Mods.Common.Activities
|
|||||||
public MoveFirstHalf(Move move, WPos from, WPos to, int fromFacing, int toFacing, int startingFraction)
|
public MoveFirstHalf(Move move, WPos from, WPos to, int fromFacing, int toFacing, int startingFraction)
|
||||||
: base(move, from, to, fromFacing, toFacing, startingFraction) { }
|
: base(move, from, to, fromFacing, toFacing, startingFraction) { }
|
||||||
|
|
||||||
static bool IsTurn(Mobile mobile, CPos nextCell)
|
static bool IsTurn(Mobile mobile, CPos nextCell, Map map)
|
||||||
{
|
{
|
||||||
return nextCell - mobile.ToCell !=
|
// Tight U-turns should be done in place instead of making silly looking loops.
|
||||||
mobile.ToCell - mobile.FromCell;
|
var nextFacing = map.FacingBetween(nextCell, mobile.ToCell, mobile.Facing);
|
||||||
|
var currentFacing = map.FacingBetween(mobile.ToCell, mobile.FromCell, mobile.Facing);
|
||||||
|
var delta = Util.NormalizeFacing(nextFacing - currentFacing);
|
||||||
|
return delta != 0 && (delta < 96 || delta > 160);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override MovePart OnComplete(Actor self, Mobile mobile, Move parent)
|
protected override MovePart OnComplete(Actor self, Mobile mobile, Move parent)
|
||||||
@@ -412,7 +491,7 @@ namespace OpenRA.Mods.Common.Activities
|
|||||||
var nextCell = parent.PopPath(self);
|
var nextCell = parent.PopPath(self);
|
||||||
if (nextCell != null)
|
if (nextCell != null)
|
||||||
{
|
{
|
||||||
if (IsTurn(mobile, nextCell.Value.First))
|
if (IsTurn(mobile, nextCell.Value.First, map))
|
||||||
{
|
{
|
||||||
var nextSubcellOffset = map.Grid.OffsetOfSubCell(nextCell.Value.Second);
|
var nextSubcellOffset = map.Grid.OffsetOfSubCell(nextCell.Value.Second);
|
||||||
var ret = new MoveFirstHalf(
|
var ret = new MoveFirstHalf(
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ namespace OpenRA.Mods.Common.Activities
|
|||||||
|
|
||||||
protected override void OnFirstRun(Actor self)
|
protected override void OnFirstRun(Actor self)
|
||||||
{
|
{
|
||||||
QueueChild(Mobile.MoveTo(() => CalculatePathToTarget(self)));
|
QueueChild(Mobile.MoveTo(check => CalculatePathToTarget(self, check)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool Tick(Actor self)
|
public override bool Tick(Actor self)
|
||||||
@@ -112,14 +112,14 @@ namespace OpenRA.Mods.Common.Activities
|
|||||||
|
|
||||||
// Target has moved, and MoveAdjacentTo is still valid.
|
// Target has moved, and MoveAdjacentTo is still valid.
|
||||||
if (!IsCanceling && shouldRepath)
|
if (!IsCanceling && shouldRepath)
|
||||||
QueueChild(Mobile.MoveTo(() => CalculatePathToTarget(self)));
|
QueueChild(Mobile.MoveTo(check => CalculatePathToTarget(self, check)));
|
||||||
|
|
||||||
// The last queued childactivity is guaranteed to be the inner move, so if the childactivity
|
// The last queued childactivity is guaranteed to be the inner move, so if the childactivity
|
||||||
// queue is empty it means we have reached our destination.
|
// queue is empty it means we have reached our destination.
|
||||||
return TickChild(self);
|
return TickChild(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<CPos> CalculatePathToTarget(Actor self)
|
List<CPos> CalculatePathToTarget(Actor self, BlockedByActor check)
|
||||||
{
|
{
|
||||||
var targetCells = CandidateMovementCells(self);
|
var targetCells = CandidateMovementCells(self);
|
||||||
var searchCells = new List<CPos>();
|
var searchCells = new List<CPos>();
|
||||||
@@ -132,8 +132,8 @@ namespace OpenRA.Mods.Common.Activities
|
|||||||
if (!searchCells.Any())
|
if (!searchCells.Any())
|
||||||
return NoPath;
|
return NoPath;
|
||||||
|
|
||||||
using (var fromSrc = PathSearch.FromPoints(self.World, Mobile.Locomotor, self, searchCells, loc, true))
|
using (var fromSrc = PathSearch.FromPoints(self.World, Mobile.Locomotor, self, searchCells, loc, check))
|
||||||
using (var fromDest = PathSearch.FromPoint(self.World, Mobile.Locomotor, self, loc, lastVisibleTargetLocation, true).Reverse())
|
using (var fromDest = PathSearch.FromPoint(self.World, Mobile.Locomotor, self, loc, lastVisibleTargetLocation, check).Reverse())
|
||||||
return pathFinder.FindBidiPath(fromSrc, fromDest);
|
return pathFinder.FindBidiPath(fromSrc, fromDest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ namespace OpenRA.Mods.Common.Activities
|
|||||||
|
|
||||||
// Find the cells that are blocked by transient actors
|
// Find the cells that are blocked by transient actors
|
||||||
return cargo.CurrentAdjacentCells
|
return cargo.CurrentAdjacentCells
|
||||||
.Where(c => pos.CanEnterCell(c, null, true) != pos.CanEnterCell(c, null, false));
|
.Where(c => pos.CanEnterCell(c, null, BlockedByActor.All) != pos.CanEnterCell(c, null, BlockedByActor.None));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnFirstRun(Actor self)
|
protected override void OnFirstRun(Actor self)
|
||||||
|
|||||||
@@ -30,37 +30,47 @@ namespace OpenRA.Mods.Common.Pathfinder
|
|||||||
this.cacheStorage = cacheStorage;
|
this.cacheStorage = cacheStorage;
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
using (new PerfSample("Pathfinder"))
|
using (new PerfSample("Pathfinder"))
|
||||||
{
|
{
|
||||||
var key = "FindUnitPath" + self.ActorID + source.X + source.Y + target.X + target.Y;
|
var key = "FindUnitPath" + self.ActorID + source.X + source.Y + target.X + target.Y;
|
||||||
var cachedPath = cacheStorage.Retrieve(key);
|
|
||||||
|
|
||||||
if (cachedPath != null)
|
// Only cache path when transient actors are ignored, otherwise there is no guarantee that the path
|
||||||
return cachedPath;
|
// is still valid at the next check.
|
||||||
|
if (check == BlockedByActor.None)
|
||||||
|
{
|
||||||
|
var cachedPath = cacheStorage.Retrieve(key);
|
||||||
|
if (cachedPath != null)
|
||||||
|
return cachedPath;
|
||||||
|
}
|
||||||
|
|
||||||
var pb = pathFinder.FindUnitPath(source, target, self, ignoreActor);
|
var pb = pathFinder.FindUnitPath(source, target, self, ignoreActor, check);
|
||||||
|
|
||||||
cacheStorage.Store(key, pb);
|
if (check == BlockedByActor.None)
|
||||||
|
cacheStorage.Store(key, pb);
|
||||||
|
|
||||||
return pb;
|
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)
|
||||||
{
|
{
|
||||||
using (new PerfSample("Pathfinder"))
|
using (new PerfSample("Pathfinder"))
|
||||||
{
|
{
|
||||||
var key = "FindUnitPathToRange" + self.ActorID + source.X + source.Y + target.X + target.Y;
|
var key = "FindUnitPathToRange" + self.ActorID + source.X + source.Y + target.X + target.Y;
|
||||||
var cachedPath = cacheStorage.Retrieve(key);
|
|
||||||
|
|
||||||
if (cachedPath != null)
|
if (check == BlockedByActor.None)
|
||||||
return cachedPath;
|
{
|
||||||
|
var cachedPath = cacheStorage.Retrieve(key);
|
||||||
|
if (cachedPath != null)
|
||||||
|
return cachedPath;
|
||||||
|
}
|
||||||
|
|
||||||
var pb = pathFinder.FindUnitPathToRange(source, srcSub, target, range, self);
|
var pb = pathFinder.FindUnitPathToRange(source, srcSub, target, range, self, check);
|
||||||
|
|
||||||
cacheStorage.Store(key, pb);
|
if (check == BlockedByActor.None)
|
||||||
|
cacheStorage.Store(key, pb);
|
||||||
|
|
||||||
return pb;
|
return pb;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using OpenRA.Mods.Common.Traits;
|
using OpenRA.Mods.Common.Traits;
|
||||||
using OpenRA.Primitives;
|
using OpenRA.Primitives;
|
||||||
|
using OpenRA.Traits;
|
||||||
|
|
||||||
namespace OpenRA.Mods.Common.Pathfinder
|
namespace OpenRA.Mods.Common.Pathfinder
|
||||||
{
|
{
|
||||||
@@ -82,7 +83,7 @@ namespace OpenRA.Mods.Common.Pathfinder
|
|||||||
public bool InReverse { get; set; }
|
public bool InReverse { get; set; }
|
||||||
public Actor IgnoreActor { get; set; }
|
public Actor IgnoreActor { get; set; }
|
||||||
|
|
||||||
readonly CellConditions checkConditions;
|
readonly BlockedByActor checkConditions;
|
||||||
readonly Locomotor locomotor;
|
readonly Locomotor locomotor;
|
||||||
readonly LocomotorInfo.WorldMovementInfo worldMovementInfo;
|
readonly LocomotorInfo.WorldMovementInfo worldMovementInfo;
|
||||||
readonly CellInfoLayerPool.PooledCellInfoLayer pooledLayer;
|
readonly CellInfoLayerPool.PooledCellInfoLayer pooledLayer;
|
||||||
@@ -92,7 +93,7 @@ namespace OpenRA.Mods.Common.Pathfinder
|
|||||||
readonly Dictionary<byte, Pair<ICustomMovementLayer, CellLayer<CellInfo>>> customLayerInfo =
|
readonly Dictionary<byte, Pair<ICustomMovementLayer, CellLayer<CellInfo>>> customLayerInfo =
|
||||||
new Dictionary<byte, Pair<ICustomMovementLayer, CellLayer<CellInfo>>>();
|
new Dictionary<byte, Pair<ICustomMovementLayer, CellLayer<CellInfo>>>();
|
||||||
|
|
||||||
public PathGraph(CellInfoLayerPool layerPool, Locomotor locomotor, Actor actor, World world, bool checkForBlocked)
|
public PathGraph(CellInfoLayerPool layerPool, Locomotor locomotor, Actor actor, World world, BlockedByActor check)
|
||||||
{
|
{
|
||||||
pooledLayer = layerPool.Get();
|
pooledLayer = layerPool.Get();
|
||||||
groundInfo = pooledLayer.GetLayer();
|
groundInfo = pooledLayer.GetLayer();
|
||||||
@@ -108,7 +109,7 @@ namespace OpenRA.Mods.Common.Pathfinder
|
|||||||
worldMovementInfo = locomotorInfo.GetWorldMovementInfo(world);
|
worldMovementInfo = locomotorInfo.GetWorldMovementInfo(world);
|
||||||
Actor = actor;
|
Actor = actor;
|
||||||
LaneBias = 1;
|
LaneBias = 1;
|
||||||
checkConditions = checkForBlocked ? CellConditions.TransientActors : CellConditions.None;
|
checkConditions = check;
|
||||||
checkTerrainHeight = world.Map.Grid.MaximumTerrainHeight > 0;
|
checkTerrainHeight = world.Map.Grid.MaximumTerrainHeight > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,8 +173,7 @@ namespace OpenRA.Mods.Common.Pathfinder
|
|||||||
|
|
||||||
int GetCostToNode(CPos destNode, CVec direction)
|
int GetCostToNode(CPos destNode, CVec direction)
|
||||||
{
|
{
|
||||||
var movementCost = locomotor.MovementCostToEnterCell(Actor, destNode, IgnoreActor, checkConditions);
|
var movementCost = locomotor.MovementCostToEnterCell(Actor, destNode, checkConditions, IgnoreActor);
|
||||||
|
|
||||||
if (movementCost != short.MaxValue && !(CustomBlock != null && CustomBlock(destNode)))
|
if (movementCost != short.MaxValue && !(CustomBlock != null && CustomBlock(destNode)))
|
||||||
return CalculateCellCost(destNode, direction, movementCost);
|
return CalculateCellCost(destNode, direction, movementCost);
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ using System.Linq;
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using OpenRA.Mods.Common.Traits;
|
using OpenRA.Mods.Common.Traits;
|
||||||
using OpenRA.Primitives;
|
using OpenRA.Primitives;
|
||||||
|
using OpenRA.Traits;
|
||||||
|
|
||||||
namespace OpenRA.Mods.Common.Pathfinder
|
namespace OpenRA.Mods.Common.Pathfinder
|
||||||
{
|
{
|
||||||
@@ -45,18 +46,18 @@ namespace OpenRA.Mods.Common.Pathfinder
|
|||||||
considered = new LinkedList<Pair<CPos, int>>();
|
considered = new LinkedList<Pair<CPos, int>>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IPathSearch Search(World world, Locomotor locomotor, Actor self, bool checkForBlocked, Func<CPos, bool> goalCondition)
|
public static IPathSearch Search(World world, Locomotor locomotor, Actor self, BlockedByActor check, Func<CPos, bool> goalCondition)
|
||||||
{
|
{
|
||||||
var graph = new PathGraph(LayerPoolForWorld(world), locomotor, self, world, checkForBlocked);
|
var graph = new PathGraph(LayerPoolForWorld(world), locomotor, self, world, check);
|
||||||
var search = new PathSearch(graph);
|
var search = new PathSearch(graph);
|
||||||
search.isGoal = goalCondition;
|
search.isGoal = goalCondition;
|
||||||
search.heuristic = loc => 0;
|
search.heuristic = loc => 0;
|
||||||
return search;
|
return search;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IPathSearch FromPoint(World world, Locomotor locomotor, Actor self, CPos @from, CPos target, bool checkForBlocked)
|
public static IPathSearch FromPoint(World world, Locomotor locomotor, Actor self, CPos @from, CPos target, BlockedByActor check)
|
||||||
{
|
{
|
||||||
var graph = new PathGraph(LayerPoolForWorld(world), locomotor, self, world, checkForBlocked);
|
var graph = new PathGraph(LayerPoolForWorld(world), locomotor, self, world, check);
|
||||||
var search = new PathSearch(graph)
|
var search = new PathSearch(graph)
|
||||||
{
|
{
|
||||||
heuristic = DefaultEstimator(target)
|
heuristic = DefaultEstimator(target)
|
||||||
@@ -74,9 +75,9 @@ namespace OpenRA.Mods.Common.Pathfinder
|
|||||||
return search;
|
return search;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IPathSearch FromPoints(World world, Locomotor locomotor, Actor self, IEnumerable<CPos> froms, CPos target, bool checkForBlocked)
|
public static IPathSearch FromPoints(World world, Locomotor locomotor, Actor self, IEnumerable<CPos> froms, CPos target, BlockedByActor check)
|
||||||
{
|
{
|
||||||
var graph = new PathGraph(LayerPoolForWorld(world), locomotor, self, world, checkForBlocked);
|
var graph = new PathGraph(LayerPoolForWorld(world), locomotor, self, world, check);
|
||||||
var search = new PathSearch(graph)
|
var search = new PathSearch(graph)
|
||||||
{
|
{
|
||||||
heuristic = DefaultEstimator(target)
|
heuristic = DefaultEstimator(target)
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
bool IOccupySpaceInfo.SharesCell { get { return false; } }
|
bool IOccupySpaceInfo.SharesCell { get { return false; } }
|
||||||
|
|
||||||
// Used to determine if an aircraft can spawn landed
|
// 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))
|
if (!world.Map.Contains(cell))
|
||||||
return false;
|
return false;
|
||||||
@@ -170,7 +170,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
if (world.WorldActor.Trait<BuildingInfluence>().GetBuildingAt(cell) != null)
|
if (world.WorldActor.Trait<BuildingInfluence>().GetBuildingAt(cell) != null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!checkTransientActors)
|
if (check == BlockedByActor.None)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return !world.ActorMap.GetActorsAt(cell).Any(x => x != ignoreActor);
|
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 CanExistInCell(CPos cell) { return true; }
|
||||||
public bool IsLeavingCell(CPos location, SubCell subCell = SubCell.Any) { return false; } // TODO: Handle landing
|
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 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
|
// Does not use any subcell
|
||||||
return SubCell.Invalid;
|
return SubCell.Invalid;
|
||||||
|
|||||||
@@ -231,7 +231,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
if (IsTraitDisabled)
|
if (IsTraitDisabled)
|
||||||
return false;
|
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;
|
return false;
|
||||||
|
|
||||||
// PERF: Avoid LINQ.
|
// PERF: Avoid LINQ.
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
claimLayer.CanClaimCell(actor, cell);
|
claimLayer.CanClaimCell(actor, cell);
|
||||||
|
|
||||||
var path = pathfinder.FindPath(
|
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)
|
.WithCustomCost(loc => world.FindActorsInCircle(world.Map.CenterOfCell(loc), Info.HarvesterEnemyAvoidanceRadius)
|
||||||
.Where(u => !u.IsDead && actor.Owner.Stances[u.Owner] == Stance.Enemy)
|
.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)))
|
.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)
|
if (mobile.locomotor.MovementCostForCell(cell) == short.MaxValue)
|
||||||
return false;
|
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);
|
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)
|
if (checkTerrainType)
|
||||||
{
|
{
|
||||||
@@ -229,7 +229,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
}
|
}
|
||||||
|
|
||||||
return !IsEmpty(self) && (aircraft == null || aircraft.CanLand(self.Location, blockedByMobile: false))
|
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)
|
public bool CanLoad(Actor self, Actor a)
|
||||||
@@ -430,7 +430,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
void INotifyKilled.Killed(Actor self, AttackInfo e)
|
void INotifyKilled.Killed(Actor self, AttackInfo e)
|
||||||
{
|
{
|
||||||
if (Info.EjectOnDeath)
|
if (Info.EjectOnDeath)
|
||||||
while (!IsEmpty(self) && CanUnload(true))
|
while (!IsEmpty(self) && CanUnload(BlockedByActor.All))
|
||||||
{
|
{
|
||||||
var passenger = Unload(self);
|
var passenger = Unload(self);
|
||||||
var cp = self.CenterPosition;
|
var cp = self.CenterPosition;
|
||||||
@@ -438,7 +438,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
var positionable = passenger.Trait<IPositionable>();
|
var positionable = passenger.Trait<IPositionable>();
|
||||||
positionable.SetPosition(passenger, self.Location);
|
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));
|
self.World.AddFrameEndTask(w => w.Add(passenger));
|
||||||
var nbms = passenger.TraitsImplementing<INotifyBlockingMove>();
|
var nbms = passenger.TraitsImplementing<INotifyBlockingMove>();
|
||||||
|
|||||||
@@ -40,9 +40,9 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
|
|
||||||
bool IOccupySpaceInfo.SharesCell { get { return false; } }
|
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)
|
public bool CanExistInCell(World world, CPos cell)
|
||||||
@@ -57,7 +57,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
return true;
|
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))
|
if (!CanExistInCell(world, cell))
|
||||||
return SubCell.Invalid;
|
return SubCell.Invalid;
|
||||||
@@ -65,7 +65,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
if (world.WorldActor.Trait<BuildingInfluence>().GetBuildingAt(cell) != null)
|
if (world.WorldActor.Trait<BuildingInfluence>().GetBuildingAt(cell) != null)
|
||||||
return SubCell.Invalid;
|
return SubCell.Invalid;
|
||||||
|
|
||||||
if (!checkTransientActors)
|
if (check == BlockedByActor.None)
|
||||||
return SubCell.FullCell;
|
return SubCell.FullCell;
|
||||||
|
|
||||||
return !world.ActorMap.GetActorsAt(cell).Any(x => x != ignoreActor)
|
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 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 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 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)
|
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:
|
// Start a search from each refinery's delivery location:
|
||||||
List<CPos> path;
|
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 =>
|
.WithCustomCost(loc =>
|
||||||
{
|
{
|
||||||
if (!refs.ContainsKey(loc))
|
if (!refs.ContainsKey(loc))
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
|
|
||||||
bool IOccupySpaceInfo.SharesCell { get { return false; } }
|
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,
|
// 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.
|
// 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 Pair<CPos, SubCell>[] OccupiedCells() { return new[] { Pair.New(TopLeft, SubCell.FullCell) }; }
|
||||||
public bool IsLeavingCell(CPos location, SubCell subCell = SubCell.Any) { return false; }
|
public bool IsLeavingCell(CPos location, SubCell subCell = SubCell.Any) { return false; }
|
||||||
public SubCell GetValidSubCell(SubCell preferred = SubCell.Any) { return SubCell.FullCell; }
|
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))
|
if (!CanExistInCell(cell))
|
||||||
return SubCell.Invalid;
|
return SubCell.Invalid;
|
||||||
|
|
||||||
if (!checkTransientActors)
|
if (check == BlockedByActor.None)
|
||||||
return SubCell.FullCell;
|
return SubCell.FullCell;
|
||||||
|
|
||||||
return self.World.ActorMap.GetActorsAt(cell)
|
return self.World.ActorMap.GetActorsAt(cell)
|
||||||
.All(x => x == ignoreActor) ? SubCell.FullCell : SubCell.Invalid;
|
.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)); }
|
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
|
// initialized and used by CanEnterCell
|
||||||
Locomotor locomotor;
|
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
|
// PERF: Avoid repeated trait queries on the hot path
|
||||||
if (locomotor == null)
|
if (locomotor == null)
|
||||||
@@ -92,8 +92,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
if (locomotor.MovementCostForCell(cell) == short.MaxValue)
|
if (locomotor.MovementCostForCell(cell) == short.MaxValue)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var check = checkTransientActors ? CellConditions.All : CellConditions.BlockedByMovers;
|
return locomotor.CanMoveFreelyInto(self, cell, check, ignoreActor);
|
||||||
return locomotor.CanMoveFreelyInto(self, cell, ignoreActor, check);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public IReadOnlyDictionary<CPos, SubCell> OccupiedCells(ActorInfo info, CPos location, SubCell subCell = SubCell.Any)
|
public IReadOnlyDictionary<CPos, SubCell> OccupiedCells(ActorInfo info, CPos location, SubCell subCell = SubCell.Any)
|
||||||
@@ -176,6 +175,8 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
INotifyFinishedMoving[] notifyFinishedMoving;
|
INotifyFinishedMoving[] notifyFinishedMoving;
|
||||||
IWrapMove[] moveWrappers;
|
IWrapMove[] moveWrappers;
|
||||||
bool requireForceMove;
|
bool requireForceMove;
|
||||||
|
public bool TurnToMove;
|
||||||
|
public bool IsBlocking { get; private set; }
|
||||||
|
|
||||||
#region IFacing
|
#region IFacing
|
||||||
[Sync]
|
[Sync]
|
||||||
@@ -298,15 +299,6 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
if (IsTraitDisabled || IsTraitPaused)
|
if (IsTraitDisabled || IsTraitPaused)
|
||||||
return;
|
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.
|
// Pick an adjacent available cell.
|
||||||
var availCells = new List<CPos>();
|
var availCells = new List<CPos>();
|
||||||
var notStupidCells = 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)
|
public bool CanInteractWithGroundLayer(Actor self)
|
||||||
{
|
{
|
||||||
// TODO: Think about extending this to support arbitrary layer-layer checks
|
// 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);
|
&& (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, check, preferredSubCell, ignoreActor);
|
||||||
return Locomotor.GetAvailableSubCell(self, a, preferredSubCell, ignoreActor, cellConditions);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool CanExistInCell(CPos cell)
|
public bool CanExistInCell(CPos cell)
|
||||||
@@ -453,9 +455,9 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
return Locomotor.MovementCostForCell(cell) != short.MaxValue;
|
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
|
#endregion
|
||||||
@@ -474,6 +476,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
FromSubCell = fromSub;
|
FromSubCell = fromSub;
|
||||||
ToSubCell = toSub;
|
ToSubCell = toSub;
|
||||||
AddInfluence();
|
AddInfluence();
|
||||||
|
IsBlocking = false;
|
||||||
|
|
||||||
// Most custom layer conditions are added/removed when starting the transition between layers.
|
// Most custom layer conditions are added/removed when starting the transition between layers.
|
||||||
if (toCell.Layer != fromCell.Layer)
|
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 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)
|
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>();
|
var pathFinder = self.World.WorldActor.Trait<IPathFinder>();
|
||||||
List<CPos> path;
|
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))
|
loc => loc.Layer == 0 && CanEnterCell(loc))
|
||||||
.FromPoint(self.Location))
|
.FromPoint(self.Location))
|
||||||
path = pathFinder.FindPath(search);
|
path = pathFinder.FindPath(search);
|
||||||
@@ -782,7 +785,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
init.Add(new FacingInit(facing));
|
init.Add(new FacingInit(facing));
|
||||||
|
|
||||||
// Allows the husk to drag to its final position
|
// 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)));
|
init.Add(new HuskSpeedInit(MovementSpeedForCell(self, self.Location)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -804,8 +807,16 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
|
|
||||||
void INotifyBlockingMove.OnNotifyBlockingMove(Actor self, Actor blocking)
|
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);
|
Nudge(self, blocking, true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
IsBlocking = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IEnumerable<VariableObserver> GetVariableObservers()
|
public override IEnumerable<VariableObserver> GetVariableObservers()
|
||||||
|
|||||||
@@ -19,33 +19,19 @@ using OpenRA.Traits;
|
|||||||
|
|
||||||
namespace OpenRA.Mods.Common.Traits
|
namespace OpenRA.Mods.Common.Traits
|
||||||
{
|
{
|
||||||
[Flags]
|
|
||||||
public enum CellConditions
|
|
||||||
{
|
|
||||||
None = 0,
|
|
||||||
TransientActors,
|
|
||||||
BlockedByMovers,
|
|
||||||
All = TransientActors | BlockedByMovers
|
|
||||||
}
|
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
public enum CellFlag : byte
|
public enum CellFlag : byte
|
||||||
{
|
{
|
||||||
HasFreeSpace = 0,
|
HasFreeSpace = 0,
|
||||||
HasActor = 1,
|
HasMovingActor = 1,
|
||||||
HasMovingActor = 2,
|
HasStationaryActor = 2,
|
||||||
HasCrushableActor = 4,
|
HasMovableActor = 4,
|
||||||
HasTemporaryBlocker = 8
|
HasCrushableActor = 8,
|
||||||
|
HasTemporaryBlocker = 16
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class LocomoterExts
|
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)
|
public static bool HasCellFlag(this CellFlag c, CellFlag cellFlag)
|
||||||
{
|
{
|
||||||
// PERF: Enum.HasFlag is slower and requires allocations.
|
// PERF: Enum.HasFlag is slower and requires allocations.
|
||||||
@@ -204,13 +190,13 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
{
|
{
|
||||||
struct CellCache
|
struct CellCache
|
||||||
{
|
{
|
||||||
public readonly LongBitSet<PlayerBitMask> Blocking;
|
public readonly LongBitSet<PlayerBitMask> Immovable;
|
||||||
public readonly LongBitSet<PlayerBitMask> Crushable;
|
public readonly LongBitSet<PlayerBitMask> Crushable;
|
||||||
public readonly CellFlag CellFlag;
|
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;
|
Crushable = crushable;
|
||||||
CellFlag = cellFlag;
|
CellFlag = cellFlag;
|
||||||
}
|
}
|
||||||
@@ -244,7 +230,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
return cell.Layer == 0 ? cellsCost[cell] : customLayerCellsCost[cell.Layer][cell];
|
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))
|
if (!world.Map.Contains(destNode))
|
||||||
return short.MaxValue;
|
return short.MaxValue;
|
||||||
@@ -252,19 +238,20 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
var cellCost = destNode.Layer == 0 ? cellsCost[destNode] : customLayerCellsCost[destNode.Layer][destNode];
|
var cellCost = destNode.Layer == 0 ? cellsCost[destNode] : customLayerCellsCost[destNode.Layer][destNode];
|
||||||
|
|
||||||
if (cellCost == short.MaxValue ||
|
if (cellCost == short.MaxValue ||
|
||||||
!CanMoveFreelyInto(actor, destNode, ignoreActor, check))
|
!CanMoveFreelyInto(actor, destNode, check, ignoreActor))
|
||||||
return short.MaxValue;
|
return short.MaxValue;
|
||||||
|
|
||||||
return cellCost;
|
return cellCost;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determines whether the actor is blocked by other Actors
|
// 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 cellCache = GetCache(cell);
|
||||||
var cellFlag = cellCache.CellFlag;
|
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;
|
return true;
|
||||||
|
|
||||||
// No actor in the cell or free SubCell.
|
// No actor in the cell or free SubCell.
|
||||||
@@ -281,14 +268,31 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
if (cellCache.Crushable.Overlaps(actor.Owner.PlayerMask))
|
if (cellCache.Crushable.Overlaps(actor.Owner.PlayerMask))
|
||||||
return true;
|
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.
|
// Cache doesn't account for ignored actors or temporary blockers - these must use the slow path.
|
||||||
if (ignoreActor == null && !cellFlag.HasCellFlag(CellFlag.HasTemporaryBlocker))
|
if (ignoreActor == null && !cellFlag.HasCellFlag(CellFlag.HasTemporaryBlocker))
|
||||||
{
|
{
|
||||||
// We are blocked by another actor in the cell.
|
// We already know there are uncrushable actors in the cell so we are always blocked.
|
||||||
if (cellCache.Blocking.Overlaps(actor.Owner.PlayerMask))
|
if (check == BlockedByActor.All)
|
||||||
return false;
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -299,12 +303,12 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
return true;
|
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)
|
if (MovementCostForCell(cell) == short.MaxValue)
|
||||||
return SubCell.Invalid;
|
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);
|
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);
|
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)
|
if (otherActor == ignoreActor)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// If the check allows: we are not blocked by allied units moving in our direction.
|
// If the check allows: We are not blocked by units that we can force to move out of the way.
|
||||||
if (!check.HasCellCondition(CellConditions.BlockedByMovers) && cellFlag.HasCellFlag(CellFlag.HasMovingActor) &&
|
if (check <= BlockedByActor.Immovable && cellFlag.HasCellFlag(CellFlag.HasMovableActor) &&
|
||||||
self.Owner.Stances[otherActor.Owner] == Stance.Ally &&
|
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;
|
return false;
|
||||||
|
|
||||||
if (cellFlag.HasCellFlag(CellFlag.HasTemporaryBlocker))
|
if (cellFlag.HasCellFlag(CellFlag.HasTemporaryBlocker))
|
||||||
@@ -339,11 +348,8 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cellFlag.HasCellFlag(CellFlag.HasCrushableActor))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// If we cannot crush the other actor in our way, we are blocked.
|
// 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;
|
return true;
|
||||||
|
|
||||||
// If the other actor in our way cannot be crushed, we are blocked.
|
// If the other actor in our way cannot be crushed, we are blocked.
|
||||||
@@ -356,7 +362,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
return true;
|
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.
|
// 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;
|
var otherMobile = other.OccupiesSpace as Mobile;
|
||||||
@@ -368,9 +374,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
if (selfMobile == null)
|
if (selfMobile == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Moving in the same direction if the facing delta is between +/- 90 degrees
|
return true;
|
||||||
var delta = Util.NormalizeFacing(otherMobile.Facing - selfMobile.Facing);
|
|
||||||
return delta < 64 || delta > 192;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void WorldLoaded(World w, WorldRenderer wr)
|
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 cache = cell.Layer == 0 ? blockingCache : customLayerBlockingCache[cell.Layer];
|
||||||
|
|
||||||
var actors = actorMap.GetActorsAt(cell);
|
var actors = actorMap.GetActorsAt(cell);
|
||||||
|
var cellFlag = CellFlag.HasFreeSpace;
|
||||||
|
|
||||||
if (!actors.Any())
|
if (!actors.Any())
|
||||||
{
|
{
|
||||||
cache[cell] = new CellCache(default(LongBitSet<PlayerBitMask>), CellFlag.HasFreeSpace);
|
cache[cell] = new CellCache(default(LongBitSet<PlayerBitMask>), cellFlag);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sharesCell && actorMap.HasFreeSubCell(cell))
|
if (sharesCell && actorMap.HasFreeSubCell(cell))
|
||||||
{
|
{
|
||||||
cache[cell] = new CellCache(default(LongBitSet<PlayerBitMask>), CellFlag.HasFreeSpace);
|
cache[cell] = new CellCache(default(LongBitSet<PlayerBitMask>), cellFlag);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var cellFlag = CellFlag.HasActor;
|
var cellImmovablePlayers = default(LongBitSet<PlayerBitMask>);
|
||||||
var cellBlockedPlayers = default(LongBitSet<PlayerBitMask>);
|
|
||||||
var cellCrushablePlayers = world.AllPlayersMask;
|
var cellCrushablePlayers = world.AllPlayersMask;
|
||||||
|
|
||||||
foreach (var actor in actors)
|
foreach (var actor in actors)
|
||||||
{
|
{
|
||||||
var actorBlocksPlayers = world.AllPlayersMask;
|
var actorImmovablePlayers = world.AllPlayersMask;
|
||||||
var actorCrushablePlayers = world.NoPlayersMask;
|
var actorCrushablePlayers = world.NoPlayersMask;
|
||||||
|
|
||||||
var crushables = actor.TraitsImplementing<ICrushable>();
|
var crushables = actor.TraitsImplementing<ICrushable>();
|
||||||
var mobile = actor.OccupiesSpace as Mobile;
|
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())
|
if (crushables.Any())
|
||||||
{
|
{
|
||||||
@@ -490,9 +495,14 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isMoving)
|
if (isMoving)
|
||||||
{
|
|
||||||
actorBlocksPlayers = actorBlocksPlayers.Except(actor.Owner.AlliedPlayersMask);
|
|
||||||
cellFlag |= CellFlag.HasMovingActor;
|
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
|
// 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);
|
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
|
/// Calculates a path for the actor from source to destination
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>A path from start to target</returns>
|
/// <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>
|
/// <summary>
|
||||||
/// Calculates a path given a search specification
|
/// Calculates a path given a search specification
|
||||||
@@ -62,7 +62,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
this.world = world;
|
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 mobile = self.Trait<Mobile>();
|
||||||
var locomotor = mobile.Locomotor;
|
var locomotor = mobile.Locomotor;
|
||||||
@@ -78,19 +78,23 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
return EmptyPath;
|
return EmptyPath;
|
||||||
|
|
||||||
var distance = source - target;
|
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 };
|
return new List<CPos> { target };
|
||||||
|
|
||||||
List<CPos> pb;
|
List<CPos> pb;
|
||||||
|
|
||||||
using (var fromSrc = PathSearch.FromPoint(world, locomotor, self, target, source, true).WithIgnoredActor(ignoreActor))
|
using (var fromSrc = PathSearch.FromPoint(world, locomotor, self, target, source, check).WithIgnoredActor(ignoreActor))
|
||||||
using (var fromDest = PathSearch.FromPoint(world, locomotor, self, source, target, true).WithIgnoredActor(ignoreActor).Reverse())
|
using (var fromDest = PathSearch.FromPoint(world, locomotor, self, source, target, check).WithIgnoredActor(ignoreActor).Reverse())
|
||||||
pb = FindBidiPath(fromSrc, fromDest);
|
pb = FindBidiPath(fromSrc, fromDest);
|
||||||
|
|
||||||
return pb;
|
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)
|
if (!cached)
|
||||||
{
|
{
|
||||||
@@ -123,8 +127,8 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
|
|
||||||
var locomotor = mobile.Locomotor;
|
var locomotor = mobile.Locomotor;
|
||||||
|
|
||||||
using (var fromSrc = PathSearch.FromPoints(world, locomotor, self, tilesInRange, source, true))
|
using (var fromSrc = PathSearch.FromPoints(world, locomotor, self, tilesInRange, source, check))
|
||||||
using (var fromDest = PathSearch.FromPoint(world, locomotor, self, source, targetCell, true).Reverse())
|
using (var fromDest = PathSearch.FromPoint(world, locomotor, self, source, targetCell, check).Reverse())
|
||||||
return FindBidiPath(fromSrc, fromDest);
|
return FindBidiPath(fromSrc, fromDest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ namespace OpenRA.Mods.D2k.Activities
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The target reached solid ground
|
// The target reached solid ground
|
||||||
if (!positionable.CanEnterCell(targetLocation, null, false))
|
if (!positionable.CanEnterCell(targetLocation, null, BlockedByActor.None))
|
||||||
{
|
{
|
||||||
RevokeCondition(self);
|
RevokeCondition(self);
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ namespace OpenRA.Mods.D2k.Traits
|
|||||||
if (!a.Info.HasTraitInfo<AttractsWormsInfo>())
|
if (!a.Info.HasTraitInfo<AttractsWormsInfo>())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return mobile.CanEnterCell(a.Location, null, false);
|
return mobile.CanEnterCell(a.Location, null, BlockedByActor.None);
|
||||||
};
|
};
|
||||||
|
|
||||||
var actorsInRange = self.World.FindActorsInCircle(self.CenterPosition, WormInfo.MaxSearchRadius)
|
var actorsInRange = self.World.FindActorsInCircle(self.CenterPosition, WormInfo.MaxSearchRadius)
|
||||||
@@ -112,7 +112,7 @@ namespace OpenRA.Mods.D2k.Traits
|
|||||||
|
|
||||||
var moveTo = self.World.Map.CellContaining(self.CenterPosition + noiseDirection);
|
var moveTo = self.World.Map.CellContaining(self.CenterPosition + noiseDirection);
|
||||||
|
|
||||||
while (!self.World.Map.Contains(moveTo) || !mobile.CanEnterCell(moveTo, null, false))
|
while (!self.World.Map.Contains(moveTo) || !mobile.CanEnterCell(moveTo, null, BlockedByActor.None))
|
||||||
{
|
{
|
||||||
// without this check, this while can be infinity loop
|
// without this check, this while can be infinity loop
|
||||||
if (moveTo == self.Location)
|
if (moveTo == self.Location)
|
||||||
|
|||||||
Reference in New Issue
Block a user