diff --git a/OpenRA.Mods.Common/Traits/Air/Aircraft.cs b/OpenRA.Mods.Common/Traits/Air/Aircraft.cs index 753bbb3fc5d3..f322ea4914d2 100644 --- a/OpenRA.Mods.Common/Traits/Air/Aircraft.cs +++ b/OpenRA.Mods.Common/Traits/Air/Aircraft.cs @@ -286,6 +286,7 @@ public WAngle GetTurnSpeed(bool isIdleTurn) public bool RequireForceMove; readonly int creationActivityDelay; + readonly bool creationByMap; readonly CPos[] creationRallyPoint; bool notify = true; @@ -312,12 +313,13 @@ public Aircraft(ActorInitializer init, AircraftInfo info) self = init.Self; var locationInit = init.GetOrDefault(); - if (locationInit != null) - SetPosition(self, locationInit.Value); - var centerPositionInit = init.GetOrDefault(); - if (centerPositionInit != null) - SetPosition(self, centerPositionInit.Value); + if (locationInit != null || centerPositionInit != null) + { + var pos = centerPositionInit?.Value ?? self.World.Map.CenterOfCell(locationInit.Value); + creationByMap = init.Contains(); + SetPosition(self, pos); + } Facing = init.GetValue(Info.InitialFacing); creationActivityDelay = init.GetValue(0); @@ -1220,8 +1222,8 @@ void IActorPreviewInitModifier.ModifyActorPreviewInit(Actor self, TypeDictionary Activity ICreationActivity.GetCreationActivity() { - if (creationRallyPoint != null || creationActivityDelay > 0) - return new AssociateWithAirfieldActivity(self, creationActivityDelay, creationRallyPoint); + if (creationRallyPoint != null || creationActivityDelay > 0 || creationByMap) + return new AssociateWithAirfieldActivity(this, creationActivityDelay, creationRallyPoint, creationByMap); return null; } @@ -1231,32 +1233,81 @@ sealed class AssociateWithAirfieldActivity : Activity readonly Aircraft aircraft; readonly int delay; readonly CPos[] rallyPoint; + readonly bool creationByMap; - public AssociateWithAirfieldActivity(Actor self, int delay, CPos[] rallyPoint) + public AssociateWithAirfieldActivity(Aircraft self, int delay, CPos[] rallyPoint, bool creationByMap) { - aircraft = self.Trait(); + aircraft = self; this.delay = delay; this.rallyPoint = rallyPoint; + this.creationByMap = creationByMap; } protected override void OnFirstRun(Actor self) { - var host = aircraft.GetActorBelow(); - if (host != null) + var cpos = self.Location; + var pos = self.CenterPosition; + bool TryDock() { - aircraft.MakeReservation(host); + var host = aircraft.GetActorBelow(); + if (host != null) + { + // Center the actor on the resupplier. + var exit = host.NearestExitOrDefault(pos); + pos = host.CenterPosition; + pos = new WPos(pos.X, pos.Y, pos.Z - self.World.Map.DistanceAboveTerrain(pos).Length); + if (exit != null) + { + pos += exit.Info.SpawnOffset; + if (exit.Info.Facing != null) + aircraft.Facing = exit.Info.Facing.Value; + } + + aircraft.AddInfluence(cpos); + aircraft.SetPosition(self, pos); + aircraft.MakeReservation(host); + + // Freshly created aircraft shouldn't block the exit, so we allow them to yield their reservation. + aircraft.AllowYieldingReservation(); + return true; + } - // Freshly created aircraft shouldn't block the exit, so we allow them to yield their reservation. - aircraft.AllowYieldingReservation(); + return false; } - if (delay > 0) - QueueChild(new Wait(delay)); + if (creationByMap) + { + if (TryDock()) + return; + + pos = new WPos(pos.X, pos.Y, pos.Z - self.World.Map.DistanceAboveTerrain(pos).Length); + + if (!aircraft.Info.TakeOffOnCreation && aircraft.CanLand(cpos)) + { + aircraft.AddInfluence(cpos); + aircraft.SetPosition(self, pos); + } + else + { + aircraft.SetPosition(self, new WPos(pos.X, pos.Y, pos.Z + aircraft.Info.CruiseAltitude.Length)); + QueueChild(new FlyIdle(self)); + } + + if (rallyPoint != null && rallyPoint.Length > 0) + foreach (var cell in rallyPoint) + QueueChild(new AttackMoveActivity(self, () => aircraft.MoveTo(cell, 1, evaluateNearestMovableCell: true, targetLineColor: Color.OrangeRed))); + } + else + { + TryDock(); + if (delay > 0) + QueueChild(new Wait(delay)); + } } public override bool Tick(Actor self) { - if (!aircraft.Info.TakeOffOnCreation || IsCanceling) + if (!aircraft.Info.TakeOffOnCreation || IsCanceling || creationByMap) return true; if (rallyPoint != null && rallyPoint.Length > 0) @@ -1270,6 +1321,21 @@ public override bool Tick(Actor self) aircraft.UnReserve(); return true; } + + public override IEnumerable GetTargets(Actor self) + { + if (ChildActivity != null) + return ChildActivity.GetTargets(self); + + return Target.None; + } + + public override IEnumerable TargetLineNodes(Actor self) + { + if (ChildActivity != null) + foreach (var n in ChildActivity.TargetLineNodes(self)) + yield return n; + } } public class AircraftMoveOrderTargeter : IOrderTargeter diff --git a/OpenRA.Mods.Common/Traits/World/SpawnMapActors.cs b/OpenRA.Mods.Common/Traits/World/SpawnMapActors.cs index c3906d266e78..7616563b76b1 100644 --- a/OpenRA.Mods.Common/Traits/World/SpawnMapActors.cs +++ b/OpenRA.Mods.Common/Traits/World/SpawnMapActors.cs @@ -44,7 +44,7 @@ public void WorldLoaded(World world, WorldRenderer wr) } actorReference.Add(new SkipMakeAnimsInit()); - actorReference.Add(new SpawnedByMapInit(kv.Key)); + actorReference.Add(new SpawnedByMapInit()); if (PreventMapSpawn(world, actorReference, preventMapSpawns)) continue; @@ -66,9 +66,13 @@ static bool PreventMapSpawn(World world, ActorReference actorReference, IEnumera } public class SkipMakeAnimsInit : RuntimeFlagInit { } - public class SpawnedByMapInit : ValueActorInit, ISuppressInitExport, ISingleInstanceInit + public class SpawnedByMapInit : ActorInit, ISuppressInitExport, ISingleInstanceInit { - public SpawnedByMapInit(string value) - : base(value) { } + protected SpawnedByMapInit(string instanceName) + : base(instanceName) { } + + public SpawnedByMapInit() { } + + public override MiniYaml Save() => null; } } diff --git a/OpenRA.Mods.Common/Traits/World/SpawnStartingUnits.cs b/OpenRA.Mods.Common/Traits/World/SpawnStartingUnits.cs index c4337113a03b..a781408cf4d3 100644 --- a/OpenRA.Mods.Common/Traits/World/SpawnStartingUnits.cs +++ b/OpenRA.Mods.Common/Traits/World/SpawnStartingUnits.cs @@ -94,6 +94,7 @@ void SpawnUnitsForPlayer(World w, Player p) new OwnerInit(p), new SkipMakeAnimsInit(), new FacingInit(facing), + new SpawnedByMapInit(), }); } @@ -123,6 +124,7 @@ void SpawnUnitsForPlayer(World w, Player p) new LocationInit(validCell), new SubCellInit(subCell), new FacingInit(facing), + new SpawnedByMapInit(), }); } } diff --git a/mods/d2k/rules/aircraft.yaml b/mods/d2k/rules/aircraft.yaml index 7a030b32149f..e871bd55c3e8 100644 --- a/mods/d2k/rules/aircraft.yaml +++ b/mods/d2k/rules/aircraft.yaml @@ -18,6 +18,7 @@ carryall.reinforce: LandableTerrainTypes: Sand, Rock, Transition, Spice, SpiceSand, Dune, Concrete Repulsable: False AirborneCondition: airborne + CanForceLand: false CanSlide: True VTOL: true IdleTurnSpeed: 4