From 434c46058f3c329e5b93375e222119a309e2d751 Mon Sep 17 00:00:00 2001 From: tovl Date: Sun, 22 Sep 2019 23:52:11 +0200 Subject: [PATCH] Disallow units idling on service depot. --- OpenRA.Mods.Common/Activities/Move/Move.cs | 15 +++++--- .../Traits/Buildings/Building.cs | 26 +++++++++++--- OpenRA.Mods.Common/Traits/Explodes.cs | 2 +- OpenRA.Mods.Common/Traits/Mobile.cs | 34 +++++++++++++++++-- OpenRA.Mods.Common/Traits/World/Locomotor.cs | 17 +++++++++- .../D2kActorPreviewPlaceBuildingPreview.cs | 2 +- mods/cnc/rules/structures.yaml | 2 +- mods/d2k/rules/structures.yaml | 2 +- mods/ra/rules/structures.yaml | 2 +- mods/ts/rules/gdi-structures.yaml | 2 +- 10 files changed, 86 insertions(+), 18 deletions(-) diff --git a/OpenRA.Mods.Common/Activities/Move/Move.cs b/OpenRA.Mods.Common/Activities/Move/Move.cs index 53afc27fda..b115c9939a 100644 --- a/OpenRA.Mods.Common/Activities/Move/Move.cs +++ b/OpenRA.Mods.Common/Activities/Move/Move.cs @@ -146,7 +146,7 @@ namespace OpenRA.Mods.Common.Activities if (evaluateNearestMovableCell && destination.HasValue) { var movableDestination = mobile.NearestMoveableCell(destination.Value); - destination = mobile.CanEnterCell(movableDestination) ? movableDestination : (CPos?)null; + destination = mobile.CanEnterCell(movableDestination, check: BlockedByActor.Immovable) ? movableDestination : (CPos?)null; } path = EvalPath(BlockedByActor.Stationary); @@ -160,8 +160,13 @@ namespace OpenRA.Mods.Common.Activities // If the actor is inside a tunnel then we must let them move // all the way through before moving to the next activity - if (IsCanceling && self.Location.Layer != CustomMovementLayerType.Tunnel) + if (IsCanceling && self.Location.Layer != CustomMovementLayerType.Tunnel && mobile.CanStayInCell(mobile.ToCell)) + { + if (path != null) + path.Clear(); + return true; + } if (mobile.IsTraitDisabled || mobile.IsTraitPaused) return false; @@ -225,7 +230,7 @@ namespace OpenRA.Mods.Common.Activities { // Are we close enough? var cellRange = nearEnough.Length / 1024; - if (!containsTemporaryBlocker && (mobile.ToCell - destination.Value).LengthSquared <= cellRange * cellRange) + if (!containsTemporaryBlocker && (mobile.ToCell - destination.Value).LengthSquared <= cellRange * cellRange && mobile.CanStayInCell(mobile.ToCell)) { path.Clear(); return null; @@ -312,7 +317,9 @@ namespace OpenRA.Mods.Common.Activities public override void Cancel(Actor self, bool keepQueue = false) { - if (path != null) + // We need to clear the path here in order to prevent MovePart queueing new instances of itself + // when the unit is making a turn. + if (path != null && mobile.CanStayInCell(mobile.ToCell)) path.Clear(); base.Cancel(self, keepQueue); diff --git a/OpenRA.Mods.Common/Traits/Buildings/Building.cs b/OpenRA.Mods.Common/Traits/Buildings/Building.cs index 7042f96967..a986ecd9b7 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/Building.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/Building.cs @@ -23,7 +23,8 @@ namespace OpenRA.Mods.Common.Traits Empty = '_', OccupiedPassable = '=', Occupied = 'x', - OccupiedUntargetable = 'X' + OccupiedUntargetable = 'X', + OccupiedPassableTransitOnly = '+' } public class BuildingInfo : ITraitInfo, IOccupySpaceInfo, IPlaceBuildingDecorationInfo @@ -107,6 +108,9 @@ namespace OpenRA.Mods.Common.Traits foreach (var t in FootprintTiles(location, FootprintCellType.OccupiedUntargetable)) yield return t; + + foreach (var t in FootprintTiles(location, FootprintCellType.OccupiedPassableTransitOnly)) + yield return t; } public IEnumerable FrozenUnderFogTiles(CPos location) @@ -118,13 +122,16 @@ namespace OpenRA.Mods.Common.Traits yield return t; } - public IEnumerable UnpathableTiles(CPos location) + public IEnumerable OccupiedTiles(CPos location) { foreach (var t in FootprintTiles(location, FootprintCellType.Occupied)) yield return t; foreach (var t in FootprintTiles(location, FootprintCellType.OccupiedUntargetable)) yield return t; + + foreach (var t in FootprintTiles(location, FootprintCellType.OccupiedPassableTransitOnly)) + yield return t; } public IEnumerable PathableTiles(CPos location) @@ -136,6 +143,12 @@ namespace OpenRA.Mods.Common.Traits yield return t; } + public IEnumerable TransitOnlyTiles(CPos location) + { + foreach (var t in FootprintTiles(location, FootprintCellType.OccupiedPassableTransitOnly)) + yield return t; + } + public WVec CenterOffset(World w) { var off = (w.Map.CenterOfCell(new CPos(Dimensions.X, Dimensions.Y)) - w.Map.CenterOfCell(new CPos(1, 1))) / 2; @@ -225,7 +238,7 @@ namespace OpenRA.Mods.Common.Traits public IReadOnlyDictionary OccupiedCells(ActorInfo info, CPos topLeft, SubCell subCell = SubCell.Any) { - var occupied = UnpathableTiles(topLeft) + var occupied = OccupiedTiles(topLeft) .ToDictionary(c => c, c => SubCell.FullCell); return new ReadOnlyDictionary(occupied); @@ -255,6 +268,7 @@ namespace OpenRA.Mods.Common.Traits Pair[] occupiedCells; Pair[] targetableCells; + CPos[] transitOnlyCells; public CPos TopLeft { get { return topLeft; } } public WPos CenterPosition { get; private set; } @@ -266,17 +280,21 @@ namespace OpenRA.Mods.Common.Traits Info = info; influence = self.World.WorldActor.Trait(); - occupiedCells = Info.UnpathableTiles(TopLeft) + occupiedCells = Info.OccupiedTiles(TopLeft) .Select(c => Pair.New(c, SubCell.FullCell)).ToArray(); targetableCells = Info.FootprintTiles(TopLeft, FootprintCellType.Occupied) .Select(c => Pair.New(c, SubCell.FullCell)).ToArray(); + transitOnlyCells = Info.TransitOnlyTiles(TopLeft).ToArray(); + CenterPosition = init.World.Map.CenterOfCell(topLeft) + Info.CenterOffset(init.World); } public Pair[] OccupiedCells() { return occupiedCells; } + public CPos[] TransitOnlyCells() { return transitOnlyCells; } + Pair[] ITargetableCells.TargetableCells() { return targetableCells; } void INotifyAddedToWorld.AddedToWorld(Actor self) diff --git a/OpenRA.Mods.Common/Traits/Explodes.cs b/OpenRA.Mods.Common/Traits/Explodes.cs index 5f9850e3da..71d0a50e7c 100644 --- a/OpenRA.Mods.Common/Traits/Explodes.cs +++ b/OpenRA.Mods.Common/Traits/Explodes.cs @@ -118,7 +118,7 @@ namespace OpenRA.Mods.Common.Traits if (Info.Type == ExplosionType.Footprint && buildingInfo != null) { - var cells = buildingInfo.UnpathableTiles(self.Location); + var cells = buildingInfo.OccupiedTiles(self.Location); foreach (var c in cells) weapon.Impact(Target.FromPos(self.World.Map.CenterOfCell(c)), source, Enumerable.Empty()); diff --git a/OpenRA.Mods.Common/Traits/Mobile.cs b/OpenRA.Mods.Common/Traits/Mobile.cs index 6eba25a8bb..9def14deae 100644 --- a/OpenRA.Mods.Common/Traits/Mobile.cs +++ b/OpenRA.Mods.Common/Traits/Mobile.cs @@ -102,6 +102,16 @@ namespace OpenRA.Mods.Common.Traits return locomotor.CanMoveFreelyInto(self, cell, subCell, check, ignoreActor); } + public bool CanStayInCell(World world, CPos cell) + { + // PERF: Avoid repeated trait queries on the hot path + if (locomotor == null) + locomotor = world.WorldActor.TraitsImplementing() + .SingleOrDefault(l => l.Info.Name == Locomotor); + + return locomotor.CanStayInCell(cell); + } + public IReadOnlyDictionary OccupiedCells(ActorInfo info, CPos location, SubCell subCell = SubCell.Any) { return new ReadOnlyDictionary(new Dictionary() { { location, subCell } }); @@ -363,7 +373,7 @@ namespace OpenRA.Mods.Common.Traits foreach (CVec direction in CVec.Directions) { var p = ToCell + direction; - if (CanEnterCell(p)) + if (CanEnterCell(p) && CanStayInCell(p)) availCells.Add(p); else if (p != nextCell && p != ToCell) notStupidCells.Add(p); @@ -507,6 +517,11 @@ namespace OpenRA.Mods.Common.Traits return Info.CanEnterCell(self.World, self, cell, ToSubCell, ignoreActor, check); } + public bool CanStayInCell(CPos cell) + { + return Info.CanStayInCell(self.World, cell); + } + #endregion #region Local IPositionable-related @@ -666,6 +681,16 @@ namespace OpenRA.Mods.Common.Traits QueueChild(mobile.VisualMove(self, pos, self.World.Map.CenterOfSubCell(cell, subCell))); return true; } + + public override void Cancel(Actor self, bool keepQueue = false) + { + // If we are forbidden from stopping in this cell, use evaluateNearestMovableCell + // to nudge us to the nearest cell that we can stop in. + if (!mobile.CanStayInCell(cell)) + QueueChild(new Move(self, cell, WDist.Zero, null, true)); + + base.Cancel(self, keepQueue); + } } public Activity MoveToTarget(Actor self, Target target, @@ -743,11 +768,14 @@ namespace OpenRA.Mods.Common.Traits if (target.Layer != 0) target = new CPos(target.X, target.Y); - if (CanEnterCell(target)) + if (target == self.Location && CanStayInCell(target)) + return target; + + if (CanEnterCell(target, check: BlockedByActor.Immovable) && CanStayInCell(target)) return target; foreach (var tile in self.World.Map.FindTilesInAnnulus(target, minRange, maxRange)) - if (CanEnterCell(tile)) + if (CanEnterCell(tile, check: BlockedByActor.Immovable) && CanStayInCell(tile)) return tile; // Couldn't find a cell diff --git a/OpenRA.Mods.Common/Traits/World/Locomotor.cs b/OpenRA.Mods.Common/Traits/World/Locomotor.cs index cf67bbeb16..7e26b21cb3 100644 --- a/OpenRA.Mods.Common/Traits/World/Locomotor.cs +++ b/OpenRA.Mods.Common/Traits/World/Locomotor.cs @@ -27,7 +27,8 @@ namespace OpenRA.Mods.Common.Traits HasStationaryActor = 2, HasMovableActor = 4, HasCrushableActor = 8, - HasTemporaryBlocker = 16 + HasTemporaryBlocker = 16, + HasTransitOnlyActor = 32, } public static class LocomoterExts @@ -309,6 +310,11 @@ namespace OpenRA.Mods.Common.Traits return true; } + public bool CanStayInCell(CPos cell) + { + return !GetCache(cell).CellFlag.HasCellFlag(CellFlag.HasTransitOnlyActor); + } + public SubCell GetAvailableSubCell(Actor self, CPos cell, BlockedByActor check, SubCell preferredSubCell = SubCell.Any, Actor ignoreActor = null) { if (MovementCostForCell(cell) == short.MaxValue) @@ -496,6 +502,15 @@ namespace OpenRA.Mods.Common.Traits var isMovable = mobile != null && !mobile.IsTraitDisabled && !mobile.IsTraitPaused && !mobile.IsImmovable; var isMoving = isMovable && mobile.CurrentMovementTypes.HasMovementType(MovementType.Horizontal); + var building = actor.OccupiesSpace as Building; + var isTransitOnly = building != null && building.TransitOnlyCells().Contains(cell); + + if (isTransitOnly) + { + cellFlag |= CellFlag.HasTransitOnlyActor; + continue; + } + if (crushables.Any()) { cellFlag |= CellFlag.HasCrushableActor; diff --git a/OpenRA.Mods.D2k/Traits/Buildings/D2kActorPreviewPlaceBuildingPreview.cs b/OpenRA.Mods.D2k/Traits/Buildings/D2kActorPreviewPlaceBuildingPreview.cs index a8fe011e2b..e198cf02d7 100644 --- a/OpenRA.Mods.D2k/Traits/Buildings/D2kActorPreviewPlaceBuildingPreview.cs +++ b/OpenRA.Mods.D2k/Traits/Buildings/D2kActorPreviewPlaceBuildingPreview.cs @@ -58,7 +58,7 @@ namespace OpenRA.Mods.Common.Traits buildBlocked = sequences.GetSequence("overlay", "build-invalid").GetSprite(0); var buildingInfo = ai.TraitInfo(); - unpathableCells = new CachedTransform>(topLeft => buildingInfo.UnpathableTiles(topLeft).ToList()); + unpathableCells = new CachedTransform>(topLeft => buildingInfo.OccupiedTiles(topLeft).ToList()); } protected override IEnumerable RenderFootprint(WorldRenderer wr, CPos topLeft, Dictionary footprint, diff --git a/mods/cnc/rules/structures.yaml b/mods/cnc/rules/structures.yaml index fcdd384886..2341c3df80 100644 --- a/mods/cnc/rules/structures.yaml +++ b/mods/cnc/rules/structures.yaml @@ -678,7 +678,7 @@ FIX: Queue: Building.GDI, Building.Nod Description: Repairs vehicles Building: - Footprint: _=_ === _=_ + Footprint: _+_ +++ _+_ Dimensions: 3,3 Selectable: Bounds: 64,34,0,3 diff --git a/mods/d2k/rules/structures.yaml b/mods/d2k/rules/structures.yaml index 6df4130e10..c628186be8 100644 --- a/mods/d2k/rules/structures.yaml +++ b/mods/d2k/rules/structures.yaml @@ -881,7 +881,7 @@ repair_pad: Tooltip: Name: Repair Pad Building: - Footprint: === === === + Footprint: +++ +++ +++ Dimensions: 3,3 Health: HP: 30000 diff --git a/mods/ra/rules/structures.yaml b/mods/ra/rules/structures.yaml index 454a4fa7d2..4f8a6a0173 100644 --- a/mods/ra/rules/structures.yaml +++ b/mods/ra/rules/structures.yaml @@ -1989,7 +1989,7 @@ FIX: Tooltip: Name: Service Depot Building: - Footprint: _=_ === _=_ + Footprint: _+_ +++ _+_ Dimensions: 3,3 Selectable: Bounds: 68,34,0,3 diff --git a/mods/ts/rules/gdi-structures.yaml b/mods/ts/rules/gdi-structures.yaml index a1f4d1f07d..2be7a9316b 100644 --- a/mods/ts/rules/gdi-structures.yaml +++ b/mods/ts/rules/gdi-structures.yaml @@ -320,7 +320,7 @@ GADEPT: Queue: Building Description: Repairs vehicles. Building: - Footprint: === x== x== + Footprint: =+= x++ x+= Dimensions: 3,3 Selectable: Bounds: 96, 64, -6, -6