diff --git a/OpenRA.Mods.Common/Traits/AttackMove.cs b/OpenRA.Mods.Common/Traits/AttackMove.cs index eec324fc7d..000453773a 100644 --- a/OpenRA.Mods.Common/Traits/AttackMove.cs +++ b/OpenRA.Mods.Common/Traits/AttackMove.cs @@ -9,8 +9,11 @@ */ #endregion +using System.Collections.Generic; using System.Drawing; +using System.Linq; using OpenRA.Mods.Common.Activities; +using OpenRA.Orders; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits @@ -111,4 +114,54 @@ namespace OpenRA.Mods.Common.Traits } } } + + public class AttackMoveOrderGenerator : UnitOrderGenerator + { + readonly TraitPair[] subjects; + readonly MouseButton expectedButton; + + public AttackMoveOrderGenerator(IEnumerable subjects, MouseButton button) + { + expectedButton = button; + + this.subjects = subjects.SelectMany(a => a.TraitsImplementing() + .Select(am => new TraitPair(a, am))) + .ToArray(); + } + + public override IEnumerable Order(World world, CPos cell, int2 worldPixel, MouseInput mi) + { + if (mi.Button != expectedButton) + world.CancelInputMode(); + + return OrderInner(world, cell, mi); + } + + protected virtual IEnumerable OrderInner(World world, CPos cell, MouseInput mi) + { + if (mi.Button == expectedButton && world.Map.Contains(cell)) + { + world.CancelInputMode(); + + var queued = mi.Modifiers.HasModifier(Modifiers.Shift); + var orderName = mi.Modifiers.HasModifier(Modifiers.Ctrl) ? "AssaultMove" : "AttackMove"; + foreach (var s in subjects) + yield return new Order(orderName, s.Actor, queued) { TargetLocation = cell }; + } + } + + public override string GetCursor(World world, CPos cell, int2 worldPixel, MouseInput mi) + { + if (world.Map.Contains(cell)) + return mi.Modifiers.HasModifier(Modifiers.Ctrl) ? "assaultmove" : "attackmove"; + + return "generic-blocked"; + } + + public override bool InputOverridesSelection(World world, int2 xy, MouseInput mi) + { + // Custom order generators always override selection + return true; + } + } } diff --git a/OpenRA.Mods.Common/Widgets/Logic/Ingame/CommandBarLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Ingame/CommandBarLogic.cs index dc83ecf226..dc3fff3cec 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Ingame/CommandBarLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Ingame/CommandBarLogic.cs @@ -56,8 +56,7 @@ namespace OpenRA.Mods.Common.Widgets BindButtonIcon(attackMoveButton); attackMoveButton.IsDisabled = () => { UpdateStateIfNecessary(); return attackMoveDisabled; }; - attackMoveButton.IsHighlighted = () => world.OrderGenerator is GenericSelectTarget - && ((GenericSelectTarget)world.OrderGenerator).OrderName == "AttackMove"; + attackMoveButton.IsHighlighted = () => world.OrderGenerator is AttackMoveOrderGenerator; Action toggle = allowCancel => { @@ -67,8 +66,7 @@ namespace OpenRA.Mods.Common.Widgets world.CancelInputMode(); } else - world.OrderGenerator = new GenericSelectTarget(selectedActors, - "AttackMove", "attackmove", Game.Settings.Game.MouseButtonPreference.Action); + world.OrderGenerator = new AttackMoveOrderGenerator(selectedActors, Game.Settings.Game.MouseButtonPreference.Action); }; attackMoveButton.OnClick = () => toggle(true); @@ -97,7 +95,9 @@ namespace OpenRA.Mods.Common.Widgets BindButtonIcon(forceAttackButton); forceAttackButton.IsDisabled = () => { UpdateStateIfNecessary(); return forceAttackDisabled; }; - forceAttackButton.IsHighlighted = () => !forceAttackButton.IsDisabled() && IsForceModifiersActive(Modifiers.Ctrl); + forceAttackButton.IsHighlighted = () => !forceAttackButton.IsDisabled() && IsForceModifiersActive(Modifiers.Ctrl) + && !(world.OrderGenerator is AttackMoveOrderGenerator); + forceAttackButton.OnClick = () => { if (forceAttackButton.IsHighlighted()) @@ -201,6 +201,23 @@ namespace OpenRA.Mods.Common.Widgets world.OrderGenerator = new ForceModifiersOrderGenerator(Modifiers.Shift, false); }; } + + var keyOverrides = widget.GetOrNull("MODIFIER_OVERRIDES"); + if (keyOverrides != null) + { + keyOverrides.OnKeyPress = ki => + { + // HACK: enable attack move to be triggered if the ctrl key is pressed + var modified = new Hotkey(ki.Key, ki.Modifiers & ~Modifiers.Ctrl); + if (attackMoveButton.Key.GetValue() == modified) + { + attackMoveButton.OnKeyPress(ki); + return true; + } + + return false; + }; + } } public override void Tick() diff --git a/mods/cnc/chrome/ingame.yaml b/mods/cnc/chrome/ingame.yaml index c4f0126877..a8a9f1f1f2 100644 --- a/mods/cnc/chrome/ingame.yaml +++ b/mods/cnc/chrome/ingame.yaml @@ -293,6 +293,7 @@ Container@PLAYER_WIDGETS: Width: 276 Height: 36 Children: + LogicKeyListener@MODIFIER_OVERRIDES: Button@ATTACK_MOVE: X: 1 Y: 1 diff --git a/mods/cnc/cursors.yaml b/mods/cnc/cursors.yaml index b376477faf..f955af69b6 100644 --- a/mods/cnc/cursors.yaml +++ b/mods/cnc/cursors.yaml @@ -167,6 +167,12 @@ Cursors: attackmove-minimap: Start: 20 Length: 4 + assaultmove: + Start: 12 + Length: 8 + assaultmove-minimap: + Start: 20 + Length: 4 move-blocked: Start: 24 Length: 1 diff --git a/mods/d2k/chrome/ingame-player.yaml b/mods/d2k/chrome/ingame-player.yaml index 969dd2d2ac..ccdacd48b2 100644 --- a/mods/d2k/chrome/ingame-player.yaml +++ b/mods/d2k/chrome/ingame-player.yaml @@ -30,6 +30,7 @@ Container@PLAYER_WIDGETS: Width: 275 Height: 41 Children: + LogicKeyListener@MODIFIER_OVERRIDES: Button@ATTACK_MOVE: Width: 34 Height: 41 diff --git a/mods/d2k/cursors.yaml b/mods/d2k/cursors.yaml index 9b0d1511c0..eda5cde70c 100644 --- a/mods/d2k/cursors.yaml +++ b/mods/d2k/cursors.yaml @@ -138,6 +138,16 @@ Cursors: Length: 8 X: 24 Y: 24 + assaultmove: + Start: 16 + Length: 8 + X: 24 + Y: 24 + assaultmove-minimap: + Start: 16 + Length: 8 + X: 24 + Y: 24 harvest: Start: 16 Length: 8 diff --git a/mods/ra/chrome/ingame-player.yaml b/mods/ra/chrome/ingame-player.yaml index 41a781508d..cf00c561fb 100644 --- a/mods/ra/chrome/ingame-player.yaml +++ b/mods/ra/chrome/ingame-player.yaml @@ -45,6 +45,7 @@ Container@PLAYER_WIDGETS: Width: 275 Height: 26 Children: + LogicKeyListener@MODIFIER_OVERRIDES: Button@ATTACK_MOVE: Logic: AddFactionSuffixLogic Width: 34 diff --git a/mods/ra/cursors.yaml b/mods/ra/cursors.yaml index ee566cfa4f..fd2bf91cf2 100644 --- a/mods/ra/cursors.yaml +++ b/mods/ra/cursors.yaml @@ -217,3 +217,9 @@ Cursors: attackmove-minimap: Start: 4 Length: 4 + assaultmove: + Start: 0 + Length: 4 + assaultmove-minimap: + Start: 4 + Length: 4 diff --git a/mods/ts/chrome/ingame-player.yaml b/mods/ts/chrome/ingame-player.yaml index 4cacd57206..a5da73a7c5 100644 --- a/mods/ts/chrome/ingame-player.yaml +++ b/mods/ts/chrome/ingame-player.yaml @@ -34,6 +34,7 @@ Container@PLAYER_WIDGETS: Width: 275 Height: 26 Children: + LogicKeyListener@MODIFIER_OVERRIDES: Button@ATTACK_MOVE: Width: 35 Height: 32 diff --git a/mods/ts/cursors.yaml b/mods/ts/cursors.yaml index ebaafd448a..6e0e0900d9 100644 --- a/mods/ts/cursors.yaml +++ b/mods/ts/cursors.yaml @@ -108,6 +108,12 @@ Cursors: attackmove-minimap: Start: 68 Length: 5 + assaultmove: + Start: 58 + Length: 5 + assaultmove-minimap: + Start: 68 + Length: 5 harvest: Start: 53 Length: 5