Files
OpenRA/OpenRA.Mods.RA/UnitStances/UnitStanceDefensive.cs
2010-11-13 17:44:45 +13:00

163 lines
3.9 KiB
C#

using System;
using System.Drawing;
using OpenRA.Mods.RA.Move;
using OpenRA.Traits;
namespace OpenRA.Mods.RA
{
public class UnitStanceDefensiveInfo : UnitStanceInfo
{
public override object Create(ActorInitializer init) { return new UnitStanceDefensive(init.self, this); }
public readonly int MaxDistance = 5;
}
/// <summary>
/// Return Fire
///
/// Will fire only when fired upon
/// </summary>
public class UnitStanceDefensive : UnitStance, INotifyDamage
{
public enum ETargetType
{
None,
Location,
Actor
}
[Sync] public int MaxDistance;
public Target DefendTarget = Target.None;
public ETargetType TargetType = ETargetType.None;
public bool WaitingForIdle = false;
[Sync]
public bool IsReturning { get; protected set; }
public UnitStanceDefensive(Actor self, UnitStanceDefensiveInfo info)
: base(self, info)
{
MaxDistance = info.MaxDistance;
base.AllowMultiTrigger = true;
}
protected override void OnActivate(Actor self)
{
DefendThis(self.CenterLocation);
if (!self.IsIdle)
WaitForIdle();
}
protected void DefendThis(float2 target)
{
DefendTarget = Target.FromPos(target);
TargetType = ETargetType.Location;
}
protected void DefendThis(Actor target)
{
DefendTarget = Target.FromActor(target);
TargetType = ETargetType.Actor;
}
protected override void OnScan(Actor self)
{
if (TargetType == ETargetType.None) return;
if (IsReturning) return;
if (!self.IsIdle) return;
if (!self.HasTrait<AttackBase>()) return;
var target = ScanForTarget(self);
if (target == null)
return;
AttackTarget(self, target, false);
}
protected override void OnTick(Actor self)
{
if (!self.HasTrait<AttackBase>()) return;
// when the unit is doing nothing or the target actor is gone, tell him to defend the current location
if ((WaitingForIdle && self.IsIdle) || (self.IsIdle && (TargetType == ETargetType.Actor && !DefendTarget.IsValid)))
{
IsReturning = false;
WaitingForIdle = false;
DefendThis(self.CenterLocation);
return;
}
if (IsReturning && self.IsIdle)
{
IsReturning = false;
}
if (TargetType != ETargetType.None)
{
if ((self.CenterLocation - DefendTarget.CenterLocation).Length > MaxDistance * Game.CellSize)
{
Return(self);
}
}
}
protected override void OnOrder(Actor self, Order order)
{
WaitForIdle();
}
private void WaitForIdle()
{
// could be an attack or move order ... => 'disable' the stance for now (invalidate the target)
DefendTarget = Target.None;
TargetType = ETargetType.None;
WaitingForIdle = true;
}
public void Damaged(Actor self, AttackInfo e)
{
if (!Active) return;
if (TargetType == ETargetType.None) return;
if (IsReturning) return;
if (!self.HasTrait<AttackBase>()) return;
ReturnFire(self, e, false); // only triggers when standing still
}
public override string OrderString
{
get { return "StanceDefensive"; }
}
public override Color SelectionColor
{
get { return Color.LightGoldenrodYellow; }
}
protected override string Shape
{
get { return "xxxx\nxxxx"; }
}
protected void Return(Actor self)
{
if ((TargetType == ETargetType.None) || (!DefendTarget.IsValid && (TargetType == ETargetType.Actor && !DefendTarget.IsValid))) return;
IsReturning = true;
var attackBase = self.TraitOrDefault<AttackBase>();
// Reset the attack target => otherwise it will not pick up enemies anymore!
// This should result in unsetting the target (could do it directly, this seems more 'valid')
self.World.AddFrameEndTask(w =>
{
self.CancelActivity();
attackBase.ResolveOrder(self, new Order("Stop", self));
self.QueueActivity(self.Trait<Mobile>().MoveWithinRange(DefendTarget, 1));
WaitForIdle();
});
}
}
}