diff --git a/OpenRA.Game/PathFinder.cs b/OpenRA.Game/PathFinder.cs index 176f09f5e2..7e869639db 100644 --- a/OpenRA.Game/PathFinder.cs +++ b/OpenRA.Game/PathFinder.cs @@ -72,9 +72,9 @@ namespace OpenRA } var pb = FindBidiPath( - PathSearch.FromPoint(world, target, from, umt, true) + PathSearch.FromPoint(self, target, from, umt, true) .WithCustomBlocker(AvoidUnitsNear(from, 4, self)), - PathSearch.FromPoint(world, from, target, umt, true) + PathSearch.FromPoint(self, from, target, umt, true) .WithCustomBlocker(AvoidUnitsNear(from, 4, self)) .InReverse()); @@ -90,10 +90,11 @@ namespace OpenRA { using( new PerfSample( "find_unit_path_multiple_src" ) ) { + var mobile = self.traits.Get(); 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)) .InReverse()); path.Reverse(); diff --git a/OpenRA.Game/PathSearch.cs b/OpenRA.Game/PathSearch.cs index 36127f7beb..7efe42def8 100755 --- a/OpenRA.Game/PathSearch.cs +++ b/OpenRA.Game/PathSearch.cs @@ -37,14 +37,16 @@ namespace OpenRA Func customBlock; public bool checkForBlocked; public Actor ignoreBuilding; + Actor self; public bool inReverse; BuildingInfluence buildingInfluence; UnitInfluence unitInfluence; - public PathSearch(World world) + public PathSearch(Actor self) { - this.world = world; + this.self = self; + world = self.World; cellInfo = InitCellInfo(); queue = new PriorityQueue(); @@ -70,6 +72,18 @@ namespace OpenRA return this; } + public PathSearch WithHeuristic(Func h) + { + heuristic = h; + return this; + } + + public PathSearch FromPoint(int2 from) + { + AddInitialCell( self.World, from ); + return this; + } + const float LaneBias = .5f; public int2 Expand( World world, float[][ , ] passableCost ) @@ -101,8 +115,8 @@ namespace OpenRA if (!buildingInfluence.CanMoveHere(newHere, ignoreBuilding)) continue; - // Replicate real-ra behavior of not being able to enter a cell if there is a mixture of crushable and uncrushable units - if (checkForBlocked && (unitInfluence.GetUnitsAt(newHere).Any(a => !world.IsActorPathableToCrush(a, umt)))) + var mobile = self.traits.Get(); + if (checkForBlocked && !mobile.CanEnterCell(newHere)) continue; if (customBlock != null && customBlock(newHere)) @@ -157,21 +171,29 @@ namespace OpenRA cellInfo[ location.X, location.Y ] = new CellInfo( 0, location, false ); 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 ), umt = umt, checkForBlocked = checkForBlocked }; - search.AddInitialCell( world, from ); + search.AddInitialCell( self.World, from ); return search; } - public static PathSearch FromPoints(World world, IEnumerable froms, int2 target, UnitMovementType umt, bool checkForBlocked) + public static PathSearch FromPoints(Actor self, IEnumerable froms, int2 target, UnitMovementType umt, bool checkForBlocked) { - var search = new PathSearch(world) + var search = new PathSearch(self) { heuristic = DefaultEstimator(target), umt = umt, @@ -179,7 +201,7 @@ namespace OpenRA }; foreach (var sl in froms) - search.AddInitialCell(world, sl); + search.AddInitialCell(self.World, sl); return search; } diff --git a/OpenRA.Game/Traits/Activities/Move.cs b/OpenRA.Game/Traits/Activities/Move.cs index d37034965c..dccdbdb656 100755 --- a/OpenRA.Game/Traits/Activities/Move.cs +++ b/OpenRA.Game/Traits/Activities/Move.cs @@ -62,7 +62,7 @@ namespace OpenRA.Traits.Activities { this.getPath = (self, mobile) => 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 )) .WithIgnoredBuilding( ignoreBuilding )); @@ -89,17 +89,6 @@ namespace OpenRA.Traits.Activities this.nearEnough = 0; } - bool CanEnterCell( int2 c, Actor self ) - { - if (!self.World.WorldActor.traits.Get().CanMoveHere(c) - && self.World.WorldActor.traits.Get().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().GetUnitsAt(c).Any(a => a != self && !self.World.IsActorCrushableByActor(a, self))); - } - public IActivity Tick( Actor self ) { var unit = self.traits.Get(); @@ -179,7 +168,7 @@ namespace OpenRA.Traits.Activities { if( path.Count == 0 ) return null; var nextCell = path[ path.Count - 1 ]; - if( !CanEnterCell( nextCell, self ) ) + if( !mobile.CanEnterCell( nextCell ) ) { if( ( mobile.toCell - destination.Value ).LengthSquared <= nearEnough ) { @@ -255,6 +244,8 @@ namespace OpenRA.Traits.Activities var frac = (float)moveFraction / moveFractionTotal; self.CenterLocation = float2.Lerp( from, to, frac ); + // + self.traits.WithInterface().Aggregate(float2.Zero, (a, x) => a + x.CenterOffset); + if( moveFraction >= moveFractionTotal ) unit.Facing = toFacing & 0xFF; else diff --git a/OpenRA.Game/WorldUtils.cs b/OpenRA.Game/WorldUtils.cs index eaa8401fd3..c235a4c943 100755 --- a/OpenRA.Game/WorldUtils.cs +++ b/OpenRA.Game/WorldUtils.cs @@ -31,16 +31,6 @@ namespace OpenRA { public static class WorldUtils { - public static bool IsPathableCell(this World world, int2 a, UnitMovementType umt) - { - if (world.WorldActor.traits.Get().GetBuildingAt(a) != null) return false; - if (world.WorldActor.traits.Get().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) { return world.IsCellBuildable(a, waterBound, null); diff --git a/OpenRA.Mods.RA/Activities/Harvest.cs b/OpenRA.Mods.RA/Activities/Harvest.cs index da14b8f1f6..dbe79bea23 100755 --- a/OpenRA.Mods.RA/Activities/Harvest.cs +++ b/OpenRA.Mods.RA/Activities/Harvest.cs @@ -76,15 +76,9 @@ namespace OpenRA.Mods.RA.Activities self.QueueActivity(new Move( () => { - var search = new PathSearch(self.World) - { - heuristic = loc => (res.GetResource(loc) != null - && 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); + 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) + .FromPoint(self.Location)); })); self.QueueActivity(new Harvest()); } diff --git a/OpenRA.Mods.RA/Activities/HeliLand.cs b/OpenRA.Mods.RA/Activities/HeliLand.cs index d8748ef851..e5a1d857a5 100644 --- a/OpenRA.Mods.RA/Activities/HeliLand.cs +++ b/OpenRA.Mods.RA/Activities/HeliLand.cs @@ -38,8 +38,9 @@ namespace OpenRA.Mods.RA.Activities if (unit.Altitude == 0) return NextActivity; - if (requireSpace && !self.World.IsPathableCell(self.Location, UnitMovementType.Foot)) - return this; // fail to land if no space + // Todo: check if we can land here + //if (requireSpace && !self.World.IsPathableCell(self.Location, UnitMovementType.Foot)) + // return this; // fail to land if no space --unit.Altitude; return this; diff --git a/OpenRA.Mods.RA/Activities/UnloadCargo.cs b/OpenRA.Mods.RA/Activities/UnloadCargo.cs index 5dbf455bca..0f967861ba 100644 --- a/OpenRA.Mods.RA/Activities/UnloadCargo.cs +++ b/OpenRA.Mods.RA/Activities/UnloadCargo.cs @@ -30,17 +30,18 @@ namespace OpenRA.Mods.RA.Activities public IActivity NextActivity { get; set; } bool isCanceled; - int2? ChooseExitTile(Actor self) + int2? ChooseExitTile(Actor self, Actor cargo) { // is anyone still hogging this tile? if (self.World.WorldActor.traits.Get().GetUnitsAt(self.Location).Count() > 1) return null; + + var mobile = cargo.traits.Get(); for (var i = -1; i < 2; i++) for (var j = -1; j < 2; j++) if ((i != 0 || j != 0) && - self.World.IsPathableCell(self.Location + new int2(i, j), - UnitMovementType.Foot)) + mobile.CanEnterCell(self.Location + new int2(i, j))) return self.Location + new int2(i, j); return null; @@ -68,7 +69,7 @@ namespace OpenRA.Mods.RA.Activities if (ru != null) ru.PlayCustomAnimation(self, "unload", null); - var exitTile = ChooseExitTile(self); + var exitTile = ChooseExitTile(self, cargo.Peek(self)); if (exitTile == null) return this; diff --git a/OpenRA.Mods.RA/Cargo.cs b/OpenRA.Mods.RA/Cargo.cs index 0c0af8af32..7f9a73a925 100644 --- a/OpenRA.Mods.RA/Cargo.cs +++ b/OpenRA.Mods.RA/Cargo.cs @@ -70,6 +70,11 @@ namespace OpenRA.Mods.RA return cargo.Count == 0; } + public Actor Peek(Actor self) + { + return cargo[0]; + } + public Actor Unload(Actor self) { var a = cargo[0]; diff --git a/OpenRA.Mods.RA/CrateDrop.cs b/OpenRA.Mods.RA/CrateDrop.cs index b249e8f36c..9af7f3ef8e 100644 --- a/OpenRA.Mods.RA/CrateDrop.cs +++ b/OpenRA.Mods.RA/CrateDrop.cs @@ -20,6 +20,7 @@ using System; using System.Collections.Generic; +using System.Linq; using OpenRA.GameRules; using OpenRA.Traits; using OpenRA.Traits.Activities; @@ -65,23 +66,30 @@ namespace OpenRA.Mods.RA for (var n = 0; n < threshold; n++ ) { var p = self.World.ChooseRandomCell(self.World.SharedRandom); - if (self.World.IsPathableCell(p, inWater ? UnitMovementType.Float : UnitMovementType.Wheel)) - { - self.World.AddFrameEndTask(w => - { - var crate = new Actor(w, "crate", new int2(0, 0), w.WorldActor.Owner); - crates.Add(crate); + + // Don't drop on any actors + if (self.World.WorldActor.traits.Get().GetBuildingAt(p) != null) continue; + if (self.World.WorldActor.traits.Get().GetUnitsAt(p).Any()) continue; - var startPos = w.ChooseRandomEdgeCell(); - var plane = w.CreateActor("BADR", startPos, w.WorldActor.Owner); - plane.traits.Get().Facing = Util.GetFacing(p - startPos, 0); - plane.CancelActivity(); - plane.QueueActivity(new FlyCircle(p)); - plane.traits.Get().SetLZ(p, null, inWater); - plane.traits.Get().Load(plane, crate); - }); - return; - } + // 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 => + { + var crate = new Actor(w, "crate", new int2(0, 0), w.WorldActor.Owner); + crates.Add(crate); + + var startPos = w.ChooseRandomEdgeCell(); + var plane = w.CreateActor("BADR", startPos, w.WorldActor.Owner); + plane.traits.Get().Facing = Util.GetFacing(p - startPos, 0); + plane.CancelActivity(); + plane.QueueActivity(new FlyCircle(p)); + plane.traits.Get().SetLZ(p, null); + plane.traits.Get().Load(plane, crate); + }); + return; } } } diff --git a/OpenRA.Mods.RA/Harvester.cs b/OpenRA.Mods.RA/Harvester.cs index be7ba0bbc5..01b166fad3 100755 --- a/OpenRA.Mods.RA/Harvester.cs +++ b/OpenRA.Mods.RA/Harvester.cs @@ -68,23 +68,18 @@ namespace OpenRA.Mods.RA { var mobile = self.traits.Get(); - var search = new PathSearch(self.World) - { - heuristic = PathSearch.DefaultEstimator(self.Location), - umt = mobile.GetMovementType(), - checkForBlocked = false, - }; - var refineries = self.World.Queries.OwnedBy[self.Owner] + var refs = self.World.Queries.OwnedBy[self.Owner] .Where(x => x != ignore && x.traits.Contains()) .ToList(); - - foreach (var r in refineries) - search.AddInitialCell(self.World, r.Location + r.traits.Get().DeliverOffset); - - var path = self.World.PathFinder.FindPath(search); + + var path = self.World.PathFinder.FindPath(PathSearch.FromPoints(self, + refs.Select(r => r.Location + r.traits.Get().DeliverOffset), + self.Location, + mobile.GetMovementType(), + false)); path.Reverse(); if (path.Count != 0) - return refineries.FirstOrDefault(x => x.Location + x.traits.Get().DeliverOffset == path[0]); + return refs.FirstOrDefault(x => x.Location + x.traits.Get().DeliverOffset == path[0]); else return null; } diff --git a/OpenRA.Mods.RA/ParaDrop.cs b/OpenRA.Mods.RA/ParaDrop.cs index 7be1f36d6e..c92e9df6be 100644 --- a/OpenRA.Mods.RA/ParaDrop.cs +++ b/OpenRA.Mods.RA/ParaDrop.cs @@ -37,13 +37,11 @@ namespace OpenRA.Mods.RA readonly List droppedAt = new List(); int2 lz; Actor flare; - bool waterDrop; - public void SetLZ( int2 lz, Actor flare, bool waterDrop ) + public void SetLZ( int2 lz, Actor flare ) { this.lz = lz; this.flare = flare; - this.waterDrop = waterDrop; droppedAt.Clear(); } @@ -79,7 +77,7 @@ namespace OpenRA.Mods.RA bool IsSuitableCell(Actor self, int2 p) { - return self.World.IsPathableCell(p, waterDrop ? UnitMovementType.Float : UnitMovementType.Wheel); + return self.traits.Get().CanEnterCell(p); } void FinishedDropping(Actor self) diff --git a/OpenRA.Mods.RA/ParatroopersPower.cs b/OpenRA.Mods.RA/ParatroopersPower.cs index 39053d7cbd..b78103bb55 100644 --- a/OpenRA.Mods.RA/ParatroopersPower.cs +++ b/OpenRA.Mods.RA/ParatroopersPower.cs @@ -74,7 +74,7 @@ namespace OpenRA.Mods.RA a.CancelActivity(); a.QueueActivity(new FlyCircle(p)); - a.traits.Get().SetLZ(p, flare, false); + a.traits.Get().SetLZ(p, flare); var cargo = a.traits.Get(); foreach (var i in items)