From d5915431dde371dc84c44020d30ffa77f9d3a874 Mon Sep 17 00:00:00 2001 From: reaperrr Date: Sat, 14 May 2016 02:12:18 +0200 Subject: [PATCH 1/5] Made AutoTarget upgradable --- OpenRA.Mods.Common/Traits/AutoTarget.cs | 32 +++++++++++++++---------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/OpenRA.Mods.Common/Traits/AutoTarget.cs b/OpenRA.Mods.Common/Traits/AutoTarget.cs index 1f4c856e4b..c02b99efe7 100644 --- a/OpenRA.Mods.Common/Traits/AutoTarget.cs +++ b/OpenRA.Mods.Common/Traits/AutoTarget.cs @@ -17,7 +17,7 @@ using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits { [Desc("The actor will automatically engage the enemy when it is in range.")] - public class AutoTargetInfo : ITraitInfo, Requires, UsesInit + public class AutoTargetInfo : UpgradableTraitInfo, Requires, UsesInit { [Desc("It will try to hunt down the enemy if it is not set to defend.")] public readonly bool AllowMovement = true; @@ -45,14 +45,13 @@ namespace OpenRA.Mods.Common.Traits public readonly bool TargetWhenDamaged = true; - public object Create(ActorInitializer init) { return new AutoTarget(init, this); } + public override object Create(ActorInitializer init) { return new AutoTarget(init, this); } } public enum UnitStance { HoldFire, ReturnFire, Defend, AttackAnything } - public class AutoTarget : INotifyIdle, INotifyDamage, ITick, IResolveOrder, ISync + public class AutoTarget : UpgradableTrait, INotifyIdle, INotifyDamage, ITick, IResolveOrder, ISync { - readonly AutoTargetInfo info; readonly AttackBase attack; readonly AttackFollow at; [Sync] int nextScanTime = 0; @@ -65,9 +64,9 @@ namespace OpenRA.Mods.Common.Traits public UnitStance PredictedStance; public AutoTarget(ActorInitializer init, AutoTargetInfo info) + : base(info) { var self = init.Self; - this.info = info; attack = self.Trait(); if (init.Contains()) @@ -81,13 +80,16 @@ namespace OpenRA.Mods.Common.Traits public void ResolveOrder(Actor self, Order order) { - if (order.OrderString == "SetUnitStance" && info.EnableStances) + if (order.OrderString == "SetUnitStance" && Info.EnableStances) Stance = (UnitStance)order.ExtraData; } public void Damaged(Actor self, AttackInfo e) { - if (!self.IsIdle || !info.TargetWhenDamaged) + if (IsTraitDisabled) + return; + + if (!self.IsIdle || !Info.TargetWhenDamaged) return; var attacker = e.Attacker; @@ -115,23 +117,29 @@ namespace OpenRA.Mods.Common.Traits return; Aggressor = attacker; - var allowMove = info.AllowMovement && Stance != UnitStance.Defend; if (at == null || !at.IsReachableTarget(at.Target, allowMove)) + var allowMove = Info.AllowMovement && Stance != UnitStance.Defend; Attack(self, Aggressor, allowMove); } public void TickIdle(Actor self) { - if (Stance < UnitStance.Defend || !info.TargetWhenIdle) + if (IsTraitDisabled) + return; + + if (Stance < UnitStance.Defend || !Info.TargetWhenIdle) return; - var allowMove = info.AllowMovement && Stance != UnitStance.Defend; if (at == null || !at.IsReachableTarget(at.Target, allowMove)) + var allowMove = Info.AllowMovement && Stance != UnitStance.Defend; ScanAndAttack(self, allowMove); } public void Tick(Actor self) { + if (IsTraitDisabled) + return; + if (nextScanTime > 0) --nextScanTime; } @@ -140,13 +148,13 @@ namespace OpenRA.Mods.Common.Traits { if (nextScanTime <= 0) { - nextScanTime = self.World.SharedRandom.Next(info.MinimumScanTimeInterval, info.MaximumScanTimeInterval); + nextScanTime = self.World.SharedRandom.Next(Info.MinimumScanTimeInterval, Info.MaximumScanTimeInterval); // If we can't attack right now, there's no need to try and find a target. var attackStances = attack.UnforcedAttackTargetStances(); if (attackStances != OpenRA.Traits.Stance.None) { - var range = info.ScanRadius > 0 ? WDist.FromCells(info.ScanRadius) : attack.GetMaximumRange(); + var range = Info.ScanRadius > 0 ? WDist.FromCells(Info.ScanRadius) : attack.GetMaximumRange(); return ChooseTarget(self, attackStances, range, allowMove); } } From 1b6f67e9ee1946b1d462be68ad8d6603003d0724 Mon Sep 17 00:00:00 2001 From: reaperrr Date: Sat, 14 May 2016 02:15:46 +0200 Subject: [PATCH 2/5] Improve AutoTarget code readability --- OpenRA.Mods.Common/Traits/AutoTarget.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/OpenRA.Mods.Common/Traits/AutoTarget.cs b/OpenRA.Mods.Common/Traits/AutoTarget.cs index c02b99efe7..4b77d5b353 100644 --- a/OpenRA.Mods.Common/Traits/AutoTarget.cs +++ b/OpenRA.Mods.Common/Traits/AutoTarget.cs @@ -53,7 +53,7 @@ namespace OpenRA.Mods.Common.Traits public class AutoTarget : UpgradableTrait, INotifyIdle, INotifyDamage, ITick, IResolveOrder, ISync { readonly AttackBase attack; - readonly AttackFollow at; + readonly AttackFollow attackFollow; [Sync] int nextScanTime = 0; public UnitStance Stance; @@ -75,7 +75,7 @@ namespace OpenRA.Mods.Common.Traits Stance = self.Owner.IsBot || !self.Owner.Playable ? info.InitialStanceAI : info.InitialStance; PredictedStance = Stance; - at = self.TraitOrDefault(); + attackFollow = self.TraitsImplementing().FirstOrDefault(Exts.IsTraitEnabled); } public void ResolveOrder(Actor self, Order order) @@ -117,8 +117,8 @@ namespace OpenRA.Mods.Common.Traits return; Aggressor = attacker; - if (at == null || !at.IsReachableTarget(at.Target, allowMove)) var allowMove = Info.AllowMovement && Stance != UnitStance.Defend; + if (attackFollow == null || !attackFollow.IsReachableTarget(attackFollow.Target, allowMove)) Attack(self, Aggressor, allowMove); } @@ -130,8 +130,8 @@ namespace OpenRA.Mods.Common.Traits if (Stance < UnitStance.Defend || !Info.TargetWhenIdle) return; - if (at == null || !at.IsReachableTarget(at.Target, allowMove)) var allowMove = Info.AllowMovement && Stance != UnitStance.Defend; + if (attackFollow == null || !attackFollow.IsReachableTarget(attackFollow.Target, allowMove)) ScanAndAttack(self, allowMove); } From 2a19f66a0e8d8572f9e7c7a6e070a944439af2b1 Mon Sep 17 00:00:00 2001 From: reaperrr Date: Sat, 14 May 2016 02:19:58 +0200 Subject: [PATCH 3/5] Make AutoTarget able to handle multiple Attack* traits --- OpenRA.Mods.Common/Traits/AutoTarget.cs | 35 ++++++++++++++++--------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/OpenRA.Mods.Common/Traits/AutoTarget.cs b/OpenRA.Mods.Common/Traits/AutoTarget.cs index 4b77d5b353..7954f23daf 100644 --- a/OpenRA.Mods.Common/Traits/AutoTarget.cs +++ b/OpenRA.Mods.Common/Traits/AutoTarget.cs @@ -52,7 +52,7 @@ namespace OpenRA.Mods.Common.Traits public class AutoTarget : UpgradableTrait, INotifyIdle, INotifyDamage, ITick, IResolveOrder, ISync { - readonly AttackBase attack; + readonly AttackBase[] attackBases; readonly AttackFollow attackFollow; [Sync] int nextScanTime = 0; @@ -67,7 +67,7 @@ namespace OpenRA.Mods.Common.Traits : base(info) { var self = init.Self; - attack = self.Trait(); + attackBases = self.TraitsImplementing().ToArray(); if (init.Contains()) Stance = init.Get(); @@ -105,7 +105,7 @@ namespace OpenRA.Mods.Common.Traits } // not a lot we can do about things we can't hurt... although maybe we should automatically run away? - if (!attack.HasAnyValidWeapons(Target.FromActor(attacker))) + if (attackBases.All(a => a.IsTraitDisabled || !a.HasAnyValidWeapons(Target.FromActor(attacker)))) return; // don't retaliate against own units force-firing on us. It's usually not what the player wanted. @@ -146,16 +146,22 @@ namespace OpenRA.Mods.Common.Traits public Actor ScanForTarget(Actor self, bool allowMove) { - if (nextScanTime <= 0) + var activeAttackBases = attackBases.Where(Exts.IsTraitEnabled); + if (activeAttackBases.Any() && nextScanTime <= 0) { nextScanTime = self.World.SharedRandom.Next(Info.MinimumScanTimeInterval, Info.MaximumScanTimeInterval); - // If we can't attack right now, there's no need to try and find a target. - var attackStances = attack.UnforcedAttackTargetStances(); - if (attackStances != OpenRA.Traits.Stance.None) + foreach (var ab in activeAttackBases) { - var range = Info.ScanRadius > 0 ? WDist.FromCells(Info.ScanRadius) : attack.GetMaximumRange(); - return ChooseTarget(self, attackStances, range, allowMove); + // If we can't attack right now, there's no need to try and find a target. + var attackStances = ab.UnforcedAttackTargetStances(); + if (attackStances != OpenRA.Traits.Stance.None) + { + var range = Info.ScanRadius > 0 ? WDist.FromCells(Info.ScanRadius) : ab.GetMaximumRange(); + return ChooseTarget(self, ab, attackStances, range, allowMove); + } + + continue; } } @@ -174,10 +180,13 @@ namespace OpenRA.Mods.Common.Traits TargetedActor = targetActor; var target = Target.FromActor(targetActor); self.SetTargetLine(target, Color.Red, false); - attack.AttackTarget(target, false, allowMove); + + var activeAttackBases = attackBases.Where(Exts.IsTraitEnabled); + foreach (var ab in activeAttackBases) + ab.AttackTarget(target, false, allowMove); } - Actor ChooseTarget(Actor self, Stance attackStances, WDist range, bool allowMove) + Actor ChooseTarget(Actor self, AttackBase ab, Stance attackStances, WDist range, bool allowMove) { var actorsByArmament = new Dictionary>(); var actorsInRange = self.World.FindActorsInCircle(self.CenterPosition, range); @@ -197,7 +206,7 @@ namespace OpenRA.Mods.Common.Traits // it will be thanks to the first armament anyways, since that is the first selection // criterion var target = Target.FromActor(actor); - var armaments = attack.ChooseArmamentsForTarget(target, false); + var armaments = ab.ChooseArmamentsForTarget(target, false); if (!allowMove) armaments = armaments.Where(arm => target.IsInRange(self.CenterPosition, arm.MaxRange()) && @@ -219,7 +228,7 @@ namespace OpenRA.Mods.Common.Traits // And then according to distance from actor // This enables preferential treatment of certain armaments // (e.g. tesla trooper's tesla zap should have precedence over tesla charge) - foreach (var arm in attack.Armaments) + foreach (var arm in ab.Armaments) { List actors; if (actorsByArmament.TryGetValue(arm, out actors)) From 6089599b3f1f362a3fc23bb81cf231c32c65e108 Mon Sep 17 00:00:00 2001 From: reaperrr Date: Sat, 14 May 2016 12:43:49 +0200 Subject: [PATCH 4/5] Make AutoTarget handling of AttackFollow more robust At least in theory, it's possible for an actor to have multiple AttackFollow traits, or AttackFollow is disabled initially but enabled via upgrade later. To avoid crashes or other issues, let's play it safe and take the same approach as with AttackBase look-ups. --- OpenRA.Mods.Common/Traits/AutoTarget.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/OpenRA.Mods.Common/Traits/AutoTarget.cs b/OpenRA.Mods.Common/Traits/AutoTarget.cs index 7954f23daf..1ec70432ed 100644 --- a/OpenRA.Mods.Common/Traits/AutoTarget.cs +++ b/OpenRA.Mods.Common/Traits/AutoTarget.cs @@ -53,7 +53,7 @@ namespace OpenRA.Mods.Common.Traits public class AutoTarget : UpgradableTrait, INotifyIdle, INotifyDamage, ITick, IResolveOrder, ISync { readonly AttackBase[] attackBases; - readonly AttackFollow attackFollow; + readonly AttackFollow[] attackFollows; [Sync] int nextScanTime = 0; public UnitStance Stance; @@ -75,7 +75,7 @@ namespace OpenRA.Mods.Common.Traits Stance = self.Owner.IsBot || !self.Owner.Playable ? info.InitialStanceAI : info.InitialStance; PredictedStance = Stance; - attackFollow = self.TraitsImplementing().FirstOrDefault(Exts.IsTraitEnabled); + attackFollows = self.TraitsImplementing().ToArray(); } public void ResolveOrder(Actor self, Order order) @@ -118,7 +118,7 @@ namespace OpenRA.Mods.Common.Traits Aggressor = attacker; var allowMove = Info.AllowMovement && Stance != UnitStance.Defend; - if (attackFollow == null || !attackFollow.IsReachableTarget(attackFollow.Target, allowMove)) + if (attackFollows.All(a => a.IsTraitDisabled || !a.IsReachableTarget(a.Target, allowMove))) Attack(self, Aggressor, allowMove); } @@ -131,7 +131,7 @@ namespace OpenRA.Mods.Common.Traits return; var allowMove = Info.AllowMovement && Stance != UnitStance.Defend; - if (attackFollow == null || !attackFollow.IsReachableTarget(attackFollow.Target, allowMove)) + if (attackFollows.All(a => a.IsTraitDisabled || !a.IsReachableTarget(a.Target, allowMove))) ScanAndAttack(self, allowMove); } From d7b26509af7204da6102b8c8543c553e2db53df0 Mon Sep 17 00:00:00 2001 From: reaperrr Date: Sat, 14 May 2016 02:20:38 +0200 Subject: [PATCH 5/5] Enable AutoTarget on TS Tick Tank --- mods/ts/rules/nod-vehicles.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/mods/ts/rules/nod-vehicles.yaml b/mods/ts/rules/nod-vehicles.yaml index 3141529ed8..8d4ec54367 100644 --- a/mods/ts/rules/nod-vehicles.yaml +++ b/mods/ts/rules/nod-vehicles.yaml @@ -162,6 +162,7 @@ TTNK: Type: Concrete UpgradeTypes: deployed UpgradeMinEnabledLevel: 1 + AutoTarget: ART2: Inherits: ^VoxelTank