Merge pull request #11276 from reaperrr/fix-ticktank-autotarget

Fix AutoTarget handling of multiple/upgradable Attack* traits
This commit is contained in:
Matthias Mailänder
2016-05-26 19:44:13 +02:00
2 changed files with 46 additions and 28 deletions

View File

@@ -17,7 +17,7 @@ using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits namespace OpenRA.Mods.Common.Traits
{ {
[Desc("The actor will automatically engage the enemy when it is in range.")] [Desc("The actor will automatically engage the enemy when it is in range.")]
public class AutoTargetInfo : ITraitInfo, Requires<AttackBaseInfo>, UsesInit<StanceInit> public class AutoTargetInfo : UpgradableTraitInfo, Requires<AttackBaseInfo>, UsesInit<StanceInit>
{ {
[Desc("It will try to hunt down the enemy if it is not set to defend.")] [Desc("It will try to hunt down the enemy if it is not set to defend.")]
public readonly bool AllowMovement = true; public readonly bool AllowMovement = true;
@@ -45,16 +45,15 @@ namespace OpenRA.Mods.Common.Traits
public readonly bool TargetWhenDamaged = true; public readonly bool TargetWhenDamaged = true;
public object Create(ActorInitializer init) { return new AutoTarget(init, this); } public override object Create(ActorInitializer init) { return new AutoTarget(init, this); }
} }
public enum UnitStance { HoldFire, ReturnFire, Defend, AttackAnything } public enum UnitStance { HoldFire, ReturnFire, Defend, AttackAnything }
public class AutoTarget : INotifyIdle, INotifyDamage, ITick, IResolveOrder, ISync public class AutoTarget : UpgradableTrait<AutoTargetInfo>, INotifyIdle, INotifyDamage, ITick, IResolveOrder, ISync
{ {
readonly AutoTargetInfo info; readonly AttackBase[] attackBases;
readonly AttackBase attack; readonly AttackFollow[] attackFollows;
readonly AttackFollow at;
[Sync] int nextScanTime = 0; [Sync] int nextScanTime = 0;
public UnitStance Stance; public UnitStance Stance;
@@ -65,10 +64,10 @@ namespace OpenRA.Mods.Common.Traits
public UnitStance PredictedStance; public UnitStance PredictedStance;
public AutoTarget(ActorInitializer init, AutoTargetInfo info) public AutoTarget(ActorInitializer init, AutoTargetInfo info)
: base(info)
{ {
var self = init.Self; var self = init.Self;
this.info = info; attackBases = self.TraitsImplementing<AttackBase>().ToArray();
attack = self.Trait<AttackBase>();
if (init.Contains<StanceInit>()) if (init.Contains<StanceInit>())
Stance = init.Get<StanceInit, UnitStance>(); Stance = init.Get<StanceInit, UnitStance>();
@@ -76,18 +75,21 @@ namespace OpenRA.Mods.Common.Traits
Stance = self.Owner.IsBot || !self.Owner.Playable ? info.InitialStanceAI : info.InitialStance; Stance = self.Owner.IsBot || !self.Owner.Playable ? info.InitialStanceAI : info.InitialStance;
PredictedStance = Stance; PredictedStance = Stance;
at = self.TraitOrDefault<AttackFollow>(); attackFollows = self.TraitsImplementing<AttackFollow>().ToArray();
} }
public void ResolveOrder(Actor self, Order order) public void ResolveOrder(Actor self, Order order)
{ {
if (order.OrderString == "SetUnitStance" && info.EnableStances) if (order.OrderString == "SetUnitStance" && Info.EnableStances)
Stance = (UnitStance)order.ExtraData; Stance = (UnitStance)order.ExtraData;
} }
public void Damaged(Actor self, AttackInfo e) public void Damaged(Actor self, AttackInfo e)
{ {
if (!self.IsIdle || !info.TargetWhenDamaged) if (IsTraitDisabled)
return;
if (!self.IsIdle || !Info.TargetWhenDamaged)
return; return;
var attacker = e.Attacker; var attacker = e.Attacker;
@@ -103,7 +105,7 @@ namespace OpenRA.Mods.Common.Traits
} }
// not a lot we can do about things we can't hurt... although maybe we should automatically run away? // not a lot we can do about things we can't hurt... although maybe we should automatically run away?
if (!attack.HasAnyValidWeapons(Target.FromActor(attacker))) if (attackBases.All(a => a.IsTraitDisabled || !a.HasAnyValidWeapons(Target.FromActor(attacker))))
return; return;
// don't retaliate against own units force-firing on us. It's usually not what the player wanted. // don't retaliate against own units force-firing on us. It's usually not what the player wanted.
@@ -115,39 +117,51 @@ namespace OpenRA.Mods.Common.Traits
return; return;
Aggressor = attacker; Aggressor = attacker;
var allowMove = info.AllowMovement && Stance != UnitStance.Defend; var allowMove = Info.AllowMovement && Stance != UnitStance.Defend;
if (at == null || !at.IsReachableTarget(at.Target, allowMove)) if (attackFollows.All(a => a.IsTraitDisabled || !a.IsReachableTarget(a.Target, allowMove)))
Attack(self, Aggressor, allowMove); Attack(self, Aggressor, allowMove);
} }
public void TickIdle(Actor self) public void TickIdle(Actor self)
{ {
if (Stance < UnitStance.Defend || !info.TargetWhenIdle) if (IsTraitDisabled)
return; return;
var allowMove = info.AllowMovement && Stance != UnitStance.Defend; if (Stance < UnitStance.Defend || !Info.TargetWhenIdle)
if (at == null || !at.IsReachableTarget(at.Target, allowMove)) return;
var allowMove = Info.AllowMovement && Stance != UnitStance.Defend;
if (attackFollows.All(a => a.IsTraitDisabled || !a.IsReachableTarget(a.Target, allowMove)))
ScanAndAttack(self, allowMove); ScanAndAttack(self, allowMove);
} }
public void Tick(Actor self) public void Tick(Actor self)
{ {
if (IsTraitDisabled)
return;
if (nextScanTime > 0) if (nextScanTime > 0)
--nextScanTime; --nextScanTime;
} }
public Actor ScanForTarget(Actor self, bool allowMove) public Actor ScanForTarget(Actor self, bool allowMove)
{ {
if (nextScanTime <= 0) var activeAttackBases = attackBases.Where(Exts.IsTraitEnabled);
if (activeAttackBases.Any() && nextScanTime <= 0)
{ {
nextScanTime = self.World.SharedRandom.Next(info.MinimumScanTimeInterval, info.MaximumScanTimeInterval); nextScanTime = self.World.SharedRandom.Next(Info.MinimumScanTimeInterval, Info.MaximumScanTimeInterval);
foreach (var ab in activeAttackBases)
{
// If we can't attack right now, there's no need to try and find a target. // If we can't attack right now, there's no need to try and find a target.
var attackStances = attack.UnforcedAttackTargetStances(); var attackStances = ab.UnforcedAttackTargetStances();
if (attackStances != OpenRA.Traits.Stance.None) if (attackStances != OpenRA.Traits.Stance.None)
{ {
var range = info.ScanRadius > 0 ? WDist.FromCells(info.ScanRadius) : attack.GetMaximumRange(); var range = Info.ScanRadius > 0 ? WDist.FromCells(Info.ScanRadius) : ab.GetMaximumRange();
return ChooseTarget(self, attackStances, range, allowMove); return ChooseTarget(self, ab, attackStances, range, allowMove);
}
continue;
} }
} }
@@ -166,10 +180,13 @@ namespace OpenRA.Mods.Common.Traits
TargetedActor = targetActor; TargetedActor = targetActor;
var target = Target.FromActor(targetActor); var target = Target.FromActor(targetActor);
self.SetTargetLine(target, Color.Red, false); self.SetTargetLine(target, Color.Red, false);
attack.AttackTarget(target, false, allowMove);
var activeAttackBases = attackBases.Where(Exts.IsTraitEnabled);
foreach (var ab in activeAttackBases)
ab.AttackTarget(target, false, allowMove);
} }
Actor ChooseTarget(Actor self, Stance attackStances, WDist range, bool allowMove) Actor ChooseTarget(Actor self, AttackBase ab, Stance attackStances, WDist range, bool allowMove)
{ {
var actorsByArmament = new Dictionary<Armament, List<Actor>>(); var actorsByArmament = new Dictionary<Armament, List<Actor>>();
var actorsInRange = self.World.FindActorsInCircle(self.CenterPosition, range); var actorsInRange = self.World.FindActorsInCircle(self.CenterPosition, range);
@@ -189,7 +206,7 @@ namespace OpenRA.Mods.Common.Traits
// it will be thanks to the first armament anyways, since that is the first selection // it will be thanks to the first armament anyways, since that is the first selection
// criterion // criterion
var target = Target.FromActor(actor); var target = Target.FromActor(actor);
var armaments = attack.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()) &&
@@ -211,7 +228,7 @@ namespace OpenRA.Mods.Common.Traits
// 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)
foreach (var arm in attack.Armaments) foreach (var arm in ab.Armaments)
{ {
List<Actor> actors; List<Actor> actors;
if (actorsByArmament.TryGetValue(arm, out actors)) if (actorsByArmament.TryGetValue(arm, out actors))

View File

@@ -162,6 +162,7 @@ TTNK:
Type: Concrete Type: Concrete
UpgradeTypes: deployed UpgradeTypes: deployed
UpgradeMinEnabledLevel: 1 UpgradeMinEnabledLevel: 1
AutoTarget:
ART2: ART2:
Inherits: ^VoxelTank Inherits: ^VoxelTank