Some initial hacks towards multiple-infantry-per-cell. Make the pathfinder smart enough to do what we need, and remove a *lot* of stupid duplication. Needs more work.

This commit is contained in:
Paul Chote
2010-06-23 19:54:22 +12:00
parent 308a7b0cf6
commit b7c8e55d14
12 changed files with 93 additions and 87 deletions

View File

@@ -72,9 +72,9 @@ namespace OpenRA
} }
var pb = FindBidiPath( var pb = FindBidiPath(
PathSearch.FromPoint(world, target, from, umt, true) PathSearch.FromPoint(self, target, from, umt, true)
.WithCustomBlocker(AvoidUnitsNear(from, 4, self)), .WithCustomBlocker(AvoidUnitsNear(from, 4, self)),
PathSearch.FromPoint(world, from, target, umt, true) PathSearch.FromPoint(self, from, target, umt, true)
.WithCustomBlocker(AvoidUnitsNear(from, 4, self)) .WithCustomBlocker(AvoidUnitsNear(from, 4, self))
.InReverse()); .InReverse());
@@ -90,10 +90,11 @@ namespace OpenRA
{ {
using( new PerfSample( "find_unit_path_multiple_src" ) ) using( new PerfSample( "find_unit_path_multiple_src" ) )
{ {
var mobile = self.traits.Get<Mobile>();
var tilesInRange = world.FindTilesInCircle(target, range) var tilesInRange = world.FindTilesInCircle(target, range)
.Where( t => world.IsPathableCell( t, umt ) ); .Where( t => mobile.CanEnterCell(t));
var path = FindPath( PathSearch.FromPoints( world, tilesInRange, src, umt, false ) var path = FindPath( PathSearch.FromPoints( self, tilesInRange, src, umt, false )
.WithCustomBlocker(AvoidUnitsNear(src, 4, self)) .WithCustomBlocker(AvoidUnitsNear(src, 4, self))
.InReverse()); .InReverse());
path.Reverse(); path.Reverse();

View File

@@ -37,14 +37,16 @@ namespace OpenRA
Func<int2, bool> customBlock; Func<int2, bool> customBlock;
public bool checkForBlocked; public bool checkForBlocked;
public Actor ignoreBuilding; public Actor ignoreBuilding;
Actor self;
public bool inReverse; public bool inReverse;
BuildingInfluence buildingInfluence; BuildingInfluence buildingInfluence;
UnitInfluence unitInfluence; UnitInfluence unitInfluence;
public PathSearch(World world) public PathSearch(Actor self)
{ {
this.world = world; this.self = self;
world = self.World;
cellInfo = InitCellInfo(); cellInfo = InitCellInfo();
queue = new PriorityQueue<PathDistance>(); queue = new PriorityQueue<PathDistance>();
@@ -70,6 +72,18 @@ namespace OpenRA
return this; return this;
} }
public PathSearch WithHeuristic(Func<int2, float> h)
{
heuristic = h;
return this;
}
public PathSearch FromPoint(int2 from)
{
AddInitialCell( self.World, from );
return this;
}
const float LaneBias = .5f; const float LaneBias = .5f;
public int2 Expand( World world, float[][ , ] passableCost ) public int2 Expand( World world, float[][ , ] passableCost )
@@ -101,8 +115,8 @@ namespace OpenRA
if (!buildingInfluence.CanMoveHere(newHere, ignoreBuilding)) if (!buildingInfluence.CanMoveHere(newHere, ignoreBuilding))
continue; continue;
// Replicate real-ra behavior of not being able to enter a cell if there is a mixture of crushable and uncrushable units var mobile = self.traits.Get<Mobile>();
if (checkForBlocked && (unitInfluence.GetUnitsAt(newHere).Any(a => !world.IsActorPathableToCrush(a, umt)))) if (checkForBlocked && !mobile.CanEnterCell(newHere))
continue; continue;
if (customBlock != null && customBlock(newHere)) if (customBlock != null && customBlock(newHere))
@@ -158,20 +172,28 @@ namespace OpenRA
queue.Add( new PathDistance( heuristic( location ), location ) ); queue.Add( new PathDistance( heuristic( location ), location ) );
} }
public static PathSearch FromPoint( World world, int2 from, int2 target, UnitMovementType umt, bool checkForBlocked ) public static PathSearch Search( Actor self, UnitMovementType umt, bool checkForBlocked )
{ {
var search = new PathSearch(world) { var search = new PathSearch(self) {
umt = umt,
checkForBlocked = checkForBlocked };
return search;
}
public static PathSearch FromPoint( Actor self, int2 from, int2 target, UnitMovementType umt, bool checkForBlocked )
{
var search = new PathSearch(self) {
heuristic = DefaultEstimator( target ), heuristic = DefaultEstimator( target ),
umt = umt, umt = umt,
checkForBlocked = checkForBlocked }; checkForBlocked = checkForBlocked };
search.AddInitialCell( world, from ); search.AddInitialCell( self.World, from );
return search; return search;
} }
public static PathSearch FromPoints(World world, IEnumerable<int2> froms, int2 target, UnitMovementType umt, bool checkForBlocked) public static PathSearch FromPoints(Actor self, IEnumerable<int2> froms, int2 target, UnitMovementType umt, bool checkForBlocked)
{ {
var search = new PathSearch(world) var search = new PathSearch(self)
{ {
heuristic = DefaultEstimator(target), heuristic = DefaultEstimator(target),
umt = umt, umt = umt,
@@ -179,7 +201,7 @@ namespace OpenRA
}; };
foreach (var sl in froms) foreach (var sl in froms)
search.AddInitialCell(world, sl); search.AddInitialCell(self.World, sl);
return search; return search;
} }

View File

@@ -62,7 +62,7 @@ namespace OpenRA.Traits.Activities
{ {
this.getPath = (self, mobile) => this.getPath = (self, mobile) =>
self.World.PathFinder.FindPath( self.World.PathFinder.FindPath(
PathSearch.FromPoint( self.World, self.Location, destination, mobile.GetMovementType(), false ) PathSearch.FromPoint( self, self.Location, destination, mobile.GetMovementType(), false )
.WithCustomBlocker( self.World.PathFinder.AvoidUnitsNear( self.Location, 4, self )) .WithCustomBlocker( self.World.PathFinder.AvoidUnitsNear( self.Location, 4, self ))
.WithIgnoredBuilding( ignoreBuilding )); .WithIgnoredBuilding( ignoreBuilding ));
@@ -89,17 +89,6 @@ namespace OpenRA.Traits.Activities
this.nearEnough = 0; this.nearEnough = 0;
} }
bool CanEnterCell( int2 c, Actor self )
{
if (!self.World.WorldActor.traits.Get<BuildingInfluence>().CanMoveHere(c)
&& self.World.WorldActor.traits.Get<BuildingInfluence>().GetBuildingAt(c) != ignoreBuilding)
return false;
// Cannot enter a cell if any unit inside is uncrushable
// This will need to be updated for multiple-infantry-in-a-cell
return (!self.World.WorldActor.traits.Get<UnitInfluence>().GetUnitsAt(c).Any(a => a != self && !self.World.IsActorCrushableByActor(a, self)));
}
public IActivity Tick( Actor self ) public IActivity Tick( Actor self )
{ {
var unit = self.traits.Get<Unit>(); var unit = self.traits.Get<Unit>();
@@ -179,7 +168,7 @@ namespace OpenRA.Traits.Activities
{ {
if( path.Count == 0 ) return null; if( path.Count == 0 ) return null;
var nextCell = path[ path.Count - 1 ]; var nextCell = path[ path.Count - 1 ];
if( !CanEnterCell( nextCell, self ) ) if( !mobile.CanEnterCell( nextCell ) )
{ {
if( ( mobile.toCell - destination.Value ).LengthSquared <= nearEnough ) if( ( mobile.toCell - destination.Value ).LengthSquared <= nearEnough )
{ {
@@ -255,6 +244,8 @@ namespace OpenRA.Traits.Activities
var frac = (float)moveFraction / moveFractionTotal; var frac = (float)moveFraction / moveFractionTotal;
self.CenterLocation = float2.Lerp( from, to, frac ); self.CenterLocation = float2.Lerp( from, to, frac );
// + self.traits.WithInterface<IOffsetCenterLocation>().Aggregate(float2.Zero, (a, x) => a + x.CenterOffset);
if( moveFraction >= moveFractionTotal ) if( moveFraction >= moveFractionTotal )
unit.Facing = toFacing & 0xFF; unit.Facing = toFacing & 0xFF;
else else

View File

@@ -31,16 +31,6 @@ namespace OpenRA
{ {
public static class WorldUtils public static class WorldUtils
{ {
public static bool IsPathableCell(this World world, int2 a, UnitMovementType umt)
{
if (world.WorldActor.traits.Get<BuildingInfluence>().GetBuildingAt(a) != null) return false;
if (world.WorldActor.traits.Get<UnitInfluence>().GetUnitsAt(a).Any()) return false;
return world.Map.IsInMap(a.X, a.Y) &&
Rules.TerrainTypes[world.TileSet.GetTerrainType(world.Map.MapTiles[a.X, a.Y])]
.GetCost(umt) < float.PositiveInfinity;
}
public static bool IsCellBuildable(this World world, int2 a, bool waterBound) public static bool IsCellBuildable(this World world, int2 a, bool waterBound)
{ {
return world.IsCellBuildable(a, waterBound, null); return world.IsCellBuildable(a, waterBound, null);

View File

@@ -76,15 +76,9 @@ namespace OpenRA.Mods.RA.Activities
self.QueueActivity(new Move( self.QueueActivity(new Move(
() => () =>
{ {
var search = new PathSearch(self.World) return self.World.PathFinder.FindPath(PathSearch.Search(self, UnitMovementType.Wheel, true)
{ .WithHeuristic(loc => (res.GetResource(loc) != null && harv.Resources.Contains( res.GetResource(loc).info.Name )) ? 0 : 1)
heuristic = loc => (res.GetResource(loc) != null .FromPoint(self.Location));
&& harv.Resources.Contains( res.GetResource(loc).info.Name )) ? 0 : 1,
umt = UnitMovementType.Wheel,
checkForBlocked = true
};
search.AddInitialCell(self.World, self.Location);
return self.World.PathFinder.FindPath(search);
})); }));
self.QueueActivity(new Harvest()); self.QueueActivity(new Harvest());
} }

View File

@@ -38,8 +38,9 @@ namespace OpenRA.Mods.RA.Activities
if (unit.Altitude == 0) if (unit.Altitude == 0)
return NextActivity; return NextActivity;
if (requireSpace && !self.World.IsPathableCell(self.Location, UnitMovementType.Foot)) // Todo: check if we can land here
return this; // fail to land if no space //if (requireSpace && !self.World.IsPathableCell(self.Location, UnitMovementType.Foot))
// return this; // fail to land if no space
--unit.Altitude; --unit.Altitude;
return this; return this;

View File

@@ -30,17 +30,18 @@ namespace OpenRA.Mods.RA.Activities
public IActivity NextActivity { get; set; } public IActivity NextActivity { get; set; }
bool isCanceled; bool isCanceled;
int2? ChooseExitTile(Actor self) int2? ChooseExitTile(Actor self, Actor cargo)
{ {
// is anyone still hogging this tile? // is anyone still hogging this tile?
if (self.World.WorldActor.traits.Get<UnitInfluence>().GetUnitsAt(self.Location).Count() > 1) if (self.World.WorldActor.traits.Get<UnitInfluence>().GetUnitsAt(self.Location).Count() > 1)
return null; return null;
var mobile = cargo.traits.Get<Mobile>();
for (var i = -1; i < 2; i++) for (var i = -1; i < 2; i++)
for (var j = -1; j < 2; j++) for (var j = -1; j < 2; j++)
if ((i != 0 || j != 0) && if ((i != 0 || j != 0) &&
self.World.IsPathableCell(self.Location + new int2(i, j), mobile.CanEnterCell(self.Location + new int2(i, j)))
UnitMovementType.Foot))
return self.Location + new int2(i, j); return self.Location + new int2(i, j);
return null; return null;
@@ -68,7 +69,7 @@ namespace OpenRA.Mods.RA.Activities
if (ru != null) if (ru != null)
ru.PlayCustomAnimation(self, "unload", null); ru.PlayCustomAnimation(self, "unload", null);
var exitTile = ChooseExitTile(self); var exitTile = ChooseExitTile(self, cargo.Peek(self));
if (exitTile == null) if (exitTile == null)
return this; return this;

View File

@@ -70,6 +70,11 @@ namespace OpenRA.Mods.RA
return cargo.Count == 0; return cargo.Count == 0;
} }
public Actor Peek(Actor self)
{
return cargo[0];
}
public Actor Unload(Actor self) public Actor Unload(Actor self)
{ {
var a = cargo[0]; var a = cargo[0];

View File

@@ -20,6 +20,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using OpenRA.GameRules; using OpenRA.GameRules;
using OpenRA.Traits; using OpenRA.Traits;
using OpenRA.Traits.Activities; using OpenRA.Traits.Activities;
@@ -65,8 +66,16 @@ namespace OpenRA.Mods.RA
for (var n = 0; n < threshold; n++ ) for (var n = 0; n < threshold; n++ )
{ {
var p = self.World.ChooseRandomCell(self.World.SharedRandom); var p = self.World.ChooseRandomCell(self.World.SharedRandom);
if (self.World.IsPathableCell(p, inWater ? UnitMovementType.Float : UnitMovementType.Wheel))
{ // Don't drop on any actors
if (self.World.WorldActor.traits.Get<BuildingInfluence>().GetBuildingAt(p) != null) continue;
if (self.World.WorldActor.traits.Get<UnitInfluence>().GetUnitsAt(p).Any()) continue;
// Don't drop on unpathable cells
if (!(self.World.Map.IsInMap(p.X, p.Y) &&
Rules.TerrainTypes[self.World.TileSet.GetTerrainType(self.World.Map.MapTiles[p.X, p.Y])]
.GetCost(inWater ? UnitMovementType.Float : UnitMovementType.Wheel) < float.PositiveInfinity)) continue;
self.World.AddFrameEndTask(w => self.World.AddFrameEndTask(w =>
{ {
var crate = new Actor(w, "crate", new int2(0, 0), w.WorldActor.Owner); var crate = new Actor(w, "crate", new int2(0, 0), w.WorldActor.Owner);
@@ -77,12 +86,11 @@ namespace OpenRA.Mods.RA
plane.traits.Get<Unit>().Facing = Util.GetFacing(p - startPos, 0); plane.traits.Get<Unit>().Facing = Util.GetFacing(p - startPos, 0);
plane.CancelActivity(); plane.CancelActivity();
plane.QueueActivity(new FlyCircle(p)); plane.QueueActivity(new FlyCircle(p));
plane.traits.Get<ParaDrop>().SetLZ(p, null, inWater); plane.traits.Get<ParaDrop>().SetLZ(p, null);
plane.traits.Get<Cargo>().Load(plane, crate); plane.traits.Get<Cargo>().Load(plane, crate);
}); });
return; return;
} }
} }
} }
}
} }

View File

@@ -68,23 +68,18 @@ namespace OpenRA.Mods.RA
{ {
var mobile = self.traits.Get<Mobile>(); var mobile = self.traits.Get<Mobile>();
var search = new PathSearch(self.World) var refs = self.World.Queries.OwnedBy[self.Owner]
{
heuristic = PathSearch.DefaultEstimator(self.Location),
umt = mobile.GetMovementType(),
checkForBlocked = false,
};
var refineries = self.World.Queries.OwnedBy[self.Owner]
.Where(x => x != ignore && x.traits.Contains<IAcceptOre>()) .Where(x => x != ignore && x.traits.Contains<IAcceptOre>())
.ToList(); .ToList();
foreach (var r in refineries) var path = self.World.PathFinder.FindPath(PathSearch.FromPoints(self,
search.AddInitialCell(self.World, r.Location + r.traits.Get<IAcceptOre>().DeliverOffset); refs.Select(r => r.Location + r.traits.Get<IAcceptOre>().DeliverOffset),
self.Location,
var path = self.World.PathFinder.FindPath(search); mobile.GetMovementType(),
false));
path.Reverse(); path.Reverse();
if (path.Count != 0) if (path.Count != 0)
return refineries.FirstOrDefault(x => x.Location + x.traits.Get<IAcceptOre>().DeliverOffset == path[0]); return refs.FirstOrDefault(x => x.Location + x.traits.Get<IAcceptOre>().DeliverOffset == path[0]);
else else
return null; return null;
} }

View File

@@ -37,13 +37,11 @@ namespace OpenRA.Mods.RA
readonly List<int2> droppedAt = new List<int2>(); readonly List<int2> droppedAt = new List<int2>();
int2 lz; int2 lz;
Actor flare; Actor flare;
bool waterDrop;
public void SetLZ( int2 lz, Actor flare, bool waterDrop ) public void SetLZ( int2 lz, Actor flare )
{ {
this.lz = lz; this.lz = lz;
this.flare = flare; this.flare = flare;
this.waterDrop = waterDrop;
droppedAt.Clear(); droppedAt.Clear();
} }
@@ -79,7 +77,7 @@ namespace OpenRA.Mods.RA
bool IsSuitableCell(Actor self, int2 p) bool IsSuitableCell(Actor self, int2 p)
{ {
return self.World.IsPathableCell(p, waterDrop ? UnitMovementType.Float : UnitMovementType.Wheel); return self.traits.Get<Mobile>().CanEnterCell(p);
} }
void FinishedDropping(Actor self) void FinishedDropping(Actor self)

View File

@@ -74,7 +74,7 @@ namespace OpenRA.Mods.RA
a.CancelActivity(); a.CancelActivity();
a.QueueActivity(new FlyCircle(p)); a.QueueActivity(new FlyCircle(p));
a.traits.Get<ParaDrop>().SetLZ(p, flare, false); a.traits.Get<ParaDrop>().SetLZ(p, flare);
var cargo = a.traits.Get<Cargo>(); var cargo = a.traits.Get<Cargo>();
foreach (var i in items) foreach (var i in items)