diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj
index f7b5de4468..be87080899 100644
--- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj
+++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj
@@ -559,6 +559,7 @@
+
diff --git a/OpenRA.Mods.Common/Widgets/UnitCommandWidget.cs b/OpenRA.Mods.Common/Widgets/UnitCommandWidget.cs
new file mode 100644
index 0000000000..e5c6947f20
--- /dev/null
+++ b/OpenRA.Mods.Common/Widgets/UnitCommandWidget.cs
@@ -0,0 +1,178 @@
+#region Copyright & License Information
+/*
+ * Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
+ * This file is part of OpenRA, which is free software. It is made
+ * available to you under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version. For more
+ * information, see COPYING.
+ */
+#endregion
+
+using System;
+using System.Drawing;
+using System.Linq;
+using OpenRA.Graphics;
+using OpenRA.Mods.Common.Orders;
+using OpenRA.Mods.Common.Traits;
+using OpenRA.Orders;
+using OpenRA.Primitives;
+using OpenRA.Widgets;
+
+namespace OpenRA.Mods.Common.Widgets
+{
+ /// Contains all functions that are unit-specific.
+ public class UnitCommandWidget : Widget
+ {
+ readonly World world;
+
+ [ObjectCreator.UseCtor]
+ public UnitCommandWidget(World world, WorldRenderer worldRenderer)
+ {
+ this.world = world;
+ }
+
+ public override string GetCursor(int2 pos) { return null; }
+ public override Rectangle GetEventBounds() { return Rectangle.Empty; }
+
+ public override bool HandleKeyPress(KeyInput e)
+ {
+ if (world == null)
+ return false;
+
+ // Put all functions that are valid for observers/spectators inside WorldCommandWidget.
+ if (world.LocalPlayer == null || world.LocalPlayer.Spectating)
+ return false;
+
+ if (world.IsGameOver || !world.Selection.Actors.Any())
+ return false;
+
+ return ProcessInput(e);
+ }
+
+ bool ProcessInput(KeyInput e)
+ {
+ if (e.Event == KeyInputEvent.Down)
+ {
+ var key = Hotkey.FromKeyInput(e);
+ var ks = Game.Settings.Keys;
+
+ if (key == ks.AttackMoveKey)
+ return PerformAttackMove();
+
+ if (key == ks.StopKey)
+ return PerformStop();
+
+ if (key == ks.ScatterKey)
+ return PerformScatter();
+
+ if (key == ks.DeployKey)
+ return PerformDeploy();
+
+ if (key == ks.StanceCycleKey)
+ return PerformStanceCycle();
+
+ if (key == ks.GuardKey)
+ return PerformGuard();
+ }
+
+ return false;
+ }
+
+ // TODO: take ALL this garbage and route it through the OrderTargeter stuff.
+ bool PerformAttackMove()
+ {
+ var actors = world.Selection.Actors
+ .Where(a => a.Owner == world.LocalPlayer)
+ .ToArray();
+
+ if (actors.Any(a => a.Info.HasTraitInfo() && a.Info.HasTraitInfo()))
+ world.OrderGenerator = new GenericSelectTarget(actors,
+ "AttackMove", "attackmove", Game.Settings.Game.MouseButtonPreference.Action);
+
+ return true;
+ }
+
+ void PerformKeyboardOrderOnSelection(Func f)
+ {
+ var orders = world.Selection.Actors
+ .Where(a => a.Owner == world.LocalPlayer && !a.Disposed)
+ .Select(f)
+ .ToArray();
+
+ foreach (var o in orders)
+ world.IssueOrder(o);
+
+ world.PlayVoiceForOrders(orders);
+ }
+
+ bool PerformStop()
+ {
+ PerformKeyboardOrderOnSelection(a => new Order("Stop", a, false));
+ return true;
+ }
+
+ bool PerformScatter()
+ {
+ PerformKeyboardOrderOnSelection(a => new Order("Scatter", a, false));
+ return true;
+ }
+
+ bool PerformDeploy()
+ {
+ // HACK: multiple orders here
+ PerformKeyboardOrderOnSelection(a => new Order("ReturnToBase", a, false));
+ PerformKeyboardOrderOnSelection(a => new Order("DeployTransform", a, false));
+ PerformKeyboardOrderOnSelection(a => new Order("Unload", a, false));
+ PerformKeyboardOrderOnSelection(a => new Order("Detonate", a, false));
+ PerformKeyboardOrderOnSelection(a => new Order("DeployToUpgrade", a, false));
+ return true;
+ }
+
+ bool PerformStanceCycle()
+ {
+ var actor = world.Selection.Actors
+ .Where(a => a.Owner == world.LocalPlayer && !a.Disposed)
+ .Select(a => Pair.New(a, a.TraitOrDefault()))
+ .FirstOrDefault(a => a.Second != null);
+
+ if (actor.First == null)
+ return true;
+
+ var ati = actor.First.Info.TraitInfoOrDefault();
+ if (ati == null || !ati.EnableStances)
+ return false;
+
+ var stances = Enum.GetValues();
+ var nextStance = stances.Concat(stances)
+ .SkipWhile(s => s != actor.Second.PredictedStance)
+ .Skip(1)
+ .First();
+
+ PerformKeyboardOrderOnSelection(a =>
+ {
+ var at = a.TraitOrDefault();
+ if (at != null)
+ at.PredictedStance = nextStance;
+
+ return new Order("SetUnitStance", a, false) { ExtraData = (uint)nextStance };
+ });
+
+ Game.Debug("Unit stance set to: {0}".F(nextStance));
+
+ return true;
+ }
+
+ bool PerformGuard()
+ {
+ var actors = world.Selection.Actors
+ .Where(a => !a.Disposed && a.Owner == world.LocalPlayer);
+
+ if (actors.Any(a => a.Info.HasTraitInfo() && a.Info.HasTraitInfo()))
+ world.OrderGenerator = new GuardOrderGenerator(actors,
+ "Guard", "guard", Game.Settings.Game.MouseButtonPreference.Action);
+
+ return true;
+ }
+ }
+}
diff --git a/OpenRA.Mods.Common/Widgets/WorldCommandWidget.cs b/OpenRA.Mods.Common/Widgets/WorldCommandWidget.cs
index 5be929c83a..da5be78811 100644
--- a/OpenRA.Mods.Common/Widgets/WorldCommandWidget.cs
+++ b/OpenRA.Mods.Common/Widgets/WorldCommandWidget.cs
@@ -13,15 +13,13 @@ using System;
using System.Drawing;
using System.Linq;
using OpenRA.Graphics;
-using OpenRA.Mods.Common.Orders;
using OpenRA.Mods.Common.Traits;
-using OpenRA.Orders;
-using OpenRA.Primitives;
using OpenRA.Traits;
using OpenRA.Widgets;
namespace OpenRA.Mods.Common.Widgets
{
+ /// Contains all functions that are valid for players and observers/spectators.
public class WorldCommandWidget : Widget
{
readonly World world;
@@ -65,133 +63,11 @@ namespace OpenRA.Mods.Common.Widgets
if (key == ks.ToSelectionKey)
return ToSelection();
-
- // Put all functions that are valid for observers/spectators above this line.
- if (world.LocalPlayer == null || world.LocalPlayer.Spectating)
- return false;
-
- // Put all functions that aren't unit-specific before this line!
- if (!world.Selection.Actors.Any() || world.IsGameOver)
- return false;
-
- if (key == ks.AttackMoveKey)
- return PerformAttackMove();
-
- if (key == ks.StopKey)
- return PerformStop();
-
- if (key == ks.ScatterKey)
- return PerformScatter();
-
- if (key == ks.DeployKey)
- return PerformDeploy();
-
- if (key == ks.StanceCycleKey)
- return PerformStanceCycle();
-
- if (key == ks.GuardKey)
- return PerformGuard();
}
return false;
}
- // TODO: take ALL this garbage and route it through the OrderTargeter stuff.
- bool PerformAttackMove()
- {
- var actors = world.Selection.Actors
- .Where(a => a.Owner == world.LocalPlayer)
- .ToArray();
-
- if (actors.Any(a => a.Info.HasTraitInfo() && a.Info.HasTraitInfo()))
- world.OrderGenerator = new GenericSelectTarget(actors,
- "AttackMove", "attackmove", Game.Settings.Game.MouseButtonPreference.Action);
-
- return true;
- }
-
- void PerformKeyboardOrderOnSelection(Func f)
- {
- var orders = world.Selection.Actors
- .Where(a => a.Owner == world.LocalPlayer && !a.Disposed)
- .Select(f)
- .ToArray();
-
- foreach (var o in orders)
- world.IssueOrder(o);
-
- world.PlayVoiceForOrders(orders);
- }
-
- bool PerformStop()
- {
- PerformKeyboardOrderOnSelection(a => new Order("Stop", a, false));
- return true;
- }
-
- bool PerformScatter()
- {
- PerformKeyboardOrderOnSelection(a => new Order("Scatter", a, false));
- return true;
- }
-
- bool PerformDeploy()
- {
- // HACK: multiple orders here
- PerformKeyboardOrderOnSelection(a => new Order("ReturnToBase", a, false));
- PerformKeyboardOrderOnSelection(a => new Order("DeployTransform", a, false));
- PerformKeyboardOrderOnSelection(a => new Order("Unload", a, false));
- PerformKeyboardOrderOnSelection(a => new Order("Detonate", a, false));
- PerformKeyboardOrderOnSelection(a => new Order("DeployToUpgrade", a, false));
- return true;
- }
-
- bool PerformStanceCycle()
- {
- var actor = world.Selection.Actors
- .Where(a => a.Owner == world.LocalPlayer && !a.Disposed)
- .Select(a => Pair.New(a, a.TraitOrDefault()))
- .FirstOrDefault(a => a.Second != null);
-
- if (actor.First == null)
- return true;
-
- var ati = actor.First.Info.TraitInfoOrDefault();
- if (ati == null || !ati.EnableStances)
- return false;
-
- var stances = Enum.GetValues();
- var nextStance = stances.Concat(stances)
- .SkipWhile(s => s != actor.Second.PredictedStance)
- .Skip(1)
- .First();
-
- PerformKeyboardOrderOnSelection(a =>
- {
- var at = a.TraitOrDefault();
- if (at != null)
- at.PredictedStance = nextStance;
-
- return new Order("SetUnitStance", a, false) { ExtraData = (uint)nextStance };
- });
-
- Game.Debug("Unit stance set to: {0}".F(nextStance));
-
- return true;
- }
-
- bool PerformGuard()
- {
- var actors = world.Selection.Actors
- .Where(a => !a.Disposed && a.Owner == world.LocalPlayer);
-
- if (actors.Any(a => a.Info.HasTraitInfo() && a.Info.HasTraitInfo()))
- world.OrderGenerator = new GuardOrderGenerator(actors,
- "Guard", "guard", Game.Settings.Game.MouseButtonPreference.Action);
-
- return true;
- }
-
bool CycleBases()
{
var player = world.RenderPlayer ?? world.LocalPlayer;
diff --git a/mods/cnc/chrome/ingame.yaml b/mods/cnc/chrome/ingame.yaml
index 44d24487d9..8b09519a1e 100644
--- a/mods/cnc/chrome/ingame.yaml
+++ b/mods/cnc/chrome/ingame.yaml
@@ -235,6 +235,9 @@ Container@PLAYER_WIDGETS:
LogicKeyListener@CONTROLGROUP_KEYHANDLER:
Logic: ControlGroupLogic
LogicTicker@SIDEBAR_TICKER:
+ UnitCommand:
+ Width: WINDOW_RIGHT
+ Height: WINDOW_BOTTOM
Container@SUPPORT_POWERS:
Logic: SupportPowerBinLogic
X: 10
diff --git a/mods/d2k/chrome/ingame-player.yaml b/mods/d2k/chrome/ingame-player.yaml
index 6edba7d1ec..659a51d73a 100644
--- a/mods/d2k/chrome/ingame-player.yaml
+++ b/mods/d2k/chrome/ingame-player.yaml
@@ -3,6 +3,9 @@ Container@PLAYER_WIDGETS:
LogicKeyListener@CONTROLGROUP_KEYHANDLER:
Logic: ControlGroupLogic
LogicTicker@SIDEBAR_TICKER:
+ UnitCommand:
+ Width: WINDOW_RIGHT
+ Height: WINDOW_BOTTOM
Container@SUPPORT_POWERS:
Logic: SupportPowerBinLogic
X: 10
diff --git a/mods/ra/chrome/ingame-player.yaml b/mods/ra/chrome/ingame-player.yaml
index 4bdae9a94a..f1ac520c14 100644
--- a/mods/ra/chrome/ingame-player.yaml
+++ b/mods/ra/chrome/ingame-player.yaml
@@ -3,6 +3,9 @@ Container@PLAYER_WIDGETS:
LogicKeyListener@CONTROLGROUP_KEYHANDLER:
Logic: ControlGroupLogic
LogicTicker@SIDEBAR_TICKER:
+ UnitCommand:
+ Width: WINDOW_RIGHT
+ Height: WINDOW_BOTTOM
Container@SUPPORT_POWERS:
Logic: SupportPowerBinLogic
X: 10