From adc7e902e3f0de1fd39178841f2d641142c49426 Mon Sep 17 00:00:00 2001 From: RoosterDragon Date: Sun, 3 Jan 2016 00:12:37 +0000 Subject: [PATCH] Speed up AutoTarget.ChooseTarget among groups of allied units. Most auto target scans will be conducted among groups of allied units unable to target each other because they are allied and their weapons only target enemies. Since they cannot target each other, the scan will be repeated constantly. Realising this, we can significantly reduce the performance impact of auto target scanning by bailing early in this scenario to avoid carrying out expensive targeting checks on friendly units which we know will fail anyway. --- .../Traits/Attack/AttackBase.cs | 11 +++++++++++ OpenRA.Mods.Common/Traits/AutoTarget.cs | 19 ++++++++++++++++--- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/OpenRA.Mods.Common/Traits/Attack/AttackBase.cs b/OpenRA.Mods.Common/Traits/Attack/AttackBase.cs index c941d439ce..f375a35257 100644 --- a/OpenRA.Mods.Common/Traits/Attack/AttackBase.cs +++ b/OpenRA.Mods.Common/Traits/Attack/AttackBase.cs @@ -287,6 +287,17 @@ namespace OpenRA.Mods.Common.Traits && (target.IsInRange(self.CenterPosition, GetMaximumRange()) || (allowMove && self.Info.HasTraitInfo())); } + public Stance UnforcedAttackTargetStances() + { + // PERF: Avoid LINQ. + var stances = Stance.None; + foreach (var armament in Armaments) + if (!armament.IsTraitDisabled) + stances |= armament.Info.TargetStances; + + return stances; + } + class AttackOrderTargeter : IOrderTargeter { readonly AttackBase ab; diff --git a/OpenRA.Mods.Common/Traits/AutoTarget.cs b/OpenRA.Mods.Common/Traits/AutoTarget.cs index 38882c3822..c1a4a146f3 100644 --- a/OpenRA.Mods.Common/Traits/AutoTarget.cs +++ b/OpenRA.Mods.Common/Traits/AutoTarget.cs @@ -140,8 +140,14 @@ namespace OpenRA.Mods.Common.Traits if (nextScanTime <= 0) { nextScanTime = self.World.SharedRandom.Next(info.MinimumScanTimeInterval, info.MaximumScanTimeInterval); - var range = info.ScanRadius > 0 ? WDist.FromCells(info.ScanRadius) : attack.GetMaximumRange(); - return ChooseTarget(self, range, allowMove); + + // 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(); + return ChooseTarget(self, attackStances, range, allowMove); + } } return null; @@ -162,12 +168,19 @@ namespace OpenRA.Mods.Common.Traits attack.AttackTarget(target, false, allowMove); } - Actor ChooseTarget(Actor self, WDist range, bool allowMove) + Actor ChooseTarget(Actor self, Stance attackStances, WDist range, bool allowMove) { var actorsByArmament = new Dictionary>(); var actorsInRange = self.World.FindActorsInCircle(self.CenterPosition, range); foreach (var actor in actorsInRange) { + // PERF: Most units can only attack enemy units. If this is the case but the target is not an enemy, we + // can bail early and avoid the more expensive targeting checks and armament selection. For groups of + // allied units, this helps significantly reduce the cost of auto target scans. This is important as + // these groups will continuously rescan their allies until an enemy finally comes into range. + if (attackStances == OpenRA.Traits.Stance.Enemy && !actor.AppearsHostileTo(self)) + continue; + if (PreventsAutoTarget(self, actor) || !self.Owner.CanTargetActor(actor)) continue;