From 104b520b21a3ac7419f90409fb998c043bd3d4ff Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sat, 22 Mar 2014 16:02:11 +1300 Subject: [PATCH 1/6] Divide the shroud selector into groups. --- .../Logic/ObserverShroudSelectorLogic.cs | 40 +++++++++++++++---- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/OpenRA.Mods.RA/Widgets/Logic/ObserverShroudSelectorLogic.cs b/OpenRA.Mods.RA/Widgets/Logic/ObserverShroudSelectorLogic.cs index 51e19b1aba..1c0d1cca15 100644 --- a/OpenRA.Mods.RA/Widgets/Logic/ObserverShroudSelectorLogic.cs +++ b/OpenRA.Mods.RA/Widgets/Logic/ObserverShroudSelectorLogic.cs @@ -9,7 +9,9 @@ #endregion using System; +using System.Collections.Generic; using System.Linq; +using OpenRA.Network; using OpenRA.Widgets; namespace OpenRA.Mods.RA.Widgets.Logic @@ -32,18 +34,40 @@ namespace OpenRA.Mods.RA.Widgets.Logic static string LabelForPlayer(Player p) { - return p != null ? p.PlayerName == "Everyone" ? "Combined view" : "{0}'s view".F(p.PlayerName) : "World view"; + if (p == null) + return "Disable shroud"; + + if (p.InternalName == "Everyone") + return "Combined view"; + + return p.PlayerName; } [ObjectCreator.UseCtor] public ObserverShroudSelectorLogic(Widget widget, World world) { - var views = world.Players.Where(p => (p.NonCombatant && p.Spectating) - || !p.NonCombatant).Concat(new[] { (Player)null }).Select( - p => new CameraOption(LabelForPlayer(p), - () => world.RenderPlayer == p, - () => world.RenderPlayer = p - )).ToArray(); + var groups = new Dictionary>(); + + var teams = world.Players.Where(p => !p.NonCombatant) + .GroupBy(p => (world.LobbyInfo.ClientWithIndex(p.ClientIndex) ?? new Session.Client()).Team).OrderBy(g => g.Key); + var noTeams = teams.Count() == 1; + + foreach (var t in teams) + { + var team = t.Select(p => new CameraOption(LabelForPlayer(p), + () => world.RenderPlayer == p, + () => world.RenderPlayer = p)); + + var label = noTeams ? "Players" : t.Key == 0 ? "No Team" : "Team {0}".F(t.Key); + groups.Add(label, team); + } + + var combined = world.Players.First(p => p.InternalName == "Everyone"); + groups.Add("Other", new List() + { + new CameraOption(LabelForPlayer(combined), () => world.RenderPlayer == combined, () => world.RenderPlayer = combined), + new CameraOption(LabelForPlayer(null), () => world.RenderPlayer == null, () => world.RenderPlayer = null) + }); var shroudSelector = widget.Get("SHROUD_SELECTOR"); shroudSelector.GetText = () => LabelForPlayer(world.RenderPlayer); @@ -55,7 +79,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic item.Get("LABEL").GetText = () => option.Label; return item; }; - shroudSelector.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", views.Length * 30, views, setupItem); + shroudSelector.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 400, groups, setupItem); }; } } From 2c8fc4603a5eeebb66d97a970e62b8eb54dfeb26 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sat, 22 Mar 2014 16:20:06 +1300 Subject: [PATCH 2/6] Add flags and player colours to the observer selector. --- .../Logic/ObserverShroudSelectorLogic.cs | 89 +++++++++----- mods/cnc/chrome/dialogs.yaml | 37 ++++++ mods/cnc/chrome/ingame.yaml | 14 +++ mods/d2k/chrome/dropdowns.yaml | 80 +++++++++++++ mods/d2k/chrome/ingame-observer.yaml | 113 ++++++++++++++++++ mods/d2k/mod.yaml | 4 +- mods/ra/chrome/dropdowns.yaml | 39 +++++- mods/ra/chrome/ingame-observer.yaml | 14 +++ 8 files changed, 360 insertions(+), 30 deletions(-) create mode 100644 mods/d2k/chrome/dropdowns.yaml create mode 100644 mods/d2k/chrome/ingame-observer.yaml diff --git a/OpenRA.Mods.RA/Widgets/Logic/ObserverShroudSelectorLogic.cs b/OpenRA.Mods.RA/Widgets/Logic/ObserverShroudSelectorLogic.cs index 1c0d1cca15..8ad31630a8 100644 --- a/OpenRA.Mods.RA/Widgets/Logic/ObserverShroudSelectorLogic.cs +++ b/OpenRA.Mods.RA/Widgets/Logic/ObserverShroudSelectorLogic.cs @@ -10,6 +10,7 @@ using System; using System.Collections.Generic; +using System.Drawing; using System.Linq; using OpenRA.Network; using OpenRA.Widgets; @@ -18,31 +19,35 @@ namespace OpenRA.Mods.RA.Widgets.Logic { public class ObserverShroudSelectorLogic { + CameraOption selected; + class CameraOption { - public string Label; - public Func IsSelected; - public Action OnClick; + public readonly string Label; + public readonly Color Color; + public readonly string Race; + public readonly Func IsSelected; + public readonly Action OnClick; - public CameraOption(string label, Func isSelected, Action onClick) + public CameraOption(ObserverShroudSelectorLogic logic, Player p) + { + Label = p.PlayerName; + Color = p.Color.RGB; + Race = p.Country.Race; + IsSelected = () => p.World.RenderPlayer == p; + OnClick = () => { p.World.RenderPlayer = p; logic.selected = this; }; + } + + public CameraOption(ObserverShroudSelectorLogic logic, World w, string label, Player p) { Label = label; - IsSelected = isSelected; - OnClick = onClick; + Color = Color.White; + Race = null; + IsSelected = () => w.RenderPlayer == p; + OnClick = () => { w.RenderPlayer = p; logic.selected = this; }; } } - static string LabelForPlayer(Player p) - { - if (p == null) - return "Disable shroud"; - - if (p.InternalName == "Everyone") - return "Combined view"; - - return p.PlayerName; - } - [ObjectCreator.UseCtor] public ObserverShroudSelectorLogic(Widget widget, World world) { @@ -54,33 +59,63 @@ namespace OpenRA.Mods.RA.Widgets.Logic foreach (var t in teams) { - var team = t.Select(p => new CameraOption(LabelForPlayer(p), - () => world.RenderPlayer == p, - () => world.RenderPlayer = p)); - var label = noTeams ? "Players" : t.Key == 0 ? "No Team" : "Team {0}".F(t.Key); - groups.Add(label, team); + groups.Add(label, t.Select(p => new CameraOption(this, p))); } var combined = world.Players.First(p => p.InternalName == "Everyone"); + var disableShroud = new CameraOption(this, world, "Disable Shroud", null); groups.Add("Other", new List() { - new CameraOption(LabelForPlayer(combined), () => world.RenderPlayer == combined, () => world.RenderPlayer = combined), - new CameraOption(LabelForPlayer(null), () => world.RenderPlayer == null, () => world.RenderPlayer = null) + new CameraOption(this, world, "All Players", combined), + disableShroud }); var shroudSelector = widget.Get("SHROUD_SELECTOR"); - shroudSelector.GetText = () => LabelForPlayer(world.RenderPlayer); shroudSelector.OnMouseDown = _ => { Func setupItem = (option, template) => { var item = ScrollItemWidget.Setup(template, option.IsSelected, option.OnClick); - item.Get("LABEL").GetText = () => option.Label; + var showFlag = option.Race != null; + + var label = item.Get("LABEL"); + label.IsVisible = () => showFlag; + label.GetText = () => option.Label; + label.GetColor = () => option.Color; + + var flag = item.Get("FLAG"); + flag.IsVisible = () => showFlag; + flag.GetImageCollection = () => "flags"; + flag.GetImageName = () => option.Race; + + var labelAlt = item.Get("NOFLAG_LABEL"); + labelAlt.IsVisible = () => !showFlag; + labelAlt.GetText = () => option.Label; + labelAlt.GetColor = () => option.Color; + return item; }; - shroudSelector.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 400, groups, setupItem); + + shroudSelector.ShowDropDown("SPECTATOR_DROPDOWN_TEMPLATE", 400, groups, setupItem); }; + + var shroudLabel = shroudSelector.Get("LABEL"); + shroudLabel.IsVisible = () => selected.Race != null; + shroudLabel.GetText = () => selected.Label; + shroudLabel.GetColor = () => selected.Color; + + var shroudFlag = shroudSelector.Get("FLAG"); + shroudFlag.IsVisible = () => selected.Race != null; + shroudFlag.GetImageCollection = () => "flags"; + shroudFlag.GetImageName = () => selected.Race; + + var shroudLabelAlt = shroudSelector.Get("NOFLAG_LABEL"); + shroudLabelAlt.IsVisible = () => selected.Race == null; + shroudLabelAlt.GetText = () => selected.Label; + shroudLabelAlt.GetColor = () => selected.Color; + + selected = disableShroud; } } } diff --git a/mods/cnc/chrome/dialogs.yaml b/mods/cnc/chrome/dialogs.yaml index 322b8d44af..c10968cc89 100644 --- a/mods/cnc/chrome/dialogs.yaml +++ b/mods/cnc/chrome/dialogs.yaml @@ -64,6 +64,43 @@ ScrollPanel@TEAM_DROPDOWN_TEMPLATE: Height:25 Align:Center +ScrollPanel@SPECTATOR_DROPDOWN_TEMPLATE: + Width:DROPDOWN_WIDTH + Background:panel-black + Children: + ScrollItem@HEADER: + Width:PARENT_RIGHT-27 + Height:13 + X:2 + Y:0 + Visible:false + Children: + Label@LABEL: + Font:TinyBold + Width:PARENT_RIGHT + Height:10 + Align:Center + ScrollItem@TEMPLATE: + Width:PARENT_RIGHT-27 + Height:25 + X:2 + Y:0 + Visible:false + Children: + Image@FLAG: + X:4 + Y:4 + Width:32 + Height:16 + Label@LABEL: + X:40 + Width:60 + Height:25 + Label@NOFLAG_LABEL: + X:5 + Width:PARENT_RIGHT + Height:25 + Container@CONFIRM_PROMPT: X:(WINDOW_RIGHT - WIDTH)/2 Y:(WINDOW_BOTTOM - 90)/2 diff --git a/mods/cnc/chrome/ingame.yaml b/mods/cnc/chrome/ingame.yaml index f264c4cd39..7a98ec4073 100644 --- a/mods/cnc/chrome/ingame.yaml +++ b/mods/cnc/chrome/ingame.yaml @@ -158,6 +158,20 @@ Container@OBSERVER_WIDGETS: Width:168 Height:25 Font:Bold + Children: + Image@FLAG: + X:4 + Y:4 + Width:32 + Height:16 + Label@LABEL: + X:40 + Width:60 + Height:25 + Label@NOFLAG_LABEL: + X:5 + Width:PARENT_RIGHT + Height:25 Container@PLAYER_WIDGETS: Children: diff --git a/mods/d2k/chrome/dropdowns.yaml b/mods/d2k/chrome/dropdowns.yaml new file mode 100644 index 0000000000..450c7e2e14 --- /dev/null +++ b/mods/d2k/chrome/dropdowns.yaml @@ -0,0 +1,80 @@ +ScrollPanel@LABEL_DROPDOWN_TEMPLATE: + Width:DROPDOWN_WIDTH + Children: + ScrollItem@HEADER: + BaseName:scrollheader + Width:PARENT_RIGHT-27 + Height:13 + X:2 + Y:0 + Visible:false + Children: + Label@LABEL: + Font:TinyBold + Width:PARENT_RIGHT + Height:10 + Align:Center + ScrollItem@TEMPLATE: + Width:PARENT_RIGHT-27 + Height:25 + X:2 + Y:0 + Visible:false + Children: + Label@LABEL: + X:10 + Width:PARENT_RIGHT-20 + Height:25 + +ScrollPanel@TEAM_DROPDOWN_TEMPLATE: + Width:DROPDOWN_WIDTH + Children: + ScrollItem@TEMPLATE: + Width:PARENT_RIGHT-27 + Height:25 + X:2 + Y:0 + Visible:false + Children: + Label@LABEL: + X:0 + Width:PARENT_RIGHT + Height:25 + Align:Center + +ScrollPanel@SPECTATOR_DROPDOWN_TEMPLATE: + Width:DROPDOWN_WIDTH + Children: + ScrollItem@HEADER: + BaseName:scrollheader + Width:PARENT_RIGHT-27 + Height:13 + X:2 + Y:0 + Visible:false + Children: + Label@LABEL: + Font:TinyBold + Width:PARENT_RIGHT + Height:10 + Align:Center + ScrollItem@TEMPLATE: + Width:PARENT_RIGHT-27 + Height:25 + X:2 + Y:0 + Visible:false + Children: + Image@FLAG: + Width:23 + Height:23 + X:4 + Y:2 + Label@LABEL: + X:34 + Width:60 + Height:25 + Label@NOFLAG_LABEL: + X:5 + Width:PARENT_RIGHT + Height:25 diff --git a/mods/d2k/chrome/ingame-observer.yaml b/mods/d2k/chrome/ingame-observer.yaml new file mode 100644 index 0000000000..e72dd8a65d --- /dev/null +++ b/mods/d2k/chrome/ingame-observer.yaml @@ -0,0 +1,113 @@ +Container@OBSERVER_WIDGETS: + Children: + Button@INGAME_STATS_BUTTON: + X:162 + Y:0 + Width:160 + Height:25 + Text:Statistics (F1) + Font:Bold + Key:f1 + Background@RADAR_BG: + X:WINDOW_RIGHT-255 + Y:5 + Width:250 + Height:250 + Children: + Radar@INGAME_RADAR: + X:10 + Y:10 + Width:PARENT_RIGHT-19 + Height:PARENT_BOTTOM-19 + WorldInteractionController:INTERACTION_CONTROLLER + Background@OBSERVER_CONTROL_BG: + X:WINDOW_RIGHT-255 + Y:260 + Width:250 + Height:55 + Children: + DropDownButton@SHROUD_SELECTOR: + Logic:ObserverShroudSelectorLogic + X:15 + Y:15 + Width:220 + Height:25 + Font:Bold + Children: + Image@FLAG: + Width:23 + Height:23 + X:4 + Y:2 + Label@LABEL: + X:34 + Width:60 + Height:25 + Label@NOFLAG_LABEL: + X:5 + Width:PARENT_RIGHT + Height:25 + Container@REPLAY_PLAYER: + Logic:ReplayControlBarLogic + X:PARENT_RIGHT/2 - 80 + Y:35 + Width:160 + Height:35 + Visible:false + Children: + Button@BUTTON_PAUSE: + X:15 + Y:15 + Width:25 + Height:25 + IgnoreChildMouseOver:true + Children: + Image@IMAGE_PAUSE: + X:0 + Y:0 + Width:25 + Height:25 + ImageCollection:music + ImageName:pause + Button@BUTTON_SLOW: + X:50 + Y:15 + Width:25 + Height:25 + IgnoreChildMouseOver:true + Children: + Image@IMAGE_SLOW: + X:4 + Y:0 + Width:25 + Height:25 + ImageCollection:music + ImageName:slowmo + Button@BUTTON_NORMALSPEED: + X:85 + Y:15 + Width:25 + Height:25 + IgnoreChildMouseOver:true + Children: + Image@IMAGE_PLAY: + X:0 + Y:0 + Width:25 + Height:25 + ImageCollection:music + ImageName:play + Button@BUTTON_FASTFORWARD: + X:120 + Y:15 + Width:25 + Height:25 + IgnoreChildMouseOver:true + Children: + Image@IMAGE_FASTFORWARD: + X:4 + Y:0 + Width:25 + Height:25 + ImageCollection:music + ImageName:fastforward diff --git a/mods/d2k/mod.yaml b/mods/d2k/mod.yaml index f8e876d9c9..6fab881eb4 100644 --- a/mods/d2k/mod.yaml +++ b/mods/d2k/mod.yaml @@ -62,7 +62,7 @@ ChromeLayout: mods/ra/chrome/ingame-diplomacy.yaml mods/ra/chrome/ingame-fmvplayer.yaml mods/ra/chrome/ingame-menu.yaml - mods/ra/chrome/ingame-observer.yaml + mods/d2k/chrome/ingame-observer.yaml mods/ra/chrome/ingame-observerstats.yaml mods/d2k/chrome/ingame-player.yaml mods/d2k/chrome/mainmenu.yaml @@ -79,7 +79,7 @@ ChromeLayout: mods/ra/chrome/connection.yaml mods/ra/chrome/directconnect.yaml mods/ra/chrome/replaybrowser.yaml - mods/ra/chrome/dropdowns.yaml + mods/d2k/chrome/dropdowns.yaml mods/ra/chrome/modchooser.yaml mods/ra/chrome/cheats.yaml mods/ra/chrome/musicplayer.yaml diff --git a/mods/ra/chrome/dropdowns.yaml b/mods/ra/chrome/dropdowns.yaml index 92b8ac3c3e..9bcc479319 100644 --- a/mods/ra/chrome/dropdowns.yaml +++ b/mods/ra/chrome/dropdowns.yaml @@ -40,4 +40,41 @@ ScrollPanel@TEAM_DROPDOWN_TEMPLATE: X:0 Width:PARENT_RIGHT Height:25 - Align:Center \ No newline at end of file + Align:Center + +ScrollPanel@SPECTATOR_DROPDOWN_TEMPLATE: + Width:DROPDOWN_WIDTH + Children: + ScrollItem@HEADER: + BaseName:scrollheader + Width:PARENT_RIGHT-27 + Height:13 + X:2 + Y:0 + Visible:false + Children: + Label@LABEL: + Font:TinyBold + Width:PARENT_RIGHT + Height:10 + Align:Center + ScrollItem@TEMPLATE: + Width:PARENT_RIGHT-27 + Height:25 + X:2 + Y:0 + Visible:false + Children: + Image@FLAG: + X:4 + Y:4 + Width:32 + Height:16 + Label@LABEL: + X:40 + Width:60 + Height:25 + Label@NOFLAG_LABEL: + X:5 + Width:PARENT_RIGHT + Height:25 diff --git a/mods/ra/chrome/ingame-observer.yaml b/mods/ra/chrome/ingame-observer.yaml index 8d6cd4fc45..ebb3cf3082 100644 --- a/mods/ra/chrome/ingame-observer.yaml +++ b/mods/ra/chrome/ingame-observer.yaml @@ -33,6 +33,20 @@ Container@OBSERVER_WIDGETS: Width:220 Height:25 Font:Bold + Children: + Image@FLAG: + X:4 + Y:4 + Width:32 + Height:16 + Label@LABEL: + X:40 + Width:60 + Height:25 + Label@NOFLAG_LABEL: + X:5 + Width:PARENT_RIGHT + Height:25 Container@REPLAY_PLAYER: Logic:ReplayControlBarLogic X:PARENT_RIGHT/2 - 80 From 8beb9ffc578c5762620ff6c7244991b653c47734 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sat, 22 Mar 2014 22:20:51 +1300 Subject: [PATCH 3/6] Add LogicKeyListenerWidget for binding keys to UI logic. --- OpenRA.Mods.RA/OpenRA.Mods.RA.csproj | 1 + .../Widgets/LogicKeyListenerWidget.cs | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 OpenRA.Mods.RA/Widgets/LogicKeyListenerWidget.cs diff --git a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj index 869c7ea7e7..29356da817 100644 --- a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj +++ b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj @@ -489,6 +489,7 @@ + diff --git a/OpenRA.Mods.RA/Widgets/LogicKeyListenerWidget.cs b/OpenRA.Mods.RA/Widgets/LogicKeyListenerWidget.cs new file mode 100644 index 0000000000..5fca8c630d --- /dev/null +++ b/OpenRA.Mods.RA/Widgets/LogicKeyListenerWidget.cs @@ -0,0 +1,25 @@ +#region Copyright & License Information +/* + * Copyright 2007-2014 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. For more information, + * see COPYING. + */ +#endregion + +using System; +using OpenRA.Widgets; + +namespace OpenRA.Mods.RA.Widgets +{ + public class LogicKeyListenerWidget : Widget + { + public Func OnKeyPress = _ => false; + + public override bool HandleKeyPress(KeyInput e) + { + return OnKeyPress(e); + } + } +} From a31cdec87a889e10a0af7b0b9ca1ae72b4aa2557 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sat, 22 Mar 2014 22:32:08 +1300 Subject: [PATCH 4/6] Move control group logic into its own logic class. --- .../WorldInteractionControllerWidget.cs | 8 +---- OpenRA.Mods.RA/OpenRA.Mods.RA.csproj | 1 + .../Widgets/Logic/ControlGroupLogic.cs | 35 +++++++++++++++++++ mods/cnc/chrome/ingame.yaml | 2 ++ mods/d2k/chrome/ingame-player.yaml | 2 ++ mods/ra/chrome/ingame-player.yaml | 2 ++ 6 files changed, 43 insertions(+), 7 deletions(-) create mode 100644 OpenRA.Mods.RA/Widgets/Logic/ControlGroupLogic.cs diff --git a/OpenRA.Game/Widgets/WorldInteractionControllerWidget.cs b/OpenRA.Game/Widgets/WorldInteractionControllerWidget.cs index 9b2d5b83e9..fc7d673431 100644 --- a/OpenRA.Game/Widgets/WorldInteractionControllerWidget.cs +++ b/OpenRA.Game/Widgets/WorldInteractionControllerWidget.cs @@ -198,13 +198,7 @@ namespace OpenRA.Widgets { if (e.Event == KeyInputEvent.Down) { - if (e.Key >= Keycode.NUMBER_0 && e.Key <= Keycode.NUMBER_9) - { - var group = (int)e.Key - (int)Keycode.NUMBER_0; - World.Selection.DoControlGroup(World, worldRenderer, group, e.Modifiers, e.MultiTapCount); - return true; - } - else if (Hotkey.FromKeyInput(e) == Game.Settings.Keys.PauseKey && World.LocalPlayer != null) // Disable pausing for spectators + if (Hotkey.FromKeyInput(e) == Game.Settings.Keys.PauseKey && World.LocalPlayer != null) // Disable pausing for spectators World.SetPauseState(!World.Paused); else if (Hotkey.FromKeyInput(e) == Game.Settings.Keys.SelectAllUnitsKey) { diff --git a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj index 29356da817..751ef99f70 100644 --- a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj +++ b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj @@ -490,6 +490,7 @@ + diff --git a/OpenRA.Mods.RA/Widgets/Logic/ControlGroupLogic.cs b/OpenRA.Mods.RA/Widgets/Logic/ControlGroupLogic.cs new file mode 100644 index 0000000000..09353fd932 --- /dev/null +++ b/OpenRA.Mods.RA/Widgets/Logic/ControlGroupLogic.cs @@ -0,0 +1,35 @@ +#region Copyright & License Information +/* + * Copyright 2007-2014 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. For more information, + * see COPYING. + */ +#endregion + +using OpenRA.Graphics; +using OpenRA.Widgets; + +namespace OpenRA.Mods.RA.Widgets.Logic +{ + public class ControlGroupLogic + { + [ObjectCreator.UseCtor] + public ControlGroupLogic(Widget widget, World world, WorldRenderer worldRenderer) + { + var keyhandler = widget.Get("CONTROLGROUP_KEYHANDLER"); + keyhandler.OnKeyPress = e => + { + if (e.Key >= Keycode.NUMBER_0 && e.Key <= Keycode.NUMBER_9) + { + var group = (int)e.Key - (int)Keycode.NUMBER_0; + world.Selection.DoControlGroup(world, worldRenderer, group, e.Modifiers, e.MultiTapCount); + return true; + } + + return false; + }; + } + } +} diff --git a/mods/cnc/chrome/ingame.yaml b/mods/cnc/chrome/ingame.yaml index 7a98ec4073..6869282c68 100644 --- a/mods/cnc/chrome/ingame.yaml +++ b/mods/cnc/chrome/ingame.yaml @@ -175,6 +175,8 @@ Container@OBSERVER_WIDGETS: Container@PLAYER_WIDGETS: Children: + LogicKeyListener@CONTROLGROUP_KEYHANDLER: + Logic:ControlGroupLogic LogicTicker@SIDEBAR_TICKER: WorldCommand: Width:WINDOW_RIGHT diff --git a/mods/d2k/chrome/ingame-player.yaml b/mods/d2k/chrome/ingame-player.yaml index 2d4731e64f..b4b2c54a68 100644 --- a/mods/d2k/chrome/ingame-player.yaml +++ b/mods/d2k/chrome/ingame-player.yaml @@ -1,5 +1,7 @@ Container@PLAYER_WIDGETS: Children: + LogicKeyListener@CONTROLGROUP_KEYHANDLER: + Logic:ControlGroupLogic LogicTicker@SIDEBAR_TICKER: Button@INGAME_DIPLOMACY_BUTTON: X:162 diff --git a/mods/ra/chrome/ingame-player.yaml b/mods/ra/chrome/ingame-player.yaml index 7f1313dddb..e50f119644 100644 --- a/mods/ra/chrome/ingame-player.yaml +++ b/mods/ra/chrome/ingame-player.yaml @@ -1,5 +1,7 @@ Container@PLAYER_WIDGETS: Children: + LogicKeyListener@CONTROLGROUP_KEYHANDLER: + Logic:ControlGroupLogic LogicTicker@SIDEBAR_TICKER: Button@INGAME_DIPLOMACY_BUTTON: X:162 From 3a8c94d8f8f59b172d25b6651bdc52409116e0c1 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sat, 22 Mar 2014 21:50:05 +1300 Subject: [PATCH 5/6] Hook up observer view keybindings. Fixes #4435. Uses 0-9 to select view (plus shift to cycle backwards), '-' for combined shroud and '=' for world view. --- OpenRA.Game/GameRules/Settings.cs | 3 + .../Logic/ObserverShroudSelectorLogic.cs | 69 ++++++++++++++++--- OpenRA.Mods.RA/Widgets/Logic/SettingsLogic.cs | 13 ++++ mods/cnc/chrome/ingame.yaml | 1 + mods/d2k/chrome/ingame-observer.yaml | 1 + mods/ra/chrome/ingame-observer.yaml | 1 + 6 files changed, 77 insertions(+), 11 deletions(-) diff --git a/OpenRA.Game/GameRules/Settings.cs b/OpenRA.Game/GameRules/Settings.cs index f1804f9fda..d388883f7f 100644 --- a/OpenRA.Game/GameRules/Settings.cs +++ b/OpenRA.Game/GameRules/Settings.cs @@ -165,6 +165,9 @@ namespace OpenRA.GameRules public Hotkey DeployKey = new Hotkey(Keycode.F, Modifiers.None); public Hotkey StanceCycleKey = new Hotkey(Keycode.Z, Modifiers.None); public Hotkey GuardKey = new Hotkey(Keycode.D, Modifiers.None); + + public Hotkey ObserverCombinedView = new Hotkey(Keycode.MINUS, Modifiers.None); + public Hotkey ObserverWorldView = new Hotkey(Keycode.EQUALS, Modifiers.None); } public class IrcSettings diff --git a/OpenRA.Mods.RA/Widgets/Logic/ObserverShroudSelectorLogic.cs b/OpenRA.Mods.RA/Widgets/Logic/ObserverShroudSelectorLogic.cs index 8ad31630a8..90e5d443c2 100644 --- a/OpenRA.Mods.RA/Widgets/Logic/ObserverShroudSelectorLogic.cs +++ b/OpenRA.Mods.RA/Widgets/Logic/ObserverShroudSelectorLogic.cs @@ -20,9 +20,12 @@ namespace OpenRA.Mods.RA.Widgets.Logic public class ObserverShroudSelectorLogic { CameraOption selected; + CameraOption combined, disableShroud; + IOrderedEnumerable> teams; class CameraOption { + public readonly Player Player; public readonly string Label; public readonly Color Color; public readonly string Race; @@ -31,6 +34,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic public CameraOption(ObserverShroudSelectorLogic logic, Player p) { + Player = p; Label = p.PlayerName; Color = p.Color.RGB; Race = p.Country.Race; @@ -40,6 +44,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic public CameraOption(ObserverShroudSelectorLogic logic, World w, string label, Player p) { + Player = p; Label = label; Color = Color.White; Race = null; @@ -53,23 +58,21 @@ namespace OpenRA.Mods.RA.Widgets.Logic { var groups = new Dictionary>(); - var teams = world.Players.Where(p => !p.NonCombatant) - .GroupBy(p => (world.LobbyInfo.ClientWithIndex(p.ClientIndex) ?? new Session.Client()).Team).OrderBy(g => g.Key); - var noTeams = teams.Count() == 1; + teams = world.Players.Where(p => !p.NonCombatant) + .Select(p => new CameraOption(this, p)) + .GroupBy(p => (world.LobbyInfo.ClientWithIndex(p.Player.ClientIndex) ?? new Session.Client()).Team) + .OrderBy(g => g.Key); + var noTeams = teams.Count() == 1; foreach (var t in teams) { var label = noTeams ? "Players" : t.Key == 0 ? "No Team" : "Team {0}".F(t.Key); - groups.Add(label, t.Select(p => new CameraOption(this, p))); + groups.Add(label, t); } - var combined = world.Players.First(p => p.InternalName == "Everyone"); - var disableShroud = new CameraOption(this, world, "Disable Shroud", null); - groups.Add("Other", new List() - { - new CameraOption(this, world, "All Players", combined), - disableShroud - }); + combined = new CameraOption(this, world, "All Players", world.Players.First(p => p.InternalName == "Everyone")); + disableShroud = new CameraOption(this, world, "Disable Shroud", null); + groups.Add("Other", new List() { combined, disableShroud }); var shroudSelector = widget.Get("SHROUD_SELECTOR"); shroudSelector.OnMouseDown = _ => @@ -115,7 +118,51 @@ namespace OpenRA.Mods.RA.Widgets.Logic shroudLabelAlt.GetText = () => selected.Label; shroudLabelAlt.GetColor = () => selected.Color; + var keyhandler = shroudSelector.Get("SHROUD_KEYHANDLER"); + keyhandler.OnKeyPress = HandleKeyPress; + selected = disableShroud; } + + public bool HandleKeyPress(KeyInput e) + { + if (e.Event == KeyInputEvent.Down) + { + var h = Hotkey.FromKeyInput(e); + if (h == Game.Settings.Keys.ObserverCombinedView) + { + selected = combined; + selected.OnClick(); + + return true; + } + + if (h == Game.Settings.Keys.ObserverWorldView) + { + selected = disableShroud; + selected.OnClick(); + + return true; + } + + if (e.Key >= Keycode.NUMBER_0 && e.Key <= Keycode.NUMBER_9) + { + var key = (int)e.Key - (int)Keycode.NUMBER_0; + var team = teams.Where(t => t.Key == key).SelectMany(s => s); + if (!team.Any()) + return false; + + if (e.Modifiers == Modifiers.Shift) + team = team.Reverse(); + + selected = team.SkipWhile(t => t.Player != selected.Player).Skip(1).FirstOrDefault() ?? team.FirstOrDefault(); + selected.OnClick(); + + return true; + } + } + + return false; + } } } diff --git a/OpenRA.Mods.RA/Widgets/Logic/SettingsLogic.cs b/OpenRA.Mods.RA/Widgets/Logic/SettingsLogic.cs index d59cc7a376..ad4fb07e4c 100644 --- a/OpenRA.Mods.RA/Widgets/Logic/SettingsLogic.cs +++ b/OpenRA.Mods.RA/Widgets/Logic/SettingsLogic.cs @@ -279,6 +279,12 @@ namespace OpenRA.Mods.RA.Widgets.Logic { "GuardKey", "Guard" } }; + var observerHotkeys = new Dictionary() + { + { "ObserverCombinedView", "All Players" }, + { "ObserverWorldView", "Disable Shroud" } + }; + var gs = Game.Settings.Game; var ks = Game.Settings.Keys; @@ -304,6 +310,13 @@ namespace OpenRA.Mods.RA.Widgets.Logic foreach (var kv in specialHotkeys) BindHotkeyPref(kv, ks, globalTemplate, hotkeyList); + var observerHeader = ScrollItemWidget.Setup(hotkeyHeader, () => true, () => {}); + observerHeader.Get("LABEL").GetText = () => "Observer Commands"; + hotkeyList.AddChild(observerHeader); + + foreach (var kv in observerHotkeys) + BindHotkeyPref(kv, ks, globalTemplate, hotkeyList); + var unitHeader = ScrollItemWidget.Setup(hotkeyHeader, () => true, () => {}); unitHeader.Get("LABEL").GetText = () => "Unit Commands"; hotkeyList.AddChild(unitHeader); diff --git a/mods/cnc/chrome/ingame.yaml b/mods/cnc/chrome/ingame.yaml index 6869282c68..4c2c4a8732 100644 --- a/mods/cnc/chrome/ingame.yaml +++ b/mods/cnc/chrome/ingame.yaml @@ -159,6 +159,7 @@ Container@OBSERVER_WIDGETS: Height:25 Font:Bold Children: + LogicKeyListener@SHROUD_KEYHANDLER: Image@FLAG: X:4 Y:4 diff --git a/mods/d2k/chrome/ingame-observer.yaml b/mods/d2k/chrome/ingame-observer.yaml index e72dd8a65d..e6b4c3b3e4 100644 --- a/mods/d2k/chrome/ingame-observer.yaml +++ b/mods/d2k/chrome/ingame-observer.yaml @@ -34,6 +34,7 @@ Container@OBSERVER_WIDGETS: Height:25 Font:Bold Children: + LogicKeyListener@SHROUD_KEYHANDLER: Image@FLAG: Width:23 Height:23 diff --git a/mods/ra/chrome/ingame-observer.yaml b/mods/ra/chrome/ingame-observer.yaml index ebb3cf3082..fd36d32d45 100644 --- a/mods/ra/chrome/ingame-observer.yaml +++ b/mods/ra/chrome/ingame-observer.yaml @@ -34,6 +34,7 @@ Container@OBSERVER_WIDGETS: Height:25 Font:Bold Children: + LogicKeyListener@SHROUD_KEYHANDLER: Image@FLAG: X:4 Y:4 From 71357950f74df47e2e339292487c0ac8412333c7 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sat, 22 Mar 2014 23:01:44 +1300 Subject: [PATCH 6/6] Update changelog. --- CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index ab481d73c9..e8f3987360 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -24,7 +24,8 @@ NEW: Order lines are now shown on unit selection. Fixed chat synchronization in replays. Added a combined shroud view of every player to the replay viewer and spectator mode. - Added pause, slowdown, play and fastforward buttons for replays. + Improved the observer/replay view selector with teams, factions, player colors, and hotkeys. + Added playback speed controls to replays. Fixed the game sometimes crashing when deploying and activating the guard cursor at the same time. Build time is now set when an item reaches the front of a queue, instead of immediately when queued. The attack cursor now changes if the target is out of range.