diff --git a/OpenRA.Game/Traits/TraitsInterfaces.cs b/OpenRA.Game/Traits/TraitsInterfaces.cs index 0de5f917e9..594acfa0e6 100644 --- a/OpenRA.Game/Traits/TraitsInterfaces.cs +++ b/OpenRA.Game/Traits/TraitsInterfaces.cs @@ -180,11 +180,11 @@ namespace OpenRA.Traits public interface IPositionable : IOccupySpace { - bool IsLeavingCell(CPos location, SubCell subCell = SubCell.AnySubCell); + bool IsLeavingCell(CPos location, SubCell subCell = SubCell.Any); bool CanEnterCell(CPos location, Actor ignoreActor = null, bool checkTransientActors = true); - SubCell GetValidSubCell(SubCell preferred = SubCell.AnySubCell); - SubCell GetAvailableSubCell(CPos location, SubCell preferredSubCell = SubCell.AnySubCell, Actor ignoreActor = null, bool checkTransientActors = true); - void SetPosition(Actor self, CPos cell, SubCell subCell = SubCell.AnySubCell); + SubCell GetValidSubCell(SubCell preferred = SubCell.Any); + SubCell GetAvailableSubCell(CPos location, SubCell preferredSubCell = SubCell.Any, Actor ignoreActor = null, bool checkTransientActors = true); + void SetPosition(Actor self, CPos cell, SubCell subCell = SubCell.Any); void SetPosition(Actor self, WPos pos); void SetVisualPosition(Actor self, WPos pos); } @@ -197,7 +197,7 @@ namespace OpenRA.Traits Activity MoveWithinRange(Target target, WRange range); Activity MoveWithinRange(Target target, WRange minRange, WRange maxRange); Activity MoveFollow(Actor self, Target target, WRange minRange, WRange maxRange); - Activity MoveIntoWorld(Actor self, CPos cell, SubCell subCell = SubCell.AnySubCell); + Activity MoveIntoWorld(Actor self, CPos cell, SubCell subCell = SubCell.Any); Activity VisualMove(Actor self, WPos fromPos, WPos toPos); CPos NearestMoveableCell(CPos target); bool IsMoving { get; set; } diff --git a/OpenRA.Game/Traits/World/ActorMap.cs b/OpenRA.Game/Traits/World/ActorMap.cs index 59ee3c6246..022f9341d0 100644 --- a/OpenRA.Game/Traits/World/ActorMap.cs +++ b/OpenRA.Game/Traits/World/ActorMap.cs @@ -14,7 +14,7 @@ using System.Linq; namespace OpenRA.Traits { - public enum SubCell { InvalidSubCell = int.MinValue, AnySubCell = int.MinValue / 2, FullCell = 0, FirstSubCell = 1 } + public enum SubCell { Invalid = int.MinValue, Any = int.MinValue / 2, FullCell = 0, First = 1 } public class ActorMapInfo : ITraitInfo { @@ -85,35 +85,35 @@ namespace OpenRA.Traits public bool HasFreeSubCell(CPos a, bool checkTransient = true) { - return FreeSubCell(a, SubCell.AnySubCell, checkTransient) != SubCell.InvalidSubCell; + return FreeSubCell(a, SubCell.Any, checkTransient) != SubCell.Invalid; } - public SubCell FreeSubCell(CPos a, SubCell preferredSubCell = SubCell.AnySubCell, bool checkTransient = true) + public SubCell FreeSubCell(CPos a, SubCell preferredSubCell = SubCell.Any, bool checkTransient = true) { - if (preferredSubCell > SubCell.AnySubCell && !AnyUnitsAt(a, preferredSubCell, checkTransient)) + if (preferredSubCell > SubCell.Any && !AnyUnitsAt(a, preferredSubCell, checkTransient)) return preferredSubCell; if (!AnyUnitsAt(a)) return map.DefaultSubCell; - for (var i = (int)SubCell.FirstSubCell; i < map.SubCellOffsets.Length; i++) + for (var i = (int)SubCell.First; i < map.SubCellOffsets.Length; i++) if (i != (int)preferredSubCell && !AnyUnitsAt(a, (SubCell)i, checkTransient)) return (SubCell)i; - return SubCell.InvalidSubCell; + return SubCell.Invalid; } public SubCell FreeSubCell(CPos a, SubCell preferredSubCell, Func checkIfBlocker) { - if (preferredSubCell > SubCell.AnySubCell && !AnyUnitsAt(a, preferredSubCell, checkIfBlocker)) + if (preferredSubCell > SubCell.Any && !AnyUnitsAt(a, preferredSubCell, checkIfBlocker)) return preferredSubCell; if (!AnyUnitsAt(a)) return map.DefaultSubCell; - for (var i = (int)SubCell.FirstSubCell; i < map.SubCellOffsets.Length; i++) + for (var i = (int)SubCell.First; i < map.SubCellOffsets.Length; i++) if (i != (int)preferredSubCell && !AnyUnitsAt(a, (SubCell)i, checkIfBlocker)) return (SubCell)i; - return SubCell.InvalidSubCell; + return SubCell.Invalid; } // NOTE: always includes transients with influence @@ -125,7 +125,7 @@ namespace OpenRA.Traits // NOTE: can not check aircraft public bool AnyUnitsAt(CPos a, SubCell sub, bool checkTransient = true) { - bool always = sub == SubCell.FullCell || sub == SubCell.AnySubCell; + bool always = sub == SubCell.FullCell || sub == SubCell.Any; for (var i = influence[a]; i != null; i = i.Next) if (always || i.SubCell == sub || i.SubCell == SubCell.FullCell) { @@ -142,7 +142,7 @@ namespace OpenRA.Traits // NOTE: can not check aircraft public bool AnyUnitsAt(CPos a, SubCell sub, Func withCondition) { - bool always = sub == SubCell.FullCell || sub == SubCell.AnySubCell; + bool always = sub == SubCell.FullCell || sub == SubCell.Any; for (var i = influence[a]; i != null; i = i.Next) if (always || i.SubCell == sub || i.SubCell == SubCell.FullCell) if (withCondition(i.Actor)) diff --git a/OpenRA.Mods.RA/Activities/UnloadCargo.cs b/OpenRA.Mods.RA/Activities/UnloadCargo.cs index 8319089186..74a1f41031 100644 --- a/OpenRA.Mods.RA/Activities/UnloadCargo.cs +++ b/OpenRA.Mods.RA/Activities/UnloadCargo.cs @@ -40,7 +40,7 @@ namespace OpenRA.Mods.RA.Activities .Shuffle(self.World.SharedRandom) .Select(c => Pair.New(c, pos.GetAvailableSubCell(c))) .Cast?>() - .FirstOrDefault(s => s.Value.Second != SubCell.InvalidSubCell); + .FirstOrDefault(s => s.Value.Second != SubCell.Invalid); } IEnumerable BlockedExitCells(Actor passenger) diff --git a/OpenRA.Mods.RA/Air/Aircraft.cs b/OpenRA.Mods.RA/Air/Aircraft.cs index bbd7151824..7ac89f57d4 100644 --- a/OpenRA.Mods.RA/Air/Aircraft.cs +++ b/OpenRA.Mods.RA/Air/Aircraft.cs @@ -170,7 +170,7 @@ namespace OpenRA.Mods.RA.Air } // Changes position, but not altitude - public void SetPosition(Actor self, CPos cell, SubCell subCell = SubCell.AnySubCell) + public void SetPosition(Actor self, CPos cell, SubCell subCell = SubCell.Any) { SetPosition(self, self.World.Map.CenterOfCell(cell) + new WVec(0, 0, CenterPosition.Z)); } @@ -199,9 +199,9 @@ namespace OpenRA.Mods.RA.Air || info.RepairBuildings.Contains(a.Info.Name); } - public bool IsLeavingCell(CPos location, SubCell subCell = SubCell.AnySubCell) { return false; } // TODO: Handle landing - public SubCell GetValidSubCell(SubCell preferred) { return SubCell.InvalidSubCell; } - public SubCell GetAvailableSubCell(CPos a, SubCell preferredSubCell = SubCell.AnySubCell, Actor ignoreActor = null, bool checkTransientActors = true) { return SubCell.InvalidSubCell; } // Does not use any subcell + public bool IsLeavingCell(CPos location, SubCell subCell = SubCell.Any) { return false; } // TODO: Handle landing + public SubCell GetValidSubCell(SubCell preferred) { return SubCell.Invalid; } + public SubCell GetAvailableSubCell(CPos a, SubCell preferredSubCell = SubCell.Any, Actor ignoreActor = null, bool checkTransientActors = true) { return SubCell.Invalid; } // Does not use any subcell public bool CanEnterCell(CPos cell, Actor ignoreActor = null, bool checkTransientActors = true) { return true; } public int MovementSpeed diff --git a/OpenRA.Mods.RA/Air/Helicopter.cs b/OpenRA.Mods.RA/Air/Helicopter.cs index 5ff1071313..52cfb744ef 100644 --- a/OpenRA.Mods.RA/Air/Helicopter.cs +++ b/OpenRA.Mods.RA/Air/Helicopter.cs @@ -148,7 +148,7 @@ namespace OpenRA.Mods.RA.Air public Activity MoveFollow(Actor self, Target target, WRange minRange, WRange maxRange) { return new Follow(self, target, minRange, maxRange); } public CPos NearestMoveableCell(CPos cell) { return cell; } - public Activity MoveIntoWorld(Actor self, CPos cell, SubCell subCell = SubCell.AnySubCell) + public Activity MoveIntoWorld(Actor self, CPos cell, SubCell subCell = SubCell.Any) { return new HeliFly(self, Target.FromCell(self.World, cell, subCell)); } diff --git a/OpenRA.Mods.RA/Air/Plane.cs b/OpenRA.Mods.RA/Air/Plane.cs index 69fe8467c9..e2292f5e27 100644 --- a/OpenRA.Mods.RA/Air/Plane.cs +++ b/OpenRA.Mods.RA/Air/Plane.cs @@ -127,7 +127,7 @@ namespace OpenRA.Mods.RA.Air public Activity MoveFollow(Actor self, Target target, WRange minRange, WRange maxRange) { return new FlyFollow(self, target, minRange, maxRange); } public CPos NearestMoveableCell(CPos cell) { return cell; } - public Activity MoveIntoWorld(Actor self, CPos cell, SubCell subCell = SubCell.AnySubCell) { return new Fly(self, Target.FromCell(self.World, cell)); } + public Activity MoveIntoWorld(Actor self, CPos cell, SubCell subCell = SubCell.Any) { return new Fly(self, Target.FromCell(self.World, cell)); } public Activity VisualMove(Actor self, WPos fromPos, WPos toPos) { return Util.SequenceActivities(new CallFunc(() => SetVisualPosition(self, fromPos)), new Fly(self, Target.FromPos(toPos))); } } } diff --git a/OpenRA.Mods.RA/Crate.cs b/OpenRA.Mods.RA/Crate.cs index bed857265a..47108a6776 100644 --- a/OpenRA.Mods.RA/Crate.cs +++ b/OpenRA.Mods.RA/Crate.cs @@ -95,34 +95,34 @@ namespace OpenRA.Mods.RA public void SetPosition(Actor self, WPos pos) { SetPosition(self, self.World.Map.CellContaining(pos)); } public void SetVisualPosition(Actor self, WPos pos) { SetPosition(self, self.World.Map.CellContaining(pos)); } - public bool IsLeavingCell(CPos location, SubCell subCell = SubCell.AnySubCell) { return self.Location == location && ticks + 1 == info.Lifetime * 25; } - public SubCell GetValidSubCell(SubCell preferred = SubCell.AnySubCell) { return SubCell.FullCell; } - public SubCell GetAvailableSubCell(CPos cell, SubCell preferredSubCell = SubCell.AnySubCell, Actor ignoreActor = null, bool checkTransientActors = true) + public bool IsLeavingCell(CPos location, SubCell subCell = SubCell.Any) { return self.Location == location && ticks + 1 == info.Lifetime * 25; } + public SubCell GetValidSubCell(SubCell preferred = SubCell.Any) { return SubCell.FullCell; } + public SubCell GetAvailableSubCell(CPos cell, SubCell preferredSubCell = SubCell.Any, Actor ignoreActor = null, bool checkTransientActors = true) { if (!self.World.Map.Contains(cell)) - return SubCell.InvalidSubCell; + return SubCell.Invalid; var type = self.World.Map.GetTerrainInfo(cell).Type; if (!info.TerrainTypes.Contains(type)) - return SubCell.InvalidSubCell; + return SubCell.Invalid; if (self.World.WorldActor.Trait().GetBuildingAt(cell) != null) - return SubCell.InvalidSubCell; + return SubCell.Invalid; if (!checkTransientActors) return SubCell.FullCell; return !self.World.ActorMap.GetUnitsAt(cell) .Where(x => x != ignoreActor) - .Any() ? SubCell.FullCell : SubCell.InvalidSubCell; + .Any() ? SubCell.FullCell : SubCell.Invalid; } public bool CanEnterCell(CPos a, Actor ignoreActor = null, bool checkTransientActors = true) { - return GetAvailableSubCell(a, SubCell.AnySubCell, ignoreActor, checkTransientActors) != SubCell.InvalidSubCell; + return GetAvailableSubCell(a, SubCell.Any, ignoreActor, checkTransientActors) != SubCell.Invalid; } - public void SetPosition(Actor self, CPos cell, SubCell subCell = SubCell.AnySubCell) + public void SetPosition(Actor self, CPos cell, SubCell subCell = SubCell.Any) { self.World.ActorMap.RemoveInfluence(self, this); Location = cell; diff --git a/OpenRA.Mods.RA/Husk.cs b/OpenRA.Mods.RA/Husk.cs index 819ac0b122..4e43874f7e 100644 --- a/OpenRA.Mods.RA/Husk.cs +++ b/OpenRA.Mods.RA/Husk.cs @@ -53,30 +53,30 @@ namespace OpenRA.Mods.RA } public IEnumerable> OccupiedCells() { yield return Pair.New(TopLeft, SubCell.FullCell); } - public bool IsLeavingCell(CPos location, SubCell subCell = SubCell.AnySubCell) { return false; } - public SubCell GetValidSubCell(SubCell preferred = SubCell.AnySubCell) { return SubCell.FullCell; } - public SubCell GetAvailableSubCell(CPos cell, SubCell preferredSubCell = SubCell.AnySubCell, Actor ignoreActor = null, bool checkTransientActors = true) + public bool IsLeavingCell(CPos location, SubCell subCell = SubCell.Any) { return false; } + public SubCell GetValidSubCell(SubCell preferred = SubCell.Any) { return SubCell.FullCell; } + public SubCell GetAvailableSubCell(CPos cell, SubCell preferredSubCell = SubCell.Any, Actor ignoreActor = null, bool checkTransientActors = true) { if (!self.World.Map.Contains(cell)) - return SubCell.InvalidSubCell; + return SubCell.Invalid; if (!info.AllowedTerrain.Contains(self.World.Map.GetTerrainInfo(cell).Type)) - return SubCell.InvalidSubCell; + return SubCell.Invalid; if (!checkTransientActors) return SubCell.FullCell; return !self.World.ActorMap.GetUnitsAt(cell) .Where(x => x != ignoreActor) - .Any() ? SubCell.FullCell : SubCell.InvalidSubCell; + .Any() ? SubCell.FullCell : SubCell.Invalid; } public bool CanEnterCell(CPos a, Actor ignoreActor = null, bool checkTransientActors = true) { - return GetAvailableSubCell(a, SubCell.AnySubCell, ignoreActor, checkTransientActors) != SubCell.InvalidSubCell; + return GetAvailableSubCell(a, SubCell.Any, ignoreActor, checkTransientActors) != SubCell.Invalid; } - public void SetPosition(Actor self, CPos cell, SubCell subCell = SubCell.AnySubCell) { 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)); } public void SetVisualPosition(Actor self, WPos pos) { diff --git a/OpenRA.Mods.RA/Move/Mobile.cs b/OpenRA.Mods.RA/Move/Mobile.cs index 98d50f6274..149885ce6d 100644 --- a/OpenRA.Mods.RA/Move/Mobile.cs +++ b/OpenRA.Mods.RA/Move/Mobile.cs @@ -171,18 +171,22 @@ namespace OpenRA.Mods.RA.Move if (check.HasFlag(CellConditions.TransientActors)) { var canIgnoreMovingAllies = self != null && !check.HasFlag(CellConditions.BlockedByMovers); - var needsCellExclusively = self == null || Crushes == null; + var needsCellExclusively = self == null || Crushes == null || !Crushes.Any(); foreach(var a in world.ActorMap.GetUnitsAt(cell)) { - if (a == ignoreActor) continue; + if (a == ignoreActor) + continue; // Neutral/enemy units are blockers. Allied units that are moving are not blockers. if (canIgnoreMovingAllies && self.Owner.Stances[a.Owner] == Stance.Ally && IsMovingInMyDirection(self, a)) continue; // Non-sharable unit can enter a cell with shareable units only if it can crush all of them. - if (needsCellExclusively) return false; - if (!a.HasTrait()) return false; - foreach (var crushable in a.TraitsImplementing()) + if (needsCellExclusively) + return false; + var crushables = a.TraitsImplementing(); + if (!crushables.Any()) + return false; + foreach (var crushable in crushables) if (!crushable.CrushableBy(Crushes, self.Owner)) return false; } @@ -191,10 +195,10 @@ namespace OpenRA.Mods.RA.Move return true; } - public SubCell GetAvailableSubCell(World world, Actor self, CPos cell, SubCell preferredSubCell = SubCell.AnySubCell, Actor ignoreActor = null, CellConditions check = CellConditions.All) + public SubCell GetAvailableSubCell(World world, Actor self, CPos cell, SubCell preferredSubCell = SubCell.Any, Actor ignoreActor = null, CellConditions check = CellConditions.All) { if (MovementCostForCell(world, cell) == int.MaxValue) - return SubCell.InvalidSubCell; + return SubCell.Invalid; if (check.HasFlag(CellConditions.TransientActors)) { @@ -213,9 +217,10 @@ namespace OpenRA.Mods.RA.Move // Non-sharable unit can enter a cell with shareable units only if it can crush all of them. if (needsCellExclusively) return true; - if (!a.HasTrait()) + var crushables = a.TraitsImplementing(); + if (!crushables.Any()) return true; - foreach (var crushable in a.TraitsImplementing()) + foreach (var crushable in crushables) if (!crushable.CrushableBy(Crushes, self.Owner)) return true; @@ -223,13 +228,13 @@ namespace OpenRA.Mods.RA.Move }; if (!SharesCell) - return world.ActorMap.AnyUnitsAt(cell, SubCell.FullCell, checkTransient) ? SubCell.InvalidSubCell : SubCell.FullCell; + return world.ActorMap.AnyUnitsAt(cell, SubCell.FullCell, checkTransient) ? SubCell.Invalid : SubCell.FullCell; return world.ActorMap.FreeSubCell(cell, preferredSubCell, checkTransient); } if (!SharesCell) - return world.ActorMap.AnyUnitsAt(cell, SubCell.FullCell)? SubCell.InvalidSubCell : SubCell.FullCell; + return world.ActorMap.AnyUnitsAt(cell, SubCell.FullCell)? SubCell.Invalid : SubCell.FullCell; return world.ActorMap.FreeSubCell(cell, preferredSubCell); } @@ -306,10 +311,10 @@ namespace OpenRA.Mods.RA.Move } // Returns a valid sub-cell - public SubCell GetValidSubCell(SubCell preferred = SubCell.AnySubCell) + public SubCell GetValidSubCell(SubCell preferred = SubCell.Any) { // Try same sub-cell - if (preferred == SubCell.AnySubCell) + if (preferred == SubCell.Any) preferred = fromSubCell; // Fix sub-cell assignment @@ -326,7 +331,7 @@ namespace OpenRA.Mods.RA.Move return preferred; } - public void SetPosition(Actor self, CPos cell, SubCell subCell = SubCell.AnySubCell) + public void SetPosition(Actor self, CPos cell, SubCell subCell = SubCell.Any) { subCell = GetValidSubCell(subCell); SetLocation(cell, subCell, cell, subCell); @@ -486,13 +491,13 @@ namespace OpenRA.Mods.RA.Move } } - public bool IsLeavingCell(CPos location, SubCell subCell = SubCell.AnySubCell) + public bool IsLeavingCell(CPos location, SubCell subCell = SubCell.Any) { return toCell != location && __fromCell == location - && (subCell == SubCell.AnySubCell || 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.AnySubCell, Actor ignoreActor = null, bool checkTransientActors = true) + public SubCell GetAvailableSubCell(CPos a, SubCell preferredSubCell = SubCell.Any, Actor ignoreActor = null, bool checkTransientActors = true) { return Info.GetAvailableSubCell(self.World, self, a, preferredSubCell, ignoreActor, checkTransientActors? CellConditions.All : CellConditions.None); } @@ -504,24 +509,18 @@ namespace OpenRA.Mods.RA.Move public void EnteringCell(Actor self) { - var crushable = self.World.ActorMap.GetUnitsAt(toCell).Where(a => a != self && a.HasTrait()); - foreach (var a in crushable) - { - var crushActions = a.TraitsImplementing().Where(b => b.CrushableBy(Info.Crushes, self.Owner)); - foreach (var b in crushActions) - b.WarnCrush(self); - } + var crushables = self.World.ActorMap.GetUnitsAt(toCell).Where(a => a != self) + .SelectMany(a => a.TraitsImplementing().Where(b => b.CrushableBy(Info.Crushes, self.Owner))); + foreach (var crushable in crushables) + crushable.WarnCrush(self); } public void FinishedMoving(Actor self) { - var crushable = self.World.ActorMap.GetUnitsAt(toCell).Where(a => a != self && a.HasTrait()); - foreach (var a in crushable) - { - var crushActions = a.TraitsImplementing().Where(b => b.CrushableBy(Info.Crushes, self.Owner)); - foreach (var b in crushActions) - b.OnCrush(self); - } + var crushables = self.World.ActorMap.GetUnitsAt(toCell).Where(a => a != self) + .SelectMany(a => a.TraitsImplementing().Where(c => c.CrushableBy(Info.Crushes, self.Owner))); + foreach (var crushable in crushables) + crushable.OnCrush(self); } public int MovementSpeedForCell(Actor self, CPos cell) @@ -646,15 +645,15 @@ namespace OpenRA.Mods.RA.Move Nudge(self, blocking, true); } - public Activity MoveIntoWorld(Actor self, CPos cell, SubCell subCell = SubCell.AnySubCell) + public Activity MoveIntoWorld(Actor self, CPos cell, SubCell subCell = SubCell.Any) { var pos = self.CenterPosition; - if (subCell == SubCell.AnySubCell) + if (subCell == SubCell.Any) subCell = self.World.ActorMap.FreeSubCell(cell, subCell); // TODO: solve/reduce cell is full problem - if (subCell == SubCell.InvalidSubCell) + if (subCell == SubCell.Invalid) subCell = self.World.Map.DefaultSubCell; // Reserve the exit cell diff --git a/OpenRA.Mods.RA/Move/Move.cs b/OpenRA.Mods.RA/Move/Move.cs index 80c47e8cf7..6df61ce348 100644 --- a/OpenRA.Mods.RA/Move/Move.cs +++ b/OpenRA.Mods.RA/Move/Move.cs @@ -245,7 +245,7 @@ namespace OpenRA.Mods.RA.Move hasWaited = false; path.RemoveAt(path.Count - 1); - var subCell = mobile.GetAvailableSubCell(nextCell, SubCell.AnySubCell, ignoreBuilding); + var subCell = mobile.GetAvailableSubCell(nextCell, SubCell.Any, ignoreBuilding); return Pair.New(nextCell, subCell); }