diff --git a/OpenRA.Mods.Common/Activities/Air/FlyAttack.cs b/OpenRA.Mods.Common/Activities/Air/FlyAttack.cs index b3bcd89735..fd2ee98560 100644 --- a/OpenRA.Mods.Common/Activities/Air/FlyAttack.cs +++ b/OpenRA.Mods.Common/Activities/Air/FlyAttack.cs @@ -150,14 +150,29 @@ namespace OpenRA.Mods.Common.Activities return this; } - if (self.World.Map.DistanceAboveTerrain(self.CenterPosition).Length < aircraft.Info.MinAirborneAltitude) + var delta = attackAircraft.GetTargetPosition(pos, target) - pos; + var desiredFacing = delta.HorizontalLengthSquared != 0 ? delta.Yaw.Facing : aircraft.Facing; + var isAirborne = self.World.Map.DistanceAboveTerrain(pos).Length >= aircraft.Info.MinAirborneAltitude; + + if (!isAirborne) QueueChild(self, new TakeOff(self), true); - if (attackAircraft != null && target.IsInRange(self.CenterPosition, attackAircraft.GetMinimumRange())) - QueueChild(self, new FlyTimed(ticksUntilTurn, self), true); + if (!aircraft.Info.CanHover) + { + if (target.IsInRange(pos, attackAircraft.GetMinimumRange())) + QueueChild(self, new FlyTimed(ticksUntilTurn, self), true); - QueueChild(self, new Fly(self, target, checkTarget.CenterPosition, Color.Red), true); - QueueChild(self, new FlyTimed(ticksUntilTurn, self)); + QueueChild(self, new Fly(self, target, target.CenterPosition, Color.Red), true); + QueueChild(self, new FlyTimed(ticksUntilTurn, self)); + } + else + { + var minimumRange = attackAircraft.GetMinimumRangeVersusTarget(target); + if (!target.IsInRange(pos, lastVisibleMaximumRange) || target.IsInRange(pos, minimumRange)) + QueueChild(self, new Fly(self, target, minimumRange, lastVisibleMaximumRange, target.CenterPosition, Color.Red), true); + else if (isAirborne) // Don't use 'else' to avoid conflict with TakeOff + Fly.VerticalTakeOffOrLandTick(self, aircraft, desiredFacing, aircraft.Info.CruiseAltitude); + } return this; } diff --git a/OpenRA.Mods.Common/Activities/Air/HeliAttack.cs b/OpenRA.Mods.Common/Activities/Air/HeliAttack.cs deleted file mode 100644 index a324653b54..0000000000 --- a/OpenRA.Mods.Common/Activities/Air/HeliAttack.cs +++ /dev/null @@ -1,189 +0,0 @@ -#region Copyright & License Information -/* - * Copyright 2007-2019 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 System.Linq; -using OpenRA.Activities; -using OpenRA.Mods.Common.Traits; -using OpenRA.Primitives; -using OpenRA.Traits; - -namespace OpenRA.Mods.Common.Activities -{ - public class HeliAttack : Activity, IActivityNotifyStanceChanged - { - readonly Aircraft aircraft; - readonly AttackAircraft attackAircraft; - readonly Rearmable rearmable; - readonly bool forceAttack; - - Target target; - Target lastVisibleTarget; - WDist lastVisibleMaximumRange; - BitSet lastVisibleTargetTypes; - Player lastVisibleOwner; - bool useLastVisibleTarget; - bool hasTicked; - - public HeliAttack(Actor self, Target target, bool forceAttack) - { - this.target = target; - this.forceAttack = forceAttack; - aircraft = self.Trait(); - attackAircraft = self.Trait(); - rearmable = self.TraitOrDefault(); - - // The target may become hidden between the initial order request and the first tick (e.g. if queued) - // Moving to any position (even if quite stale) is still better than immediately giving up - if ((target.Type == TargetType.Actor && target.Actor.CanBeViewedByPlayer(self.Owner)) - || target.Type == TargetType.FrozenActor || target.Type == TargetType.Terrain) - { - lastVisibleTarget = Target.FromPos(target.CenterPosition); - lastVisibleMaximumRange = attackAircraft.GetMaximumRangeVersusTarget(target); - - if (target.Type == TargetType.Actor) - { - lastVisibleOwner = target.Actor.Owner; - lastVisibleTargetTypes = target.Actor.GetEnabledTargetTypes(); - } - else if (target.Type == TargetType.FrozenActor) - { - lastVisibleOwner = target.FrozenActor.Owner; - lastVisibleTargetTypes = target.FrozenActor.TargetTypes; - } - } - } - - 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. - if (aircraft.ForceLanding) - Cancel(self); - - if (IsCanceling) - { - // Cancel the requested target, but keep firing on it while in range - if (attackAircraft.Info.PersistentTargeting) - { - attackAircraft.OpportunityTarget = attackAircraft.RequestedTarget; - attackAircraft.OpportunityForceAttack = attackAircraft.RequestedForceAttack; - attackAircraft.OpportunityTargetIsPersistentTarget = true; - } - - attackAircraft.RequestedTarget = Target.Invalid; - return NextActivity; - } - - // Check that AttackFollow hasn't cancelled the target by modifying attack.Target - // Having both this and AttackFollow modify that field is a horrible hack. - if (hasTicked && attackAircraft.RequestedTarget.Type == TargetType.Invalid) - return NextActivity; - - if (attackAircraft.IsTraitPaused) - return this; - - bool targetIsHiddenActor; - attackAircraft.RequestedTarget = target = target.Recalculate(self.Owner, out targetIsHiddenActor); - attackAircraft.RequestedTargetLastTick = self.World.WorldTick; - hasTicked = true; - - if (!targetIsHiddenActor && target.Type == TargetType.Actor) - { - lastVisibleTarget = Target.FromTargetPositions(target); - lastVisibleMaximumRange = attackAircraft.GetMaximumRangeVersusTarget(target); - lastVisibleOwner = target.Actor.Owner; - lastVisibleTargetTypes = target.Actor.GetEnabledTargetTypes(); - } - - var oldUseLastVisibleTarget = useLastVisibleTarget; - useLastVisibleTarget = targetIsHiddenActor || !target.IsValidFor(self); - - // Update target lines if required - if (useLastVisibleTarget != oldUseLastVisibleTarget) - self.SetTargetLine(useLastVisibleTarget ? lastVisibleTarget : target, Color.Red, false); - - // Target is hidden or dead, and we don't have a fallback position to move towards - if (useLastVisibleTarget && !lastVisibleTarget.IsValidFor(self)) - { - attackAircraft.RequestedTarget = Target.Invalid; - return NextActivity; - } - - // 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 ReturnToBase(self, aircraft.Info.AbortOnResupply), true); - return this; - } - - var pos = self.CenterPosition; - var checkTarget = useLastVisibleTarget ? lastVisibleTarget : target; - - // Update facing and altitude - var delta = attackAircraft.GetTargetPosition(pos, checkTarget) - pos; - var desiredFacing = delta.HorizontalLengthSquared != 0 ? delta.Yaw.Facing : aircraft.Facing; - aircraft.Facing = Util.TickFacing(aircraft.Facing, desiredFacing, aircraft.TurnSpeed); - if (Fly.VerticalTakeOffOrLandTick(self, aircraft, aircraft.Facing, aircraft.Info.CruiseAltitude)) - return this; - - var move = aircraft.FlyStep(desiredFacing); - - // We don't know where the target actually is, so move to where we last saw it - if (useLastVisibleTarget) - { - // We've reached the assumed position but it is not there - give up - if (checkTarget.IsInRange(pos, lastVisibleMaximumRange)) - { - attackAircraft.RequestedTarget = Target.Invalid; - return NextActivity; - } - - // Fly towards the last known position - Fly.FlyTick(self, aircraft, aircraft.Facing, aircraft.Info.CruiseAltitude, move); - return this; - } - - // Fly towards the target if outside max range and backwards if within min range - if (!target.IsInRange(pos, attackAircraft.GetMaximumRangeVersusTarget(target))) - Fly.FlyTick(self, aircraft, aircraft.Facing, aircraft.Info.CruiseAltitude, move); - else if (target.IsInRange(pos, attackAircraft.GetMinimumRangeVersusTarget(target))) - { - // Facing 0 doesn't work with the following position change - var facing = 1; - if (desiredFacing != 0) - facing = desiredFacing; - else if (aircraft.Facing != 0) - facing = aircraft.Facing; - - move = aircraft.FlyStep(-facing); - Fly.FlyTick(self, aircraft, aircraft.Facing, aircraft.Info.CruiseAltitude, move); - } - - return this; - } - - void IActivityNotifyStanceChanged.StanceChanged(Actor self, AutoTarget autoTarget, UnitStance oldStance, UnitStance newStance) - { - // Cancel non-forced targets when switching to a more restrictive stance if they are no longer valid for auto-targeting - if (newStance > oldStance || forceAttack) - return; - - if (!autoTarget.HasValidTargetPriority(self, lastVisibleOwner, lastVisibleTargetTypes)) - attackAircraft.RequestedTarget = Target.Invalid; - } - } -} diff --git a/OpenRA.Mods.Common/Traits/Air/AttackAircraft.cs b/OpenRA.Mods.Common/Traits/Air/AttackAircraft.cs index f8527dc9f7..36c3f003c4 100644 --- a/OpenRA.Mods.Common/Traits/Air/AttackAircraft.cs +++ b/OpenRA.Mods.Common/Traits/Air/AttackAircraft.cs @@ -37,9 +37,6 @@ namespace OpenRA.Mods.Common.Traits public override Activity GetAttackActivity(Actor self, Target newTarget, bool allowMove, bool forceAttack) { - if (aircraftInfo.CanHover) - return new HeliAttack(self, newTarget, forceAttack); - return new FlyAttack(self, newTarget, forceAttack); }