make order queuing work for buildings and turreted units, too

This commit is contained in:
Bob
2010-11-14 15:48:02 +13:00
parent 7c146a9d5d
commit f8e6245903
16 changed files with 128 additions and 129 deletions

View File

@@ -218,7 +218,7 @@ namespace OpenRA.Traits
float2 pos; float2 pos;
bool valid; bool valid;
public static Target FromActor(Actor a) { return new Target { actor = a, valid = true }; } public static Target FromActor(Actor a) { return new Target { actor = a, valid = (a != null) }; }
public static Target FromPos(float2 p) { return new Target { pos = p, valid = true }; } public static Target FromPos(float2 p) { return new Target { pos = p, valid = true }; }
public static Target FromCell(int2 c) { return new Target { pos = Util.CenterOfCell(c), valid = true }; } public static Target FromCell(int2 c) { return new Target { pos = Util.CenterOfCell(c), valid = true }; }
public static Target FromOrder(Order o) public static Target FromOrder(Order o)

View File

@@ -61,7 +61,6 @@ namespace OpenRA.Mods.RA.Activities
if (facing.Facing != desiredFacing) if (facing.Facing != desiredFacing)
return Util.SequenceActivities( new Turn( desiredFacing ), this ); return Util.SequenceActivities( new Turn( desiredFacing ), this );
attack.target = Target;
attack.DoAttack(self, Target); attack.DoAttack(self, Target);
return this; return this;
} }

View File

@@ -27,10 +27,10 @@ namespace OpenRA.Mods.RA.Air
return new FlyAttack( newTarget ); return new FlyAttack( newTarget );
} }
protected override bool CanAttack(Actor self) protected override bool CanAttack(Actor self, Target target)
{ {
// dont fire while landed // dont fire while landed
return base.CanAttack(self) return base.CanAttack(self, target)
&& self.Trait<Aircraft>().Altitude > 0; && self.Trait<Aircraft>().Altitude > 0;
} }
} }

View File

@@ -28,7 +28,6 @@ namespace OpenRA.Mods.RA.Air
Cancel( self ); Cancel( self );
var attack = self.Trait<AttackPlane>(); var attack = self.Trait<AttackPlane>();
attack.target = Target;
attack.DoAttack( self, Target ); attack.DoAttack( self, Target );
if( inner == null ) if( inner == null )

View File

@@ -46,7 +46,6 @@ namespace OpenRA.Mods.RA.Air
if( !float2.WithinEpsilon( float2.Zero, dist, range * Game.CellSize ) ) if( !float2.WithinEpsilon( float2.Zero, dist, range * Game.CellSize ) )
aircraft.TickMove( 1024 * aircraft.MovementSpeed, desiredFacing ); aircraft.TickMove( 1024 * aircraft.MovementSpeed, desiredFacing );
attack.target = target;
attack.DoAttack( self, target ); attack.DoAttack( self, target );
return this; return this;

View File

@@ -48,7 +48,6 @@ namespace OpenRA.Mods.RA
int nextScanTime = 0; int nextScanTime = 0;
public bool IsAttacking { get; internal set; } public bool IsAttacking { get; internal set; }
public Target target;
public List<Weapon> Weapons = new List<Weapon>(); public List<Weapon> Weapons = new List<Weapon>();
public List<Turret> Turrets = new List<Turret>(); public List<Turret> Turrets = new List<Turret>();
@@ -73,7 +72,7 @@ namespace OpenRA.Mods.RA
info.SecondaryOffset != null ? Turrets[1] : Turrets[0], info.SecondaryLocalOffset)); info.SecondaryOffset != null ? Turrets[1] : Turrets[0], info.SecondaryLocalOffset));
} }
protected virtual bool CanAttack(Actor self) protected virtual bool CanAttack(Actor self, Target target)
{ {
if (!target.IsValid) return false; if (!target.IsValid) return false;
if (Weapons.All(w => w.IsReloading)) return false; if (Weapons.All(w => w.IsReloading)) return false;
@@ -113,7 +112,7 @@ namespace OpenRA.Mods.RA
public void DoAttack(Actor self, Target target) public void DoAttack(Actor self, Target target)
{ {
if( !CanAttack( self ) ) return; if( !CanAttack( self, target ) ) return;
var move = self.TraitOrDefault<IMove>(); var move = self.TraitOrDefault<IMove>();
var facing = self.TraitOrDefault<IFacing>(); var facing = self.TraitOrDefault<IFacing>();
@@ -121,7 +120,7 @@ namespace OpenRA.Mods.RA
w.CheckFire(self, this, move, facing, target); w.CheckFire(self, this, move, facing, target);
} }
public virtual int FireDelay( Actor self, AttackBaseInfo info ) public virtual int FireDelay( Actor self, Target target, AttackBaseInfo info )
{ {
return info.FireDelay; return info.FireDelay;
} }
@@ -147,31 +146,18 @@ namespace OpenRA.Mods.RA
public void ResolveOrder(Actor self, Order order) public void ResolveOrder(Actor self, Order order)
{ {
if (order.OrderString == "Attack" || order.OrderString == "AttackHold") if (order.OrderString == "Attack")
AttackTarget( Target.FromOrder( order ), order.Queued, true );
else if(order.OrderString == "AttackHold")
AttackTarget( Target.FromOrder( order ), order.Queued, false );
else
{ {
bool allowMove = order.OrderString == "Attack"; /* hack */
self.QueueActivity(order.Queued, GetAttackActivity(self, Target.FromOrder(order), allowMove)); if (self.HasTrait<Turreted>() && self.Info.Traits.Get<AttackBaseInfo>().AlignIdleTurrets)
self.Trait<Turreted>().desiredFacing = null;
if (self.Owner == self.World.LocalPlayer) }
self.World.AddFrameEndTask(w =>
{
if (self.Destroyed) return;
if (order.TargetActor != null)
w.Add(new FlashTarget(order.TargetActor));
var line = self.TraitOrDefault<DrawLineToTarget>();
if (line != null)
if (order.TargetActor != null) line.SetTarget(self, Target.FromOrder(order), Color.Red);
else line.SetTarget(self, Target.FromOrder(order), Color.Red);
});
return;
} // else not an attack order
target = Target.None;
/* hack */
if (self.HasTrait<Turreted>() && self.Info.Traits.Get<AttackBaseInfo>().AlignIdleTurrets)
self.Trait<Turreted>().desiredFacing = null;
} }
public string VoicePhraseForOrder(Actor self, Order order) public string VoicePhraseForOrder(Actor self, Order order)
@@ -186,31 +172,32 @@ namespace OpenRA.Mods.RA
public Weapon ChooseWeaponForTarget(Target t) { return Weapons.FirstOrDefault(w => w.IsValidAgainst(self.World, t)); } public Weapon ChooseWeaponForTarget(Target t) { return Weapons.FirstOrDefault(w => w.IsValidAgainst(self.World, t)); }
public void AttackTarget(Actor self, Actor target, bool allowMovement) public void AttackTarget( Target target, bool queued, bool allowMove )
{ {
AttackTarget(self, target, allowMovement, false); if( !target.IsValid ) return;
} self.QueueActivity(queued, GetAttackActivity(self, target, allowMove));
public void AttackTarget(Actor self, Actor target, bool allowMovement, bool holdStill) if (self.Owner == self.World.LocalPlayer)
{ self.World.AddFrameEndTask(w =>
var attack = self.Trait<AttackBase>(); {
if (target != null) if (self.Destroyed) return;
{ if (target.IsActor)
if (allowMovement) w.Add(new FlashTarget(target.Actor));
attack.ResolveOrder(self, new Order((holdStill) ? "AttackHold" : "Attack", self, target, false));
else var line = self.TraitOrDefault<DrawLineToTarget>();
attack.target = Target.FromActor(target); // for turreted things on rails. if (line != null)
} line.SetTarget(self, target, Color.Red);
});
} }
public void ScanAndAttack(Actor self, bool allowMovement, bool holdStill) public void ScanAndAttack(Actor self, bool allowMovement, bool holdStill)
{ {
if (--nextScanTime <= 0) if (--nextScanTime <= 0)
{ {
var targetActor = ScanForTarget(self); var targetActor = ScanForTarget(self, null);
if (targetActor != null) if (targetActor != null)
AttackTarget(self, targetActor, allowMovement, holdStill); AttackTarget(Target.FromActor(targetActor), false, allowMovement && !holdStill);
var info = self.Info.Traits.Get<AttackBaseInfo>(); var info = self.Info.Traits.Get<AttackBaseInfo>();
nextScanTime = (int)(25 * (info.ScanTimeAverage + nextScanTime = (int)(25 * (info.ScanTimeAverage +
@@ -218,15 +205,14 @@ namespace OpenRA.Mods.RA
} }
} }
public Actor ScanForTarget(Actor self) public Actor ScanForTarget(Actor self, Actor currentTarget)
{ {
var attack = self.Trait<AttackBase>(); var range = GetMaximumRange();
var range = attack.GetMaximumRange();
if ((!attack.target.IsValid || self.IsIdle) || !Combat.IsInRange(self.CenterLocation, range, attack.target)) if (self.IsIdle || currentTarget == null || !Combat.IsInRange(self.CenterLocation, range, currentTarget))
return ChooseTarget(self, range); return ChooseTarget(self, range);
return null; return currentTarget;
} }
public void ScanAndAttack(Actor self, bool allowMovement) public void ScanAndAttack(Actor self, bool allowMovement)
@@ -237,11 +223,10 @@ namespace OpenRA.Mods.RA
Actor ChooseTarget(Actor self, float range) Actor ChooseTarget(Actor self, float range)
{ {
var inRange = self.World.FindUnitsInCircle(self.CenterLocation, Game.CellSize * range); var inRange = self.World.FindUnitsInCircle(self.CenterLocation, Game.CellSize * range);
var attack = self.Trait<AttackBase>();
return inRange return inRange
.Where(a => a.Owner != null && self.Owner.Stances[a.Owner] == Stance.Enemy) .Where(a => a.Owner != null && self.Owner.Stances[a.Owner] == Stance.Enemy)
.Where(a => attack.HasAnyValidWeapons(Target.FromActor(a))) .Where(a => HasAnyValidWeapons(Target.FromActor(a)))
.Where(a => !a.HasTrait<Cloak>() || a.Trait<Cloak>().IsVisible(a, self.Owner)) .Where(a => !a.HasTrait<Cloak>() || a.Trait<Cloak>().IsVisible(a, self.Owner))
.OrderBy(a => (a.CenterLocation - self.CenterLocation).LengthSquared) .OrderBy(a => (a.CenterLocation - self.CenterLocation).LengthSquared)
.FirstOrDefault(); .FirstOrDefault();

View File

@@ -26,9 +26,9 @@ namespace OpenRA.Mods.RA
public AttackFrontal(Actor self, AttackFrontalInfo info) public AttackFrontal(Actor self, AttackFrontalInfo info)
: base( self ) { this.info = info; } : base( self ) { this.info = info; }
protected override bool CanAttack( Actor self ) protected override bool CanAttack( Actor self, Target target )
{ {
if( !base.CanAttack( self ) ) if( !base.CanAttack( self, target ) )
return false; return false;
var facing = self.Trait<IFacing>().Facing; var facing = self.Trait<IFacing>().Facing;

View File

@@ -22,6 +22,7 @@ namespace OpenRA.Mods.RA
class AttackLeap : AttackBase class AttackLeap : AttackBase
{ {
internal bool IsLeaping; internal bool IsLeaping;
protected Target target;
public AttackLeap(Actor self) public AttackLeap(Actor self)
: base(self) {} : base(self) {}
@@ -33,7 +34,7 @@ namespace OpenRA.Mods.RA
if (!target.IsValid) return; if (!target.IsValid) return;
if (IsLeaping) return; if (IsLeaping) return;
var weapon = self.Trait<AttackBase>().Weapons[0].Info; var weapon = Weapons[0].Info;
if( !Combat.IsInRange( self.CenterLocation, weapon.Range, target ) ) return; if( !Combat.IsInRange( self.CenterLocation, weapon.Range, target ) ) return;
self.CancelActivity(); self.CancelActivity();

View File

@@ -22,14 +22,15 @@ namespace OpenRA.Mods.RA
class AttackOmni : AttackBase, INotifyBuildComplete class AttackOmni : AttackBase, INotifyBuildComplete
{ {
bool buildComplete = false; bool buildComplete = false;
protected Target target;
public void BuildingComplete(Actor self) { buildComplete = true; } public void BuildingComplete(Actor self) { buildComplete = true; }
public AttackOmni(Actor self) : base(self) { } public AttackOmni(Actor self) : base(self) { }
protected override bool CanAttack( Actor self ) protected override bool CanAttack( Actor self, Target target )
{ {
var isBuilding = ( self.HasTrait<Building>() && !buildComplete ); var isBuilding = ( self.HasTrait<Building>() && !buildComplete );
return base.CanAttack( self ) && !isBuilding; return base.CanAttack( self, target ) && !isBuilding;
} }
public override void Tick(Actor self) public override void Tick(Actor self)
@@ -50,9 +51,11 @@ namespace OpenRA.Mods.RA
public override IActivity Tick( Actor self ) public override IActivity Tick( Actor self )
{ {
if( !IsCanceled ) if( IsCanceled || !target.IsValid )
self.Trait<AttackBase>().target = target; return NextActivity;
return NextActivity;
self.Trait<AttackOmni>().target = target;
return this;
} }
} }
} }

View File

@@ -32,9 +32,9 @@ namespace OpenRA.Mods.RA
charges = self.Info.Traits.Get<AttackTeslaInfo>().MaxCharges; charges = self.Info.Traits.Get<AttackTeslaInfo>().MaxCharges;
} }
protected override bool CanAttack( Actor self ) protected override bool CanAttack( Actor self, Target target )
{ {
return base.CanAttack( self ) && ( charges > 0 ); return base.CanAttack( self, target ) && ( charges > 0 );
} }
public override void Tick( Actor self ) public override void Tick( Actor self )
@@ -53,9 +53,9 @@ namespace OpenRA.Mods.RA
Actor previousTarget; Actor previousTarget;
public override int FireDelay( Actor self, AttackBaseInfo info ) public override int FireDelay( Actor self, Target target, AttackBaseInfo info )
{ {
return target.Actor == previousTarget ? 3 : base.FireDelay(self, info); return target.Actor == previousTarget ? 3 : base.FireDelay(self, target, info);
} }
public void Attacking(Actor self) public void Attacking(Actor self)

View File

@@ -24,9 +24,10 @@ namespace OpenRA.Mods.RA
class AttackTurreted : AttackBase, INotifyBuildComplete class AttackTurreted : AttackBase, INotifyBuildComplete
{ {
protected Target target;
public AttackTurreted(Actor self) : base(self) { } public AttackTurreted(Actor self) : base(self) { }
protected override bool CanAttack( Actor self ) protected override bool CanAttack( Actor self, Target target )
{ {
if( self.HasTrait<Building>() && !buildComplete ) if( self.HasTrait<Building>() && !buildComplete )
return false; return false;
@@ -37,7 +38,7 @@ namespace OpenRA.Mods.RA
if( turreted.desiredFacing != turreted.turretFacing ) if( turreted.desiredFacing != turreted.turretFacing )
return false; return false;
return base.CanAttack( self ); return base.CanAttack( self, target );
} }
public override void Tick(Actor self) public override void Tick(Actor self)
@@ -56,26 +57,27 @@ namespace OpenRA.Mods.RA
class AttackActivity : CancelableActivity class AttackActivity : CancelableActivity
{ {
readonly Target newTarget; readonly Target target;
public AttackActivity( Target newTarget ) { this.newTarget = newTarget; } public AttackActivity( Target newTarget ) { this.target = newTarget; }
public override IActivity Tick( Actor self ) public override IActivity Tick( Actor self )
{ {
if( IsCanceled ) return NextActivity; if( IsCanceled || !target.IsValid ) return NextActivity;
if (self.TraitsImplementing<IDisable>().Any(d => d.Disabled)) if (self.TraitsImplementing<IDisable>().Any(d => d.Disabled))
return this; return this;
var attack = self.Trait<AttackTurreted>(); var attack = self.Trait<AttackTurreted>();
const int RangeTolerance = 1; /* how far inside our maximum range we should try to sit */ const int RangeTolerance = 1; /* how far inside our maximum range we should try to sit */
var weapon = attack.ChooseWeaponForTarget(newTarget); var weapon = attack.ChooseWeaponForTarget(target);
if (weapon != null) if (weapon != null)
{ {
attack.target = newTarget; attack.target = target;
if (self.HasTrait<Mobile>() && !self.Info.Traits.Get<MobileInfo>().OnRails) if (self.HasTrait<Mobile>() && !self.Info.Traits.Get<MobileInfo>().OnRails)
self.QueueActivity( new Follow( newTarget, return Util.SequenceActivities(
Math.Max( 0, (int)weapon.Info.Range - RangeTolerance ) ) ); new Follow( target, Math.Max( 0, (int)weapon.Info.Range - RangeTolerance ) ),
this );
} }
return NextActivity; return NextActivity;
} }

View File

@@ -11,61 +11,76 @@
using System.Linq; using System.Linq;
using OpenRA.Mods.RA.Activities; using OpenRA.Mods.RA.Activities;
using OpenRA.Traits; using OpenRA.Traits;
using OpenRA.Traits.Activities;
namespace OpenRA.Mods.RA namespace OpenRA.Mods.RA
{ {
class AutoHealInfo : TraitInfo<AutoHeal> { } class AutoHealInfo : TraitInfo<AutoHeal> { }
class AutoHeal : ITick class AutoHeal : INotifyIdle
{ {
void AttackTarget(Actor self, Actor target) public void Idle( Actor self )
{ {
var attack = self.Trait<AttackBase>(); self.QueueActivity( new IdleHealActivity() );
if (target != null)
attack.ResolveOrder(self, new Order("Attack", self, target, false));
else
if (attack.IsAttacking)
self.CancelActivity();
} }
bool NeedsNewTarget(Actor self) class IdleHealActivity : Idle
{ {
var attack = self.Trait<AttackBase>(); Actor currentTarget;
var range = attack.GetMaximumRange();
if (!attack.target.IsValid) public override IActivity Tick( Actor self )
return true; // he's dead. {
if( !Combat.IsInRange( self.CenterLocation, range, attack.target ) ) if( NextActivity != null )
return true; // wandered off faster than we could follow return NextActivity;
if (attack.target.IsActor
&& attack.target.Actor.GetDamageState() == DamageState.Undamaged)
return true; // fully healed
return false; var attack = self.Trait<AttackBase>();
} var range = attack.GetMaximumRange();
public void Tick(Actor self) if (NeedsNewTarget(self))
{ AttackTarget(self, ChooseTarget(self, range));
var attack = self.Trait<AttackBase>();
var range = attack.GetMaximumRange();
if (NeedsNewTarget(self)) return this;
AttackTarget(self, ChooseTarget(self, range)); }
}
Actor ChooseTarget(Actor self, float range) void AttackTarget(Actor self, Actor target)
{ {
var inRange = self.World.FindUnitsInCircle(self.CenterLocation, Game.CellSize * range); var attack = self.Trait<AttackBase>();
var attack = self.Trait<AttackBase>(); if (target != null)
attack.AttackTarget(Target.FromActor( target), false, true );
else
if (attack.IsAttacking)
self.CancelActivity();
}
return inRange bool NeedsNewTarget(Actor self)
.Where(a => a != self && self.Owner.Stances[a.Owner] == Stance.Ally) {
.Where(a => a.IsInWorld && !a.IsDead()) var attack = self.Trait<AttackBase>();
.Where(a => a.HasTrait<Health>() && a.GetDamageState() > DamageState.Undamaged) var range = attack.GetMaximumRange();
.Where(a => attack.HasAnyValidWeapons(Target.FromActor(a)))
.OrderBy(a => (a.CenterLocation - self.CenterLocation).LengthSquared) if (currentTarget == null || !currentTarget.IsInWorld)
.FirstOrDefault(); return true; // he's dead.
if( !Combat.IsInRange( self.CenterLocation, range, currentTarget ) )
return true; // wandered off faster than we could follow
if (currentTarget.GetDamageState() == DamageState.Undamaged)
return true; // fully healed
return false;
}
Actor ChooseTarget(Actor self, float range)
{
var inRange = self.World.FindUnitsInCircle(self.CenterLocation, Game.CellSize * range);
var attack = self.Trait<AttackBase>();
return inRange
.Where(a => a != self && self.Owner.Stances[a.Owner] == Stance.Ally)
.Where(a => a.IsInWorld && !a.IsDead())
.Where(a => a.HasTrait<Health>() && a.GetDamageState() > DamageState.Undamaged)
.Where(a => attack.HasAnyValidWeapons(Target.FromActor(a)))
.OrderBy(a => (a.CenterLocation - self.CenterLocation).LengthSquared)
.FirstOrDefault();
}
} }
} }
} }

View File

@@ -40,7 +40,7 @@ namespace OpenRA.Mods.RA
if (e.Damage < 0) return; // don't retaliate against healers if (e.Damage < 0) return; // don't retaliate against healers
self.Trait<AttackBase>().AttackTarget(self, e.Attacker, self.Info.Traits.Get<AutoTargetInfo>().AllowMovement); self.Trait<AttackBase>().AttackTarget(Target.FromActor(e.Attacker), false, self.Info.Traits.Get<AutoTargetInfo>().AllowMovement);
} }
} }
} }

View File

@@ -58,9 +58,9 @@ namespace OpenRA.Mods.RA
if (ticks == 430) if (ticks == 430)
{ {
Actors["mig1"].Trait<AttackPlane>().AttackTarget(Actors["mig1"], Actors["greeceweap"], true); Actors["mig1"].Trait<AttackPlane>().AttackTarget(Target.FromActor(Actors["greeceweap"]), false, true);
Actors["mig2"].Trait<AttackPlane>().AttackTarget(Actors["mig2"], Actors["greeceweap"], true); Actors["mig2"].Trait<AttackPlane>().AttackTarget(Target.FromActor(Actors["greeceweap"]), false, true);
Actors["mig3"].Trait<AttackPlane>().AttackTarget(Actors["mig3"], Actors["greeceweap"], true); Actors["mig3"].Trait<AttackPlane>().AttackTarget(Target.FromActor(Actors["greeceweap"]), false, true);
} }
ticks++; ticks++;

View File

@@ -8,7 +8,7 @@ using OpenRA.Traits;
namespace OpenRA.Mods.RA namespace OpenRA.Mods.RA
{ {
public class UnitStanceInfo : ITraitInfo public class UnitStanceInfo : ITraitInfo, ITraitPrerequisite<AttackBaseInfo>
{ {
public readonly bool Default = false; public readonly bool Default = false;
public readonly int ScanDelayMin = 12; public readonly int ScanDelayMin = 12;
@@ -167,12 +167,8 @@ namespace OpenRA.Mods.RA
public static void AttackTarget(Actor self, Actor target, bool holdStill) public static void AttackTarget(Actor self, Actor target, bool holdStill)
{ {
var attack = self.Trait<AttackBase>(); if (target != null)
self.Trait<AttackBase>().AttackTarget(Target.FromActor(target), false, !holdStill);
if (attack != null && target != null)
{
attack.ResolveOrder(self, new Order((holdStill) ? "AttackHold" : "Attack", self, target, false));
}
} }
public static void StopAttack(Actor self) public static void StopAttack(Actor self)
@@ -199,7 +195,7 @@ namespace OpenRA.Mods.RA
public static Actor ScanForTarget(Actor self) public static Actor ScanForTarget(Actor self)
{ {
return self.Trait<AttackBase>().ScanForTarget(self); return self.Trait<AttackBase>().ScanForTarget(self, null);
} }
public void ResolveOrder(Actor self, Order order) public void ResolveOrder(Actor self, Order order)

View File

@@ -135,7 +135,7 @@ namespace OpenRA.Mods.RA
.Product() .Product()
}; };
attack.ScheduleDelayedAction( attack.FireDelay( self, self.Info.Traits.Get<AttackBaseInfo>() ), () => attack.ScheduleDelayedAction( attack.FireDelay( self, target, self.Info.Traits.Get<AttackBaseInfo>() ), () =>
{ {
if (args.weapon.Projectile != null) if (args.weapon.Projectile != null)
{ {