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;
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 FromCell(int2 c) { return new Target { pos = Util.CenterOfCell(c), valid = true }; }
public static Target FromOrder(Order o)

View File

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

View File

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

View File

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

View File

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

View File

@@ -48,7 +48,6 @@ namespace OpenRA.Mods.RA
int nextScanTime = 0;
public bool IsAttacking { get; internal set; }
public Target target;
public List<Weapon> Weapons = new List<Weapon>();
public List<Turret> Turrets = new List<Turret>();
@@ -73,7 +72,7 @@ namespace OpenRA.Mods.RA
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 (Weapons.All(w => w.IsReloading)) return false;
@@ -113,7 +112,7 @@ namespace OpenRA.Mods.RA
public void DoAttack(Actor self, Target target)
{
if( !CanAttack( self ) ) return;
if( !CanAttack( self, target ) ) return;
var move = self.TraitOrDefault<IMove>();
var facing = self.TraitOrDefault<IFacing>();
@@ -121,7 +120,7 @@ namespace OpenRA.Mods.RA
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;
}
@@ -147,32 +146,19 @@ namespace OpenRA.Mods.RA
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";
self.QueueActivity(order.Queued, GetAttackActivity(self, Target.FromOrder(order), allowMove));
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)
{
@@ -186,31 +172,32 @@ namespace OpenRA.Mods.RA
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 (allowMovement)
attack.ResolveOrder(self, new Order((holdStill) ? "AttackHold" : "Attack", self, target, false));
else
attack.target = Target.FromActor(target); // for turreted things on rails.
}
if (self.Destroyed) return;
if (target.IsActor)
w.Add(new FlashTarget(target.Actor));
var line = self.TraitOrDefault<DrawLineToTarget>();
if (line != null)
line.SetTarget(self, target, Color.Red);
});
}
public void ScanAndAttack(Actor self, bool allowMovement, bool holdStill)
{
if (--nextScanTime <= 0)
{
var targetActor = ScanForTarget(self);
var targetActor = ScanForTarget(self, null);
if (targetActor != null)
AttackTarget(self, targetActor, allowMovement, holdStill);
AttackTarget(Target.FromActor(targetActor), false, allowMovement && !holdStill);
var info = self.Info.Traits.Get<AttackBaseInfo>();
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 = attack.GetMaximumRange();
var range = 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 null;
return currentTarget;
}
public void ScanAndAttack(Actor self, bool allowMovement)
@@ -237,11 +223,10 @@ namespace OpenRA.Mods.RA
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.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))
.OrderBy(a => (a.CenterLocation - self.CenterLocation).LengthSquared)
.FirstOrDefault();

View File

@@ -26,9 +26,9 @@ namespace OpenRA.Mods.RA
public AttackFrontal(Actor self, AttackFrontalInfo 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;
var facing = self.Trait<IFacing>().Facing;

View File

@@ -22,6 +22,7 @@ namespace OpenRA.Mods.RA
class AttackLeap : AttackBase
{
internal bool IsLeaping;
protected Target target;
public AttackLeap(Actor self)
: base(self) {}
@@ -33,7 +34,7 @@ namespace OpenRA.Mods.RA
if (!target.IsValid) 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;
self.CancelActivity();

View File

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

View File

@@ -24,9 +24,10 @@ namespace OpenRA.Mods.RA
class AttackTurreted : AttackBase, INotifyBuildComplete
{
protected Target target;
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 )
return false;
@@ -37,7 +38,7 @@ namespace OpenRA.Mods.RA
if( turreted.desiredFacing != turreted.turretFacing )
return false;
return base.CanAttack( self );
return base.CanAttack( self, target );
}
public override void Tick(Actor self)
@@ -56,26 +57,27 @@ namespace OpenRA.Mods.RA
class AttackActivity : CancelableActivity
{
readonly Target newTarget;
public AttackActivity( Target newTarget ) { this.newTarget = newTarget; }
readonly Target target;
public AttackActivity( Target newTarget ) { this.target = newTarget; }
public override IActivity Tick( Actor self )
{
if( IsCanceled ) return NextActivity;
if( IsCanceled || !target.IsValid ) return NextActivity;
if (self.TraitsImplementing<IDisable>().Any(d => d.Disabled))
return this;
var attack = self.Trait<AttackTurreted>();
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)
{
attack.target = newTarget;
attack.target = target;
if (self.HasTrait<Mobile>() && !self.Info.Traits.Get<MobileInfo>().OnRails)
self.QueueActivity( new Follow( newTarget,
Math.Max( 0, (int)weapon.Info.Range - RangeTolerance ) ) );
return Util.SequenceActivities(
new Follow( target, Math.Max( 0, (int)weapon.Info.Range - RangeTolerance ) ),
this );
}
return NextActivity;
}

View File

@@ -11,18 +11,42 @@
using System.Linq;
using OpenRA.Mods.RA.Activities;
using OpenRA.Traits;
using OpenRA.Traits.Activities;
namespace OpenRA.Mods.RA
{
class AutoHealInfo : TraitInfo<AutoHeal> { }
class AutoHeal : ITick
class AutoHeal : INotifyIdle
{
public void Idle( Actor self )
{
self.QueueActivity( new IdleHealActivity() );
}
class IdleHealActivity : Idle
{
Actor currentTarget;
public override IActivity Tick( Actor self )
{
if( NextActivity != null )
return NextActivity;
var attack = self.Trait<AttackBase>();
var range = attack.GetMaximumRange();
if (NeedsNewTarget(self))
AttackTarget(self, ChooseTarget(self, range));
return this;
}
void AttackTarget(Actor self, Actor target)
{
var attack = self.Trait<AttackBase>();
if (target != null)
attack.ResolveOrder(self, new Order("Attack", self, target, false));
attack.AttackTarget(Target.FromActor( target), false, true );
else
if (attack.IsAttacking)
self.CancelActivity();
@@ -33,27 +57,17 @@ namespace OpenRA.Mods.RA
var attack = self.Trait<AttackBase>();
var range = attack.GetMaximumRange();
if (!attack.target.IsValid)
if (currentTarget == null || !currentTarget.IsInWorld)
return true; // he's dead.
if( !Combat.IsInRange( self.CenterLocation, range, attack.target ) )
if( !Combat.IsInRange( self.CenterLocation, range, currentTarget ) )
return true; // wandered off faster than we could follow
if (attack.target.IsActor
&& attack.target.Actor.GetDamageState() == DamageState.Undamaged)
if (currentTarget.GetDamageState() == DamageState.Undamaged)
return true; // fully healed
return false;
}
public void Tick(Actor self)
{
var attack = self.Trait<AttackBase>();
var range = attack.GetMaximumRange();
if (NeedsNewTarget(self))
AttackTarget(self, ChooseTarget(self, range));
}
Actor ChooseTarget(Actor self, float range)
{
var inRange = self.World.FindUnitsInCircle(self.CenterLocation, Game.CellSize * range);
@@ -69,3 +83,4 @@ namespace OpenRA.Mods.RA
}
}
}
}

View File

@@ -40,7 +40,7 @@ namespace OpenRA.Mods.RA
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)
{
Actors["mig1"].Trait<AttackPlane>().AttackTarget(Actors["mig1"], Actors["greeceweap"], true);
Actors["mig2"].Trait<AttackPlane>().AttackTarget(Actors["mig2"], Actors["greeceweap"], true);
Actors["mig3"].Trait<AttackPlane>().AttackTarget(Actors["mig3"], Actors["greeceweap"], true);
Actors["mig1"].Trait<AttackPlane>().AttackTarget(Target.FromActor(Actors["greeceweap"]), false, true);
Actors["mig2"].Trait<AttackPlane>().AttackTarget(Target.FromActor(Actors["greeceweap"]), false, true);
Actors["mig3"].Trait<AttackPlane>().AttackTarget(Target.FromActor(Actors["greeceweap"]), false, true);
}
ticks++;

View File

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

View File

@@ -135,7 +135,7 @@ namespace OpenRA.Mods.RA
.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)
{