diff --git a/AUTHORS b/AUTHORS index 25f50feceb..01924c9da8 100644 --- a/AUTHORS +++ b/AUTHORS @@ -105,6 +105,7 @@ Also thanks to: * Paolo Chiodi (paolochiodi) * Paul Dovydaitis (pdovy) * Pavlos Touboulidis (pav) + * Pedro Ferreira Ramos (bateramos) * Pizzaoverhead * Piët Delport (pjdelport) * Psydev diff --git a/OpenRA.Mods.Common/Activities/Air/FlyAttack.cs b/OpenRA.Mods.Common/Activities/Air/FlyAttack.cs index 9a49f8c4b2..cf2b263cb7 100644 --- a/OpenRA.Mods.Common/Activities/Air/FlyAttack.cs +++ b/OpenRA.Mods.Common/Activities/Air/FlyAttack.cs @@ -41,7 +41,7 @@ namespace OpenRA.Mods.Common.Activities // Move to the next activity only if all ammo pools are depleted and none reload automatically // TODO: This should check whether there is ammo left that is actually suitable for the target if (ammoPools.All(x => !x.Info.SelfReloads && !x.HasAmmo())) - return NextActivity; + return ActivityUtils.SequenceActivities(new ReturnToBase(self), NextActivity); if (attackPlane != null) attackPlane.DoAttack(self, target); diff --git a/OpenRA.Mods.Common/Activities/Air/FlyCircleTimed.cs b/OpenRA.Mods.Common/Activities/Air/FlyCircleTimed.cs new file mode 100644 index 0000000000..2ab061ed9d --- /dev/null +++ b/OpenRA.Mods.Common/Activities/Air/FlyCircleTimed.cs @@ -0,0 +1,36 @@ +#region Copyright & License Information +/* + * Copyright 2007-2016 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. For more + * information, see COPYING. + */ +#endregion + +using OpenRA.Activities; +using OpenRA.Mods.Common.Traits; + +namespace OpenRA.Mods.Common.Activities +{ + public class FlyCircleTimed : FlyCircle + { + int remainingTicks; + + public FlyCircleTimed(int ticks, Actor self) : base(self) + { + remainingTicks = ticks; + } + + public override Activity Tick(Actor self) + { + if (IsCanceled || remainingTicks-- == 0) + return NextActivity; + + base.Tick(self); + + return this; + } + } +} diff --git a/OpenRA.Mods.Common/Activities/Air/HeliReturnToBase.cs b/OpenRA.Mods.Common/Activities/Air/HeliReturnToBase.cs index 32cf0454c3..471352b9c1 100644 --- a/OpenRA.Mods.Common/Activities/Air/HeliReturnToBase.cs +++ b/OpenRA.Mods.Common/Activities/Air/HeliReturnToBase.cs @@ -44,13 +44,28 @@ namespace OpenRA.Mods.Common.Activities { var rearmBuildings = heli.Info.RearmBuildings; var nearestHpad = self.World.ActorsHavingTrait() - .Where(a => a.Owner == self.Owner && rearmBuildings.Contains(a.Info.Name)) - .ClosestTo(self); + .Where(a => a.Owner == self.Owner && rearmBuildings.Contains(a.Info.Name)) + .ClosestTo(self); if (nearestHpad == null) return ActivityUtils.SequenceActivities(new Turn(self, initialFacing), new HeliLand(self, true), NextActivity); else - return ActivityUtils.SequenceActivities(new HeliFly(self, Target.FromActor(nearestHpad))); + { + var distanceFromHelipad = (nearestHpad.CenterPosition - self.CenterPosition).HorizontalLength; + var distanceLength = heli.Info.WaitDistanceFromResupplyBase.Length; + + // If no pad is available, move near one and wait + if (distanceFromHelipad > distanceLength) + { + var randomPosition = WVec.FromPDF(self.World.SharedRandom, 2) * distanceLength / 1024; + + var target = Target.FromPos(nearestHpad.CenterPosition + randomPosition); + + return ActivityUtils.SequenceActivities(new HeliFly(self, target, WDist.Zero, heli.Info.WaitDistanceFromResupplyBase), this); + } + + return this; + } } heli.MakeReservation(dest); @@ -62,7 +77,8 @@ namespace OpenRA.Mods.Common.Activities new HeliFly(self, Target.FromPos(dest.CenterPosition + offset)), new Turn(self, initialFacing), new HeliLand(self, false), - new ResupplyAircraft(self)); + new ResupplyAircraft(self), + NextActivity); } } } diff --git a/OpenRA.Mods.Common/Activities/Air/ResupplyAircraft.cs b/OpenRA.Mods.Common/Activities/Air/ResupplyAircraft.cs index 8864e54b06..adcb5949ee 100644 --- a/OpenRA.Mods.Common/Activities/Air/ResupplyAircraft.cs +++ b/OpenRA.Mods.Common/Activities/Air/ResupplyAircraft.cs @@ -32,8 +32,17 @@ namespace OpenRA.Mods.Common.Activities if (host == null) return NextActivity; + if (aircraft.IsPlane) + return ActivityUtils.SequenceActivities( + aircraft.GetResupplyActivities(host) + .Append(new CallFunc(() => aircraft.UnReserve())) + .Append(new WaitFor(() => NextActivity != null || Reservable.IsReserved(host))) + .Append(new TakeOff(self)) + .Append(NextActivity).ToArray()); + + // If is helicopter move away as soon as the resupply ends return ActivityUtils.SequenceActivities( - aircraft.GetResupplyActivities(host).Append(NextActivity).ToArray()); + aircraft.GetResupplyActivities(host).Append(new TakeOff(self)).Append(NextActivity).ToArray()); } } } diff --git a/OpenRA.Mods.Common/Activities/Air/ReturnToBase.cs b/OpenRA.Mods.Common/Activities/Air/ReturnToBase.cs index 25c0fc6091..61ff36d59d 100644 --- a/OpenRA.Mods.Common/Activities/Air/ReturnToBase.cs +++ b/OpenRA.Mods.Common/Activities/Air/ReturnToBase.cs @@ -106,11 +106,13 @@ namespace OpenRA.Mods.Common.Activities { var nearestAfld = ChooseAirfield(self, false); - self.CancelActivity(); if (nearestAfld != null) - return ActivityUtils.SequenceActivities(new Fly(self, Target.FromActor(nearestAfld)), new FlyCircle(self)); + return ActivityUtils.SequenceActivities( + new Fly(self, Target.FromActor(nearestAfld), WDist.Zero, plane.Info.WaitDistanceFromResupplyBase), + new FlyCircleTimed(plane.Info.NumberOfTicksToVerifyAvailableAirport, self), + this); else - return new FlyCircle(self); + return NextActivity; } List landingProcedures = new List(); @@ -123,6 +125,7 @@ namespace OpenRA.Mods.Common.Activities // Fix a problem when the airplane is send to resupply near the airport landingProcedures.Add(new Fly(self, Target.FromPos(w3), WDist.Zero, new WDist(turnRadius / 2))); landingProcedures.Add(new Land(self, Target.FromActor(dest))); + landingProcedures.Add(new ResupplyAircraft(self)); landingProcedures.Add(NextActivity); return ActivityUtils.SequenceActivities(landingProcedures.ToArray()); diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj index 44835881df..aa47b17679 100644 --- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj +++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj @@ -774,6 +774,7 @@ + diff --git a/OpenRA.Mods.Common/Traits/Air/Aircraft.cs b/OpenRA.Mods.Common/Traits/Air/Aircraft.cs index c9ac2ad0bc..9e4f0311b8 100644 --- a/OpenRA.Mods.Common/Traits/Air/Aircraft.cs +++ b/OpenRA.Mods.Common/Traits/Air/Aircraft.cs @@ -83,6 +83,12 @@ namespace OpenRA.Mods.Common.Traits [Desc("Sound to play when the actor is landing.")] public readonly string LandingSound = null; + [Desc("The distance of the resupply base that the aircraft will wait for its turn.")] + public readonly WDist WaitDistanceFromResupplyBase = new WDist(3072); + + [Desc("The number of ticks that a airplane will wait to make a new search for an available airport.")] + public readonly int NumberOfTicksToVerifyAvailableAirport = 150; + public IReadOnlyDictionary OccupiedCells(ActorInfo info, CPos location, SubCell subCell = SubCell.Any) { return new ReadOnlyDictionary(); } bool IOccupySpaceInfo.SharesCell { get { return false; } } } @@ -98,6 +104,7 @@ namespace OpenRA.Mods.Common.Traits UpgradeManager um; IDisposable reservation; + Actor reservedActor; IEnumerable speedModifiers; [Sync] public int Facing { get; set; } @@ -181,6 +188,13 @@ namespace OpenRA.Mods.Common.Traits if (!Info.Repulsable) return WVec.Zero; + if (reservation != null) + { + var distanceFromReservationActor = (reservedActor.CenterPosition - self.CenterPosition).HorizontalLength; + if (distanceFromReservationActor < Info.WaitDistanceFromResupplyBase.Length) + return WVec.Zero; + } + // Repulsion only applies when we're flying! var altitude = self.World.Map.DistanceAboveTerrain(CenterPosition).Length; if (altitude != Info.CruiseAltitude.Length) @@ -261,7 +275,10 @@ namespace OpenRA.Mods.Common.Traits UnReserve(); var reservable = target.TraitOrDefault(); if (reservable != null) + { reservation = reservable.Reserve(target, self, this); + reservedActor = target; + } } public void UnReserve() @@ -271,6 +288,8 @@ namespace OpenRA.Mods.Common.Traits reservation.Dispose(); reservation = null; + reservedActor = null; + if (self.World.Map.DistanceAboveTerrain(CenterPosition).Length <= Info.LandAltitude.Length) self.QueueActivity(new TakeOff(self)); }