diff --git a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj
index 8426b012fe..3354aa0606 100644
--- a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj
+++ b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj
@@ -59,6 +59,7 @@
+
diff --git a/OpenRA.Mods.RA/UnitStances/UnitStanceDefensive.cs b/OpenRA.Mods.RA/UnitStances/UnitStanceDefensive.cs
new file mode 100644
index 0000000000..02e3806487
--- /dev/null
+++ b/OpenRA.Mods.RA/UnitStances/UnitStanceDefensive.cs
@@ -0,0 +1,163 @@
+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;
+ }
+
+ ///
+ /// Return Fire
+ ///
+ /// Will fire only when fired upon
+ ///
+ 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()) return;
+
+ var target = ScanForTarget(self);
+ if (target == null)
+ return;
+
+ AttackTarget(self, target, false);
+ }
+
+ protected override void OnTick(Actor self)
+ {
+ if (!self.HasTrait()) 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()) 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();
+
+
+
+ // 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().MoveWithinRange(DefendTarget, 1));
+ WaitForIdle();
+ });
+ }
+ }
+}
\ No newline at end of file
diff --git a/OpenRA.Mods.RA/Widgets/WorldCommandWidget.cs b/OpenRA.Mods.RA/Widgets/WorldCommandWidget.cs
index 20bd63110d..aee79fc293 100644
--- a/OpenRA.Mods.RA/Widgets/WorldCommandWidget.cs
+++ b/OpenRA.Mods.RA/Widgets/WorldCommandWidget.cs
@@ -14,10 +14,10 @@ namespace OpenRA.Mods.RA.Widgets
public char AttackMoveKey = 'a';
public char HoldGroundKey = 'g'; // Hold (G)round
- // public char DefensiveKey = 'd'; // (D)efensive
+ public char DefensiveKey = 'd'; // (D)efensive
public char AggressiveKey = 'a'; // (A)ggressive
public char ReturnFireKey = 'r'; // (R)eturn Fire
- public char HoldFire = 'h'; // (h)old fire
+ public char HoldFireKey = 'h'; // (h)old fire
public readonly OrderManager OrderManager;
[ObjectCreator.UseCtor]
@@ -73,11 +73,17 @@ namespace OpenRA.Mods.RA.Widgets
// stance: Hold Fire
// description: Prevents attacking (ie no autotarget is being done)
- if (e.KeyChar == HoldFire && (e.Modifiers.HasModifier(Modifiers.Alt)))
+ if (e.KeyChar == HoldFireKey && (e.Modifiers.HasModifier(Modifiers.Alt)))
{
return EnableStance();
}
+ // stance: Defensive
+ if (e.KeyChar == DefensiveKey && (e.Modifiers.HasModifier(Modifiers.Alt)))
+ {
+ return EnableStance();
+ }
+
return false;
}