Add door electronics access configuration menu (#17778)

* Add door electronics configuration menu

* Use file-scoped namespaces

Signed-off-by: c4llv07e <kseandi@gmail.com>

* Open door electronics configuration menu only with network configurator

Signed-off-by: c4llv07e <kseandi@gmail.com>

* Doors will now try to move their AccessReaderComponent to their door electronics when the map is initialized

Signed-off-by: c4llv07e <kseandi@gmail.com>

* Make the access list in the id card computer a separate control

Signed-off-by: c4llv07e <kseandi@gmail.com>

* Fix merge conflict

Signed-off-by: c4llv07e <kseandi@gmail.com>

* Remove DoorElectronics tag

Signed-off-by: c4llv07e <kseandi@gmail.com>

* Integrate doors with #17927

Signed-off-by: c4llv07e <kseandi@gmail.com>

* Move door electornics ui stuff to the right place

Signed-off-by: c4llv07e <kseandi@gmail.com>

* Some review fixes

Signed-off-by: c4llv07e <kseandi@gmail.com>

* More fixes

Signed-off-by: c4llv07e <kseandi@gmail.com>

* review fix

Signed-off-by: c4llv07e <kseandi@gmail.com>

* move all accesses from airlock prototypes to door electronics

Signed-off-by: c4llv07e <kseandi@gmail.com>

* rework door electronics config access list

Signed-off-by: c4llv07e <kseandi@gmail.com>

* Remove Linq from the door electronics user interface

* [WIP] Add EntityWhitelist to the activatable ui component

Signed-off-by: c4llv07e <kseandi@gmail.com>

* Better interaction system

Signed-off-by: c4llv07e <kseandi@gmail.com>

* Refactor

Signed-off-by: c4llv07e <kseandi@gmail.com>

* Fix some door electronics not working without AccessReaderComponent

Signed-off-by: c4llv07e <kseandi@gmail.com>

* Move AccessReaderComponent update code to the AccessReaderSystem

Signed-off-by: c4llv07e <kseandi@gmail.com>

* Remove unnecesary newlines in the door access prototypes

Signed-off-by: c4llv07e <kseandi@gmail.com>

* Remove unused variables in access level control

Signed-off-by: c4llv07e <kseandi@gmail.com>

* Remove unnecessary method from the door electronics configuration menu

Signed-off-by: c4llv07e <kseandi@gmail.com>

* [WIP] change access type from string to ProtoId<AccessLevelPrototype>

Signed-off-by: c4llv07e <kseandi@gmail.com>

* Remove unused methods

Signed-off-by: c4llv07e <kseandi@gmail.com>

* Newline fix

Signed-off-by: c4llv07e <kseandi@gmail.com>

* Restored to a functional state

Signed-off-by: c4llv07e <kseandi@gmail.com>

* Fix access configurator not working with door electronics AccessReaderComponent

Signed-off-by: c4llv07e <kseandi@gmail.com>

* Replace all string access fields with ProtoId

Signed-off-by: c4llv07e <kseandi@gmail.com>

* move access level control initialization into Populate method

Signed-off-by: c4llv07e <kseandi@gmail.com>

* Review

---------

Signed-off-by: c4llv07e <kseandi@gmail.com>
Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
This commit is contained in:
c4llv07e
2024-04-01 09:06:13 +03:00
committed by GitHub
parent 72bdcac1e2
commit 64bb8dbdd5
40 changed files with 1079 additions and 532 deletions

View File

@@ -0,0 +1,4 @@
<GridContainer xmlns="https://spacestation14.io"
Columns="5"
HorizontalAlignment="Center">
</GridContainer>

View File

@@ -0,0 +1,52 @@
using System.Linq;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
using Content.Shared.Access;
using Content.Shared.Access.Systems;
namespace Content.Client.Access.UI;
[GenerateTypedNameReferences]
public sealed partial class AccessLevelControl : GridContainer
{
public readonly Dictionary<ProtoId<AccessLevelPrototype>, Button> ButtonsList = new();
public AccessLevelControl()
{
RobustXamlLoader.Load(this);
}
public void Populate(List<ProtoId<AccessLevelPrototype>> accessLevels, IPrototypeManager prototypeManager)
{
foreach (var access in accessLevels)
{
if (!prototypeManager.TryIndex(access, out var accessLevel))
{
Logger.Error($"Unable to find accesslevel for {access}");
continue;
}
var newButton = new Button
{
Text = accessLevel.GetAccessLevelName(),
ToggleMode = true,
};
AddChild(newButton);
ButtonsList.Add(accessLevel.ID, newButton);
}
}
public void UpdateState(
List<ProtoId<AccessLevelPrototype>> pressedList,
List<ProtoId<AccessLevelPrototype>>? enabledList = null)
{
foreach (var (accessName, button) in ButtonsList)
{
button.Pressed = pressedList.Contains(accessName);
button.Disabled = !(enabledList?.Contains(accessName) ?? true);
}
}
}

View File

@@ -64,7 +64,7 @@ namespace Content.Client.Access.UI
_window?.UpdateState(castState);
}
public void SubmitData(List<string> newAccessList)
public void SubmitData(List<ProtoId<AccessLevelPrototype>> newAccessList)
{
SendMessage(new WriteToTargetAccessReaderIdMessage(newAccessList));
}

View File

@@ -16,7 +16,6 @@ namespace Content.Client.Access.UI
[Dependency] private readonly ILogManager _logManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
private readonly ISawmill _logMill = default!;
private readonly AccessOverriderBoundUserInterface _owner;
private readonly Dictionary<string, Button> _accessButtons = new();
@@ -25,7 +24,7 @@ namespace Content.Client.Access.UI
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
_logMill = _logManager.GetSawmill(SharedAccessOverriderSystem.Sawmill);
var logMill = _logManager.GetSawmill(SharedAccessOverriderSystem.Sawmill);
_owner = owner;
@@ -33,13 +32,13 @@ namespace Content.Client.Access.UI
{
if (!prototypeManager.TryIndex(access, out var accessLevel))
{
_logMill.Error($"Unable to find accesslevel for {access}");
logMill.Error($"Unable to find accesslevel for {access}");
continue;
}
var newButton = new Button
{
Text = GetAccessLevelName(accessLevel),
Text = accessLevel.GetAccessLevelName(),
ToggleMode = true,
};
@@ -49,14 +48,6 @@ namespace Content.Client.Access.UI
}
}
private static string GetAccessLevelName(AccessLevelPrototype prototype)
{
if (prototype.Name is { } name)
return Loc.GetString(name);
return prototype.ID;
}
public void UpdateState(AccessOverriderBoundUserInterfaceState state)
{
PrivilegedIdLabel.Text = state.PrivilegedIdName;
@@ -105,7 +96,7 @@ namespace Content.Client.Access.UI
_owner.SubmitData(
// Iterate over the buttons dictionary, filter by `Pressed`, only get key from the key/value pair
_accessButtons.Where(x => x.Value.Pressed).Select(x => x.Key).ToList());
_accessButtons.Where(x => x.Value.Pressed).Select(x => new ProtoId<AccessLevelPrototype>(x.Key)).ToList());
}
}
}

View File

@@ -1,5 +1,6 @@
using Content.Shared.Access;
using Content.Shared.Access.Components;
using Content.Shared.Access;
using Content.Shared.Access.Systems;
using Content.Shared.Containers.ItemSlots;
using Content.Shared.CrewManifest;
@@ -28,7 +29,6 @@ namespace Content.Client.Access.UI
if (EntMan.TryGetComponent<IdCardConsoleComponent>(Owner, out var idCard))
{
accessLevels = idCard.AccessLevels;
accessLevels.Sort();
}
else
{
@@ -65,7 +65,7 @@ namespace Content.Client.Access.UI
_window?.UpdateState(castState);
}
public void SubmitData(string newFullName, string newJobTitle, List<string> newAccessList, string newJobPrototype)
public void SubmitData(string newFullName, string newJobTitle, List<ProtoId<AccessLevelPrototype>> newAccessList, string newJobPrototype)
{
if (newFullName.Length > MaxFullNameLength)
newFullName = newFullName[..MaxFullNameLength];

View File

@@ -30,10 +30,6 @@
<Label Text="{Loc 'id-card-console-window-job-selection-label'}" />
<OptionButton Name="JobPresetOptionButton" />
</GridContainer>
<GridContainer Name="AccessLevelGrid" Columns="5" HorizontalAlignment="Center">
<!-- Access level buttons are added here by the C# code -->
</GridContainer>
<Control Name="AccessLevelControlContainer" />
</BoxContainer>
</DefaultWindow>

View File

@@ -20,7 +20,7 @@ namespace Content.Client.Access.UI
private readonly IdCardConsoleBoundUserInterface _owner;
private readonly Dictionary<string, Button> _accessButtons = new();
private AccessLevelControl _accessButtons = new();
private readonly List<string> _jobPrototypeIds = new();
private string? _lastFullName;
@@ -66,36 +66,18 @@ namespace Content.Client.Access.UI
JobPresetOptionButton.OnItemSelected += SelectJobPreset;
foreach (var access in accessLevels)
_accessButtons.Populate(accessLevels, prototypeManager);
AccessLevelControlContainer.AddChild(_accessButtons);
foreach (var (id, button) in _accessButtons.ButtonsList)
{
if (!prototypeManager.TryIndex<AccessLevelPrototype>(access, out var accessLevel))
{
_logMill.Error($"Unable to find accesslevel for {access}");
continue;
}
var newButton = new Button
{
Text = GetAccessLevelName(accessLevel),
ToggleMode = true,
};
AccessLevelGrid.AddChild(newButton);
_accessButtons.Add(accessLevel.ID, newButton);
newButton.OnPressed += _ => SubmitData();
button.OnPressed += _ => SubmitData();
}
}
private static string GetAccessLevelName(AccessLevelPrototype prototype)
{
if (prototype.Name is { } name)
return Loc.GetString(name);
return prototype.ID;
}
private void ClearAllAccess()
{
foreach (var button in _accessButtons.Values)
foreach (var button in _accessButtons.ButtonsList.Values)
{
if (button.Pressed)
{
@@ -119,7 +101,7 @@ namespace Content.Client.Access.UI
// this is a sussy way to do this
foreach (var access in job.Access)
{
if (_accessButtons.TryGetValue(access, out var button) && !button.Disabled)
if (_accessButtons.ButtonsList.TryGetValue(access, out var button) && !button.Disabled)
{
button.Pressed = true;
}
@@ -134,7 +116,7 @@ namespace Content.Client.Access.UI
foreach (var access in groupPrototype.Tags)
{
if (_accessButtons.TryGetValue(access, out var button) && !button.Disabled)
if (_accessButtons.ButtonsList.TryGetValue(access, out var button) && !button.Disabled)
{
button.Pressed = true;
}
@@ -184,15 +166,10 @@ namespace Content.Client.Access.UI
JobPresetOptionButton.Disabled = !interfaceEnabled;
foreach (var (accessName, button) in _accessButtons)
{
button.Disabled = !interfaceEnabled;
if (interfaceEnabled)
{
button.Pressed = state.TargetIdAccessList?.Contains(accessName) ?? false;
button.Disabled = (!state.AllowedModifyAccessList?.Contains(accessName)) ?? true;
}
}
_accessButtons.UpdateState(state.TargetIdAccessList?.ToList() ??
new List<ProtoId<AccessLevelPrototype>>(),
state.AllowedModifyAccessList?.ToList() ??
new List<ProtoId<AccessLevelPrototype>>());
var jobIndex = _jobPrototypeIds.IndexOf(state.TargetIdJobPrototype);
if (jobIndex >= 0)
@@ -215,7 +192,7 @@ namespace Content.Client.Access.UI
FullNameLineEdit.Text,
JobTitleLineEdit.Text,
// Iterate over the buttons dictionary, filter by `Pressed`, only get key from the key/value pair
_accessButtons.Where(x => x.Value.Pressed).Select(x => x.Key).ToList(),
_accessButtons.ButtonsList.Where(x => x.Value.Pressed).Select(x => x.Key).ToList(),
jobProtoDirty ? _jobPrototypeIds[JobPresetOptionButton.SelectedId] : string.Empty);
}
}

View File

@@ -0,0 +1,59 @@
using Content.Shared.Access;
using Content.Shared.Doors.Electronics;
using Robust.Client.GameObjects;
using Robust.Shared.Prototypes;
namespace Content.Client.Doors.Electronics;
public sealed class DoorElectronicsBoundUserInterface : BoundUserInterface
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
private DoorElectronicsConfigurationMenu? _window;
public DoorElectronicsBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
}
protected override void Open()
{
base.Open();
List<ProtoId<AccessLevelPrototype>> accessLevels = new();
foreach (var accessLevel in _prototypeManager.EnumeratePrototypes<AccessLevelPrototype>())
{
if (accessLevel.Name != null)
{
accessLevels.Add(accessLevel.ID);
}
}
accessLevels.Sort();
_window = new DoorElectronicsConfigurationMenu(this, accessLevels, _prototypeManager);
_window.OnClose += Close;
_window.OpenCentered();
}
protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);
var castState = (DoorElectronicsConfigurationState) state;
_window?.UpdateState(castState);
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (!disposing) return;
_window?.Dispose();
}
public void UpdateConfiguration(List<ProtoId<AccessLevelPrototype>> newAccessList)
{
SendMessage(new DoorElectronicsUpdateConfigurationMessage(newAccessList));
}
}

View File

@@ -0,0 +1,6 @@
<controls:FancyWindow xmlns="https://spacestation14.io"
xmlns:ui="clr-namespace:Content.Client.UserInterface"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls">
<Control Name="AccessLevelControlContainer" />
</controls:FancyWindow>

View File

@@ -0,0 +1,41 @@
using System.Linq;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
using Content.Client.Access.UI;
using Content.Client.Doors.Electronics;
using Content.Shared.Access;
using Content.Shared.Doors.Electronics;
using FancyWindow = Content.Client.UserInterface.Controls.FancyWindow;
namespace Content.Client.Doors.Electronics;
[GenerateTypedNameReferences]
public sealed partial class DoorElectronicsConfigurationMenu : FancyWindow
{
private readonly DoorElectronicsBoundUserInterface _owner;
private AccessLevelControl _buttonsList = new();
public DoorElectronicsConfigurationMenu(DoorElectronicsBoundUserInterface ui, List<ProtoId<AccessLevelPrototype>> accessLevels, IPrototypeManager prototypeManager)
{
RobustXamlLoader.Load(this);
_owner = ui;
_buttonsList.Populate(accessLevels, prototypeManager);
AccessLevelControlContainer.AddChild(_buttonsList);
foreach (var (id, button) in _buttonsList.ButtonsList)
{
button.OnPressed += _ => _owner.UpdateConfiguration(
_buttonsList.ButtonsList.Where(x => x.Value.Pressed).Select(x => x.Key).ToList());
}
}
public void UpdateState(DoorElectronicsConfigurationState state)
{
_buttonsList.UpdateState(state.AccessList);
}
}