From 7a93b9ea8c43a1b2fa24201ee224d98299c4cdbc Mon Sep 17 00:00:00 2001 From: Ivaylo Draganov Date: Tue, 14 Sep 2021 22:54:45 +0300 Subject: [PATCH] Make control group hotkeys configurable - Split control groups management to its own interface - Add hotkeys for selecting, creating, adding to and combining with control groups - Add a ControlGroups widget to manage the player interaction --- OpenRA.Game/Traits/TraitsInterfaces.cs | 17 +- OpenRA.Game/World.cs | 2 + OpenRA.Mods.Cnc/Traits/ConyardChronoReturn.cs | 4 +- OpenRA.Mods.Common/Activities/Transform.cs | 4 +- .../WithSpriteControlGroupDecoration.cs | 2 +- .../Render/WithTextControlGroupDecoration.cs | 5 +- .../Traits/World/ControlGroups.cs | 149 +++++++++++++ OpenRA.Mods.Common/Traits/World/Selection.cs | 79 +------ .../Widgets/ControlGroupsWidget.cs | 174 +++++++++++++++ .../Widgets/Logic/Ingame/ControlGroupLogic.cs | 36 ---- .../RemoveFromControlGroupHotkeyLogic.cs | 2 +- mods/cnc/chrome/ingame.yaml | 8 +- mods/cnc/chrome/settings-hotkeys.yaml | 3 + mods/cnc/mod.yaml | 1 + mods/cnc/rules/world.yaml | 1 + mods/cnc/sequences/misc.yaml | 1 + mods/common/chrome/settings-hotkeys.yaml | 3 + mods/common/hotkeys/control-groups.yaml | 203 ++++++++++++++++++ mods/common/hotkeys/game.yaml | 4 - mods/d2k/chrome/ingame-player.yaml | 8 +- mods/d2k/mod.yaml | 1 + mods/d2k/rules/world.yaml | 1 + mods/d2k/sequences/misc.yaml | 2 +- mods/ra/chrome/ingame-player.yaml | 8 +- mods/ra/mod.yaml | 1 + mods/ra/rules/world.yaml | 1 + mods/ra/sequences/misc.yaml | 2 +- mods/ts/chrome/ingame-player.yaml | 8 +- mods/ts/chrome/settings-hotkeys.yaml | 3 + mods/ts/mod.yaml | 1 + mods/ts/rules/world.yaml | 1 + 31 files changed, 598 insertions(+), 137 deletions(-) create mode 100644 OpenRA.Mods.Common/Traits/World/ControlGroups.cs create mode 100644 OpenRA.Mods.Common/Widgets/ControlGroupsWidget.cs delete mode 100644 OpenRA.Mods.Common/Widgets/Logic/Ingame/ControlGroupLogic.cs create mode 100644 mods/common/hotkeys/control-groups.yaml diff --git a/OpenRA.Game/Traits/TraitsInterfaces.cs b/OpenRA.Game/Traits/TraitsInterfaces.cs index 3d4dc5155c..8309b0461a 100644 --- a/OpenRA.Game/Traits/TraitsInterfaces.cs +++ b/OpenRA.Game/Traits/TraitsInterfaces.cs @@ -477,10 +477,25 @@ namespace OpenRA.Traits void Clear(); bool RolloverContains(Actor a); void SetRollover(IEnumerable actors); - void DoControlGroup(World world, WorldRenderer worldRenderer, int group, Modifiers mods, int multiTapCount); + } + + public interface IControlGroupsInfo : ITraitInfoInterface + { + string[] Groups { get; } + } + + public interface IControlGroups + { + string[] Groups { get; } + + void SelectControlGroup(int group); + void CreateControlGroup(int group); + void AddSelectionToControlGroup(int group); + void CombineSelectionWithControlGroup(int group); void AddToControlGroup(Actor a, int group); void RemoveFromControlGroup(Actor a); int? GetControlGroupForActor(Actor a); + IEnumerable GetActorsInControlGroup(int group); } /// diff --git a/OpenRA.Game/World.cs b/OpenRA.Game/World.cs index fd8f15bba4..ca1b71d3ba 100644 --- a/OpenRA.Game/World.cs +++ b/OpenRA.Game/World.cs @@ -158,6 +158,7 @@ namespace OpenRA } public readonly ISelection Selection; + public readonly IControlGroups ControlGroups; public void CancelInputMode() { OrderGenerator = new UnitOrderGenerator(); } @@ -201,6 +202,7 @@ namespace OpenRA ActorMap = WorldActor.Trait(); ScreenMap = WorldActor.Trait(); Selection = WorldActor.Trait(); + ControlGroups = WorldActor.Trait(); OrderValidators = WorldActor.TraitsImplementing().ToArray(); notifyDisconnected = WorldActor.TraitsImplementing().ToArray(); diff --git a/OpenRA.Mods.Cnc/Traits/ConyardChronoReturn.cs b/OpenRA.Mods.Cnc/Traits/ConyardChronoReturn.cs index 1691dd9c35..1d698e5924 100644 --- a/OpenRA.Mods.Cnc/Traits/ConyardChronoReturn.cs +++ b/OpenRA.Mods.Cnc/Traits/ConyardChronoReturn.cs @@ -158,7 +158,7 @@ namespace OpenRA.Mods.Cnc.Traits void ReturnToOrigin() { var selected = self.World.Selection.Contains(self); - var controlgroup = self.World.Selection.GetControlGroupForActor(self); + var controlgroup = self.World.ControlGroups.GetControlGroupForActor(self); var mobileInfo = self.World.Map.Rules.Actors[info.OriginalActor].TraitInfo(); var destination = ChooseBestDestinationCell(mobileInfo, origin); @@ -190,7 +190,7 @@ namespace OpenRA.Mods.Cnc.Traits self.World.Selection.Add(a); if (controlgroup.HasValue) - self.World.Selection.AddToControlGroup(a, controlgroup.Value); + self.World.ControlGroups.AddToControlGroup(a, controlgroup.Value); Game.Sound.Play(SoundType.World, info.ChronoshiftSound, self.World.Map.CenterOfCell(destination.Value)); self.Dispose(); diff --git a/OpenRA.Mods.Common/Activities/Transform.cs b/OpenRA.Mods.Common/Activities/Transform.cs index 858112f0e1..7959738b6b 100644 --- a/OpenRA.Mods.Common/Activities/Transform.cs +++ b/OpenRA.Mods.Common/Activities/Transform.cs @@ -88,7 +88,7 @@ namespace OpenRA.Mods.Common.Activities nt.OnTransform(self); var selected = w.Selection.Contains(self); - var controlgroup = w.Selection.GetControlGroupForActor(self); + var controlgroup = w.ControlGroups.GetControlGroupForActor(self); self.Dispose(); foreach (var s in Sounds) @@ -141,7 +141,7 @@ namespace OpenRA.Mods.Common.Activities w.Selection.Add(a); if (controlgroup.HasValue) - w.Selection.AddToControlGroup(a, controlgroup.Value); + w.ControlGroups.AddToControlGroup(a, controlgroup.Value); }); } } diff --git a/OpenRA.Mods.Common/Traits/Render/WithSpriteControlGroupDecoration.cs b/OpenRA.Mods.Common/Traits/Render/WithSpriteControlGroupDecoration.cs index bd5d646006..12e34c87ee 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithSpriteControlGroupDecoration.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithSpriteControlGroupDecoration.cs @@ -55,7 +55,7 @@ namespace OpenRA.Mods.Common.Traits.Render IEnumerable IDecoration.RenderDecoration(Actor self, WorldRenderer wr, ISelectionDecorations container) { - var group = self.World.Selection.GetControlGroupForActor(self); + var group = self.World.ControlGroups.GetControlGroupForActor(self); if (group == null) return Enumerable.Empty(); diff --git a/OpenRA.Mods.Common/Traits/Render/WithTextControlGroupDecoration.cs b/OpenRA.Mods.Common/Traits/Render/WithTextControlGroupDecoration.cs index 8a9a6d61a8..7562a106dc 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithTextControlGroupDecoration.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithTextControlGroupDecoration.cs @@ -60,14 +60,15 @@ namespace OpenRA.Mods.Common.Traits.Render this.self = self; font = Game.Renderer.Fonts[info.Font]; color = info.UsePlayerColor ? self.Owner.Color : info.Color; - label = new CachedTransform(g => g.ToString()); + + label = new CachedTransform(g => self.World.ControlGroups.Groups[g]); } bool IDecoration.RequiresSelection => true; IEnumerable IDecoration.RenderDecoration(Actor self, WorldRenderer wr, ISelectionDecorations container) { - var group = self.World.Selection.GetControlGroupForActor(self); + var group = self.World.ControlGroups.GetControlGroupForActor(self); if (group == null) return Enumerable.Empty(); diff --git a/OpenRA.Mods.Common/Traits/World/ControlGroups.cs b/OpenRA.Mods.Common/Traits/World/ControlGroups.cs new file mode 100644 index 0000000000..6b09c5dfa1 --- /dev/null +++ b/OpenRA.Mods.Common/Traits/World/ControlGroups.cs @@ -0,0 +1,149 @@ +#region Copyright & License Information +/* + * Copyright 2007-2021 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.Collections.Generic; +using System.Linq; +using OpenRA.Traits; + +namespace OpenRA.Mods.Common.Traits +{ + public class ControlGroupsInfo : TraitInfo, IControlGroupsInfo + { + public readonly string[] Groups = { "1", "2", "3", "4", "5", "6", "7", "8", "9", "0" }; + + public override object Create(ActorInitializer init) { return new ControlGroups(init.World, this); } + + string[] IControlGroupsInfo.Groups => Groups; + } + + [TraitLocation(SystemActors.World | SystemActors.EditorWorld)] + public class ControlGroups : IControlGroups, ITick, IGameSaveTraitData + { + readonly World world; + public string[] Groups { get; private set; } + + readonly List[] controlGroups; + + public ControlGroups(World world, ControlGroupsInfo info) + { + this.world = world; + Groups = info.Groups; + controlGroups = Enumerable.Range(0, Groups.Length).Select(_ => new List()).ToArray(); + } + + public void SelectControlGroup(int group) + { + world.Selection.Combine(world, GetActorsInControlGroup(group), false, false); + } + + public void CreateControlGroup(int group) + { + if (!world.Selection.Actors.Any()) + return; + + controlGroups[group].Clear(); + + RemoveActorsFromAllControlGroups(world.Selection.Actors); + + controlGroups[group].AddRange(world.Selection.Actors.Where(a => a.Owner == world.LocalPlayer)); + } + + public void AddSelectionToControlGroup(int group) + { + if (!world.Selection.Actors.Any()) + return; + + RemoveActorsFromAllControlGroups(world.Selection.Actors); + + controlGroups[group].AddRange(world.Selection.Actors.Where(a => a.Owner == world.LocalPlayer)); + } + + public void CombineSelectionWithControlGroup(int group) + { + world.Selection.Combine(world, GetActorsInControlGroup(group), true, false); + } + + public void AddToControlGroup(Actor a, int group) + { + if (!controlGroups[group].Contains(a)) + controlGroups[group].Add(a); + } + + public void RemoveFromControlGroup(Actor a) + { + var group = GetControlGroupForActor(a); + if (group.HasValue) + controlGroups[group.Value].Remove(a); + } + + public int? GetControlGroupForActor(Actor a) + { + for (var i = 0; i < controlGroups.Length; i++) + if (controlGroups[i].Contains(a)) + return i; + + return null; + } + + void RemoveActorsFromAllControlGroups(IEnumerable actors) + { + for (var i = 0; i < Groups.Length; i++) + controlGroups[i].RemoveAll(a => actors.Contains(a)); + } + + public IEnumerable GetActorsInControlGroup(int group) + { + return controlGroups[group].Where(a => a.IsInWorld); + } + + void ITick.Tick(Actor self) + { + foreach (var cg in controlGroups) + { + // note: NOT `!a.IsInWorld`, since that would remove things that are in transports. + cg.RemoveAll(a => a.Disposed || a.Owner != world.LocalPlayer); + } + } + + List IGameSaveTraitData.IssueTraitData(Actor self) + { + var groups = new List(); + for (var i = 0; i < controlGroups.Length; i++) + { + var cg = controlGroups[i]; + if (cg.Any()) + { + var actorIds = cg.Select(a => a.ActorID).ToArray(); + groups.Add(new MiniYamlNode(i.ToString(), FieldSaver.FormatValue(actorIds))); + } + } + + return new List() + { + new MiniYamlNode("Groups", new MiniYaml("", groups)) + }; + } + + void IGameSaveTraitData.ResolveTraitData(Actor self, List data) + { + var groupsNode = data.FirstOrDefault(n => n.Key == "Groups"); + if (groupsNode != null) + { + foreach (var n in groupsNode.Value.Nodes) + { + var group = FieldLoader.GetValue(n.Key, n.Value.Value) + .Select(a => self.World.GetActorById(a)).Where(a => a != null); + controlGroups[int.Parse(n.Key)].AddRange(group); + } + } + } + } +} diff --git a/OpenRA.Mods.Common/Traits/World/Selection.cs b/OpenRA.Mods.Common/Traits/World/Selection.cs index b048fa054b..79097b48b7 100644 --- a/OpenRA.Mods.Common/Traits/World/Selection.cs +++ b/OpenRA.Mods.Common/Traits/World/Selection.cs @@ -11,8 +11,6 @@ using System.Collections.Generic; using System.Linq; -using OpenRA.Graphics; -using OpenRA.Primitives; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits @@ -174,77 +172,13 @@ namespace OpenRA.Mods.Common.Traits foreach (var ns in worldNotifySelection) ns.SelectionChanged(); } - - foreach (var cg in controlGroups.Values) - { - // note: NOT `!a.IsInWorld`, since that would remove things that are in transports. - cg.RemoveAll(a => a.Disposed || a.Owner != world.LocalPlayer); - } - } - - readonly Cache> controlGroups = new Cache>(_ => new List()); - - public void DoControlGroup(World world, WorldRenderer worldRenderer, int group, Modifiers mods, int multiTapCount) - { - var addModifier = Platform.CurrentPlatform == PlatformType.OSX ? Modifiers.Meta : Modifiers.Ctrl; - if (mods.HasModifier(addModifier)) - { - if (actors.Count == 0) - return; - - if (!mods.HasModifier(Modifiers.Shift)) - controlGroups[group].Clear(); - - for (var i = 0; i < 10; i++) // all control groups - controlGroups[i].RemoveAll(a => actors.Contains(a)); - - controlGroups[group].AddRange(actors.Where(a => a.Owner == world.LocalPlayer)); - return; - } - - var groupActors = controlGroups[group].Where(a => a.IsInWorld); - - if (mods.HasModifier(Modifiers.Alt) || multiTapCount >= 2) - { - worldRenderer.Viewport.Center(groupActors); - return; - } - - Combine(world, groupActors, mods.HasModifier(Modifiers.Shift), false); - } - - public void AddToControlGroup(Actor a, int group) - { - if (!controlGroups[group].Contains(a)) - controlGroups[group].Add(a); - } - - public void RemoveFromControlGroup(Actor a) - { - var group = GetControlGroupForActor(a); - if (group.HasValue) - controlGroups[group.Value].Remove(a); - } - - public int? GetControlGroupForActor(Actor a) - { - return controlGroups.Where(g => g.Value.Contains(a)) - .Select(g => (int?)g.Key) - .FirstOrDefault(); } List IGameSaveTraitData.IssueTraitData(Actor self) { - var groups = controlGroups - .Where(cg => cg.Value.Any()) - .Select(cg => new MiniYamlNode(cg.Key.ToString(), - FieldSaver.FormatValue(cg.Value.Select(a => a.ActorID).ToArray()))) - .ToList(); - return new List() { - new MiniYamlNode("Selection", FieldSaver.FormatValue(Actors.Select(a => a.ActorID).ToArray())), - new MiniYamlNode("Groups", new MiniYaml("", groups)) + new MiniYamlNode("Selection", FieldSaver.FormatValue(Actors.Select(a => a.ActorID).ToArray())) }; } @@ -257,17 +191,6 @@ namespace OpenRA.Mods.Common.Traits .Select(a => self.World.GetActorById(a)).Where(a => a != null); Combine(self.World, selected, false, false); } - - var groupsNode = data.FirstOrDefault(n => n.Key == "Groups"); - if (groupsNode != null) - { - foreach (var n in groupsNode.Value.Nodes) - { - var group = FieldLoader.GetValue(n.Key, n.Value.Value) - .Select(a => self.World.GetActorById(a)).Where(a => a != null); - controlGroups[int.Parse(n.Key)].AddRange(group); - } - } } } } diff --git a/OpenRA.Mods.Common/Widgets/ControlGroupsWidget.cs b/OpenRA.Mods.Common/Widgets/ControlGroupsWidget.cs new file mode 100644 index 0000000000..d9ec5457a8 --- /dev/null +++ b/OpenRA.Mods.Common/Widgets/ControlGroupsWidget.cs @@ -0,0 +1,174 @@ +#region Copyright & License Information +/* + * Copyright 2007-2021 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.Collections.Generic; +using System.Linq; +using OpenRA.Graphics; +using OpenRA.Mods.Common.Lint; +using OpenRA.Traits; +using OpenRA.Widgets; + +namespace OpenRA.Mods.Common.Widgets +{ + public class ControlGroupsWidget : Widget + { + readonly ModData modData; + readonly World world; + readonly WorldRenderer worldRenderer; + readonly int hotkeyCount; + + HotkeyReference[] selectGroupHotkeys; + HotkeyReference[] createGroupHotkeys; + HotkeyReference[] addToGroupHotkeys; + HotkeyReference[] combineWithGroupHotkeys; + HotkeyReference[] jumpToGroupHotkeys; + + // Note: LinterHotkeyNames assumes that these are disabled by default + public readonly string SelectGroupKeyPrefix = null; + public readonly string CreateGroupKeyPrefix = null; + public readonly string AddToGroupKeyPrefix = null; + public readonly string CombineWithGroupKeyPrefix = null; + public readonly string JumpToGroupKeyPrefix = null; + + [CustomLintableHotkeyNames] + public static IEnumerable LinterHotkeyNames(MiniYamlNode widgetNode, Action emitError, Action emitWarning) + { + var count = Game.ModData.DefaultRules.Actors[SystemActors.World].TraitInfo().Groups.Length; + if (count == 0) + yield break; + + var selectPrefix = ""; + var selectPrefixNode = widgetNode.Value.Nodes.FirstOrDefault(n => n.Key == "SelectGroupKeyPrefix"); + if (selectPrefixNode != null) + selectPrefix = selectPrefixNode.Value.Value; + + var createPrefix = ""; + var createPrefixNode = widgetNode.Value.Nodes.FirstOrDefault(n => n.Key == "CreateGroupKeyPrefix"); + if (createPrefixNode != null) + createPrefix = createPrefixNode.Value.Value; + + var addToPrefix = ""; + var addToPrefixNode = widgetNode.Value.Nodes.FirstOrDefault(n => n.Key == "AddToGroupKeyPrefix"); + if (addToPrefixNode != null) + addToPrefix = addToPrefixNode.Value.Value; + + var combineWithPrefix = ""; + var combineWithPrefixNode = widgetNode.Value.Nodes.FirstOrDefault(n => n.Key == "CombineWithGroupKeyPrefix"); + if (combineWithPrefixNode != null) + combineWithPrefix = combineWithPrefixNode.Value.Value; + + var jumpToPrefix = ""; + var jumpToPrefixNode = widgetNode.Value.Nodes.FirstOrDefault(n => n.Key == "JumpToGroupKeyPrefix"); + if (jumpToPrefixNode != null) + jumpToPrefix = jumpToPrefixNode.Value.Value; + + if (string.IsNullOrEmpty(selectPrefix)) + emitError($"{widgetNode.Location} must define SelectGroupKeyPrefix if control groups count is greater than 0."); + + if (string.IsNullOrEmpty(createPrefix)) + emitError($"{widgetNode.Location} must define CreateGroupKeyPrefix if control groups count is greater than 0."); + + if (string.IsNullOrEmpty(addToPrefix)) + emitError($"{widgetNode.Location} must define AddToGroupKeyPrefix if control groups count is greater than 0."); + + if (string.IsNullOrEmpty(combineWithPrefix)) + emitError($"{widgetNode.Location} must define CombineWithGroupKeyPrefix if control groups count is greater than 0."); + + if (string.IsNullOrEmpty(jumpToPrefix)) + emitError($"{widgetNode.Location} must define JumpToGroupKeyPrefix if control groups count is greater than 0."); + + for (var i = 0; i < count; i++) + { + var suffix = (i + 1).ToString("D2"); + yield return selectPrefix + suffix; + yield return createPrefix + suffix; + yield return addToPrefix + suffix; + yield return combineWithPrefix + suffix; + yield return jumpToPrefix + suffix; + } + } + + [ObjectCreator.UseCtor] + public ControlGroupsWidget(ModData modData, World world, WorldRenderer worldRenderer) + { + this.modData = modData; + this.world = world; + this.worldRenderer = worldRenderer; + hotkeyCount = world.ControlGroups.Groups.Length; + } + + public override void Initialize(WidgetArgs args) + { + base.Initialize(args); + + selectGroupHotkeys = Exts.MakeArray(hotkeyCount, + i => modData.Hotkeys[SelectGroupKeyPrefix + (i + 1).ToString("D2")]); + + createGroupHotkeys = Exts.MakeArray(hotkeyCount, + i => modData.Hotkeys[CreateGroupKeyPrefix + (i + 1).ToString("D2")]); + + addToGroupHotkeys = Exts.MakeArray(hotkeyCount, + i => modData.Hotkeys[AddToGroupKeyPrefix + (i + 1).ToString("D2")]); + + combineWithGroupHotkeys = Exts.MakeArray(hotkeyCount, + i => modData.Hotkeys[CombineWithGroupKeyPrefix + (i + 1).ToString("D2")]); + + jumpToGroupHotkeys = Exts.MakeArray(hotkeyCount, + i => modData.Hotkeys[JumpToGroupKeyPrefix + (i + 1).ToString("D2")]); + } + + public override bool HandleKeyPress(KeyInput e) + { + if (e.Event != KeyInputEvent.Down) + return false; + + for (var i = 0; i < hotkeyCount; i++) + { + if (selectGroupHotkeys[i].IsActivatedBy(e)) + { + world.ControlGroups.SelectControlGroup(i); + + if (e.MultiTapCount >= 2) + worldRenderer.Viewport.Center(world.ControlGroups.GetActorsInControlGroup(i)); + + return true; + } + + if (createGroupHotkeys[i].IsActivatedBy(e)) + { + world.ControlGroups.CreateControlGroup(i); + return true; + } + + if (addToGroupHotkeys[i].IsActivatedBy(e)) + { + world.ControlGroups.AddSelectionToControlGroup(i); + return true; + } + + if (combineWithGroupHotkeys[i].IsActivatedBy(e)) + { + world.ControlGroups.CombineSelectionWithControlGroup(i); + return true; + } + + if (jumpToGroupHotkeys[i].IsActivatedBy(e)) + { + worldRenderer.Viewport.Center(world.ControlGroups.GetActorsInControlGroup(i)); + return true; + } + } + + return false; + } + } +} diff --git a/OpenRA.Mods.Common/Widgets/Logic/Ingame/ControlGroupLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Ingame/ControlGroupLogic.cs deleted file mode 100644 index 6dec45926f..0000000000 --- a/OpenRA.Mods.Common/Widgets/Logic/Ingame/ControlGroupLogic.cs +++ /dev/null @@ -1,36 +0,0 @@ -#region Copyright & License Information -/* - * Copyright 2007-2021 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 OpenRA.Graphics; -using OpenRA.Widgets; - -namespace OpenRA.Mods.Common.Widgets.Logic -{ - public class ControlGroupLogic : ChromeLogic - { - [ObjectCreator.UseCtor] - public ControlGroupLogic(Widget widget, World world, WorldRenderer worldRenderer) - { - var keyhandler = widget.Get("CONTROLGROUP_KEYHANDLER"); - keyhandler.AddHandler(e => - { - if (e.Event == KeyInputEvent.Down && 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/OpenRA.Mods.Common/Widgets/Logic/Ingame/Hotkeys/RemoveFromControlGroupHotkeyLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Ingame/Hotkeys/RemoveFromControlGroupHotkeyLogic.cs index 653371ce57..c5a6826a3c 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Ingame/Hotkeys/RemoveFromControlGroupHotkeyLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Ingame/Hotkeys/RemoveFromControlGroupHotkeyLogic.cs @@ -38,7 +38,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic.Ingame .ToArray(); foreach (var a in selectedActors) - selection.RemoveFromControlGroup(a); + world.ControlGroups.RemoveFromControlGroup(a); return true; } diff --git a/mods/cnc/chrome/ingame.yaml b/mods/cnc/chrome/ingame.yaml index 3c4c674dd9..d71f48caae 100644 --- a/mods/cnc/chrome/ingame.yaml +++ b/mods/cnc/chrome/ingame.yaml @@ -1162,8 +1162,12 @@ Container@PLAYER_WIDGETS: BookmarkSaveKeyPrefix: MapBookmarkSave BookmarkRestoreKeyPrefix: MapBookmarkRestore BookmarkKeyCount: 4 - LogicKeyListener@CONTROLGROUP_KEYHANDLER: - Logic: ControlGroupLogic + ControlGroups@CONTROLGROUPS: + SelectGroupKeyPrefix: ControlGroupSelect + CreateGroupKeyPrefix: ControlGroupCreate + AddToGroupKeyPrefix: ControlGroupAddTo + CombineWithGroupKeyPrefix: ControlGroupCombineWith + JumpToGroupKeyPrefix: ControlGroupJumpTo LogicTicker@SIDEBAR_TICKER: Container@SUPPORT_POWERS: Logic: SupportPowerBinLogic diff --git a/mods/cnc/chrome/settings-hotkeys.yaml b/mods/cnc/chrome/settings-hotkeys.yaml index b91d4ec788..e9afe7e5c5 100644 --- a/mods/cnc/chrome/settings-hotkeys.yaml +++ b/mods/cnc/chrome/settings-hotkeys.yaml @@ -19,6 +19,9 @@ Container@HOTKEYS_PANEL: Types: Music Chat Commands: Types: Chat + Control Groups: + Template: TWO_COLUMN + Types: ControlGroups Width: PARENT_RIGHT Height: PARENT_BOTTOM Children: diff --git a/mods/cnc/mod.yaml b/mods/cnc/mod.yaml index cd6ef315af..e8cabc00c7 100644 --- a/mods/cnc/mod.yaml +++ b/mods/cnc/mod.yaml @@ -159,6 +159,7 @@ Hotkeys: common|hotkeys/supportpowers.yaml common|hotkeys/viewport.yaml common|hotkeys/chat.yaml + common|hotkeys/control-groups.yaml cnc|hotkeys.yaml LoadScreen: CncLoadScreen diff --git a/mods/cnc/rules/world.yaml b/mods/cnc/rules/world.yaml index a9c3fea571..a54dd997ed 100644 --- a/mods/cnc/rules/world.yaml +++ b/mods/cnc/rules/world.yaml @@ -4,6 +4,7 @@ ScreenMap: ActorMap: Selection: + ControlGroups: MusicPlaylist: VictoryMusic: win1 DefeatMusic: nod_map1 diff --git a/mods/cnc/sequences/misc.yaml b/mods/cnc/sequences/misc.yaml index fb33b1785d..08476b5349 100644 --- a/mods/cnc/sequences/misc.yaml +++ b/mods/cnc/sequences/misc.yaml @@ -270,6 +270,7 @@ pips: groups: pdigits Length: * Offset: 9, 5 + Frames: 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 pip-hazmat: pip-hazmat Offset: -3, 0 diff --git a/mods/common/chrome/settings-hotkeys.yaml b/mods/common/chrome/settings-hotkeys.yaml index 84eb882de1..f51968761f 100644 --- a/mods/common/chrome/settings-hotkeys.yaml +++ b/mods/common/chrome/settings-hotkeys.yaml @@ -19,6 +19,9 @@ Container@HOTKEYS_PANEL: Types: Music Chat Commands: Types: Chat + Control Groups: + Template: TWO_COLUMN + Types: ControlGroups Width: PARENT_RIGHT Height: PARENT_BOTTOM Children: diff --git a/mods/common/hotkeys/control-groups.yaml b/mods/common/hotkeys/control-groups.yaml new file mode 100644 index 0000000000..5b139b26a2 --- /dev/null +++ b/mods/common/hotkeys/control-groups.yaml @@ -0,0 +1,203 @@ +ControlGroupSelect01: NUMBER_1 + Description: Select group 1 + Types: ControlGroups, Player + +ControlGroupSelect02: NUMBER_2 + Description: Select group 2 + Types: ControlGroups, Player + +ControlGroupSelect03: NUMBER_3 + Description: Select group 3 + Types: ControlGroups, Player + +ControlGroupSelect04: NUMBER_4 + Description: Select group 4 + Types: ControlGroups, Player + +ControlGroupSelect05: NUMBER_5 + Description: Select group 5 + Types: ControlGroups, Player + +ControlGroupSelect06: NUMBER_6 + Description: Select group 6 + Types: ControlGroups, Player + +ControlGroupSelect07: NUMBER_7 + Description: Select group 7 + Types: ControlGroups, Player + +ControlGroupSelect08: NUMBER_8 + Description: Select group 8 + Types: ControlGroups, Player + +ControlGroupSelect09: NUMBER_9 + Description: Select group 9 + Types: ControlGroups, Player + +ControlGroupSelect10: NUMBER_0 + Description: Select group 0 + Types: ControlGroups, Player + +ControlGroupCreate01: NUMBER_1 Ctrl + Description: Create group 1 + Types: ControlGroups, Player + +ControlGroupCreate02: NUMBER_2 Ctrl + Description: Create group 2 + Types: ControlGroups, Player + +ControlGroupCreate03: NUMBER_3 Ctrl + Description: Create group 3 + Types: ControlGroups, Player + +ControlGroupCreate04: NUMBER_4 Ctrl + Description: Create group 4 + Types: ControlGroups, Player + +ControlGroupCreate05: NUMBER_5 Ctrl + Description: Create group 5 + Types: ControlGroups, Player + +ControlGroupCreate06: NUMBER_6 Ctrl + Description: Create group 6 + Types: ControlGroups, Player + +ControlGroupCreate07: NUMBER_7 Ctrl + Description: Create group 7 + Types: ControlGroups, Player + +ControlGroupCreate08: NUMBER_8 Ctrl + Description: Create group 8 + Types: ControlGroups, Player + +ControlGroupCreate09: NUMBER_9 Ctrl + Description: Create group 9 + Types: ControlGroups, Player + +ControlGroupCreate10: NUMBER_0 Ctrl + Description: Create group 0 + Types: ControlGroups, Player + +ControlGroupAddTo01: NUMBER_1 Ctrl, Shift + Description: Add to group 1 + Types: ControlGroups, Player + +ControlGroupAddTo02: NUMBER_2 Ctrl, Shift + Description: Add to group 2 + Types: ControlGroups, Player + +ControlGroupAddTo03: NUMBER_3 Ctrl, Shift + Description: Add to group 3 + Types: ControlGroups, Player + +ControlGroupAddTo04: NUMBER_4 Ctrl, Shift + Description: Add to group 4 + Types: ControlGroups, Player + +ControlGroupAddTo05: NUMBER_5 Ctrl, Shift + Description: Add to group 5 + Types: ControlGroups, Player + +ControlGroupAddTo06: NUMBER_6 Ctrl, Shift + Description: Add to group 6 + Types: ControlGroups, Player + +ControlGroupAddTo07: NUMBER_7 Ctrl, Shift + Description: Add to group 7 + Types: ControlGroups, Player + +ControlGroupAddTo08: NUMBER_8 Ctrl, Shift + Description: Add to group 8 + Types: ControlGroups, Player + +ControlGroupAddTo09: NUMBER_9 Ctrl, Shift + Description: Add to group 9 + Types: ControlGroups, Player + +ControlGroupAddTo10: NUMBER_0 Ctrl, Shift + Description: Add to group 0 + Types: ControlGroups, Player + +ControlGroupCombineWith01: NUMBER_1 Shift + Description: Combine with group 1 + Types: ControlGroups, Player + +ControlGroupCombineWith02: NUMBER_2 Shift + Description: Combine with group 2 + Types: ControlGroups, Player + +ControlGroupCombineWith03: NUMBER_3 Shift + Description: Combine with group 3 + Types: ControlGroups, Player + +ControlGroupCombineWith04: NUMBER_4 Shift + Description: Combine with group 4 + Types: ControlGroups, Player + +ControlGroupCombineWith05: NUMBER_5 Shift + Description: Combine with group 5 + Types: ControlGroups, Player + +ControlGroupCombineWith06: NUMBER_6 Shift + Description: Combine with group 6 + Types: ControlGroups, Player + +ControlGroupCombineWith07: NUMBER_7 Shift + Description: Combine with group 7 + Types: ControlGroups, Player + +ControlGroupCombineWith08: NUMBER_8 Shift + Description: Combine with group 8 + Types: ControlGroups, Player + +ControlGroupCombineWith09: NUMBER_9 Shift + Description: Combine with group 9 + Types: ControlGroups, Player + +ControlGroupCombineWith10: NUMBER_0 Shift + Description: Combine with group 0 + Types: ControlGroups, Player + +ControlGroupJumpTo01: NUMBER_1 Alt + Description: Jump to group 1 + Types: ControlGroups, Player + +ControlGroupJumpTo02: NUMBER_2 Alt + Description: Jump to group 2 + Types: ControlGroups, Player + +ControlGroupJumpTo03: NUMBER_3 Alt + Description: Jump to group 3 + Types: ControlGroups, Player + +ControlGroupJumpTo04: NUMBER_4 Alt + Description: Jump to group 4 + Types: ControlGroups, Player + +ControlGroupJumpTo05: NUMBER_5 Alt + Description: Jump to group 5 + Types: ControlGroups, Player + +ControlGroupJumpTo06: NUMBER_6 Alt + Description: Jump to group 6 + Types: ControlGroups, Player + +ControlGroupJumpTo07: NUMBER_7 Alt + Description: Jump to group 7 + Types: ControlGroups, Player + +ControlGroupJumpTo08: NUMBER_8 Alt + Description: Jump to group 8 + Types: ControlGroups, Player + +ControlGroupJumpTo09: NUMBER_9 Alt + Description: Jump to group 9 + Types: ControlGroups, Player + +ControlGroupJumpTo10: NUMBER_0 Alt + Description: Jump to group 0 + Types: ControlGroups, Player + +RemoveFromControlGroup: + Description: Remove from control group + Types: ControlGroups, Player diff --git a/mods/common/hotkeys/game.yaml b/mods/common/hotkeys/game.yaml index bf49aa1193..fca0c26b2f 100644 --- a/mods/common/hotkeys/game.yaml +++ b/mods/common/hotkeys/game.yaml @@ -22,10 +22,6 @@ CycleHarvesters: N Description: Cycle Harvesters Types: World, Player, Spectator -RemoveFromControlGroup: - Description: Remove from control group - Types: World, Player - Pause: PAUSE Description: Pause / Unpause Types: World, Player, Spectator diff --git a/mods/d2k/chrome/ingame-player.yaml b/mods/d2k/chrome/ingame-player.yaml index f28cf7383e..14806a4ecc 100644 --- a/mods/d2k/chrome/ingame-player.yaml +++ b/mods/d2k/chrome/ingame-player.yaml @@ -2,8 +2,12 @@ Container@PLAYER_WIDGETS: Logic: LoadIngameChatLogic Children: Container@CHAT_ROOT: - LogicKeyListener@CONTROLGROUP_KEYHANDLER: - Logic: ControlGroupLogic + ControlGroups@CONTROLGROUPS: + SelectGroupKeyPrefix: ControlGroupSelect + CreateGroupKeyPrefix: ControlGroupCreate + AddToGroupKeyPrefix: ControlGroupAddTo + CombineWithGroupKeyPrefix: ControlGroupCombineWith + JumpToGroupKeyPrefix: ControlGroupJumpTo LogicTicker@SIDEBAR_TICKER: Container@SUPPORT_POWERS: Logic: SupportPowerBinLogic diff --git a/mods/d2k/mod.yaml b/mods/d2k/mod.yaml index c35180904e..55589c573e 100644 --- a/mods/d2k/mod.yaml +++ b/mods/d2k/mod.yaml @@ -144,6 +144,7 @@ Hotkeys: common|hotkeys/supportpowers.yaml common|hotkeys/viewport.yaml common|hotkeys/chat.yaml + common|hotkeys/control-groups.yaml d2k|hotkeys.yaml LoadScreen: LogoStripeLoadScreen diff --git a/mods/d2k/rules/world.yaml b/mods/d2k/rules/world.yaml index 1720ceae50..c9e27c3e2a 100644 --- a/mods/d2k/rules/world.yaml +++ b/mods/d2k/rules/world.yaml @@ -4,6 +4,7 @@ ScreenMap: ActorMap: Selection: + ControlGroups: MusicPlaylist: VictoryMusic: score DefeatMusic: score diff --git a/mods/d2k/sequences/misc.yaml b/mods/d2k/sequences/misc.yaml index c57807bdcd..fbaffd0cc4 100644 --- a/mods/d2k/sequences/misc.yaml +++ b/mods/d2k/sequences/misc.yaml @@ -128,8 +128,8 @@ sandtrail: pips: groups: DATA.R8 - Start: 17 Length: 10 + Frames: 18, 19, 20, 21, 22, 23, 24, 25, 26, 17 Offset: 3, 3 pickup-indicator: DATA.R8 Start: 112 diff --git a/mods/ra/chrome/ingame-player.yaml b/mods/ra/chrome/ingame-player.yaml index d6eb485c04..79452b8183 100644 --- a/mods/ra/chrome/ingame-player.yaml +++ b/mods/ra/chrome/ingame-player.yaml @@ -2,8 +2,12 @@ Container@PLAYER_WIDGETS: Logic: LoadIngameChatLogic Children: Container@CHAT_ROOT: - LogicKeyListener@CONTROLGROUP_KEYHANDLER: - Logic: ControlGroupLogic + ControlGroups@CONTROLGROUPS: + SelectGroupKeyPrefix: ControlGroupSelect + CreateGroupKeyPrefix: ControlGroupCreate + AddToGroupKeyPrefix: ControlGroupAddTo + CombineWithGroupKeyPrefix: ControlGroupCombineWith + JumpToGroupKeyPrefix: ControlGroupJumpTo LogicTicker@SIDEBAR_TICKER: Container@SUPPORT_POWERS: Logic: SupportPowerBinLogic diff --git a/mods/ra/mod.yaml b/mods/ra/mod.yaml index e303c775ef..c390f11083 100644 --- a/mods/ra/mod.yaml +++ b/mods/ra/mod.yaml @@ -165,6 +165,7 @@ Hotkeys: common|hotkeys/supportpowers.yaml common|hotkeys/viewport.yaml common|hotkeys/chat.yaml + common|hotkeys/control-groups.yaml ra|hotkeys.yaml LoadScreen: LogoStripeLoadScreen diff --git a/mods/ra/rules/world.yaml b/mods/ra/rules/world.yaml index bbf1d32919..4706ba683a 100644 --- a/mods/ra/rules/world.yaml +++ b/mods/ra/rules/world.yaml @@ -4,6 +4,7 @@ ActorMap: ScreenMap: Selection: + ControlGroups: MusicPlaylist: VictoryMusic: score DefeatMusic: map diff --git a/mods/ra/sequences/misc.yaml b/mods/ra/sequences/misc.yaml index fbcac2a711..0ee20efe92 100644 --- a/mods/ra/sequences/misc.yaml +++ b/mods/ra/sequences/misc.yaml @@ -96,8 +96,8 @@ burn-s: pips: groups: - Start: 8 Length: 10 + Frames: 9, 10, 11, 12, 13, 14, 15, 16, 17, 8 Offset: 9, 5 medic: Start: 20 diff --git a/mods/ts/chrome/ingame-player.yaml b/mods/ts/chrome/ingame-player.yaml index 0e99d17b63..489a6a7ee3 100644 --- a/mods/ts/chrome/ingame-player.yaml +++ b/mods/ts/chrome/ingame-player.yaml @@ -9,8 +9,12 @@ Container@PLAYER_WIDGETS: DecreaseDepthPreviewContrastKey: DecreaseDepthPreviewContrast IncreaseDepthPreviewOffsetKey: IncreaseDepthPreviewOffset DecreaseDepthPreviewOffsetKey: DecreaseDepthPreviewOffset - LogicKeyListener@CONTROLGROUP_KEYHANDLER: - Logic: ControlGroupLogic + ControlGroups@CONTROLGROUPS: + SelectGroupKeyPrefix: ControlGroupSelect + CreateGroupKeyPrefix: ControlGroupCreate + AddToGroupKeyPrefix: ControlGroupAddTo + CombineWithGroupKeyPrefix: ControlGroupCombineWith + JumpToGroupKeyPrefix: ControlGroupJumpTo LogicTicker@SIDEBAR_TICKER: Container@SUPPORT_POWERS: Logic: SupportPowerBinLogic diff --git a/mods/ts/chrome/settings-hotkeys.yaml b/mods/ts/chrome/settings-hotkeys.yaml index 1731311445..56b40ca7ad 100644 --- a/mods/ts/chrome/settings-hotkeys.yaml +++ b/mods/ts/chrome/settings-hotkeys.yaml @@ -19,6 +19,9 @@ Container@HOTKEYS_PANEL: Types: Music Chat Commands: Types: Chat + Control Groups: + Template: TWO_COLUMN + Types: ControlGroups Depth Preview Debug: Types: DepthDebug Width: PARENT_RIGHT diff --git a/mods/ts/mod.yaml b/mods/ts/mod.yaml index d7ee36c2cf..845a65ebd3 100644 --- a/mods/ts/mod.yaml +++ b/mods/ts/mod.yaml @@ -201,6 +201,7 @@ Hotkeys: common|hotkeys/supportpowers.yaml common|hotkeys/viewport.yaml common|hotkeys/chat.yaml + common|hotkeys/control-groups.yaml ts|hotkeys.yaml LoadScreen: LogoStripeLoadScreen diff --git a/mods/ts/rules/world.yaml b/mods/ts/rules/world.yaml index f4bba32cc3..22557d0219 100644 --- a/mods/ts/rules/world.yaml +++ b/mods/ts/rules/world.yaml @@ -4,6 +4,7 @@ ScreenMap: ActorMap: Selection: + ControlGroups: MusicPlaylist: VictoryMusic: score DefeatMusic: maps