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; }