diff --git a/OpenRA.Mods.Common/Traits/AutoTarget.cs b/OpenRA.Mods.Common/Traits/AutoTarget.cs index 52fba3d7a1..38882c3822 100644 --- a/OpenRA.Mods.Common/Traits/AutoTarget.cs +++ b/OpenRA.Mods.Common/Traits/AutoTarget.cs @@ -164,43 +164,57 @@ namespace OpenRA.Mods.Common.Traits Actor ChooseTarget(Actor self, WDist range, bool allowMove) { - var inRange = self.World.FindActorsInCircle(self.CenterPosition, range) - .Where(a => - !a.TraitsImplementing().Any(t => t.PreventsAutoTarget(a, self))); + var actorsByArmament = new Dictionary>(); + var actorsInRange = self.World.FindActorsInCircle(self.CenterPosition, range); + foreach (var actor in actorsInRange) + { + if (PreventsAutoTarget(self, actor) || !self.Owner.CanTargetActor(actor)) + continue; + + // Select only the first compatible armament for each actor: if this actor is selected + // 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); + if (!allowMove) + armaments = armaments.Where(arm => + target.IsInRange(self.CenterPosition, arm.MaxRange()) && + !target.IsInRange(self.CenterPosition, arm.Weapon.MinRange)); + + var armament = armaments.FirstOrDefault(); + if (armament == null) + continue; + + List actors; + if (actorsByArmament.TryGetValue(armament, out actors)) + actors.Add(actor); + else + actorsByArmament.Add(armament, new List { actor }); + } // Armaments are enumerated in attack.Armaments in construct order // When autotargeting, first choose targets according to the used armament construct order // 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) - var actorByArmament = inRange - - // Select only the first compatible armament for each actor: if this actor is selected - // it will be thanks to the first armament anyways, since that is the first selection - // criterion - .Select(a => - { - var target = Target.FromActor(a); - return new KeyValuePair( - attack.ChooseArmamentsForTarget(target, false) - .FirstOrDefault(arm => allowMove - || (target.IsInRange(self.CenterPosition, arm.MaxRange()) - && !target.IsInRange(self.CenterPosition, arm.Weapon.MinRange))), a); - }) - - .Where(kv => kv.Key != null && self.Owner.CanTargetActor(kv.Value)) - .GroupBy(kv => kv.Key, kv => kv.Value) - .ToDictionary(kv => kv.Key, kv => kv.ClosestTo(self)); - foreach (var arm in attack.Armaments) { - Actor actor; - if (actorByArmament.TryGetValue(arm, out actor)) - return actor; + List actors; + if (actorsByArmament.TryGetValue(arm, out actors)) + return actors.ClosestTo(self); } return null; } + + bool PreventsAutoTarget(Actor attacker, Actor target) + { + foreach (var pat in target.TraitsImplementing()) + if (pat.PreventsAutoTarget(target, attacker)) + return true; + + return false; + } } [Desc("Will not get automatically targeted by enemy (like walls)")]