diff --git a/OpenRA.Game/Traits/TraitsInterfaces.cs b/OpenRA.Game/Traits/TraitsInterfaces.cs index 2d59e8808c..afcada806a 100644 --- a/OpenRA.Game/Traits/TraitsInterfaces.cs +++ b/OpenRA.Game/Traits/TraitsInterfaces.cs @@ -265,7 +265,11 @@ namespace OpenRA.Traits [RequireExplicitImplementation] public interface ISelectionBar { float GetValue(); Color GetColor(); bool DisplayWhenEmpty { get; } } - public interface IPositionableInfo : ITraitInfoInterface { } + public interface IPositionableInfo : IOccupySpaceInfo + { + bool CanEnterCell(World world, Actor self, CPos cell, Actor ignoreActor = null, bool checkTransientActors = true); + } + public interface IPositionable : IOccupySpace { bool IsLeavingCell(CPos location, SubCell subCell = SubCell.Any); diff --git a/OpenRA.Mods.Common/Traits/Air/Aircraft.cs b/OpenRA.Mods.Common/Traits/Air/Aircraft.cs index a345723198..389eb3007c 100644 --- a/OpenRA.Mods.Common/Traits/Air/Aircraft.cs +++ b/OpenRA.Mods.Common/Traits/Air/Aircraft.cs @@ -21,7 +21,7 @@ using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits { - public class AircraftInfo : ITraitInfo, IPositionableInfo, IFacingInfo, IOccupySpaceInfo, IMoveInfo, ICruiseAltitudeInfo, + public class AircraftInfo : ITraitInfo, IPositionableInfo, IFacingInfo, IMoveInfo, ICruiseAltitudeInfo, UsesInit, UsesInit { public readonly WDist CruiseAltitude = new WDist(1280); @@ -94,6 +94,25 @@ namespace OpenRA.Mods.Common.Traits public IReadOnlyDictionary OccupiedCells(ActorInfo info, CPos location, SubCell subCell = SubCell.Any) { return new ReadOnlyDictionary(); } bool IOccupySpaceInfo.SharesCell { get { return false; } } + + // Used to determine if an aircraft can spawn landed + public bool CanEnterCell(World world, Actor self, CPos cell, Actor ignoreActor = null, bool checkTransientActors = true) + { + if (!world.Map.Contains(cell)) + return false; + + var type = world.Map.GetTerrainInfo(cell).Type; + if (!LandableTerrainTypes.Contains(type)) + return false; + + if (world.WorldActor.Trait().GetBuildingAt(cell) != null) + return false; + + if (!checkTransientActors) + return true; + + return !world.ActorMap.GetActorsAt(cell).Any(x => x != ignoreActor); + } } public class Aircraft : ITick, ISync, IFacing, IPositionable, IMove, IIssueOrder, IResolveOrder, IOrderVoice, IDeathActorInitModifier, diff --git a/OpenRA.Mods.Common/Traits/Crates/Crate.cs b/OpenRA.Mods.Common/Traits/Crates/Crate.cs index 596957d364..4291e450bc 100644 --- a/OpenRA.Mods.Common/Traits/Crates/Crate.cs +++ b/OpenRA.Mods.Common/Traits/Crates/Crate.cs @@ -17,7 +17,7 @@ using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits { - class CrateInfo : ITraitInfo, IPositionableInfo, IOccupySpaceInfo, Requires + class CrateInfo : ITraitInfo, IPositionableInfo, Requires { [Desc("Length of time (in seconds) until the crate gets removed automatically. " + "A value of zero disables auto-removal.")] @@ -38,6 +38,30 @@ namespace OpenRA.Mods.Common.Traits } bool IOccupySpaceInfo.SharesCell { get { return false; } } + + public bool CanEnterCell(World world, Actor self, CPos cell, Actor ignoreActor = null, bool checkTransientActors = true) + { + return GetAvailableSubCell(world, cell, ignoreActor, checkTransientActors) != SubCell.Invalid; + } + + public SubCell GetAvailableSubCell(World world, CPos cell, Actor ignoreActor = null, bool checkTransientActors = true) + { + if (!world.Map.Contains(cell)) + return SubCell.Invalid; + + var type = world.Map.GetTerrainInfo(cell).Type; + if (!TerrainTypes.Contains(type)) + return SubCell.Invalid; + + if (world.WorldActor.Trait().GetBuildingAt(cell) != null) + return SubCell.Invalid; + + if (!checkTransientActors) + return SubCell.FullCell; + + return !world.ActorMap.GetActorsAt(cell).Any(x => x != ignoreActor) + ? SubCell.FullCell : SubCell.Invalid; + } } class Crate : ITick, IPositionable, ICrushable, ISync, @@ -174,22 +198,7 @@ namespace OpenRA.Mods.Common.Traits 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.Invalid; - - var type = self.World.Map.GetTerrainInfo(cell).Type; - if (!info.TerrainTypes.Contains(type)) - return SubCell.Invalid; - - if (self.World.WorldActor.Trait().GetBuildingAt(cell) != null) - return SubCell.Invalid; - - if (!checkTransientActors) - return SubCell.FullCell; - - return !self.World.ActorMap.GetActorsAt(cell) - .Any(x => x != ignoreActor) - ? SubCell.FullCell : SubCell.Invalid; + return info.GetAvailableSubCell(self.World, cell, ignoreActor, checkTransientActors); } public bool CanEnterCell(CPos a, Actor ignoreActor = null, bool checkTransientActors = true) diff --git a/OpenRA.Mods.Common/Traits/Crates/GiveUnitCrateAction.cs b/OpenRA.Mods.Common/Traits/Crates/GiveUnitCrateAction.cs index 7c6985d5a7..5b50971f8b 100644 --- a/OpenRA.Mods.Common/Traits/Crates/GiveUnitCrateAction.cs +++ b/OpenRA.Mods.Common/Traits/Crates/GiveUnitCrateAction.cs @@ -97,11 +97,11 @@ namespace OpenRA.Mods.Common.Traits IEnumerable GetSuitableCells(CPos near, string unitName) { - var mi = self.World.Map.Rules.Actors[unitName].TraitInfo(); + var ip = self.World.Map.Rules.Actors[unitName].TraitInfo(); for (var i = -1; i < 2; i++) for (var j = -1; j < 2; j++) - if (mi.CanEnterCell(self.World, self, near + new CVec(i, j))) + if (ip.CanEnterCell(self.World, self, near + new CVec(i, j))) yield return near + new CVec(i, j); } diff --git a/OpenRA.Mods.Common/Traits/Mobile.cs b/OpenRA.Mods.Common/Traits/Mobile.cs index fba438ae69..6b344d3785 100644 --- a/OpenRA.Mods.Common/Traits/Mobile.cs +++ b/OpenRA.Mods.Common/Traits/Mobile.cs @@ -48,7 +48,7 @@ namespace OpenRA.Mods.Common.Traits } [Desc("Unit is able to move.")] - public class MobileInfo : ConditionalTraitInfo, IMoveInfo, IPositionableInfo, IOccupySpaceInfo, IFacingInfo, + public class MobileInfo : ConditionalTraitInfo, IMoveInfo, IPositionableInfo, IFacingInfo, UsesInit, UsesInit, UsesInit { [FieldLoader.LoadUsing("LoadSpeeds", true)] @@ -263,11 +263,12 @@ namespace OpenRA.Mods.Common.Traits (current, terrainInfo) => unchecked(current * 31 + terrainInfo.Cost)); } - public bool CanEnterCell(World world, Actor self, CPos cell, Actor ignoreActor = null, CellConditions check = CellConditions.All) + public bool CanEnterCell(World world, Actor self, CPos cell, Actor ignoreActor = null, bool checkTransientActors = true) { if (MovementCostForCell(world, cell) == int.MaxValue) return false; + var check = checkTransientActors ? CellConditions.All : CellConditions.BlockedByMovers; return CanMoveFreelyInto(world, self, cell, ignoreActor, check); } @@ -380,7 +381,7 @@ namespace OpenRA.Mods.Common.Traits } public class Mobile : ConditionalTrait, INotifyCreated, IIssueOrder, IResolveOrder, IOrderVoice, IPositionable, IMove, - IFacing, ISync, IDeathActorInitModifier, INotifyAddedToWorld, INotifyRemovedFromWorld, INotifyBlockingMove, IActorPreviewInitModifier + IFacing, IDeathActorInitModifier, INotifyAddedToWorld, INotifyRemovedFromWorld, INotifyBlockingMove, IActorPreviewInitModifier { const int AverageTicksBeforePathing = 5; const int SpreadTicksBeforePathing = 5; @@ -699,7 +700,7 @@ namespace OpenRA.Mods.Common.Traits public bool CanEnterCell(CPos cell, Actor ignoreActor = null, bool checkTransientActors = true) { - return Info.CanEnterCell(self.World, self, cell, ignoreActor, checkTransientActors ? CellConditions.All : CellConditions.BlockedByMovers); + return Info.CanEnterCell(self.World, self, cell, ignoreActor, checkTransientActors); } public bool CanMoveFreelyInto(CPos cell, Actor ignoreActor = null, bool checkTransientActors = true) diff --git a/OpenRA.Mods.Common/Traits/World/SpawnMPUnits.cs b/OpenRA.Mods.Common/Traits/World/SpawnMPUnits.cs index b4966061cc..8b47b08f36 100644 --- a/OpenRA.Mods.Common/Traits/World/SpawnMPUnits.cs +++ b/OpenRA.Mods.Common/Traits/World/SpawnMPUnits.cs @@ -86,8 +86,9 @@ namespace OpenRA.Mods.Common.Traits foreach (var s in unitGroup.SupportActors) { - var mi = w.Map.Rules.Actors[s.ToLowerInvariant()].TraitInfo(); - var validCells = supportSpawnCells.Where(c => mi.CanEnterCell(w, null, c)); + var actorRules = w.Map.Rules.Actors[s.ToLowerInvariant()]; + var ip = actorRules.TraitInfo(); + var validCells = supportSpawnCells.Where(c => ip.CanEnterCell(w, null, c)); if (!validCells.Any()) { Log.Write("debug", "No cells available to spawn starting unit {0} for player {1}".F(s, p)); @@ -95,7 +96,7 @@ namespace OpenRA.Mods.Common.Traits } var cell = validCells.Random(w.SharedRandom); - var subCell = mi.SharesCell ? w.ActorMap.FreeSubCell(cell) : 0; + var subCell = ip.SharesCell ? w.ActorMap.FreeSubCell(cell) : 0; w.CreateActor(s.ToLowerInvariant(), new TypeDictionary {