From f3b85a1db12f40c958ed7c7e210a6e41e5c49f08 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sun, 19 May 2019 16:29:59 +0100 Subject: [PATCH] Drop targets when switching to a more restrictive stance. --- OpenRA.Mods.Cnc/Activities/LeapAttack.cs | 31 +++++++++++- OpenRA.Mods.Cnc/Traits/Attack/AttackLeap.cs | 2 +- OpenRA.Mods.Cnc/Traits/Attack/AttackTesla.cs | 28 +++++++++-- .../Activities/Air/FlyAttack.cs | 31 +++++++++++- .../Activities/Air/HeliAttack.cs | 31 +++++++++++- OpenRA.Mods.Common/Activities/Attack.cs | 28 ++++++++++- .../Traits/Air/AttackAircraft.cs | 4 +- .../Traits/Attack/AttackFollow.cs | 49 ++++++++++++++++++- .../Traits/Attack/AttackOmni.cs | 28 +++++++++-- OpenRA.Mods.Common/Traits/AutoTarget.cs | 19 +++++++ 10 files changed, 233 insertions(+), 18 deletions(-) diff --git a/OpenRA.Mods.Cnc/Activities/LeapAttack.cs b/OpenRA.Mods.Cnc/Activities/LeapAttack.cs index a2f2ea7aaf64..761f413acfdf 100644 --- a/OpenRA.Mods.Cnc/Activities/LeapAttack.cs +++ b/OpenRA.Mods.Cnc/Activities/LeapAttack.cs @@ -20,25 +20,29 @@ namespace OpenRA.Mods.Cnc.Activities { - public class LeapAttack : Activity + public class LeapAttack : Activity, IActivityNotifyStanceChanged { readonly AttackLeapInfo info; readonly AttackLeap attack; readonly Mobile mobile; readonly bool allowMovement; + readonly bool forceAttack; Target target; Target lastVisibleTarget; bool useLastVisibleTarget; WDist lastVisibleMinRange; WDist lastVisibleMaxRange; + BitSet lastVisibleTargetTypes; + Player lastVisibleOwner; - public LeapAttack(Actor self, Target target, bool allowMovement, AttackLeap attack, AttackLeapInfo info) + public LeapAttack(Actor self, Target target, bool allowMovement, bool forceAttack, AttackLeap attack, AttackLeapInfo info) { this.target = target; this.info = info; this.attack = attack; this.allowMovement = allowMovement; + this.forceAttack = forceAttack; mobile = self.Trait(); // The target may become hidden between the initial order request and the first tick (e.g. if queued) @@ -49,6 +53,17 @@ public LeapAttack(Actor self, Target target, bool allowMovement, AttackLeap atta lastVisibleTarget = Target.FromPos(target.CenterPosition); lastVisibleMinRange = attack.GetMinimumRangeVersusTarget(target); lastVisibleMaxRange = attack.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; + } } } @@ -76,6 +91,8 @@ public override Activity Tick(Actor self) lastVisibleTarget = Target.FromTargetPositions(target); lastVisibleMinRange = attack.GetMinimumRangeVersusTarget(target); lastVisibleMaxRange = attack.GetMaximumRangeVersusTarget(target); + lastVisibleOwner = target.Actor.Owner; + lastVisibleTargetTypes = target.Actor.GetEnabledTargetTypes(); } var oldUseLastVisibleTarget = useLastVisibleTarget; @@ -141,5 +158,15 @@ protected override void OnLastRun(Actor self) { attack.IsAiming = false; } + + 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)) + target = Target.Invalid; + } } } diff --git a/OpenRA.Mods.Cnc/Traits/Attack/AttackLeap.cs b/OpenRA.Mods.Cnc/Traits/Attack/AttackLeap.cs index 2b2896b94909..d25310353383 100644 --- a/OpenRA.Mods.Cnc/Traits/Attack/AttackLeap.cs +++ b/OpenRA.Mods.Cnc/Traits/Attack/AttackLeap.cs @@ -73,7 +73,7 @@ public void RevokeLeapCondition(Actor self) public override Activity GetAttackActivity(Actor self, Target newTarget, bool allowMove, bool forceAttack) { - return new LeapAttack(self, newTarget, allowMove, this, info); + return new LeapAttack(self, newTarget, allowMove, forceAttack, this, info); } } } diff --git a/OpenRA.Mods.Cnc/Traits/Attack/AttackTesla.cs b/OpenRA.Mods.Cnc/Traits/Attack/AttackTesla.cs index d269c79ab601..cebdf0d6ac58 100644 --- a/OpenRA.Mods.Cnc/Traits/Attack/AttackTesla.cs +++ b/OpenRA.Mods.Cnc/Traits/Attack/AttackTesla.cs @@ -75,18 +75,20 @@ void INotifyAttack.Attacking(Actor self, Target target, Armament a, Barrel barre public override Activity GetAttackActivity(Actor self, Target newTarget, bool allowMove, bool forceAttack) { - return new ChargeAttack(this, newTarget); + return new ChargeAttack(this, newTarget, forceAttack); } - class ChargeAttack : Activity + class ChargeAttack : Activity, IActivityNotifyStanceChanged { readonly AttackTesla attack; readonly Target target; + readonly bool forceAttack; - public ChargeAttack(AttackTesla attack, Target target) + public ChargeAttack(AttackTesla attack, Target target, bool forceAttack) { this.attack = attack; this.target = target; + this.forceAttack = forceAttack; } public override Activity Tick(Actor self) @@ -114,6 +116,26 @@ public override Activity Tick(Actor self) QueueChild(self, new ChargeFire(attack, target)); 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 (target.Type == TargetType.Actor) + { + var a = target.Actor; + if (!autoTarget.HasValidTargetPriority(self, a.Owner, a.GetEnabledTargetTypes())) + Cancel(self, true); + } + else if (target.Type == TargetType.FrozenActor) + { + var fa = target.FrozenActor; + if (!autoTarget.HasValidTargetPriority(self, fa.Owner, fa.TargetTypes)) + Cancel(self, true); + } + } } class ChargeFire : Activity diff --git a/OpenRA.Mods.Common/Activities/Air/FlyAttack.cs b/OpenRA.Mods.Common/Activities/Air/FlyAttack.cs index 59432b8f7408..b3bcd89735c4 100644 --- a/OpenRA.Mods.Common/Activities/Air/FlyAttack.cs +++ b/OpenRA.Mods.Common/Activities/Air/FlyAttack.cs @@ -17,22 +17,26 @@ namespace OpenRA.Mods.Common.Activities { - public class FlyAttack : Activity + public class FlyAttack : Activity, IActivityNotifyStanceChanged { readonly Aircraft aircraft; readonly AttackAircraft attackAircraft; readonly Rearmable rearmable; + readonly bool forceAttack; readonly int ticksUntilTurn; Target target; Target lastVisibleTarget; WDist lastVisibleMaximumRange; + BitSet lastVisibleTargetTypes; + Player lastVisibleOwner; bool useLastVisibleTarget; bool hasTicked; - public FlyAttack(Actor self, Target target) + public FlyAttack(Actor self, Target target, bool forceAttack) { this.target = target; + this.forceAttack = forceAttack; aircraft = self.Trait(); attackAircraft = self.Trait(); rearmable = self.TraitOrDefault(); @@ -45,6 +49,17 @@ public FlyAttack(Actor self, Target target) { 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; + } } } @@ -92,6 +107,8 @@ public override Activity Tick(Actor self) { lastVisibleTarget = Target.FromTargetPositions(target); lastVisibleMaximumRange = attackAircraft.GetMaximumRangeVersusTarget(target); + lastVisibleOwner = target.Actor.Owner; + lastVisibleTargetTypes = target.Actor.GetEnabledTargetTypes(); } var oldUseLastVisibleTarget = useLastVisibleTarget; @@ -144,5 +161,15 @@ public override Activity Tick(Actor self) 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/Activities/Air/HeliAttack.cs b/OpenRA.Mods.Common/Activities/Air/HeliAttack.cs index c4f941f967b3..2371828af256 100644 --- a/OpenRA.Mods.Common/Activities/Air/HeliAttack.cs +++ b/OpenRA.Mods.Common/Activities/Air/HeliAttack.cs @@ -17,21 +17,25 @@ namespace OpenRA.Mods.Common.Activities { - public class HeliAttack : Activity + 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) + public HeliAttack(Actor self, Target target, bool forceAttack) { this.target = target; + this.forceAttack = forceAttack; aircraft = self.Trait(); attackAircraft = self.Trait(); rearmable = self.TraitOrDefault(); @@ -43,6 +47,17 @@ public HeliAttack(Actor self, Target target) { 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; + } } } @@ -90,6 +105,8 @@ public override Activity Tick(Actor self) { lastVisibleTarget = Target.FromTargetPositions(target); lastVisibleMaximumRange = attackAircraft.GetMaximumRangeVersusTarget(target); + lastVisibleOwner = target.Actor.Owner; + lastVisibleTargetTypes = target.Actor.GetEnabledTargetTypes(); } var oldUseLastVisibleTarget = useLastVisibleTarget; @@ -156,5 +173,15 @@ public override Activity Tick(Actor self) 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/Activities/Attack.cs b/OpenRA.Mods.Common/Activities/Attack.cs index f934b4ee8611..de2a3f518245 100644 --- a/OpenRA.Mods.Common/Activities/Attack.cs +++ b/OpenRA.Mods.Common/Activities/Attack.cs @@ -20,7 +20,7 @@ namespace OpenRA.Mods.Common.Activities { /* non-turreted attack */ - public class Attack : Activity + public class Attack : Activity, IActivityNotifyStanceChanged { [Flags] protected enum AttackStatus { UnableToAttack, NeedsToTurn, NeedsToMove, Attacking } @@ -35,6 +35,8 @@ protected enum AttackStatus { UnableToAttack, NeedsToTurn, NeedsToMove, Attackin protected Target target; Target lastVisibleTarget; WDist lastVisibleMaximumRange; + BitSet lastVisibleTargetTypes; + Player lastVisibleOwner; bool useLastVisibleTarget; bool wasMovingWithinRange; @@ -62,6 +64,17 @@ public Attack(Actor self, Target target, bool allowMovement, bool forceAttack) lastVisibleTarget = Target.FromPos(target.CenterPosition); lastVisibleMaximumRange = attackTraits.Where(x => !x.IsTraitDisabled) .Min(x => x.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; + } } } @@ -88,6 +101,9 @@ public override Activity Tick(Actor self) lastVisibleTarget = Target.FromTargetPositions(target); lastVisibleMaximumRange = attackTraits.Where(x => !x.IsTraitDisabled) .Min(x => x.GetMaximumRangeVersusTarget(target)); + + lastVisibleOwner = target.Actor.Owner; + lastVisibleTargetTypes = target.Actor.GetEnabledTargetTypes(); } var oldUseLastVisibleTarget = useLastVisibleTarget; @@ -211,5 +227,15 @@ protected virtual void DoAttack(Actor self, AttackFrontal attack, IEnumerable oldStance || forceAttack) + return; + + if (!autoTarget.HasValidTargetPriority(self, lastVisibleOwner, lastVisibleTargetTypes)) + target = Target.Invalid; + } } } diff --git a/OpenRA.Mods.Common/Traits/Air/AttackAircraft.cs b/OpenRA.Mods.Common/Traits/Air/AttackAircraft.cs index a6df9da8f826..f8527dc9f7be 100644 --- a/OpenRA.Mods.Common/Traits/Air/AttackAircraft.cs +++ b/OpenRA.Mods.Common/Traits/Air/AttackAircraft.cs @@ -38,9 +38,9 @@ public AttackAircraft(Actor self, AttackAircraftInfo info) public override Activity GetAttackActivity(Actor self, Target newTarget, bool allowMove, bool forceAttack) { if (aircraftInfo.CanHover) - return new HeliAttack(self, newTarget); + return new HeliAttack(self, newTarget, forceAttack); - return new FlyAttack(self, newTarget); + return new FlyAttack(self, newTarget, forceAttack); } protected override bool CanAttack(Actor self, Target target) diff --git a/OpenRA.Mods.Common/Traits/Attack/AttackFollow.cs b/OpenRA.Mods.Common/Traits/Attack/AttackFollow.cs index 3604ad2b7910..ad218d9b44a1 100644 --- a/OpenRA.Mods.Common/Traits/Attack/AttackFollow.cs +++ b/OpenRA.Mods.Common/Traits/Attack/AttackFollow.cs @@ -29,7 +29,7 @@ public class AttackFollowInfo : AttackBaseInfo public override object Create(ActorInitializer init) { return new AttackFollow(init.Self, this); } } - public class AttackFollow : AttackBase, INotifyOwnerChanged, IDisableAutoTarget + public class AttackFollow : AttackBase, INotifyOwnerChanged, IDisableAutoTarget, INotifyStanceChanged { public new readonly AttackFollowInfo Info; public Target RequestedTarget; @@ -161,7 +161,27 @@ bool IDisableAutoTarget.DisableAutoTarget(Actor self) (OpportunityTargetIsPersistentTarget && OpportunityTarget.Type != TargetType.Invalid); } - class AttackActivity : Activity + void INotifyStanceChanged.StanceChanged(Actor self, AutoTarget autoTarget, UnitStance oldStance, UnitStance newStance) + { + // Cancel opportunity targets when switching to a more restrictive stance if they are no longer valid for auto-targeting + if (newStance > oldStance || OpportunityForceAttack) + return; + + if (OpportunityTarget.Type == TargetType.Actor) + { + var a = OpportunityTarget.Actor; + if (!autoTarget.HasValidTargetPriority(self, a.Owner, a.GetEnabledTargetTypes())) + OpportunityTarget = Target.Invalid; + } + else if (OpportunityTarget.Type == TargetType.FrozenActor) + { + var fa = OpportunityTarget.FrozenActor; + if (!autoTarget.HasValidTargetPriority(self, fa.Owner, fa.TargetTypes)) + OpportunityTarget = Target.Invalid; + } + } + + class AttackActivity : Activity, IActivityNotifyStanceChanged { readonly AttackFollow attack; readonly RevealsShroud[] revealsShroud; @@ -173,6 +193,8 @@ class AttackActivity : Activity bool useLastVisibleTarget; WDist lastVisibleMaximumRange; WDist lastVisibleMinimumRange; + BitSet lastVisibleTargetTypes; + Player lastVisibleOwner; bool wasMovingWithinRange; bool hasTicked; @@ -193,6 +215,17 @@ public AttackActivity(Actor self, Target target, bool allowMove, bool forceAttac lastVisibleTarget = Target.FromPos(target.CenterPosition); lastVisibleMaximumRange = attack.GetMaximumRangeVersusTarget(target); lastVisibleMinimumRange = attack.GetMinimumRangeVersusTarget(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; + } } } @@ -238,6 +271,8 @@ public override Activity Tick(Actor self) lastVisibleTarget = Target.FromTargetPositions(target); lastVisibleMaximumRange = attack.GetMaximumRangeVersusTarget(target); lastVisibleMinimumRange = attack.GetMinimumRange(); + lastVisibleOwner = target.Actor.Owner; + lastVisibleTargetTypes = target.Actor.GetEnabledTargetTypes(); // Try and sit at least one cell away from the min or max ranges to give some leeway if the target starts moving. if (move != null && target.Actor.Info.HasTraitInfo()) @@ -312,6 +347,16 @@ public override Activity Tick(Actor self) QueueChild(self, move.MoveWithinRange(target, minRange, maxRange, checkTarget.CenterPosition, Color.Red), true); 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)) + attack.RequestedTarget = Target.Invalid; + } } } } diff --git a/OpenRA.Mods.Common/Traits/Attack/AttackOmni.cs b/OpenRA.Mods.Common/Traits/Attack/AttackOmni.cs index 238cfca4050b..236e22648a02 100644 --- a/OpenRA.Mods.Common/Traits/Attack/AttackOmni.cs +++ b/OpenRA.Mods.Common/Traits/Attack/AttackOmni.cs @@ -27,21 +27,23 @@ public AttackOmni(Actor self, AttackOmniInfo info) public override Activity GetAttackActivity(Actor self, Target newTarget, bool allowMove, bool forceAttack) { - return new SetTarget(this, newTarget, allowMove); + return new SetTarget(this, newTarget, allowMove, forceAttack); } // Some 3rd-party mods rely on this being public - public class SetTarget : Activity + public class SetTarget : Activity, IActivityNotifyStanceChanged { readonly AttackOmni attack; readonly bool allowMove; + readonly bool forceAttack; Target target; - public SetTarget(AttackOmni attack, Target target, bool allowMove) + public SetTarget(AttackOmni attack, Target target, bool allowMove, bool forceAttack) { this.target = target; this.attack = attack; this.allowMove = allowMove; + this.forceAttack = forceAttack; } public override Activity Tick(Actor self) @@ -55,6 +57,26 @@ public override Activity Tick(Actor self) attack.DoAttack(self, target); 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 (target.Type == TargetType.Actor) + { + var a = target.Actor; + if (!autoTarget.HasValidTargetPriority(self, a.Owner, a.GetEnabledTargetTypes())) + target = Target.Invalid; + } + else if (target.Type == TargetType.FrozenActor) + { + var fa = target.FrozenActor; + if (!autoTarget.HasValidTargetPriority(self, fa.Owner, fa.TargetTypes)) + target = Target.Invalid; + } + } } } } diff --git a/OpenRA.Mods.Common/Traits/AutoTarget.cs b/OpenRA.Mods.Common/Traits/AutoTarget.cs index 0d3a07aa718d..0373724b0cdd 100644 --- a/OpenRA.Mods.Common/Traits/AutoTarget.cs +++ b/OpenRA.Mods.Common/Traits/AutoTarget.cs @@ -313,6 +313,25 @@ void Attack(Actor self, Target target, bool allowMove) ab.AttackTarget(target, false, allowMove); } + public bool HasValidTargetPriority(Actor self, Player owner, BitSet targetTypes) + { + if (Stance <= UnitStance.ReturnFire) + return false; + + return activeTargetPriorities.Any(ati => + { + // Incompatible stances + if (!ati.ValidStances.HasStance(self.Owner.Stances[owner])) + return false; + + // Incompatible target types + if (!ati.ValidTargets.Overlaps(targetTypes) || ati.InvalidTargets.Overlaps(targetTypes)) + return false; + + return true; + }); + } + Target ChooseTarget(Actor self, AttackBase ab, Stance attackStances, WDist scanRange, bool allowMove, bool allowTurn) { var chosenTarget = Target.Invalid;