Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge HeliReturnToBase into ReturnToBase #16241

Merged
merged 6 commits into from Apr 13, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion OpenRA.Mods.Common/Activities/Air/FlyCircle.cs
Expand Up @@ -29,7 +29,7 @@ public FlyCircle(Actor self, int ticks = -1, int turnSpeedOverride = -1)

public override Activity Tick(Actor self)
{
if (NextActivity != null && remainingTicks <= 0)
if (remainingTicks == 0 || (NextActivity != null && remainingTicks < 0))
return NextActivity;

// Refuse to take off if it would land immediately again.
Expand Down
2 changes: 1 addition & 1 deletion OpenRA.Mods.Common/Activities/Air/HeliAttack.cs
Expand Up @@ -83,7 +83,7 @@ public override Activity Tick(Actor self)
// If all valid weapons have depleted their ammo and Rearmable trait exists, return to RearmActor to reload and then resume the activity
if (rearmable != null && !useLastVisibleTarget && attackAircraft.Armaments.All(x => x.IsTraitPaused || !x.Weapon.IsValidAgainst(target, self.World, self)))
{
QueueChild(self, new HeliReturnToBase(self, aircraft.Info.AbortOnResupply), true);
QueueChild(self, new ReturnToBase(self, aircraft.Info.AbortOnResupply), true);
return this;
}

Expand Down
127 changes: 0 additions & 127 deletions OpenRA.Mods.Common/Activities/Air/HeliReturnToBase.cs

This file was deleted.

117 changes: 94 additions & 23 deletions OpenRA.Mods.Common/Activities/Air/ReturnToBase.cs
Expand Up @@ -27,6 +27,7 @@ public class ReturnToBase : Activity
readonly bool alwaysLand;
readonly bool abortOnResupply;
bool isCalculated;
bool resupplied;
Actor dest;
WPos w1, w2, w3;

Expand All @@ -47,21 +48,23 @@ public static Actor ChooseResupplier(Actor self, bool unreservedOnly)
return null;

return self.World.ActorsHavingTrait<Reservable>()
.Where(a => a.Owner == self.Owner
.Where(a => !a.IsDead
&& a.Owner == self.Owner
&& rearmInfo.RearmActors.Contains(a.Info.Name)
&& (!unreservedOnly || Reservable.IsAvailableFor(a, self)))
.ClosestTo(self);
}

// Calculates non-CanHover/non-VTOL approach vector and waypoints
void Calculate(Actor self)
{
if (dest == null || dest.IsDead || !Reservable.IsAvailableFor(dest, self))
dest = ChooseResupplier(self, true);

if (dest == null)
return;

var landPos = dest.CenterPosition;
var exit = dest.FirstExitOrDefault(null);
var offset = exit != null ? exit.Info.SpawnOffset : WVec.Zero;

var landPos = dest.CenterPosition + offset;
var altitude = aircraft.Info.CruiseAltitude.Length;

// Distance required for descent.
Expand Down Expand Up @@ -119,26 +122,79 @@ bool ShouldLandAtBuilding(Actor self, Actor dest)

public override Activity Tick(Actor self)
{
if (ChildActivity != null)
{
ChildActivity = ActivityUtils.RunActivity(self, ChildActivity);
if (ChildActivity != null)
return this;
}

// Refuse to take off if it would land immediately again.
// Special case: Don't kill other deploy hotkey activities.
if (aircraft.ForceLanding)
return NextActivity;

if (IsCanceling || self.IsDead)
// If a Cancel was triggered at this point, it's unlikely that previously queued child activities finished,
// so 'resupplied' needs to be set to false, else it + abortOnResupply might cause another Cancel
// that would cancel any other activities that were queued after the first Cancel was triggered.
// TODO: This is a mess, we need to somehow make the activity cancelling a bit less tricky.
if (resupplied && IsCanceling)
resupplied = false;

if (resupplied && abortOnResupply)
Cancel(self);

if (resupplied || IsCanceling || self.IsDead)
return NextActivity;

reaperrr marked this conversation as resolved.
Show resolved Hide resolved
if (dest == null || dest.IsDead || !Reservable.IsAvailableFor(dest, self))
dest = ReturnToBase.ChooseResupplier(self, true);

if (!isCalculated)
Calculate(self);

if (dest == null || dest.IsDead)
if (dest == null)
{
var nearestResupplier = ChooseResupplier(self, false);
reaperrr marked this conversation as resolved.
Show resolved Hide resolved

if (nearestResupplier != null)
return ActivityUtils.SequenceActivities(self,
new Fly(self, Target.FromActor(nearestResupplier), WDist.Zero, aircraft.Info.WaitDistanceFromResupplyBase, targetLineColor: Color.Green),
new FlyCircle(self, aircraft.Info.NumberOfTicksToVerifyAvailableAirport),
this);
{
if (aircraft.Info.CanHover)
{
var distanceFromResupplier = (nearestResupplier.CenterPosition - self.CenterPosition).HorizontalLength;
var distanceLength = aircraft.Info.WaitDistanceFromResupplyBase.Length;

// If no pad is available, move near one and wait
if (distanceFromResupplier > distanceLength)
{
var randomPosition = WVec.FromPDF(self.World.SharedRandom, 2) * distanceLength / 1024;
var target = Target.FromPos(nearestResupplier.CenterPosition + randomPosition);

QueueChild(self, new HeliFly(self, target, WDist.Zero, aircraft.Info.WaitDistanceFromResupplyBase, targetLineColor: Color.Green), true);
}

return this;
}
else
{
QueueChild(self,
new Fly(self, Target.FromActor(nearestResupplier), WDist.Zero, aircraft.Info.WaitDistanceFromResupplyBase, targetLineColor: Color.Green),
true);

QueueChild(self, new FlyCircle(self, aircraft.Info.NumberOfTicksToVerifyAvailableAirport), true);
return this;
}
}
else if (nearestResupplier == null && aircraft.Info.VTOL && aircraft.Info.LandWhenIdle)
{
// Using Queue instead of QueueChild here is intentional, as we want VTOLs with LandWhenIdle to land and stay there in this situation
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be nicer to have them return to a fallback position (such as the player spawn point or construction yard) instead of landing on the spot as that is usually the exact opposite of what you want when ordering return to base in such a situation. This is probably slightly out of scope though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree about the idea, but that's what #15873 (or a follow-up to that) will take care of, so I'd like to leave this as-is in this PR.

Cancel(self);
if (aircraft.Info.TurnToLand)
Queue(self, new Turn(self, aircraft.Info.InitialFacing));

Queue(self, new HeliLand(self, true));
return NextActivity;
}
else
{
// Prevent an infinite loop in case we'd return to the activity that called ReturnToBase in the first place. Go idle instead.
Expand All @@ -147,28 +203,43 @@ public override Activity Tick(Actor self)
}
}

List<Activity> landingProcedures = new List<Activity>();
var exit = dest.FirstExitOrDefault(null);
var offset = exit != null ? exit.Info.SpawnOffset : WVec.Zero;

var turnRadius = Fly.CalculateTurnRadius(aircraft.Info.Speed, aircraft.Info.TurnSpeed);
if (aircraft.Info.CanHover)
QueueChild(self, new HeliFly(self, Target.FromPos(dest.CenterPosition + offset)), true);
else if (aircraft.Info.VTOL)
QueueChild(self, new Fly(self, Target.FromPos(dest.CenterPosition + offset)), true);
else
{
var turnRadius = Fly.CalculateTurnRadius(aircraft.Info.Speed, aircraft.Info.TurnSpeed);

landingProcedures.Add(new Fly(self, Target.FromPos(w1), WDist.Zero, new WDist(turnRadius * 3)));
landingProcedures.Add(new Fly(self, Target.FromPos(w2)));
QueueChild(self, new Fly(self, Target.FromPos(w1), WDist.Zero, new WDist(turnRadius * 3)), true);
QueueChild(self, new Fly(self, Target.FromPos(w2)), true);

// 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)));
// Fix a problem when the airplane is sent to resupply near the airport
reaperrr marked this conversation as resolved.
Show resolved Hide resolved
QueueChild(self, new Fly(self, Target.FromPos(w3), WDist.Zero, new WDist(turnRadius / 2)), true);
}

if (ShouldLandAtBuilding(self, dest))
{
aircraft.MakeReservation(dest);

landingProcedures.Add(new Land(self, Target.FromActor(dest)));
landingProcedures.Add(new ResupplyAircraft(self));
}
if (aircraft.Info.VTOL)
{
if (aircraft.Info.TurnToDock)
QueueChild(self, new Turn(self, aircraft.Info.InitialFacing), true);

QueueChild(self, new HeliLand(self, false), true);
}
else
QueueChild(self, new Land(self, Target.FromPos(dest.CenterPosition + offset)), true);

if (!abortOnResupply)
landingProcedures.Add(NextActivity);
QueueChild(self, new ResupplyAircraft(self), true);
resupplied = true;
}

return ActivityUtils.SequenceActivities(self, landingProcedures.ToArray());
return this;
}
}
}
1 change: 0 additions & 1 deletion OpenRA.Mods.Common/OpenRA.Mods.Common.csproj
Expand Up @@ -75,7 +75,6 @@
<Compile Include="Activities\Air\HeliFly.cs" />
<Compile Include="Activities\Air\HeliFlyCircle.cs" />
<Compile Include="Activities\Air\HeliLand.cs" />
<Compile Include="Activities\Air\HeliReturnToBase.cs" />
<Compile Include="Activities\Air\Land.cs" />
<Compile Include="Activities\Air\ResupplyAircraft.cs" />
<Compile Include="Activities\Air\ReturnToBase.cs" />
Expand Down
Expand Up @@ -41,10 +41,7 @@ public void Move(CPos cell)
[Desc("Return to the base, which is either the destination given, or an auto-selected one otherwise.")]
public void ReturnToBase(Actor destination = null)
{
if (!aircraftInfo.CanHover)
Self.QueueActivity(new ReturnToBase(Self, false, destination));
else
Self.QueueActivity(new HeliReturnToBase(Self, false, destination));
Self.QueueActivity(new ReturnToBase(Self, false, destination));
}

[ScriptActorPropertyActivity]
Expand Down
16 changes: 8 additions & 8 deletions OpenRA.Mods.Common/Traits/Air/Aircraft.cs
Expand Up @@ -844,10 +844,7 @@ public void ResolveOrder(Actor self, Order order)
if (Reservable.IsAvailableFor(targetActor, self))
self.SetTargetLine(Target.FromActor(targetActor), Color.Green);

if (!Info.CanHover && !Info.VTOL)
self.QueueActivity(order.Queued, new ReturnToBase(self, Info.AbortOnResupply, targetActor));
else
self.QueueActivity(order.Queued, new HeliReturnToBase(self, Info.AbortOnResupply, targetActor));
self.QueueActivity(order.Queued, new ReturnToBase(self, Info.AbortOnResupply, targetActor));
}
else if (order.OrderString == "Stop")
{
Expand All @@ -862,13 +859,16 @@ public void ResolveOrder(Actor self, Order order)
}
else if (order.OrderString == "ReturnToBase" && rearmable != null && rearmable.Info.RearmActors.Any())
{
// Don't restart activity every time deploy hotkey is triggered
if (self.CurrentActivity is ReturnToBase || GetActorBelow() != null)
return;

if (!order.Queued)
UnReserve();

if (!Info.CanHover)
self.QueueActivity(order.Queued, new ReturnToBase(self, Info.AbortOnResupply, null, false));
else
self.QueueActivity(order.Queued, new HeliReturnToBase(self, Info.AbortOnResupply, null, false));
// Aircraft with TakeOffOnResupply would immediately take off again, so there's no point in forcing them to land
// on a resupplier. For aircraft without it, it makes more sense to land than to idle above a free resupplier, though.
self.QueueActivity(order.Queued, new ReturnToBase(self, Info.AbortOnResupply, null, !Info.TakeOffOnResupply));
}
}

Expand Down