Optimize AutoTarget.ChooseTarget.
Avoid using LINQ for filtering, grouping and dictionary building. Instead, we do these ourselves to reduce overhead. Actors are checked for AutoTargetIgnoreInfo (rather than the trait) since info checks are marginally faster. This check can now be done immediately to allow us to skip such actors right away. The ClosestTo calculation is now only run on the group on actors for the selected armament, rather than all potential armaments.
This commit is contained in:
@@ -164,43 +164,57 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
|
|
||||||
Actor ChooseTarget(Actor self, WDist range, bool allowMove)
|
Actor ChooseTarget(Actor self, WDist range, bool allowMove)
|
||||||
{
|
{
|
||||||
var inRange = self.World.FindActorsInCircle(self.CenterPosition, range)
|
var actorsByArmament = new Dictionary<Armament, List<Actor>>();
|
||||||
.Where(a =>
|
var actorsInRange = self.World.FindActorsInCircle(self.CenterPosition, range);
|
||||||
!a.TraitsImplementing<IPreventsAutoTarget>().Any(t => t.PreventsAutoTarget(a, self)));
|
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<Actor> actors;
|
||||||
|
if (actorsByArmament.TryGetValue(armament, out actors))
|
||||||
|
actors.Add(actor);
|
||||||
|
else
|
||||||
|
actorsByArmament.Add(armament, new List<Actor> { actor });
|
||||||
|
}
|
||||||
|
|
||||||
// Armaments are enumerated in attack.Armaments in construct order
|
// Armaments are enumerated in attack.Armaments in construct order
|
||||||
// When autotargeting, first choose targets according to the used armament construct order
|
// When autotargeting, first choose targets according to the used armament construct order
|
||||||
// And then according to distance from actor
|
// And then according to distance from actor
|
||||||
// This enables preferential treatment of certain armaments
|
// This enables preferential treatment of certain armaments
|
||||||
// (e.g. tesla trooper's tesla zap should have precedence over tesla charge)
|
// (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<Armament, Actor>(
|
|
||||||
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)
|
foreach (var arm in attack.Armaments)
|
||||||
{
|
{
|
||||||
Actor actor;
|
List<Actor> actors;
|
||||||
if (actorByArmament.TryGetValue(arm, out actor))
|
if (actorsByArmament.TryGetValue(arm, out actors))
|
||||||
return actor;
|
return actors.ClosestTo(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PreventsAutoTarget(Actor attacker, Actor target)
|
||||||
|
{
|
||||||
|
foreach (var pat in target.TraitsImplementing<IPreventsAutoTarget>())
|
||||||
|
if (pat.PreventsAutoTarget(target, attacker))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Desc("Will not get automatically targeted by enemy (like walls)")]
|
[Desc("Will not get automatically targeted by enemy (like walls)")]
|
||||||
|
|||||||
Reference in New Issue
Block a user