Add AutoTargetPriority trait for smarter AutoTarget logic.
This commit is contained in:
@@ -815,6 +815,7 @@
|
||||
<Compile Include="Widgets\Logic\Ingame\CommandBarLogic.cs" />
|
||||
<Compile Include="Widgets\Logic\Ingame\StanceSelectorLogic.cs" />
|
||||
<Compile Include="Widgets\Logic\ButtonTooltipWithDescHighlightLogic.cs" />
|
||||
<Compile Include="Traits\AutoTargetPriority.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Target Name="AfterBuild">
|
||||
|
||||
@@ -102,6 +102,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
UnitStance stance;
|
||||
ConditionManager conditionManager;
|
||||
AutoTargetPriority[] targetPriorities;
|
||||
int conditionToken = ConditionManager.InvalidConditionToken;
|
||||
|
||||
public void SetStance(Actor self, UnitStance value)
|
||||
@@ -144,6 +145,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
void INotifyCreated.Created(Actor self)
|
||||
{
|
||||
conditionManager = self.TraitOrDefault<ConditionManager>();
|
||||
targetPriorities = self.TraitsImplementing<AutoTargetPriority>().ToArray();
|
||||
ApplyStanceCondition(self);
|
||||
}
|
||||
|
||||
@@ -265,10 +267,21 @@ namespace OpenRA.Mods.Common.Traits
|
||||
ab.AttackTarget(target, false, allowMove);
|
||||
}
|
||||
|
||||
Actor ChooseTarget(Actor self, AttackBase ab, Stance attackStances, WDist range, bool allowMove)
|
||||
Actor ChooseTarget(Actor self, AttackBase ab, Stance attackStances, WDist scanRange, bool allowMove)
|
||||
{
|
||||
var actorsByArmament = new Dictionary<Armament, List<Actor>>();
|
||||
var actorsInRange = self.World.FindActorsInCircle(self.CenterPosition, range);
|
||||
Actor chosenTarget = null;
|
||||
var chosenTargetPriority = int.MinValue;
|
||||
int chosenTargetRange = 0;
|
||||
|
||||
var activePriorities = targetPriorities.Where(Exts.IsTraitEnabled)
|
||||
.Select(at => at.Info)
|
||||
.OrderByDescending(ati => ati.Priority)
|
||||
.ToList();
|
||||
|
||||
if (!activePriorities.Any())
|
||||
return null;
|
||||
|
||||
var actorsInRange = self.World.FindActorsInCircle(self.CenterPosition, scanRange);
|
||||
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
|
||||
@@ -278,43 +291,53 @@ namespace OpenRA.Mods.Common.Traits
|
||||
if (attackStances == OpenRA.Traits.Stance.Enemy && !actor.AppearsHostileTo(self))
|
||||
continue;
|
||||
|
||||
if (PreventsAutoTarget(self, actor) || !self.Owner.CanTargetActor(actor))
|
||||
// Check whether we can auto-target this actor
|
||||
var targetTypes = actor.TraitsImplementing<ITargetable>()
|
||||
.Where(Exts.IsTraitEnabled).SelectMany(t => t.TargetTypes)
|
||||
.ToHashSet();
|
||||
|
||||
var target = Target.FromActor(actor);
|
||||
var validPriorities = activePriorities.Where(ati =>
|
||||
{
|
||||
// Already have a higher priority target
|
||||
if (ati.Priority < chosenTargetPriority)
|
||||
return false;
|
||||
|
||||
// Incompatible target types
|
||||
if (!targetTypes.Overlaps(ati.ValidTargets) || targetTypes.Overlaps(ati.InvalidTargets))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}).ToList();
|
||||
|
||||
if (!validPriorities.Any() || 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);
|
||||
// Make sure that we can actually fire on the actor
|
||||
var armaments = ab.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)
|
||||
if (!armaments.Any())
|
||||
continue;
|
||||
|
||||
List<Actor> actors;
|
||||
if (actorsByArmament.TryGetValue(armament, out actors))
|
||||
actors.Add(actor);
|
||||
else
|
||||
actorsByArmament.Add(armament, new List<Actor> { actor });
|
||||
// Evaluate whether we want to target this actor
|
||||
var targetRange = (target.CenterPosition - self.CenterPosition).Length;
|
||||
foreach (var ati in validPriorities)
|
||||
{
|
||||
if (chosenTarget == null || chosenTargetPriority < ati.Priority
|
||||
|| (chosenTargetPriority == ati.Priority && targetRange < chosenTargetRange))
|
||||
{
|
||||
chosenTarget = actor;
|
||||
chosenTargetPriority = ati.Priority;
|
||||
chosenTargetRange = targetRange;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
foreach (var arm in ab.Armaments)
|
||||
{
|
||||
List<Actor> actors;
|
||||
if (actorsByArmament.TryGetValue(arm, out actors))
|
||||
return actors.ClosestTo(self);
|
||||
}
|
||||
|
||||
return null;
|
||||
return chosenTarget;
|
||||
}
|
||||
|
||||
bool PreventsAutoTarget(Actor attacker, Actor target)
|
||||
@@ -327,23 +350,6 @@ namespace OpenRA.Mods.Common.Traits
|
||||
}
|
||||
}
|
||||
|
||||
[Desc("Will not get automatically targeted by enemy (like walls)")]
|
||||
class AutoTargetIgnoreInfo : ConditionalTraitInfo
|
||||
{
|
||||
public override object Create(ActorInitializer init) { return new AutoTargetIgnore(this); }
|
||||
}
|
||||
|
||||
class AutoTargetIgnore : ConditionalTrait<AutoTargetIgnoreInfo>, IPreventsAutoTarget
|
||||
{
|
||||
public AutoTargetIgnore(AutoTargetIgnoreInfo info)
|
||||
: base(info) { }
|
||||
|
||||
public bool PreventsAutoTarget(Actor self, Actor attacker)
|
||||
{
|
||||
return !IsTraitDisabled;
|
||||
}
|
||||
}
|
||||
|
||||
public class StanceInit : IActorInit<UnitStance>
|
||||
{
|
||||
[FieldFromYamlKey] readonly UnitStance value = UnitStance.AttackAnything;
|
||||
|
||||
37
OpenRA.Mods.Common/Traits/AutoTargetPriority.cs
Normal file
37
OpenRA.Mods.Common/Traits/AutoTargetPriority.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2017 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
[Desc("Specifies the target types and relative priority used by AutoTarget to decide what to target.")]
|
||||
public class AutoTargetPriorityInfo : ConditionalTraitInfo, Requires<AutoTargetInfo>
|
||||
{
|
||||
[Desc("Target types that can be AutoTargeted.")]
|
||||
public readonly HashSet<string> ValidTargets = new HashSet<string> { "Ground", "Water", "Air" };
|
||||
|
||||
[Desc("Target types that can't be AutoTargeted.", "Overrules ValidTargets.")]
|
||||
public readonly HashSet<string> InvalidTargets = new HashSet<string>();
|
||||
|
||||
[Desc("ValidTargets with larger priorities will be AutoTargeted before lower priorities.")]
|
||||
public readonly int Priority = 1;
|
||||
|
||||
public override object Create(ActorInitializer init) { return new AutoTargetPriority(this); }
|
||||
}
|
||||
|
||||
public class AutoTargetPriority : ConditionalTrait<AutoTargetPriorityInfo>
|
||||
{
|
||||
public AutoTargetPriority(AutoTargetPriorityInfo info)
|
||||
: base(info) { }
|
||||
}
|
||||
}
|
||||
@@ -710,6 +710,18 @@ namespace OpenRA.Mods.Common.UtilityCommands
|
||||
}
|
||||
}
|
||||
|
||||
// AutoTargetIgnore replaced with AutoTargetPriority and target types
|
||||
if (engineVersion < 20170610)
|
||||
{
|
||||
if (node.Key.StartsWith("AutoTarget", StringComparison.Ordinal) || node.Key.StartsWith("-AutoTarget", StringComparison.Ordinal))
|
||||
{
|
||||
Console.WriteLine("The AutoTarget traits have been reworked to use target types:");
|
||||
Console.WriteLine(" * Actors with AutoTarget must specify one or more AutoTargetPriority traits.");
|
||||
Console.WriteLine(" * The AutoTargetIgnore trait has been removed.");
|
||||
Console.WriteLine(" Append NoAutoTarget to the target types instead.");
|
||||
}
|
||||
}
|
||||
|
||||
UpgradeActorRules(modData, engineVersion, ref node.Value.Nodes, node, depth + 1);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user