diff --git a/OpenRA.Game/Map/Map.cs b/OpenRA.Game/Map/Map.cs index 5cab3b4e59..7823c235a7 100644 --- a/OpenRA.Game/Map/Map.cs +++ b/OpenRA.Game/Map/Map.cs @@ -80,8 +80,9 @@ namespace OpenRA [FieldLoader.Ignore] public readonly WVec[] SubCellOffsets; public readonly SubCell DefaultSubCell; + public readonly SubCell LastSubCell; - public WVec OffsetOf(SubCell subCell) { return SubCellOffsets[(int)subCell]; } + public WVec OffsetOfSubCell(SubCell subCell) { return SubCellOffsets[(int)subCell]; } [FieldLoader.LoadUsing("LoadOptions")] public MapOptions Options; @@ -252,6 +253,7 @@ namespace OpenRA MapResources = Exts.Lazy(() => LoadResourceTiles()); TileShape = Game.modData.Manifest.TileShape; SubCellOffsets = Game.modData.Manifest.SubCellOffsets; + LastSubCell = (SubCell)(SubCellOffsets.Length - 1); DefaultSubCell = (SubCell)Game.modData.Manifest.SubCellDefaultIndex; // The Uid is calculated from the data on-disk, so @@ -496,7 +498,7 @@ namespace OpenRA return new WPos(512 * (cell.X - cell.Y + 1), 512 * (cell.X + cell.Y + 1), 0); } - public WPos CenterOf(CPos cell, SubCell subCell) + public WPos CenterOfSubCell(CPos cell, SubCell subCell) { var index = (int)subCell; if (index >= 0 && index <= SubCellOffsets.Length) diff --git a/OpenRA.Game/Traits/Target.cs b/OpenRA.Game/Traits/Target.cs index 41c5696abf..4c14a1e095 100644 --- a/OpenRA.Game/Traits/Target.cs +++ b/OpenRA.Game/Traits/Target.cs @@ -30,7 +30,7 @@ namespace OpenRA.Traits public static Target FromPos(WPos p) { return new Target { pos = p, type = TargetType.Terrain }; } public static Target FromCell(World w, CPos c, SubCell subCell = SubCell.FullCell) { - return new Target { pos = w.Map.CenterOf(c, subCell), type = TargetType.Terrain }; + return new Target { pos = w.Map.CenterOfSubCell(c, subCell), type = TargetType.Terrain }; } public static Target FromOrder(World w, Order o) diff --git a/OpenRA.Game/Traits/TraitsInterfaces.cs b/OpenRA.Game/Traits/TraitsInterfaces.cs index 3ba4c96816..0de5f917e9 100644 --- a/OpenRA.Game/Traits/TraitsInterfaces.cs +++ b/OpenRA.Game/Traits/TraitsInterfaces.cs @@ -180,9 +180,10 @@ namespace OpenRA.Traits public interface IPositionable : IOccupySpace { - bool IsLeaving(CPos location, SubCell subCell = SubCell.AnySubCell); + bool IsLeavingCell(CPos location, SubCell subCell = SubCell.AnySubCell); bool CanEnterCell(CPos location, Actor ignoreActor = null, bool checkTransientActors = true); - SubCell GetAvailableSubcell(CPos location, SubCell preferredSubCell = SubCell.AnySubCell, 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); void SetPosition(Actor self, WPos pos); void SetVisualPosition(Actor self, WPos pos); diff --git a/OpenRA.Game/Traits/World/ActorMap.cs b/OpenRA.Game/Traits/World/ActorMap.cs index 30765eb16d..59ee3c6246 100644 --- a/OpenRA.Game/Traits/World/ActorMap.cs +++ b/OpenRA.Game/Traits/World/ActorMap.cs @@ -116,7 +116,7 @@ namespace OpenRA.Traits return SubCell.InvalidSubCell; } - // NOTE: does not check transients, but checks aircraft + // NOTE: always includes transients with influence public bool AnyUnitsAt(CPos a) { return influence[a] != null; @@ -132,7 +132,7 @@ namespace OpenRA.Traits if (checkTransient) return true; var pos = i.Actor.TraitOrDefault(); - if (pos == null || !pos.IsLeaving(a, i.SubCell)) + if (pos == null || !pos.IsLeavingCell(a, i.SubCell)) return true; } diff --git a/OpenRA.Mods.RA/Activities/Leap.cs b/OpenRA.Mods.RA/Activities/Leap.cs index 14e5d378df..b9351fa187 100644 --- a/OpenRA.Mods.RA/Activities/Leap.cs +++ b/OpenRA.Mods.RA/Activities/Leap.cs @@ -41,7 +41,7 @@ namespace OpenRA.Mods.RA.Activities mobile.IsMoving = true; from = self.CenterPosition; - to = self.World.Map.CenterOf(targetMobile.fromCell, targetMobile.fromSubCell); + to = self.World.Map.CenterOfSubCell(targetMobile.fromCell, targetMobile.fromSubCell); length = Math.Max((to - from).Length / speed.Range, 1); self.Trait().Attacking(self, Target.FromActor(target)); diff --git a/OpenRA.Mods.RA/Activities/UnloadCargo.cs b/OpenRA.Mods.RA/Activities/UnloadCargo.cs index 8ade055576..8319089186 100644 --- a/OpenRA.Mods.RA/Activities/UnloadCargo.cs +++ b/OpenRA.Mods.RA/Activities/UnloadCargo.cs @@ -38,7 +38,7 @@ namespace OpenRA.Mods.RA.Activities return cargo.CurrentAdjacentCells .Shuffle(self.World.SharedRandom) - .Select(c => Pair.New(c, pos.GetAvailableSubcell(c, SubCell.AnySubCell, null))) + .Select(c => Pair.New(c, pos.GetAvailableSubCell(c))) .Cast?>() .FirstOrDefault(s => s.Value.Second != SubCell.InvalidSubCell); } diff --git a/OpenRA.Mods.RA/Air/Aircraft.cs b/OpenRA.Mods.RA/Air/Aircraft.cs index d8ee2f45be..bbd7151824 100644 --- a/OpenRA.Mods.RA/Air/Aircraft.cs +++ b/OpenRA.Mods.RA/Air/Aircraft.cs @@ -199,8 +199,9 @@ namespace OpenRA.Mods.RA.Air || info.RepairBuildings.Contains(a.Info.Name); } - public bool IsLeaving(CPos location, SubCell subCell = SubCell.AnySubCell) { return false; } // TODO: Handle landing - 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.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 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 8b25938960..5ff1071313 100644 --- a/OpenRA.Mods.RA/Air/Helicopter.cs +++ b/OpenRA.Mods.RA/Air/Helicopter.cs @@ -150,7 +150,7 @@ namespace OpenRA.Mods.RA.Air public Activity MoveIntoWorld(Actor self, CPos cell, SubCell subCell = SubCell.AnySubCell) { - return new HeliFly(self, Target.FromCell(self.World, cell)); + return new HeliFly(self, Target.FromCell(self.World, cell, subCell)); } public Activity VisualMove(Actor self, WPos fromPos, WPos toPos) diff --git a/OpenRA.Mods.RA/Crate.cs b/OpenRA.Mods.RA/Crate.cs index 37fda64013..bed857265a 100644 --- a/OpenRA.Mods.RA/Crate.cs +++ b/OpenRA.Mods.RA/Crate.cs @@ -95,10 +95,12 @@ 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 IsLeaving(CPos location, SubCell subCell = SubCell.AnySubCell) { return self.Location == location && ticks + 1 == info.Lifetime * 25; } - public SubCell GetAvailableSubcell(CPos cell, SubCell preferredSubCell = SubCell.AnySubCell, Actor ignoreActor = null, bool checkTransientActors = true) + 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) { - if (!self.World.Map.Contains(cell)) return SubCell.InvalidSubCell; + if (!self.World.Map.Contains(cell)) + return SubCell.InvalidSubCell; var type = self.World.Map.GetTerrainInfo(cell).Type; if (!info.TerrainTypes.Contains(type)) @@ -117,7 +119,7 @@ namespace OpenRA.Mods.RA public bool CanEnterCell(CPos a, Actor ignoreActor = null, bool checkTransientActors = true) { - return GetAvailableSubcell(a, SubCell.AnySubCell, ignoreActor, checkTransientActors) != SubCell.InvalidSubCell; + return GetAvailableSubCell(a, SubCell.AnySubCell, ignoreActor, checkTransientActors) != SubCell.InvalidSubCell; } public void SetPosition(Actor self, CPos cell, SubCell subCell = SubCell.AnySubCell) diff --git a/OpenRA.Mods.RA/Husk.cs b/OpenRA.Mods.RA/Husk.cs index bf6443ed9c..819ac0b122 100644 --- a/OpenRA.Mods.RA/Husk.cs +++ b/OpenRA.Mods.RA/Husk.cs @@ -53,8 +53,9 @@ namespace OpenRA.Mods.RA } public IEnumerable> OccupiedCells() { yield return Pair.New(TopLeft, SubCell.FullCell); } - public bool IsLeaving(CPos location, SubCell subCell = SubCell.AnySubCell) { return false; } - public SubCell GetAvailableSubcell(CPos cell, SubCell preferredSubCell = SubCell.AnySubCell, Actor ignoreActor = null, bool checkTransientActors = true) + 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) { if (!self.World.Map.Contains(cell)) return SubCell.InvalidSubCell; @@ -72,7 +73,7 @@ namespace OpenRA.Mods.RA public bool CanEnterCell(CPos a, Actor ignoreActor = null, bool checkTransientActors = true) { - return GetAvailableSubcell(a, SubCell.AnySubCell, ignoreActor, checkTransientActors) != SubCell.InvalidSubCell; + return GetAvailableSubCell(a, SubCell.AnySubCell, ignoreActor, checkTransientActors) != SubCell.InvalidSubCell; } public void SetPosition(Actor self, CPos cell, SubCell subCell = SubCell.AnySubCell) { SetPosition(self, self.World.Map.CenterOfCell(cell)); } diff --git a/OpenRA.Mods.RA/Move/Mobile.cs b/OpenRA.Mods.RA/Move/Mobile.cs old mode 100755 new mode 100644 index 33667ae8ba..98d50f6274 --- a/OpenRA.Mods.RA/Move/Mobile.cs +++ b/OpenRA.Mods.RA/Move/Mobile.cs @@ -199,18 +199,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(); Func checkTransient = a => { - if (a == ignoreActor) return false; + if (a == ignoreActor) + return false; // 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)) return false; + if (canIgnoreMovingAllies && self.Owner.Stances[a.Owner] == Stance.Ally && IsMovingInMyDirection(self, a)) + return false; // 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()) return true; + if (needsCellExclusively) + return true; + if (!a.HasTrait()) + return true; foreach (var crushable in a.TraitsImplementing()) if (!crushable.CrushableBy(Crushes, self.Owner)) return true; @@ -219,7 +223,7 @@ 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.InvalidSubCell : SubCell.FullCell; return world.ActorMap.FreeSubCell(cell, preferredSubCell, checkTransient); } @@ -290,7 +294,7 @@ namespace OpenRA.Mods.RA.Move if (init.Contains()) { this.__fromCell = this.__toCell = init.Get(); - SetVisualPosition(self, init.world.Map.CenterOf(fromCell, fromSubCell)); + SetVisualPosition(self, init.world.Map.CenterOfSubCell(fromCell, fromSubCell)); } this.Facing = init.Contains() ? init.Get() : info.InitialFacing; @@ -301,18 +305,32 @@ namespace OpenRA.Mods.RA.Move SetVisualPosition(self, init.Get()); } - public void SetPosition(Actor self, CPos cell, SubCell subCell = SubCell.AnySubCell) + // Returns a valid sub-cell + public SubCell GetValidSubCell(SubCell preferred = SubCell.AnySubCell) { // Try same sub-cell - if (subCell == SubCell.AnySubCell) - subCell = fromSubCell; + if (preferred == SubCell.AnySubCell) + preferred = fromSubCell; // Fix sub-cell assignment - if (Info.SharesCell != (subCell != SubCell.FullCell)) - subCell = Info.SharesCell ? self.World.Map.DefaultSubCell : SubCell.FullCell; + if (Info.SharesCell) + { + if (preferred <= SubCell.FullCell) + return self.World.Map.DefaultSubCell; + } + else + { + if (preferred != SubCell.FullCell) + return SubCell.FullCell; + } + return preferred; + } + public void SetPosition(Actor self, CPos cell, SubCell subCell = SubCell.AnySubCell) + { + subCell = GetValidSubCell(subCell); SetLocation(cell, subCell, cell, subCell); - SetVisualPosition(self, self.World.Map.CenterOf(cell, subCell)); + SetVisualPosition(self, self.World.Map.CenterOfSubCell(cell, subCell)); FinishedMoving(self); } @@ -468,13 +486,13 @@ namespace OpenRA.Mods.RA.Move } } - public bool IsLeaving(CPos location, SubCell subCell = SubCell.AnySubCell) + public bool IsLeavingCell(CPos location, SubCell subCell = SubCell.AnySubCell) { return toCell != location && __fromCell == location && (subCell == SubCell.AnySubCell || 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.AnySubCell, Actor ignoreActor = null, bool checkTransientActors = true) { return Info.GetAvailableSubCell(self.World, self, a, preferredSubCell, ignoreActor, checkTransientActors? CellConditions.All : CellConditions.None); } @@ -644,7 +662,7 @@ namespace OpenRA.Mods.RA.Move SetVisualPosition(self, pos); // Animate transition - var to = self.World.Map.CenterOf(cell, subCell); + var to = self.World.Map.CenterOfSubCell(cell, subCell); var speed = MovementSpeedForCell(self, cell); var length = speed > 0 ? (to - pos).Length / speed : 0; diff --git a/OpenRA.Mods.RA/Move/Move.cs b/OpenRA.Mods.RA/Move/Move.cs old mode 100755 new mode 100644 index 41e47a894e..80c47e8cf7 --- a/OpenRA.Mods.RA/Move/Move.cs +++ b/OpenRA.Mods.RA/Move/Move.cs @@ -60,7 +60,7 @@ namespace OpenRA.Mods.RA.Move public Move(CPos destination, SubCell subCell, WRange nearEnough) { this.getPath = (self, mobile) => self.World.WorldActor.Trait() - .FindUnitPathToRange(mobile.fromCell, subCell, self.World.Map.CenterOf(destination, subCell), nearEnough, self); + .FindUnitPathToRange(mobile.fromCell, subCell, self.World.Map.CenterOfSubCell(destination, subCell), nearEnough, self); this.destination = destination; this.nearEnough = nearEnough; } @@ -158,8 +158,8 @@ namespace OpenRA.Mods.RA.Move mobile.SetLocation(mobile.fromCell, mobile.fromSubCell, nextCell.Value.First, nextCell.Value.Second); var move = new MoveFirstHalf( this, - self.World.Map.CenterOf(mobile.fromCell, mobile.fromSubCell), - Util.BetweenCells(self.World, mobile.fromCell, mobile.toCell) + (self.World.Map.OffsetOf(mobile.fromSubCell) + self.World.Map.OffsetOf(mobile.toSubCell)) / 2, + self.World.Map.CenterOfSubCell(mobile.fromCell, mobile.fromSubCell), + Util.BetweenCells(self.World, mobile.fromCell, mobile.toCell) + (self.World.Map.OffsetOfSubCell(mobile.fromSubCell) + self.World.Map.OffsetOfSubCell(mobile.toSubCell)) / 2, mobile.Facing, mobile.Facing, 0); @@ -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.AnySubCell, ignoreBuilding); return Pair.New(nextCell, subCell); } @@ -355,15 +355,15 @@ namespace OpenRA.Mods.RA.Move protected override MovePart OnComplete(Actor self, Mobile mobile, Move parent) { - var fromSubcellOffset = self.World.Map.OffsetOf(mobile.fromSubCell); - var toSubcellOffset = self.World.Map.OffsetOf(mobile.toSubCell); + var fromSubcellOffset = self.World.Map.OffsetOfSubCell(mobile.fromSubCell); + var toSubcellOffset = self.World.Map.OffsetOfSubCell(mobile.toSubCell); var nextCell = parent.PopPath(self, mobile); if (nextCell != null) { if (IsTurn(mobile, nextCell.Value.First)) { - var nextSubcellOffset = self.World.Map.OffsetOf(nextCell.Value.Second); + var nextSubcellOffset = self.World.Map.OffsetOfSubCell(nextCell.Value.Second); var ret = new MoveFirstHalf( move, Util.BetweenCells(self.World, mobile.fromCell, mobile.toCell) + (fromSubcellOffset + toSubcellOffset) / 2, diff --git a/OpenRA.Mods.RA/Move/PathFinder.cs b/OpenRA.Mods.RA/Move/PathFinder.cs index 77e00b6b05..7ed014a125 100644 --- a/OpenRA.Mods.RA/Move/PathFinder.cs +++ b/OpenRA.Mods.RA/Move/PathFinder.cs @@ -89,7 +89,7 @@ namespace OpenRA.Mods.RA.Move var rangeSquared = range.Range*range.Range; // Correct for SubCell offset - target -= self.World.Map.OffsetOf(srcSub); + target -= self.World.Map.OffsetOfSubCell(srcSub); // Select only the tiles that are within range from the requested SubCell // This assumes that the SubCell does not change during the path traversal