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\CommandBarLogic.cs" />
|
||||||
<Compile Include="Widgets\Logic\Ingame\StanceSelectorLogic.cs" />
|
<Compile Include="Widgets\Logic\Ingame\StanceSelectorLogic.cs" />
|
||||||
<Compile Include="Widgets\Logic\ButtonTooltipWithDescHighlightLogic.cs" />
|
<Compile Include="Widgets\Logic\ButtonTooltipWithDescHighlightLogic.cs" />
|
||||||
|
<Compile Include="Traits\AutoTargetPriority.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
<Target Name="AfterBuild">
|
<Target Name="AfterBuild">
|
||||||
|
|||||||
@@ -102,6 +102,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
|
|
||||||
UnitStance stance;
|
UnitStance stance;
|
||||||
ConditionManager conditionManager;
|
ConditionManager conditionManager;
|
||||||
|
AutoTargetPriority[] targetPriorities;
|
||||||
int conditionToken = ConditionManager.InvalidConditionToken;
|
int conditionToken = ConditionManager.InvalidConditionToken;
|
||||||
|
|
||||||
public void SetStance(Actor self, UnitStance value)
|
public void SetStance(Actor self, UnitStance value)
|
||||||
@@ -144,6 +145,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
void INotifyCreated.Created(Actor self)
|
void INotifyCreated.Created(Actor self)
|
||||||
{
|
{
|
||||||
conditionManager = self.TraitOrDefault<ConditionManager>();
|
conditionManager = self.TraitOrDefault<ConditionManager>();
|
||||||
|
targetPriorities = self.TraitsImplementing<AutoTargetPriority>().ToArray();
|
||||||
ApplyStanceCondition(self);
|
ApplyStanceCondition(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -265,10 +267,21 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
ab.AttackTarget(target, false, allowMove);
|
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>>();
|
Actor chosenTarget = null;
|
||||||
var actorsInRange = self.World.FindActorsInCircle(self.CenterPosition, range);
|
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)
|
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
|
// 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))
|
if (attackStances == OpenRA.Traits.Stance.Enemy && !actor.AppearsHostileTo(self))
|
||||||
continue;
|
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;
|
continue;
|
||||||
|
|
||||||
// Select only the first compatible armament for each actor: if this actor is selected
|
// Make sure that we can actually fire on the actor
|
||||||
// it will be thanks to the first armament anyways, since that is the first selection
|
|
||||||
// criterion
|
|
||||||
var target = Target.FromActor(actor);
|
|
||||||
var armaments = ab.ChooseArmamentsForTarget(target, false);
|
var armaments = ab.ChooseArmamentsForTarget(target, false);
|
||||||
if (!allowMove)
|
if (!allowMove)
|
||||||
armaments = armaments.Where(arm =>
|
armaments = armaments.Where(arm =>
|
||||||
target.IsInRange(self.CenterPosition, arm.MaxRange()) &&
|
target.IsInRange(self.CenterPosition, arm.MaxRange()) &&
|
||||||
!target.IsInRange(self.CenterPosition, arm.Weapon.MinRange));
|
!target.IsInRange(self.CenterPosition, arm.Weapon.MinRange));
|
||||||
|
|
||||||
var armament = armaments.FirstOrDefault();
|
if (!armaments.Any())
|
||||||
if (armament == null)
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
List<Actor> actors;
|
// Evaluate whether we want to target this actor
|
||||||
if (actorsByArmament.TryGetValue(armament, out actors))
|
var targetRange = (target.CenterPosition - self.CenterPosition).Length;
|
||||||
actors.Add(actor);
|
foreach (var ati in validPriorities)
|
||||||
else
|
{
|
||||||
actorsByArmament.Add(armament, new List<Actor> { actor });
|
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
|
return chosenTarget;
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PreventsAutoTarget(Actor attacker, Actor target)
|
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>
|
public class StanceInit : IActorInit<UnitStance>
|
||||||
{
|
{
|
||||||
[FieldFromYamlKey] readonly UnitStance value = UnitStance.AttackAnything;
|
[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);
|
UpgradeActorRules(modData, engineVersion, ref node.Value.Nodes, node, depth + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user