Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 138 additions & 21 deletions Content.Client/Lobby/UI/Loadouts/LoadoutGroupContainer.xaml.cs
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -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<string, bool> _openedGroups = new();
// Forge-Change-End

private readonly LoadoutGroupPrototype _groupProto;

public event Action<ProtoId<LoadoutPrototype>>? OnLoadoutPressed;
Expand Down Expand Up @@ -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<ProtoId<LoadoutPrototype>> loadoutIds,
HumanoidCharacterProfile profile,
RoleLoadout loadout,
ICommonSession session,
IDependencyCollection collection,
LoadoutSystem loadoutSystem,
IPrototypeManager protoMan)
{
var validProtos = new List<LoadoutPrototype>();

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<string, List<LoadoutPrototype>> 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<LoadoutContainer>()
.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
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<PanelContainer Name="SubContainer"
xmlns="https://spacestation14.io"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<BoxContainer Name="SubGridContainer"
Orientation="Vertical"
HorizontalExpand="true"
Margin="20 0 0 0"/>
</PanelContainer>
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<Button Name="ToggleButton"
xmlns="https://spacestation14.io"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
VerticalExpand="False"
HorizontalExpand="False"
SetSize="32 32"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Margin="0 0 2 0"/>
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;

namespace Content.Client._Forge.Lobby.UI.Loadouts;

[GenerateTypedNameReferences]
public sealed partial class ToggleLoadoutButton : Button;
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using Robust.Shared.Prototypes;

namespace Content.Shared.Preferences.Loadouts;

public sealed partial class LoadoutPrototype
{
/// <summary>
/// Groups loadout variants (e.g. color options) into a single expandable row in the character editor.
/// </summary>
[DataField]
public string? GroupBy;
}
7 changes: 7 additions & 0 deletions Resources/Locale/en-US/_Forge/loadouts/contractor-neck.ftl
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
loadout-forge-niko-scarf-black = long cat's black scarf
loadout-forge-niko-scarf-blue = long cat's blue scarf
loadout-forge-niko-scarf-gray = long cat's gray scarf
loadout-forge-niko-scarf-green = long cat's green scarf
loadout-forge-niko-scarf-purple = long cat's purple scarf
loadout-forge-niko-scarf-red = long cat's red scarf
loadout-forge-niko-scarf-yellow = long cat's yellow scarf
7 changes: 7 additions & 0 deletions Resources/Locale/ru-RU/_Forge/loadouts/contractor-neck.ftl
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
loadout-forge-niko-scarf-black = длинный чёрный кошачий шарф
loadout-forge-niko-scarf-blue = длинный синий кошачий шарф
loadout-forge-niko-scarf-gray = длинный серый кошачий шарф
loadout-forge-niko-scarf-green = длинный зелёный кошачий шарф
loadout-forge-niko-scarf-purple = длинный фиолетовый кошачий шарф
loadout-forge-niko-scarf-red = длинный красный кошачий шарф
loadout-forge-niko-scarf-yellow = длинный жёлтый кошачий шарф
12 changes: 12 additions & 0 deletions Resources/Prototypes/_Forge/Loadouts/Contractor/loadout_groups.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
- type: loadoutGroup
id: ForgeContractorNikoScarfs
name: loadout-group-contractor-neck
minLimit: 0
loadouts:
- ContractorClothingNeckScarfNikoBlue
- ContractorClothingNeckScarfNikoBlack
- ContractorClothingNeckScarfNikoGray
- ContractorClothingNeckScarfNikoGreen
- ContractorClothingNeckScarfNikoPurple
- ContractorClothingNeckScarfNikoRed
- ContractorClothingNeckScarfNikoYellow
83 changes: 83 additions & 0 deletions Resources/Prototypes/_Forge/Loadouts/Contractor/neck.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
- type: loadout
id: ContractorClothingNeckScarfNikoBlue
name: loadout-forge-niko-scarf-blue
effects:
- !type:GroupLoadoutEffect
proto: ContractorT1
price: 0
previewEntity: ClothingNeckScarfBlueNiko
equipment:
neck: ClothingNeckScarfBlueNiko
groupBy: nikoScarf

- type: loadout
id: ContractorClothingNeckScarfNikoBlack
name: loadout-forge-niko-scarf-black
effects:
- !type:GroupLoadoutEffect
proto: ContractorT1
price: 0
previewEntity: ClothingNeckScarfBlackNiko
equipment:
neck: ClothingNeckScarfBlackNiko
groupBy: nikoScarf

- type: loadout
id: ContractorClothingNeckScarfNikoGray
name: loadout-forge-niko-scarf-gray
effects:
- !type:GroupLoadoutEffect
proto: ContractorT1
price: 0
previewEntity: ClothingNeckScarfGrayNiko
equipment:
neck: ClothingNeckScarfGrayNiko
groupBy: nikoScarf

- type: loadout
id: ContractorClothingNeckScarfNikoGreen
name: loadout-forge-niko-scarf-green
effects:
- !type:GroupLoadoutEffect
proto: ContractorT1
price: 0
previewEntity: ClothingNeckScarfGreenNiko
equipment:
neck: ClothingNeckScarfGreenNiko
groupBy: nikoScarf

- type: loadout
id: ContractorClothingNeckScarfNikoPurple
name: loadout-forge-niko-scarf-purple
effects:
- !type:GroupLoadoutEffect
proto: ContractorT1
price: 0
previewEntity: ClothingNeckScarfPurpleNiko
equipment:
neck: ClothingNeckScarfPurpleNiko
groupBy: nikoScarf

- type: loadout
id: ContractorClothingNeckScarfNikoRed
name: loadout-forge-niko-scarf-red
effects:
- !type:GroupLoadoutEffect
proto: ContractorT1
price: 0
previewEntity: ClothingNeckScarfRedNiko
equipment:
neck: ClothingNeckScarfRedNiko
groupBy: nikoScarf

- type: loadout
id: ContractorClothingNeckScarfNikoYellow
name: loadout-forge-niko-scarf-yellow
effects:
- !type:GroupLoadoutEffect
proto: ContractorT1
price: 0
previewEntity: ClothingNeckScarfYellowNiko
equipment:
neck: ClothingNeckScarfYellowNiko
groupBy: nikoScarf
Original file line number Diff line number Diff line change
Expand Up @@ -735,6 +735,8 @@
- ContractorClothingNeckMantleBH # Mono
- ContractorClothingNeckCloakSanabi # Mono
- ContractorClothingNeckUIOvercoat # Mono
subgroups: #Forge-Change
- ForgeContractorNikoScarfs #Forge-Change

- type: loadoutGroup
id: ContractorPDA
Expand Down
1 change: 1 addition & 0 deletions Resources/Prototypes/_NF/Loadouts/pilot_loadout_groups.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
- PilotClothingNeckScarfPilot
subgroups:
- ContractorNeck
- ForgeContractorNikoScarfs #Forge-Change

- type: loadoutGroup
id: PilotPDA
Expand Down
Loading