diff --git a/Content.Client/Lobby/UI/Loadouts/LoadoutGroupContainer.xaml.cs b/Content.Client/Lobby/UI/Loadouts/LoadoutGroupContainer.xaml.cs index a59867acdb3..1c0f744c1a3 100644 --- a/Content.Client/Lobby/UI/Loadouts/LoadoutGroupContainer.xaml.cs +++ b/Content.Client/Lobby/UI/Loadouts/LoadoutGroupContainer.xaml.cs @@ -1,10 +1,12 @@ using System.Linq; +using Content.Client._Forge.Lobby.UI.Loadouts; using Content.Shared.Clothing; using Content.Shared.Preferences; using Content.Shared.Preferences.Loadouts; using Robust.Client.AutoGenerated; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.XAML; +using Robust.Shared.Maths; using Robust.Shared.Player; using Robust.Shared.Prototypes; @@ -13,6 +15,13 @@ namespace Content.Client.Lobby.UI.Loadouts; [GenerateTypedNameReferences] public sealed partial class LoadoutGroupContainer : BoxContainer { + // Forge-Change-Start: grouped loadout color variants (GroupBy field) + private const string ClosedGroupMark = "▶"; + private const string OpenedGroupMark = "▼"; + + private Dictionary _openedGroups = new(); + // Forge-Change-End + private readonly LoadoutGroupPrototype _groupProto; public event Action>? OnLoadoutPressed; @@ -104,35 +113,143 @@ public void RefreshLoadouts(HumanoidCharacterProfile profile, RoleLoadout loadou if (!protoMan.TryIndex(subgroupProto, out var loadoutGroupProto)) continue; - foreach (var loadoutProto in loadoutGroupProto.Loadouts) - { - if (!protoMan.TryIndex(loadoutProto, out var loadProto)) - continue; + // Forge-Change: apply GroupBy grouping to subgroup loadouts only + AddGroupedLoadouts(loadoutGroupProto.Loadouts, profile, loadout, session, collection, loadoutSystem, protoMan); + } + // End Frontier + } + + // Forge-Change-Start: grouped loadout color variants (GroupBy field) + private void AddGroupedLoadouts( + IEnumerable> loadoutIds, + HumanoidCharacterProfile profile, + RoleLoadout loadout, + ICommonSession session, + IDependencyCollection collection, + LoadoutSystem loadoutSystem, + IPrototypeManager protoMan) + { + var validProtos = new List(); + + foreach (var loadoutProto in loadoutIds) + { + if (!protoMan.TryIndex(loadoutProto, out var loadProto)) + continue; + + if (loadout.IsHidden(profile, session, loadoutProto, collection)) + continue; - if (loadout.IsHidden(profile, session, loadoutProto, collection)) - continue; + validProtos.Add(loadProto); + } - var matchingLoadout = selected.FirstOrDefault(e => e.Prototype == loadoutProto); - var pressed = matchingLoadout != null; + var groups = validProtos + .GroupBy(p => string.IsNullOrEmpty(p.GroupBy) ? p.ID : p.GroupBy) + .ToDictionary(g => g.Key, g => g.ToList()); - var enabled = loadout.IsValid(profile, session, loadoutProto, collection, out var reason); - var loadoutContainer = new LoadoutContainer(loadoutProto, !enabled, reason); - loadoutContainer.Select.Pressed = pressed; - loadoutContainer.Text = string.IsNullOrEmpty(loadProto.Name) - ? loadoutSystem.GetName(loadProto) - : Loc.GetString(loadProto.Name); // Frontier: allow overriding loadout names via localization keys + foreach (var kvp in groups) + { + var protos = kvp.Value; - loadoutContainer.Select.OnPressed += args => + if (protos.Count > 1) + { + var uiElements = protos + .Select(proto => + { + var elem = CreateLoadoutUI(proto, profile, loadout, session, collection, loadoutSystem); + elem.HorizontalExpand = true; + return elem; + }) + .ToList(); + + var firstElement = uiElements.FirstOrDefault(e => e.Select.Pressed) ?? uiElements[0]; + var otherElements = uiElements.Where(e => !ReferenceEquals(e, firstElement)).ToList(); + + firstElement.HorizontalExpand = true; + var subContainer = new SubLoadoutContainer { - if (args.Button.Pressed) - OnLoadoutPressed?.Invoke(loadoutProto); - else - OnLoadoutUnpressed?.Invoke(loadoutProto); + Visible = _openedGroups.GetValueOrDefault(kvp.Key, false) }; + var toggle = CreateToggleButton(kvp, firstElement, subContainer); + + LoadoutsContainer.AddChild(firstElement); + LoadoutsContainer.AddChild(subContainer); - LoadoutsContainer.AddChild(loadoutContainer); + var subList = subContainer.Grid; + foreach (var proto in otherElements) + { + subList.AddChild(proto); + } + + UpdateToggleColor(toggle, subList); + } + else + { + LoadoutsContainer.AddChild( + CreateLoadoutUI(protos[0], profile, loadout, session, collection, loadoutSystem)); } } - // End Frontier } + + private ToggleLoadoutButton CreateToggleButton( + KeyValuePair> kvp, + LoadoutContainer firstElement, + SubLoadoutContainer subContainer) + { + var toggle = new ToggleLoadoutButton + { + Text = subContainer.Visible ? OpenedGroupMark : ClosedGroupMark + }; + + toggle.OnPressed += _ => + { + var willOpen = !subContainer.Visible; + subContainer.Visible = willOpen; + toggle.Text = willOpen ? OpenedGroupMark : ClosedGroupMark; + _openedGroups[kvp.Key] = willOpen; + }; + + firstElement.AddChild(toggle); + toggle.SetPositionFirst(); + return toggle; + } + + private void UpdateToggleColor(Button toggle, BoxContainer subList) + { + var anyActive = subList.Children + .OfType() + .Any(c => c.Select.Pressed); + + toggle.Modulate = anyActive ? Color.Green : Color.White; + } + + private LoadoutContainer CreateLoadoutUI( + LoadoutPrototype proto, + HumanoidCharacterProfile profile, + RoleLoadout loadout, + ICommonSession session, + IDependencyCollection collection, + LoadoutSystem loadoutSystem) + { + var selected = loadout.SelectedLoadouts[_groupProto.ID]; + var pressed = selected.Any(e => e.Prototype == proto.ID); + var enabled = loadout.IsValid(profile, session, proto.ID, collection, out var reason); + var cont = new LoadoutContainer(proto.ID, !enabled, reason); + + cont.Text = string.IsNullOrEmpty(proto.Name) + ? loadoutSystem.GetName(proto) + : Loc.GetString(proto.Name); + + cont.Select.Pressed = pressed; + + cont.Select.OnPressed += args => + { + if (args.Button.Pressed) + OnLoadoutPressed?.Invoke(proto.ID); + else + OnLoadoutUnpressed?.Invoke(proto.ID); + }; + + return cont; + } + // Forge-Change-End } diff --git a/Content.Client/_Forge/Lobby/UI/Loadouts/SubLoadoutContainer.xaml b/Content.Client/_Forge/Lobby/UI/Loadouts/SubLoadoutContainer.xaml new file mode 100644 index 00000000000..c98747e7b9f --- /dev/null +++ b/Content.Client/_Forge/Lobby/UI/Loadouts/SubLoadoutContainer.xaml @@ -0,0 +1,8 @@ + + + diff --git a/Content.Client/_Forge/Lobby/UI/Loadouts/SubLoadoutContainer.xaml.cs b/Content.Client/_Forge/Lobby/UI/Loadouts/SubLoadoutContainer.xaml.cs new file mode 100644 index 00000000000..84251dd42ae --- /dev/null +++ b/Content.Client/_Forge/Lobby/UI/Loadouts/SubLoadoutContainer.xaml.cs @@ -0,0 +1,10 @@ +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface.Controls; + +namespace Content.Client._Forge.Lobby.UI.Loadouts; + +[GenerateTypedNameReferences] +public sealed partial class SubLoadoutContainer : PanelContainer +{ + public BoxContainer Grid => SubGridContainer; +} diff --git a/Content.Client/_Forge/Lobby/UI/Loadouts/ToggleLoadoutButton.xaml b/Content.Client/_Forge/Lobby/UI/Loadouts/ToggleLoadoutButton.xaml new file mode 100644 index 00000000000..ffd304348ec --- /dev/null +++ b/Content.Client/_Forge/Lobby/UI/Loadouts/ToggleLoadoutButton.xaml @@ -0,0 +1,9 @@ +