Allow opportunity fire for aircraft.

This commit is contained in:
tovl
2019-03-31 22:09:43 +02:00
committed by reaperrr
parent f16ff9eaa0
commit 9abf715fd7
19 changed files with 114 additions and 92 deletions

View File

@@ -36,7 +36,7 @@ namespace OpenRA.Mods.Common.Activities
aircraft = self.Trait<Aircraft>();
attackAircraft = self.Trait<AttackAircraft>();
rearmable = self.TraitOrDefault<Rearmable>();
ticksUntilTurn = attackAircraft.AttackAircraftInfo.AttackTurnDelay;
ticksUntilTurn = attackAircraft.Info.AttackTurnDelay;
// The target may become hidden between the initial order request and the first tick (e.g. if queued)
// Moving to any position (even if quite stale) is still better than immediately giving up
@@ -64,7 +64,12 @@ namespace OpenRA.Mods.Common.Activities
if (IsCanceling)
{
// Cancel the requested target, but keep firing on it while in range
attackAircraft.OpportunityTarget = attackAircraft.RequestedTarget;
if (attackAircraft.Info.PersistentTargeting)
{
attackAircraft.OpportunityTarget = attackAircraft.RequestedTarget;
attackAircraft.OpportunityForceAttack = attackAircraft.RequestedForceAttack;
}
attackAircraft.RequestedTarget = Target.Invalid;
return NextActivity;
}
@@ -130,7 +135,6 @@ namespace OpenRA.Mods.Common.Activities
if (self.World.Map.DistanceAboveTerrain(self.CenterPosition).Length < aircraft.Info.MinAirborneAltitude)
QueueChild(self, new TakeOff(self), true);
// TODO: This should fire each weapon at its maximum range
if (attackAircraft != null && target.IsInRange(self.CenterPosition, attackAircraft.GetMinimumRange()))
QueueChild(self, new FlyTimed(ticksUntilTurn, self), true);

View File

@@ -62,7 +62,12 @@ namespace OpenRA.Mods.Common.Activities
if (IsCanceling)
{
// Cancel the requested target, but keep firing on it while in range
attackAircraft.OpportunityTarget = attackAircraft.RequestedTarget;
if (attackAircraft.Info.PersistentTargeting)
{
attackAircraft.OpportunityTarget = attackAircraft.RequestedTarget;
attackAircraft.OpportunityForceAttack = attackAircraft.RequestedForceAttack;
}
attackAircraft.RequestedTarget = Target.Invalid;
return NextActivity;
}

View File

@@ -191,10 +191,9 @@ namespace OpenRA.Mods.Common.Activities
return AttackStatus.NeedsToMove;
}
var targetedPosition = attack.GetTargetPosition(pos, target);
var desiredFacing = (targetedPosition - pos).Yaw.Facing;
if (!Util.FacingWithinTolerance(facing.Facing, desiredFacing, ((AttackFrontalInfo)attack.Info).FacingTolerance))
if (!attack.TargetInFiringArc(self, target, attack.Info.FacingTolerance))
{
var desiredFacing = (attack.GetTargetPosition(pos, target) - pos).Yaw.Facing;
attackStatus |= AttackStatus.NeedsToTurn;
QueueChild(self, new Turn(self, desiredFacing), true);
return AttackStatus.NeedsToTurn;

View File

@@ -55,7 +55,7 @@ namespace OpenRA.Mods.Common.Activities
if (attack == null && autoTarget != null)
{
var target = autoTarget.ScanForTarget(self, true);
var target = autoTarget.ScanForTarget(self, true, true);
if (target.Type != TargetType.Invalid)
{
if (inner != null)

View File

@@ -20,29 +20,18 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Delay, in game ticks, before non-hovering aircraft turns to attack.")]
public readonly int AttackTurnDelay = 50;
[Desc("Tolerance for attack angle. Range [0, 128], 128 covers 360 degrees.")]
public readonly int FacingTolerance = 0;
public override void RulesetLoaded(Ruleset rules, ActorInfo ai)
{
base.RulesetLoaded(rules, ai);
if (FacingTolerance < 0 || FacingTolerance > 128)
throw new YamlException("Facing tolerance must be in range of [0, 128], 128 covers 360 degrees.");
}
public override object Create(ActorInitializer init) { return new AttackAircraft(init.Self, this); }
}
public class AttackAircraft : AttackFollow
{
public readonly AttackAircraftInfo AttackAircraftInfo;
public new readonly AttackAircraftInfo Info;
readonly AircraftInfo aircraftInfo;
public AttackAircraft(Actor self, AttackAircraftInfo info)
: base(self, info)
{
AttackAircraftInfo = info;
Info = info;
aircraftInfo = self.Info.TraitInfo<AircraftInfo>();
}
@@ -64,14 +53,7 @@ namespace OpenRA.Mods.Common.Traits
if (!base.CanAttack(self, target))
return false;
var pos = self.CenterPosition;
var targetedPosition = GetTargetPosition(pos, target);
var delta = targetedPosition - pos;
if (delta.HorizontalLengthSquared == 0)
return true;
return Util.FacingWithinTolerance(facing.Facing, delta.Yaw.Facing, AttackAircraftInfo.FacingTolerance);
return TargetInFiringArc(self, target, base.Info.FacingTolerance);
}
}
}

View File

@@ -18,7 +18,8 @@ namespace OpenRA.Mods.Common.Traits
{
public class AttackBomberInfo : AttackBaseInfo
{
public readonly int FacingTolerance = 2;
[Desc("Tolerance for attack angle. Range [0, 128], 128 covers 360 degrees.")]
public readonly new int FacingTolerance = 2;
public override object Create(ActorInitializer init) { return new AttackBomber(init.Self, this); }
}
@@ -49,10 +50,7 @@ namespace OpenRA.Mods.Common.Traits
inAttackRange = false;
var f = facing.Facing;
var delta = target.CenterPosition - self.CenterPosition;
var facingToTarget = delta.HorizontalLengthSquared != 0 ? delta.Yaw.Facing : f;
facingTarget = Math.Abs(facingToTarget - f) % 256 <= info.FacingTolerance;
facingTarget = TargetInFiringArc(self, target, info.FacingTolerance);
foreach (var a in Armaments)
{

View File

@@ -39,6 +39,17 @@ namespace OpenRA.Mods.Common.Traits
[VoiceReference] public readonly string Voice = "Action";
[Desc("Tolerance for attack angle. Range [0, 128], 128 covers 360 degrees.")]
public readonly int FacingTolerance = 128;
public override void RulesetLoaded(Ruleset rules, ActorInfo ai)
{
base.RulesetLoaded(rules, ai);
if (FacingTolerance < 0 || FacingTolerance > 128)
throw new YamlException("Facing tolerance must be in range of [0, 128], 128 covers 360 degrees.");
}
public override abstract object Create(ActorInitializer init);
}
@@ -101,6 +112,21 @@ namespace OpenRA.Mods.Common.Traits
return () => armaments;
}
public bool TargetInFiringArc(Actor self, Target target, int facingTolerance)
{
if (facing == null)
return true;
var pos = self.CenterPosition;
var targetedPosition = GetTargetPosition(pos, target);
var delta = targetedPosition - pos;
if (delta.HorizontalLengthSquared == 0)
return true;
return Util.FacingWithinTolerance(facing.Facing, delta.Yaw.Facing, facingTolerance);
}
protected virtual bool CanAttack(Actor self, Target target)
{
if (!self.IsInWorld || IsTraitDisabled || IsTraitPaused)

View File

@@ -23,21 +23,28 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Automatically acquire and fire on targets of opportunity when not actively attacking.")]
public readonly bool OpportunityFire = true;
[Desc("Keep firing on targets even after attack order is cancelled")]
public readonly bool PersistentTargeting = true;
public override object Create(ActorInitializer init) { return new AttackFollow(init.Self, this); }
}
public class AttackFollow : AttackBase, INotifyOwnerChanged
{
public new readonly AttackFollowInfo Info;
public Target RequestedTarget;
protected bool requestedForceAttack;
public bool RequestedForceAttack;
public int RequestedTargetLastTick;
public Target OpportunityTarget;
protected bool opportunityForceAttack;
public bool OpportunityForceAttack;
Mobile mobile;
AutoTarget autoTarget;
public AttackFollow(Actor self, AttackFollowInfo info)
: base(self, info) { }
: base(self, info)
{
Info = info;
}
protected override void Created(Actor self)
{
@@ -58,7 +65,8 @@ namespace OpenRA.Mods.Common.Traits
var armaments = ChooseArmamentsForTarget(target, forceAttack);
foreach (var a in armaments)
if (target.IsInRange(pos, a.MaxRange()) && (a.Weapon.MinRange == WDist.Zero || !target.IsInRange(pos, a.Weapon.MinRange)))
return true;
if (TargetInFiringArc(self, target, Info.FacingTolerance))
return true;
return false;
}
@@ -82,7 +90,7 @@ namespace OpenRA.Mods.Common.Traits
if (RequestedTarget.Type != TargetType.Invalid)
{
IsAiming = CanAimAtTarget(self, RequestedTarget, requestedForceAttack);
IsAiming = CanAimAtTarget(self, RequestedTarget, RequestedForceAttack);
if (IsAiming)
DoAttack(self, RequestedTarget);
}
@@ -91,16 +99,16 @@ namespace OpenRA.Mods.Common.Traits
IsAiming = false;
if (OpportunityTarget.Type != TargetType.Invalid)
IsAiming = CanAimAtTarget(self, OpportunityTarget, opportunityForceAttack);
IsAiming = CanAimAtTarget(self, OpportunityTarget, OpportunityForceAttack);
if (!IsAiming && ((AttackFollowInfo)Info).OpportunityFire && autoTarget != null &&
if (!IsAiming && Info.OpportunityFire && autoTarget != null &&
!autoTarget.IsTraitDisabled && autoTarget.Stance >= UnitStance.Defend)
{
OpportunityTarget = autoTarget.ScanForTarget(self, false);
opportunityForceAttack = false;
OpportunityTarget = autoTarget.ScanForTarget(self, false, false);
OpportunityForceAttack = false;
if (OpportunityTarget.Type != TargetType.Invalid)
IsAiming = CanAimAtTarget(self, OpportunityTarget, opportunityForceAttack);
IsAiming = CanAimAtTarget(self, OpportunityTarget, OpportunityForceAttack);
}
if (IsAiming)
@@ -123,7 +131,7 @@ namespace OpenRA.Mods.Common.Traits
if (!queued)
{
RequestedTarget = target;
requestedForceAttack = forceAttack;
RequestedForceAttack = forceAttack;
RequestedTargetLastTick = self.World.WorldTick;
}
}
@@ -186,8 +194,12 @@ namespace OpenRA.Mods.Common.Traits
if (IsCanceling)
{
// Cancel the requested target, but keep firing on it while in range
attack.OpportunityTarget = attack.RequestedTarget;
attack.opportunityForceAttack = attack.requestedForceAttack;
if (attack.Info.PersistentTargeting)
{
attack.OpportunityTarget = attack.RequestedTarget;
attack.OpportunityForceAttack = attack.RequestedForceAttack;
}
attack.RequestedTarget = Target.Invalid;
return NextActivity;
}
@@ -201,7 +213,7 @@ namespace OpenRA.Mods.Common.Traits
return this;
bool targetIsHiddenActor;
attack.requestedForceAttack = forceAttack;
attack.RequestedForceAttack = forceAttack;
attack.RequestedTarget = target = target.Recalculate(self.Owner, out targetIsHiddenActor);
attack.RequestedTargetLastTick = self.World.WorldTick;
hasTicked = true;

View File

@@ -17,27 +17,20 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Unit got to face the target")]
public class AttackFrontalInfo : AttackBaseInfo, Requires<IFacingInfo>
{
public readonly int FacingTolerance = 0;
public override void RulesetLoaded(Ruleset rules, ActorInfo ai)
{
base.RulesetLoaded(rules, ai);
if (FacingTolerance < 0 || FacingTolerance > 128)
throw new YamlException("Facing tolerance must be in range of [0, 128], 128 covers 360 degrees.");
}
[Desc("Tolerance for attack angle. Range [0, 128], 128 covers 360 degrees.")]
public readonly new int FacingTolerance = 0;
public override object Create(ActorInitializer init) { return new AttackFrontal(init.Self, this); }
}
public class AttackFrontal : AttackBase
{
readonly AttackFrontalInfo info;
public new readonly AttackFrontalInfo Info;
public AttackFrontal(Actor self, AttackFrontalInfo info)
: base(self, info)
{
this.info = info;
this.Info = info;
}
protected override bool CanAttack(Actor self, Target target)
@@ -45,14 +38,7 @@ namespace OpenRA.Mods.Common.Traits
if (!base.CanAttack(self, target))
return false;
var pos = self.CenterPosition;
var targetedPosition = GetTargetPosition(pos, target);
var delta = targetedPosition - pos;
if (delta.HorizontalLengthSquared == 0)
return true;
return Util.FacingWithinTolerance(facing.Facing, delta.Yaw.Facing, info.FacingTolerance);
return TargetInFiringArc(self, target, Info.FacingTolerance);
}
public override Activity GetAttackActivity(Actor self, Target newTarget, bool allowMove, bool forceAttack)

View File

@@ -24,6 +24,9 @@ namespace OpenRA.Mods.Common.Traits
[Desc("It will try to hunt down the enemy if it is set to AttackAnything.")]
public readonly bool AllowMovement = true;
[Desc("It will try to pivot to face the enemy if stance is not HoldFire.")]
public readonly bool AllowTurning = true;
[Desc("Set to a value >1 to override weapons maximum range for this.")]
public readonly int ScanRadius = -1;
@@ -228,7 +231,8 @@ namespace OpenRA.Mods.Common.Traits
return;
var allowMove = Info.AllowMovement && Stance > UnitStance.Defend;
ScanAndAttack(self, allowMove);
var allowTurn = Info.AllowTurning && Stance > UnitStance.HoldFire;
ScanAndAttack(self, allowMove, allowTurn);
}
void ITick.Tick(Actor self)
@@ -240,7 +244,7 @@ namespace OpenRA.Mods.Common.Traits
--nextScanTime;
}
public Target ScanForTarget(Actor self, bool allowMove)
public Target ScanForTarget(Actor self, bool allowMove, bool allowTurn)
{
if (nextScanTime <= 0 && ActiveAttackBases.Any())
{
@@ -253,7 +257,7 @@ namespace OpenRA.Mods.Common.Traits
if (attackStances != OpenRA.Traits.Stance.None)
{
var range = Info.ScanRadius > 0 ? WDist.FromCells(Info.ScanRadius) : ab.GetMaximumRange();
return ChooseTarget(self, ab, attackStances, range, allowMove);
return ChooseTarget(self, ab, attackStances, range, allowMove, allowTurn);
}
}
}
@@ -261,9 +265,9 @@ namespace OpenRA.Mods.Common.Traits
return Target.Invalid;
}
public void ScanAndAttack(Actor self, bool allowMove)
public void ScanAndAttack(Actor self, bool allowMove, bool allowTurn)
{
var target = ScanForTarget(self, allowMove);
var target = ScanForTarget(self, allowMove, allowTurn);
if (target.Type != TargetType.Invalid)
Attack(self, target, allowMove);
}
@@ -276,7 +280,7 @@ namespace OpenRA.Mods.Common.Traits
ab.AttackTarget(target, false, allowMove);
}
Target ChooseTarget(Actor self, AttackBase ab, Stance attackStances, WDist scanRange, bool allowMove)
Target ChooseTarget(Actor self, AttackBase ab, Stance attackStances, WDist scanRange, bool allowMove, bool allowTurn)
{
var chosenTarget = Target.Invalid;
var chosenTargetPriority = int.MinValue;
@@ -353,6 +357,9 @@ namespace OpenRA.Mods.Common.Traits
if (!armaments.Any())
continue;
if (!allowTurn && !ab.TargetInFiringArc(self, target, ab.Info.FacingTolerance))
continue;
// Evaluate whether we want to target this actor
var targetRange = (target.CenterPosition - self.CenterPosition).Length;
foreach (var ati in validPriorities)