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); _window?.UpdateState(castState);
} }
public void SubmitData(List<string> newAccessList) public void SubmitData(List<ProtoId<AccessLevelPrototype>> newAccessList)
{ {
SendMessage(new WriteToTargetAccessReaderIdMessage(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 ILogManager _logManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
private readonly ISawmill _logMill = default!;
private readonly AccessOverriderBoundUserInterface _owner; private readonly AccessOverriderBoundUserInterface _owner;
private readonly Dictionary<string, Button> _accessButtons = new(); private readonly Dictionary<string, Button> _accessButtons = new();
@@ -25,7 +24,7 @@ namespace Content.Client.Access.UI
{ {
RobustXamlLoader.Load(this); RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this); IoCManager.InjectDependencies(this);
_logMill = _logManager.GetSawmill(SharedAccessOverriderSystem.Sawmill); var logMill = _logManager.GetSawmill(SharedAccessOverriderSystem.Sawmill);
_owner = owner; _owner = owner;
@@ -33,13 +32,13 @@ namespace Content.Client.Access.UI
{ {
if (!prototypeManager.TryIndex(access, out var accessLevel)) if (!prototypeManager.TryIndex(access, out var accessLevel))
{ {
_logMill.Error($"Unable to find accesslevel for {access}"); logMill.Error($"Unable to find accesslevel for {access}");
continue; continue;
} }
var newButton = new Button var newButton = new Button
{ {
Text = GetAccessLevelName(accessLevel), Text = accessLevel.GetAccessLevelName(),
ToggleMode = true, 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) public void UpdateState(AccessOverriderBoundUserInterfaceState state)
{ {
PrivilegedIdLabel.Text = state.PrivilegedIdName; PrivilegedIdLabel.Text = state.PrivilegedIdName;
@@ -105,7 +96,7 @@ namespace Content.Client.Access.UI
_owner.SubmitData( _owner.SubmitData(
// Iterate over the buttons dictionary, filter by `Pressed`, only get key from the key/value pair // 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;
using Content.Shared.Access.Components; using Content.Shared.Access.Components;
using Content.Shared.Access;
using Content.Shared.Access.Systems; using Content.Shared.Access.Systems;
using Content.Shared.Containers.ItemSlots; using Content.Shared.Containers.ItemSlots;
using Content.Shared.CrewManifest; using Content.Shared.CrewManifest;
@@ -28,7 +29,6 @@ namespace Content.Client.Access.UI
if (EntMan.TryGetComponent<IdCardConsoleComponent>(Owner, out var idCard)) if (EntMan.TryGetComponent<IdCardConsoleComponent>(Owner, out var idCard))
{ {
accessLevels = idCard.AccessLevels; accessLevels = idCard.AccessLevels;
accessLevels.Sort();
} }
else else
{ {
@@ -65,7 +65,7 @@ namespace Content.Client.Access.UI
_window?.UpdateState(castState); _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) if (newFullName.Length > MaxFullNameLength)
newFullName = newFullName[..MaxFullNameLength]; newFullName = newFullName[..MaxFullNameLength];

View File

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

View File

@@ -20,7 +20,7 @@ namespace Content.Client.Access.UI
private readonly IdCardConsoleBoundUserInterface _owner; private readonly IdCardConsoleBoundUserInterface _owner;
private readonly Dictionary<string, Button> _accessButtons = new(); private AccessLevelControl _accessButtons = new();
private readonly List<string> _jobPrototypeIds = new(); private readonly List<string> _jobPrototypeIds = new();
private string? _lastFullName; private string? _lastFullName;
@@ -66,36 +66,18 @@ namespace Content.Client.Access.UI
JobPresetOptionButton.OnItemSelected += SelectJobPreset; 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)) button.OnPressed += _ => SubmitData();
{
_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();
} }
} }
private static string GetAccessLevelName(AccessLevelPrototype prototype)
{
if (prototype.Name is { } name)
return Loc.GetString(name);
return prototype.ID;
}
private void ClearAllAccess() private void ClearAllAccess()
{ {
foreach (var button in _accessButtons.Values) foreach (var button in _accessButtons.ButtonsList.Values)
{ {
if (button.Pressed) if (button.Pressed)
{ {
@@ -119,7 +101,7 @@ namespace Content.Client.Access.UI
// this is a sussy way to do this // this is a sussy way to do this
foreach (var access in job.Access) 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; button.Pressed = true;
} }
@@ -134,7 +116,7 @@ namespace Content.Client.Access.UI
foreach (var access in groupPrototype.Tags) 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; button.Pressed = true;
} }
@@ -184,15 +166,10 @@ namespace Content.Client.Access.UI
JobPresetOptionButton.Disabled = !interfaceEnabled; JobPresetOptionButton.Disabled = !interfaceEnabled;
foreach (var (accessName, button) in _accessButtons) _accessButtons.UpdateState(state.TargetIdAccessList?.ToList() ??
{ new List<ProtoId<AccessLevelPrototype>>(),
button.Disabled = !interfaceEnabled; state.AllowedModifyAccessList?.ToList() ??
if (interfaceEnabled) new List<ProtoId<AccessLevelPrototype>>());
{
button.Pressed = state.TargetIdAccessList?.Contains(accessName) ?? false;
button.Disabled = (!state.AllowedModifyAccessList?.Contains(accessName)) ?? true;
}
}
var jobIndex = _jobPrototypeIds.IndexOf(state.TargetIdJobPrototype); var jobIndex = _jobPrototypeIds.IndexOf(state.TargetIdJobPrototype);
if (jobIndex >= 0) if (jobIndex >= 0)
@@ -215,7 +192,7 @@ namespace Content.Client.Access.UI
FullNameLineEdit.Text, FullNameLineEdit.Text,
JobTitleLineEdit.Text, JobTitleLineEdit.Text,
// Iterate over the buttons dictionary, filter by `Pressed`, only get key from the key/value pair // 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); 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);
}
}

View File

@@ -57,9 +57,9 @@ namespace Content.IntegrationTests.Tests.Access
var reader = new AccessReaderComponent(); var reader = new AccessReaderComponent();
Assert.Multiple(() => Assert.Multiple(() =>
{ {
Assert.That(system.AreAccessTagsAllowed(new[] { "Foo" }, reader), Is.True); Assert.That(system.AreAccessTagsAllowed(new List<ProtoId<AccessLevelPrototype>> { "Foo" }, reader), Is.True);
Assert.That(system.AreAccessTagsAllowed(new[] { "Bar" }, reader), Is.True); Assert.That(system.AreAccessTagsAllowed(new List<ProtoId<AccessLevelPrototype>> { "Bar" }, reader), Is.True);
Assert.That(system.AreAccessTagsAllowed(Array.Empty<string>(), reader), Is.True); Assert.That(system.AreAccessTagsAllowed(Array.Empty<ProtoId<AccessLevelPrototype>>(), reader), Is.True);
}); });
// test deny // test deny
@@ -67,58 +67,58 @@ namespace Content.IntegrationTests.Tests.Access
reader.DenyTags.Add("A"); reader.DenyTags.Add("A");
Assert.Multiple(() => Assert.Multiple(() =>
{ {
Assert.That(system.AreAccessTagsAllowed(new[] { "Foo" }, reader), Is.True); Assert.That(system.AreAccessTagsAllowed(new List<ProtoId<AccessLevelPrototype>> { "Foo" }, reader), Is.True);
Assert.That(system.AreAccessTagsAllowed(new[] { "A" }, reader), Is.False); Assert.That(system.AreAccessTagsAllowed(new List<ProtoId<AccessLevelPrototype>> { "A" }, reader), Is.False);
Assert.That(system.AreAccessTagsAllowed(new[] { "A", "Foo" }, reader), Is.False); Assert.That(system.AreAccessTagsAllowed(new List<ProtoId<AccessLevelPrototype>> { "A", "Foo" }, reader), Is.False);
Assert.That(system.AreAccessTagsAllowed(Array.Empty<string>(), reader), Is.True); Assert.That(system.AreAccessTagsAllowed(Array.Empty<ProtoId<AccessLevelPrototype>>(), reader), Is.True);
}); });
// test one list // test one list
reader = new AccessReaderComponent(); reader = new AccessReaderComponent();
reader.AccessLists.Add(new HashSet<string> { "A" }); reader.AccessLists.Add(new HashSet<ProtoId<AccessLevelPrototype>> { "A" });
Assert.Multiple(() => Assert.Multiple(() =>
{ {
Assert.That(system.AreAccessTagsAllowed(new[] { "A" }, reader), Is.True); Assert.That(system.AreAccessTagsAllowed(new List<ProtoId<AccessLevelPrototype>> { "A" }, reader), Is.True);
Assert.That(system.AreAccessTagsAllowed(new[] { "B" }, reader), Is.False); Assert.That(system.AreAccessTagsAllowed(new List<ProtoId<AccessLevelPrototype>> { "B" }, reader), Is.False);
Assert.That(system.AreAccessTagsAllowed(new[] { "A", "B" }, reader), Is.True); Assert.That(system.AreAccessTagsAllowed(new List<ProtoId<AccessLevelPrototype>> { "A", "B" }, reader), Is.True);
Assert.That(system.AreAccessTagsAllowed(Array.Empty<string>(), reader), Is.False); Assert.That(system.AreAccessTagsAllowed(Array.Empty<ProtoId<AccessLevelPrototype>>(), reader), Is.False);
}); });
// test one list - two items // test one list - two items
reader = new AccessReaderComponent(); reader = new AccessReaderComponent();
reader.AccessLists.Add(new HashSet<string> { "A", "B" }); reader.AccessLists.Add(new HashSet<ProtoId<AccessLevelPrototype>> { "A", "B" });
Assert.Multiple(() => Assert.Multiple(() =>
{ {
Assert.That(system.AreAccessTagsAllowed(new[] { "A" }, reader), Is.False); Assert.That(system.AreAccessTagsAllowed(new List<ProtoId<AccessLevelPrototype>> { "A" }, reader), Is.False);
Assert.That(system.AreAccessTagsAllowed(new[] { "B" }, reader), Is.False); Assert.That(system.AreAccessTagsAllowed(new List<ProtoId<AccessLevelPrototype>> { "B" }, reader), Is.False);
Assert.That(system.AreAccessTagsAllowed(new[] { "A", "B" }, reader), Is.True); Assert.That(system.AreAccessTagsAllowed(new List<ProtoId<AccessLevelPrototype>> { "A", "B" }, reader), Is.True);
Assert.That(system.AreAccessTagsAllowed(Array.Empty<string>(), reader), Is.False); Assert.That(system.AreAccessTagsAllowed(Array.Empty<ProtoId<AccessLevelPrototype>>(), reader), Is.False);
}); });
// test two list // test two list
reader = new AccessReaderComponent(); reader = new AccessReaderComponent();
reader.AccessLists.Add(new HashSet<string> { "A" }); reader.AccessLists.Add(new HashSet<ProtoId<AccessLevelPrototype>> { "A" });
reader.AccessLists.Add(new HashSet<string> { "B", "C" }); reader.AccessLists.Add(new HashSet<ProtoId<AccessLevelPrototype>> { "B", "C" });
Assert.Multiple(() => Assert.Multiple(() =>
{ {
Assert.That(system.AreAccessTagsAllowed(new[] { "A" }, reader), Is.True); Assert.That(system.AreAccessTagsAllowed(new List<ProtoId<AccessLevelPrototype>> { "A" }, reader), Is.True);
Assert.That(system.AreAccessTagsAllowed(new[] { "B" }, reader), Is.False); Assert.That(system.AreAccessTagsAllowed(new List<ProtoId<AccessLevelPrototype>> { "B" }, reader), Is.False);
Assert.That(system.AreAccessTagsAllowed(new[] { "A", "B" }, reader), Is.True); Assert.That(system.AreAccessTagsAllowed(new List<ProtoId<AccessLevelPrototype>> { "A", "B" }, reader), Is.True);
Assert.That(system.AreAccessTagsAllowed(new[] { "C", "B" }, reader), Is.True); Assert.That(system.AreAccessTagsAllowed(new List<ProtoId<AccessLevelPrototype>> { "C", "B" }, reader), Is.True);
Assert.That(system.AreAccessTagsAllowed(new[] { "C", "B", "A" }, reader), Is.True); Assert.That(system.AreAccessTagsAllowed(new List<ProtoId<AccessLevelPrototype>> { "C", "B", "A" }, reader), Is.True);
Assert.That(system.AreAccessTagsAllowed(Array.Empty<string>(), reader), Is.False); Assert.That(system.AreAccessTagsAllowed(Array.Empty<ProtoId<AccessLevelPrototype>>(), reader), Is.False);
}); });
// test deny list // test deny list
reader = new AccessReaderComponent(); reader = new AccessReaderComponent();
reader.AccessLists.Add(new HashSet<string> { "A" }); reader.AccessLists.Add(new HashSet<ProtoId<AccessLevelPrototype>> { "A" });
reader.DenyTags.Add("B"); reader.DenyTags.Add("B");
Assert.Multiple(() => Assert.Multiple(() =>
{ {
Assert.That(system.AreAccessTagsAllowed(new[] { "A" }, reader), Is.True); Assert.That(system.AreAccessTagsAllowed(new List<ProtoId<AccessLevelPrototype>> { "A" }, reader), Is.True);
Assert.That(system.AreAccessTagsAllowed(new[] { "B" }, reader), Is.False); Assert.That(system.AreAccessTagsAllowed(new List<ProtoId<AccessLevelPrototype>> { "B" }, reader), Is.False);
Assert.That(system.AreAccessTagsAllowed(new[] { "A", "B" }, reader), Is.False); Assert.That(system.AreAccessTagsAllowed(new List<ProtoId<AccessLevelPrototype>> { "A", "B" }, reader), Is.False);
Assert.That(system.AreAccessTagsAllowed(Array.Empty<string>(), reader), Is.False); Assert.That(system.AreAccessTagsAllowed(Array.Empty<ProtoId<AccessLevelPrototype>>(), reader), Is.False);
}); });
}); });
await pair.CleanReturnAsync(); await pair.CleanReturnAsync();

View File

@@ -1,5 +1,6 @@
using System.Linq; using System.Linq;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Shared.Access;
using Content.Shared.Access.Components; using Content.Shared.Access.Components;
using Content.Shared.Access.Systems; using Content.Shared.Access.Systems;
using Content.Shared.Administration.Logs; using Content.Shared.Administration.Logs;
@@ -12,6 +13,7 @@ using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems; using Robust.Shared.Audio.Systems;
using Robust.Shared.Containers; using Robust.Shared.Containers;
using Robust.Shared.Player; using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using static Content.Shared.Access.Components.AccessOverriderComponent; using static Content.Shared.Access.Components.AccessOverriderComponent;
namespace Content.Server.Access.Systems; namespace Content.Server.Access.Systems;
@@ -26,6 +28,7 @@ public sealed class AccessOverriderSystem : SharedAccessOverriderSystem
[Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly SharedAudioSystem _audioSystem = default!; [Dependency] private readonly SharedAudioSystem _audioSystem = default!;
[Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
public override void Initialize() public override void Initialize()
{ {
@@ -108,17 +111,20 @@ public sealed class AccessOverriderSystem : SharedAccessOverriderSystem
var targetLabel = Loc.GetString("access-overrider-window-no-target"); var targetLabel = Loc.GetString("access-overrider-window-no-target");
var targetLabelColor = Color.Red; var targetLabelColor = Color.Red;
string[]? possibleAccess = null; ProtoId<AccessLevelPrototype>[]? possibleAccess = null;
string[]? currentAccess = null; ProtoId<AccessLevelPrototype>[]? currentAccess = null;
string[]? missingAccess = null; ProtoId<AccessLevelPrototype>[]? missingAccess = null;
if (component.TargetAccessReaderId is { Valid: true } accessReader) if (component.TargetAccessReaderId is { Valid: true } accessReader)
{ {
targetLabel = Loc.GetString("access-overrider-window-target-label") + " " + EntityManager.GetComponent<MetaDataComponent>(component.TargetAccessReaderId).EntityName; targetLabel = Loc.GetString("access-overrider-window-target-label") + " " + EntityManager.GetComponent<MetaDataComponent>(component.TargetAccessReaderId).EntityName;
targetLabelColor = Color.White; targetLabelColor = Color.White;
List<HashSet<string>> currentAccessHashsets = EntityManager.GetComponent<AccessReaderComponent>(accessReader).AccessLists; if (!_accessReader.GetMainAccessReader(accessReader, out var accessReaderComponent))
currentAccess = ConvertAccessHashSetsToList(currentAccessHashsets)?.ToArray(); return;
var currentAccessHashsets = accessReaderComponent.AccessLists;
currentAccess = ConvertAccessHashSetsToList(currentAccessHashsets).ToArray();
} }
if (component.PrivilegedIdSlot.Item is { Valid: true } idCard) if (component.PrivilegedIdSlot.Item is { Valid: true } idCard)
@@ -151,15 +157,15 @@ public sealed class AccessOverriderSystem : SharedAccessOverriderSystem
_userInterface.TrySetUiState(uid, AccessOverriderUiKey.Key, newState); _userInterface.TrySetUiState(uid, AccessOverriderUiKey.Key, newState);
} }
private List<string> ConvertAccessHashSetsToList(List<HashSet<string>> accessHashsets) private List<ProtoId<AccessLevelPrototype>> ConvertAccessHashSetsToList(List<HashSet<ProtoId<AccessLevelPrototype>>> accessHashsets)
{ {
List<string> accessList = new List<string>(); List<ProtoId<AccessLevelPrototype>> accessList = new List<ProtoId<AccessLevelPrototype>>();
if (accessHashsets != null && accessHashsets.Any()) if (accessHashsets != null && accessHashsets.Any())
{ {
foreach (HashSet<string> hashSet in accessHashsets) foreach (HashSet<ProtoId<AccessLevelPrototype>> hashSet in accessHashsets)
{ {
foreach (string hash in hashSet.ToArray()) foreach (ProtoId<AccessLevelPrototype> hash in hashSet.ToArray())
{ {
accessList.Add(hash); accessList.Add(hash);
} }
@@ -169,15 +175,15 @@ public sealed class AccessOverriderSystem : SharedAccessOverriderSystem
return accessList; return accessList;
} }
private List<HashSet<string>> ConvertAccessListToHashSet(List<string> accessList) private List<HashSet<ProtoId<AccessLevelPrototype>>> ConvertAccessListToHashSet(List<ProtoId<AccessLevelPrototype>> accessList)
{ {
List<HashSet<string>> accessHashsets = new List<HashSet<string>>(); List<HashSet<ProtoId<AccessLevelPrototype>>> accessHashsets = new List<HashSet<ProtoId<AccessLevelPrototype>>>();
if (accessList != null && accessList.Any()) if (accessList != null && accessList.Any())
{ {
foreach (string access in accessList) foreach (ProtoId<AccessLevelPrototype> access in accessList)
{ {
accessHashsets.Add(new HashSet<string>() { access }); accessHashsets.Add(new HashSet<ProtoId<AccessLevelPrototype>>() { access });
} }
} }
@@ -188,7 +194,7 @@ public sealed class AccessOverriderSystem : SharedAccessOverriderSystem
/// Called whenever an access button is pressed, adding or removing that access requirement from the target access reader. /// Called whenever an access button is pressed, adding or removing that access requirement from the target access reader.
/// </summary> /// </summary>
private void TryWriteToTargetAccessReaderId(EntityUid uid, private void TryWriteToTargetAccessReaderId(EntityUid uid,
List<string> newAccessList, List<ProtoId<AccessLevelPrototype>> newAccessList,
EntityUid player, EntityUid player,
AccessOverriderComponent? component = null) AccessOverriderComponent? component = null)
{ {
@@ -211,9 +217,7 @@ public sealed class AccessOverriderSystem : SharedAccessOverriderSystem
return; return;
} }
TryComp(component.TargetAccessReaderId, out AccessReaderComponent? accessReader); if (!_accessReader.GetMainAccessReader(component.TargetAccessReaderId, out var accessReader))
if (accessReader == null)
return; return;
var oldTags = ConvertAccessHashSetsToList(accessReader.AccessLists); var oldTags = ConvertAccessHashSetsToList(accessReader.AccessLists);
@@ -262,10 +266,10 @@ public sealed class AccessOverriderSystem : SharedAccessOverriderSystem
if (!Resolve(uid, ref component)) if (!Resolve(uid, ref component))
return true; return true;
if (!EntityManager.TryGetComponent<AccessReaderComponent>(uid, out var reader)) if (_accessReader.GetMainAccessReader(uid, out var accessReader))
return true; return true;
var privilegedId = component.PrivilegedIdSlot.Item; var privilegedId = component.PrivilegedIdSlot.Item;
return privilegedId != null && _accessReader.IsAllowed(privilegedId.Value, uid, reader); return privilegedId != null && _accessReader.IsAllowed(privilegedId.Value, uid, accessReader);
} }
} }

View File

@@ -12,6 +12,7 @@ using Robust.Shared.Containers;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using System.Linq; using System.Linq;
using static Content.Shared.Access.Components.IdCardConsoleComponent; using static Content.Shared.Access.Components.IdCardConsoleComponent;
using Content.Shared.Access;
namespace Content.Server.Access.Systems; namespace Content.Server.Access.Systems;
@@ -54,11 +55,11 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem
return; return;
var privilegedIdName = string.Empty; var privilegedIdName = string.Empty;
string[]? possibleAccess = null; List<ProtoId<AccessLevelPrototype>>? possibleAccess = null;
if (component.PrivilegedIdSlot.Item is { Valid: true } item) if (component.PrivilegedIdSlot.Item is { Valid: true } item)
{ {
privilegedIdName = EntityManager.GetComponent<MetaDataComponent>(item).EntityName; privilegedIdName = EntityManager.GetComponent<MetaDataComponent>(item).EntityName;
possibleAccess = _accessReader.FindAccessTags(item).ToArray(); possibleAccess = _accessReader.FindAccessTags(item).ToList();
} }
IdCardConsoleBoundUserInterfaceState newState; IdCardConsoleBoundUserInterfaceState newState;
@@ -82,7 +83,7 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem
var targetIdComponent = EntityManager.GetComponent<IdCardComponent>(targetId); var targetIdComponent = EntityManager.GetComponent<IdCardComponent>(targetId);
var targetAccessComponent = EntityManager.GetComponent<AccessComponent>(targetId); var targetAccessComponent = EntityManager.GetComponent<AccessComponent>(targetId);
var jobProto = string.Empty; var jobProto = new ProtoId<AccessLevelPrototype>(string.Empty);
if (TryComp<StationRecordKeyStorageComponent>(targetId, out var keyStorage) if (TryComp<StationRecordKeyStorageComponent>(targetId, out var keyStorage)
&& keyStorage.Key is {} key && keyStorage.Key is {} key
&& _record.TryGetRecord<GeneralStationRecord>(key, out var record)) && _record.TryGetRecord<GeneralStationRecord>(key, out var record))
@@ -96,7 +97,7 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem
true, true,
targetIdComponent.FullName, targetIdComponent.FullName,
targetIdComponent.JobTitle, targetIdComponent.JobTitle,
targetAccessComponent.Tags.ToArray(), targetAccessComponent.Tags.ToList(),
possibleAccess, possibleAccess,
jobProto, jobProto,
privilegedIdName, privilegedIdName,
@@ -113,8 +114,8 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem
private void TryWriteToTargetId(EntityUid uid, private void TryWriteToTargetId(EntityUid uid,
string newFullName, string newFullName,
string newJobTitle, string newJobTitle,
List<string> newAccessList, List<ProtoId<AccessLevelPrototype>> newAccessList,
string newJobProto, ProtoId<AccessLevelPrototype> newJobProto,
EntityUid player, EntityUid player,
IdCardConsoleComponent? component = null) IdCardConsoleComponent? component = null)
{ {
@@ -140,7 +141,7 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem
return; return;
} }
var oldTags = _access.TryGetTags(targetId) ?? new List<string>(); var oldTags = _access.TryGetTags(targetId) ?? new List<ProtoId<AccessLevelPrototype>>();
oldTags = oldTags.ToList(); oldTags = oldTags.ToList();
var privilegedId = component.PrivilegedIdSlot.Item; var privilegedId = component.PrivilegedIdSlot.Item;
@@ -189,7 +190,7 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem
return privilegedId != null && _accessReader.IsAllowed(privilegedId.Value, uid, reader); return privilegedId != null && _accessReader.IsAllowed(privilegedId.Value, uid, reader);
} }
private void UpdateStationRecord(EntityUid uid, EntityUid targetId, string newFullName, string newJobTitle, JobPrototype? newJobProto) private void UpdateStationRecord(EntityUid uid, EntityUid targetId, string newFullName, ProtoId<AccessLevelPrototype> newJobTitle, JobPrototype? newJobProto)
{ {
if (!TryComp<StationRecordKeyStorageComponent>(targetId, out var keyStorage) if (!TryComp<StationRecordKeyStorageComponent>(targetId, out var keyStorage)
|| keyStorage.Key is not { } key || keyStorage.Key is not { } key

View File

@@ -35,6 +35,7 @@ using Robust.Shared.Map.Components;
using Robust.Shared.Physics; using Robust.Shared.Physics;
using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Components;
using Robust.Shared.Player; using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility; using Robust.Shared.Utility;
namespace Content.Server.Administration.Systems; namespace Content.Server.Administration.Systems;
@@ -844,14 +845,14 @@ public sealed partial class AdminVerbSystem
{ {
var allAccess = _prototypeManager var allAccess = _prototypeManager
.EnumeratePrototypes<AccessLevelPrototype>() .EnumeratePrototypes<AccessLevelPrototype>()
.Select(p => p.ID).ToArray(); .Select(p => new ProtoId<AccessLevelPrototype>(p.ID)).ToArray();
_accessSystem.TrySetTags(entity, allAccess); _accessSystem.TrySetTags(entity, allAccess);
} }
private void RevokeAllAccess(EntityUid entity) private void RevokeAllAccess(EntityUid entity)
{ {
_accessSystem.TrySetTags(entity, Array.Empty<string>()); _accessSystem.TrySetTags(entity, new List<ProtoId<AccessLevelPrototype>>());
} }
public enum TricksVerbPriorities public enum TricksVerbPriorities

View File

@@ -0,0 +1,69 @@
using System.Linq;
using Content.Server.Doors.Electronics;
using Content.Shared.Access;
using Content.Shared.Access.Components;
using Content.Shared.Access.Systems;
using Content.Shared.DeviceNetwork.Components;
using Content.Shared.Doors.Electronics;
using Content.Shared.Doors;
using Content.Shared.Interaction;
using Robust.Server.GameObjects;
using Robust.Shared.Prototypes;
namespace Content.Server.Doors.Electronics;
public sealed class DoorElectronicsSystem : EntitySystem
{
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
[Dependency] private readonly AccessReaderSystem _accessReader = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<DoorElectronicsComponent, DoorElectronicsUpdateConfigurationMessage>(OnChangeConfiguration);
SubscribeLocalEvent<DoorElectronicsComponent, AccessReaderConfigurationChangedEvent>(OnAccessReaderChanged);
SubscribeLocalEvent<DoorElectronicsComponent, BoundUIOpenedEvent>(OnBoundUIOpened);
}
public void UpdateUserInterface(EntityUid uid, DoorElectronicsComponent component)
{
var accesses = new List<ProtoId<AccessLevelPrototype>>();
if (TryComp<AccessReaderComponent>(uid, out var accessReader))
{
foreach (var accessList in accessReader.AccessLists)
{
var access = accessList.FirstOrDefault();
accesses.Add(access);
}
}
var state = new DoorElectronicsConfigurationState(accesses);
_uiSystem.TrySetUiState(uid, DoorElectronicsConfigurationUiKey.Key, state);
}
private void OnChangeConfiguration(
EntityUid uid,
DoorElectronicsComponent component,
DoorElectronicsUpdateConfigurationMessage args)
{
var accessReader = EnsureComp<AccessReaderComponent>(uid);
_accessReader.SetAccesses(uid, accessReader, args.AccessList);
}
private void OnAccessReaderChanged(
EntityUid uid,
DoorElectronicsComponent component,
AccessReaderConfigurationChangedEvent args)
{
UpdateUserInterface(uid, component);
}
private void OnBoundUIOpened(
EntityUid uid,
DoorElectronicsComponent component,
BoundUIOpenedEvent args)
{
UpdateUserInterface(uid, component);
}
}

View File

@@ -14,6 +14,7 @@ using Robust.Server.Placement;
using Robust.Server.Player; using Robust.Server.Player;
using Robust.Shared.Enums; using Robust.Shared.Enums;
using Robust.Shared.Player; using Robust.Shared.Player;
using Robust.Shared.Prototypes;
namespace Content.Server.Sandbox namespace Content.Server.Sandbox
{ {
@@ -121,7 +122,7 @@ namespace Content.Server.Sandbox
var allAccess = PrototypeManager var allAccess = PrototypeManager
.EnumeratePrototypes<AccessLevelPrototype>() .EnumeratePrototypes<AccessLevelPrototype>()
.Select(p => p.ID).ToArray(); .Select(p => new ProtoId<AccessLevelPrototype>(p.ID)).ToList();
if (_inventory.TryGetSlotEntity(attached, "id", out var slotEntity)) if (_inventory.TryGetSlotEntity(attached, "id", out var slotEntity))
{ {

View File

@@ -3,6 +3,7 @@ using Content.Server.Explosion.EntitySystems;
using Content.Server.Resist; using Content.Server.Resist;
using Content.Server.Station.Components; using Content.Server.Station.Components;
using Content.Server.Storage.Components; using Content.Server.Storage.Components;
using Content.Shared.Access;
using Content.Shared.Access.Components; using Content.Shared.Access.Components;
using Content.Shared.Coordinates; using Content.Shared.Coordinates;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
@@ -14,6 +15,7 @@ using Content.Shared.Tools.Systems;
using Robust.Shared.Containers; using Robust.Shared.Containers;
using Robust.Shared.Random; using Robust.Shared.Random;
using Robust.Shared.Timing; using Robust.Shared.Timing;
using Robust.Shared.Prototypes;
namespace Content.Server.Storage.EntitySystems; namespace Content.Server.Storage.EntitySystems;
@@ -138,7 +140,7 @@ public sealed class BluespaceLockerSystem : EntitySystem
} }
/// <returns>True if any HashSet in <paramref name="a"/> would grant access to <paramref name="b"/></returns> /// <returns>True if any HashSet in <paramref name="a"/> would grant access to <paramref name="b"/></returns>
private bool AccessMatch(IReadOnlyCollection<HashSet<string>>? a, IReadOnlyCollection<HashSet<string>>? b) private bool AccessMatch(IReadOnlyCollection<HashSet<ProtoId<AccessLevelPrototype>>>? a, IReadOnlyCollection<HashSet<ProtoId<AccessLevelPrototype>>>? b)
{ {
if ((a == null || a.Count == 0) && (b == null || b.Count == 0)) if ((a == null || a.Count == 0) && (b == null || b.Count == 0))
return true; return true;

View File

@@ -1,3 +1,6 @@
using Content.Shared.Whitelist;
using Robust.Server.GameObjects;
using Robust.Server.Player;
using Robust.Shared.Player; using Robust.Shared.Player;
using Robust.Shared.Serialization.TypeSerializers.Implementations; using Robust.Shared.Serialization.TypeSerializers.Implementations;
@@ -34,12 +37,19 @@ namespace Content.Server.UserInterface
[DataField] [DataField]
public bool RequireHands = true; public bool RequireHands = true;
/// <summary>
/// Entities that are required to open this UI.
/// </summary>
[DataField("allowedItems")]
[ViewVariables(VVAccess.ReadWrite)]
public EntityWhitelist? AllowedItems = null;
/// <summary> /// <summary>
/// Whether you can activate this ui with activateinhand or not /// Whether you can activate this ui with activateinhand or not
/// </summary> /// </summary>
[ViewVariables(VVAccess.ReadWrite)] [ViewVariables(VVAccess.ReadWrite)]
[DataField] [DataField]
public bool rightClickOnly = false; public bool RightClickOnly;
/// <summary> /// <summary>
/// Whether spectators (non-admin ghosts) should be allowed to view this UI. /// Whether spectators (non-admin ghosts) should be allowed to view this UI.
@@ -63,4 +73,3 @@ namespace Content.Server.UserInterface
public ICommonSession? CurrentSingleUser; public ICommonSession? CurrentSingleUser;
} }
} }

View File

@@ -26,6 +26,7 @@ public sealed partial class ActivatableUISystem : EntitySystem
SubscribeLocalEvent<ActivatableUIComponent, ActivateInWorldEvent>(OnActivate); SubscribeLocalEvent<ActivatableUIComponent, ActivateInWorldEvent>(OnActivate);
SubscribeLocalEvent<ActivatableUIComponent, UseInHandEvent>(OnUseInHand); SubscribeLocalEvent<ActivatableUIComponent, UseInHandEvent>(OnUseInHand);
SubscribeLocalEvent<ActivatableUIComponent, InteractUsingEvent>(OnInteractUsing);
SubscribeLocalEvent<ActivatableUIComponent, HandDeselectedEvent>(OnHandDeselected); SubscribeLocalEvent<ActivatableUIComponent, HandDeselectedEvent>(OnHandDeselected);
SubscribeLocalEvent<ActivatableUIComponent, GotUnequippedHandEvent>((uid, aui, _) => CloseAll(uid, aui)); SubscribeLocalEvent<ActivatableUIComponent, GotUnequippedHandEvent>((uid, aui, _) => CloseAll(uid, aui));
// *THIS IS A BLATANT WORKAROUND!* RATIONALE: Microwaves need it // *THIS IS A BLATANT WORKAROUND!* RATIONALE: Microwaves need it
@@ -100,12 +101,20 @@ public sealed partial class ActivatableUISystem : EntitySystem
if (args.Handled) if (args.Handled)
return; return;
if (component.rightClickOnly) if (component.RightClickOnly)
return; return;
args.Handled = InteractUI(args.User, uid, component); args.Handled = InteractUI(args.User, uid, component);
} }
private void OnInteractUsing(EntityUid uid, ActivatableUIComponent component, InteractUsingEvent args)
{
if (args.Handled) return;
if (component.AllowedItems == null) return;
if (!component.AllowedItems.IsValid(args.Used, EntityManager)) return;
args.Handled = InteractUI(args.User, uid, component);
}
private void OnParentChanged(EntityUid uid, ActivatableUIComponent aui, ref EntParentChangedMessage args) private void OnParentChanged(EntityUid uid, ActivatableUIComponent aui, ref EntParentChangedMessage args)
{ {
CloseAll(uid, aui); CloseAll(uid, aui);

View File

@@ -14,6 +14,6 @@ public sealed partial class AccessGroupPrototype : IPrototype
[IdDataField] [IdDataField]
public string ID { get; private set; } = default!; public string ID { get; private set; } = default!;
[DataField("tags", required: true, customTypeSerializer: typeof(PrototypeIdHashSetSerializer<AccessLevelPrototype>))] [DataField("tags", required: true)]
public HashSet<string> Tags = default!; public HashSet<ProtoId<AccessLevelPrototype>> Tags = default!;
} }

View File

@@ -17,5 +17,13 @@ namespace Content.Shared.Access
/// </summary> /// </summary>
[DataField("name")] [DataField("name")]
public string? Name { get; set; } public string? Name { get; set; }
public string GetAccessLevelName()
{
if (Name is { } name)
return Loc.GetString(name);
return ID;
}
} }
} }

View File

@@ -20,17 +20,17 @@ public sealed partial class AccessComponent : Component
[AutoNetworkedField] [AutoNetworkedField]
public bool Enabled = true; public bool Enabled = true;
[DataField(customTypeSerializer: typeof(PrototypeIdHashSetSerializer<AccessLevelPrototype>))] [DataField]
[Access(typeof(SharedAccessSystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends [Access(typeof(SharedAccessSystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends
[AutoNetworkedField] [AutoNetworkedField]
public HashSet<string> Tags = new(); public HashSet<ProtoId<AccessLevelPrototype>> Tags = new();
/// <summary> /// <summary>
/// Access Groups. These are added to the tags during map init. After map init this will have no effect. /// Access Groups. These are added to the tags during map init. After map init this will have no effect.
/// </summary> /// </summary>
[DataField(readOnly: true, customTypeSerializer: typeof(PrototypeIdHashSetSerializer<AccessGroupPrototype>))] [DataField(readOnly: true)]
[AutoNetworkedField] [AutoNetworkedField]
public HashSet<string> Groups = new(); public HashSet<ProtoId<AccessGroupPrototype>> Groups = new();
} }
/// <summary> /// <summary>
@@ -47,9 +47,9 @@ public struct GetAdditionalAccessEvent
} }
[ByRefEvent] [ByRefEvent]
public record struct GetAccessTagsEvent(HashSet<string> Tags, IPrototypeManager PrototypeManager) public record struct GetAccessTagsEvent(HashSet<ProtoId<AccessLevelPrototype>> Tags, IPrototypeManager PrototypeManager)
{ {
public void AddGroup(string group) public void AddGroup(ProtoId<AccessGroupPrototype> group)
{ {
if (!PrototypeManager.TryIndex<AccessGroupPrototype>(group, out var groupPrototype)) if (!PrototypeManager.TryIndex<AccessGroupPrototype>(group, out var groupPrototype))
return; return;

View File

@@ -25,9 +25,9 @@ public sealed partial class AccessOverriderComponent : Component
[Serializable, NetSerializable] [Serializable, NetSerializable]
public sealed class WriteToTargetAccessReaderIdMessage : BoundUserInterfaceMessage public sealed class WriteToTargetAccessReaderIdMessage : BoundUserInterfaceMessage
{ {
public readonly List<string> AccessList; public readonly List<ProtoId<AccessLevelPrototype>> AccessList;
public WriteToTargetAccessReaderIdMessage(List<string> accessList) public WriteToTargetAccessReaderIdMessage(List<ProtoId<AccessLevelPrototype>> accessList)
{ {
AccessList = accessList; AccessList = accessList;
} }
@@ -48,15 +48,15 @@ public sealed partial class AccessOverriderComponent : Component
public readonly string PrivilegedIdName; public readonly string PrivilegedIdName;
public readonly bool IsPrivilegedIdPresent; public readonly bool IsPrivilegedIdPresent;
public readonly bool IsPrivilegedIdAuthorized; public readonly bool IsPrivilegedIdAuthorized;
public readonly string[]? TargetAccessReaderIdAccessList; public readonly ProtoId<AccessLevelPrototype>[]? TargetAccessReaderIdAccessList;
public readonly string[]? AllowedModifyAccessList; public readonly ProtoId<AccessLevelPrototype>[]? AllowedModifyAccessList;
public readonly string[]? MissingPrivilegesList; public readonly ProtoId<AccessLevelPrototype>[]? MissingPrivilegesList;
public AccessOverriderBoundUserInterfaceState(bool isPrivilegedIdPresent, public AccessOverriderBoundUserInterfaceState(bool isPrivilegedIdPresent,
bool isPrivilegedIdAuthorized, bool isPrivilegedIdAuthorized,
string[]? targetAccessReaderIdAccessList, ProtoId<AccessLevelPrototype>[]? targetAccessReaderIdAccessList,
string[]? allowedModifyAccessList, ProtoId<AccessLevelPrototype>[]? allowedModifyAccessList,
string[]? missingPrivilegesList, ProtoId<AccessLevelPrototype>[]? missingPrivilegesList,
string privilegedIdName, string privilegedIdName,
string targetLabel, string targetLabel,
Color targetLabelColor) Color targetLabelColor)

View File

@@ -1,5 +1,6 @@
using Content.Shared.StationRecords; using Content.Shared.StationRecords;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set;
@@ -23,15 +24,15 @@ public sealed partial class AccessReaderComponent : Component
/// The set of tags that will automatically deny an allowed check, if any of them are present. /// The set of tags that will automatically deny an allowed check, if any of them are present.
/// </summary> /// </summary>
[ViewVariables(VVAccess.ReadWrite)] [ViewVariables(VVAccess.ReadWrite)]
[DataField(customTypeSerializer: typeof(PrototypeIdHashSetSerializer<AccessLevelPrototype>))] [DataField]
public HashSet<string> DenyTags = new(); public HashSet<ProtoId<AccessLevelPrototype>> DenyTags = new();
/// <summary> /// <summary>
/// List of access groups that grant access to this reader. Only a single matching group is required to gain access. /// List of access groups that grant access to this reader. Only a single matching group is required to gain access.
/// A group matches if it is a subset of the set being checked against. /// A group matches if it is a subset of the set being checked against.
/// </summary> /// </summary>
[DataField("access")] [ViewVariables(VVAccess.ReadWrite)] [DataField("access")] [ViewVariables(VVAccess.ReadWrite)]
public List<HashSet<string>> AccessLists = new(); public List<HashSet<ProtoId<AccessLevelPrototype>>> AccessLists = new();
/// <summary> /// <summary>
/// A list of <see cref="StationRecordKey"/>s that grant access. Only a single matching key is required to gain /// A list of <see cref="StationRecordKey"/>s that grant access. Only a single matching key is required to gain
@@ -88,9 +89,9 @@ public sealed class AccessReaderComponentState : ComponentState
{ {
public bool Enabled; public bool Enabled;
public HashSet<string> DenyTags; public HashSet<ProtoId<AccessLevelPrototype>> DenyTags;
public List<HashSet<string>> AccessLists; public List<HashSet<ProtoId<AccessLevelPrototype>>> AccessLists;
public List<(NetEntity, uint)> AccessKeys; public List<(NetEntity, uint)> AccessKeys;
@@ -98,7 +99,7 @@ public sealed class AccessReaderComponentState : ComponentState
public int AccessLogLimit; public int AccessLogLimit;
public AccessReaderComponentState(bool enabled, HashSet<string> denyTags, List<HashSet<string>> accessLists, List<(NetEntity, uint)> accessKeys, Queue<AccessRecord> accessLog, int accessLogLimit) public AccessReaderComponentState(bool enabled, HashSet<ProtoId<AccessLevelPrototype>> denyTags, List<HashSet<ProtoId<AccessLevelPrototype>>> accessLists, List<(NetEntity, uint)> accessKeys, Queue<AccessRecord> accessLog, int accessLogLimit)
{ {
Enabled = enabled; Enabled = enabled;
DenyTags = denyTags; DenyTags = denyTags;
@@ -108,3 +109,10 @@ public sealed class AccessReaderComponentState : ComponentState
AccessLogLimit = accessLogLimit; AccessLogLimit = accessLogLimit;
} }
} }
public sealed class AccessReaderConfigurationChangedEvent : EntityEventArgs
{
public AccessReaderConfigurationChangedEvent()
{
}
}

View File

@@ -3,6 +3,8 @@ using Content.Shared.Containers.ItemSlots;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
using Robust.Shared.Prototypes;
namespace Content.Shared.Access.Components; namespace Content.Shared.Access.Components;
@@ -27,10 +29,10 @@ public sealed partial class IdCardConsoleComponent : Component
{ {
public readonly string FullName; public readonly string FullName;
public readonly string JobTitle; public readonly string JobTitle;
public readonly List<string> AccessList; public readonly List<ProtoId<AccessLevelPrototype>> AccessList;
public readonly string JobPrototype; public readonly ProtoId<AccessLevelPrototype> JobPrototype;
public WriteToTargetIdMessage(string fullName, string jobTitle, List<string> accessList, string jobPrototype) public WriteToTargetIdMessage(string fullName, string jobTitle, List<ProtoId<AccessLevelPrototype>> accessList, ProtoId<AccessLevelPrototype> jobPrototype)
{ {
FullName = fullName; FullName = fullName;
JobTitle = jobTitle; JobTitle = jobTitle;
@@ -86,18 +88,18 @@ public sealed partial class IdCardConsoleComponent : Component
public readonly string TargetIdName; public readonly string TargetIdName;
public readonly string? TargetIdFullName; public readonly string? TargetIdFullName;
public readonly string? TargetIdJobTitle; public readonly string? TargetIdJobTitle;
public readonly string[]? TargetIdAccessList; public readonly List<ProtoId<AccessLevelPrototype>>? TargetIdAccessList;
public readonly string[]? AllowedModifyAccessList; public readonly List<ProtoId<AccessLevelPrototype>>? AllowedModifyAccessList;
public readonly string TargetIdJobPrototype; public readonly ProtoId<AccessLevelPrototype> TargetIdJobPrototype;
public IdCardConsoleBoundUserInterfaceState(bool isPrivilegedIdPresent, public IdCardConsoleBoundUserInterfaceState(bool isPrivilegedIdPresent,
bool isPrivilegedIdAuthorized, bool isPrivilegedIdAuthorized,
bool isTargetIdPresent, bool isTargetIdPresent,
string? targetIdFullName, string? targetIdFullName,
string? targetIdJobTitle, string? targetIdJobTitle,
string[]? targetIdAccessList, List<ProtoId<AccessLevelPrototype>>? targetIdAccessList,
string[]? allowedModifyAccessList, List<ProtoId<AccessLevelPrototype>>? allowedModifyAccessList,
string targetIdJobPrototype, ProtoId<AccessLevelPrototype> targetIdJobPrototype,
string privilegedIdName, string privilegedIdName,
string targetIdName) string targetIdName)
{ {

View File

@@ -112,11 +112,36 @@ public sealed class AccessReaderSystem : EntitySystem
return false; return false;
} }
public bool GetMainAccessReader(EntityUid uid, [NotNullWhen(true)] out AccessReaderComponent? component)
{
component = null;
if (!TryComp(uid, out AccessReaderComponent? accessReader))
return false;
component = accessReader;
if (component.ContainerAccessProvider == null)
return true;
if (!_containerSystem.TryGetContainer(uid, component.ContainerAccessProvider, out var container))
return true;
foreach (var entity in container.ContainedEntities)
{
if (TryComp(entity, out AccessReaderComponent? containedReader))
{
component = containedReader;
return true;
}
}
return true;
}
/// <summary> /// <summary>
/// Check whether the given access permissions satisfy an access reader's requirements. /// Check whether the given access permissions satisfy an access reader's requirements.
/// </summary> /// </summary>
public bool IsAllowed( public bool IsAllowed(
ICollection<string> access, ICollection<ProtoId<AccessLevelPrototype>> access,
ICollection<StationRecordKey> stationKeys, ICollection<StationRecordKey> stationKeys,
EntityUid target, EntityUid target,
AccessReaderComponent reader) AccessReaderComponent reader)
@@ -142,7 +167,7 @@ public sealed class AccessReaderSystem : EntitySystem
return false; return false;
} }
private bool IsAllowedInternal(ICollection<string> access, ICollection<StationRecordKey> stationKeys, AccessReaderComponent reader) private bool IsAllowedInternal(ICollection<ProtoId<AccessLevelPrototype>> access, ICollection<StationRecordKey> stationKeys, AccessReaderComponent reader)
{ {
return !reader.Enabled return !reader.Enabled
|| AreAccessTagsAllowed(access, reader) || AreAccessTagsAllowed(access, reader)
@@ -154,7 +179,7 @@ public sealed class AccessReaderSystem : EntitySystem
/// </summary> /// </summary>
/// <param name="accessTags">A list of access tags</param> /// <param name="accessTags">A list of access tags</param>
/// <param name="reader">An access reader to check against</param> /// <param name="reader">An access reader to check against</param>
public bool AreAccessTagsAllowed(ICollection<string> accessTags, AccessReaderComponent reader) public bool AreAccessTagsAllowed(ICollection<ProtoId<AccessLevelPrototype>> accessTags, AccessReaderComponent reader)
{ {
if (reader.DenyTags.Overlaps(accessTags)) if (reader.DenyTags.Overlaps(accessTags))
{ {
@@ -218,9 +243,9 @@ public sealed class AccessReaderSystem : EntitySystem
/// </summary> /// </summary>
/// <param name="uid">The entity that is being searched.</param> /// <param name="uid">The entity that is being searched.</param>
/// <param name="items">All of the items to search for access. If none are passed in, <see cref="FindPotentialAccessItems"/> will be used.</param> /// <param name="items">All of the items to search for access. If none are passed in, <see cref="FindPotentialAccessItems"/> will be used.</param>
public ICollection<string> FindAccessTags(EntityUid uid, HashSet<EntityUid>? items = null) public ICollection<ProtoId<AccessLevelPrototype>> FindAccessTags(EntityUid uid, HashSet<EntityUid>? items = null)
{ {
HashSet<string>? tags = null; HashSet<ProtoId<AccessLevelPrototype>>? tags = null;
var owned = false; var owned = false;
items ??= FindPotentialAccessItems(uid); items ??= FindPotentialAccessItems(uid);
@@ -230,7 +255,7 @@ public sealed class AccessReaderSystem : EntitySystem
FindAccessTagsItem(ent, ref tags, ref owned); FindAccessTagsItem(ent, ref tags, ref owned);
} }
return (ICollection<string>?) tags ?? Array.Empty<string>(); return (ICollection<ProtoId<AccessLevelPrototype>>?) tags ?? Array.Empty<ProtoId<AccessLevelPrototype>>();
} }
/// <summary> /// <summary>
@@ -260,7 +285,7 @@ public sealed class AccessReaderSystem : EntitySystem
/// This version merges into a set or replaces the set. /// This version merges into a set or replaces the set.
/// If owned is false, the existing tag-set "isn't ours" and can't be merged with (is read-only). /// If owned is false, the existing tag-set "isn't ours" and can't be merged with (is read-only).
/// </summary> /// </summary>
private void FindAccessTagsItem(EntityUid uid, ref HashSet<string>? tags, ref bool owned) private void FindAccessTagsItem(EntityUid uid, ref HashSet<ProtoId<AccessLevelPrototype>>? tags, ref bool owned)
{ {
if (!FindAccessTagsItem(uid, out var targetTags)) if (!FindAccessTagsItem(uid, out var targetTags))
{ {
@@ -286,6 +311,16 @@ public sealed class AccessReaderSystem : EntitySystem
} }
} }
public void SetAccesses(EntityUid uid, AccessReaderComponent component, List<ProtoId<AccessLevelPrototype>> accesses)
{
component.AccessLists.Clear();
foreach (var access in accesses)
{
component.AccessLists.Add(new HashSet<ProtoId<AccessLevelPrototype>>(){access});
}
RaiseLocalEvent(uid, new AccessReaderConfigurationChangedEvent());
}
public bool FindAccessItemsInventory(EntityUid uid, out HashSet<EntityUid> items) public bool FindAccessItemsInventory(EntityUid uid, out HashSet<EntityUid> items)
{ {
items = new(); items = new();
@@ -308,7 +343,7 @@ public sealed class AccessReaderSystem : EntitySystem
/// Try to find <see cref="AccessComponent"/> on this item /// Try to find <see cref="AccessComponent"/> on this item
/// or inside this item (if it's pda) /// or inside this item (if it's pda)
/// </summary> /// </summary>
private bool FindAccessTagsItem(EntityUid uid, out HashSet<string> tags) private bool FindAccessTagsItem(EntityUid uid, out HashSet<ProtoId<AccessLevelPrototype>> tags)
{ {
tags = new(); tags = new();
var ev = new GetAccessTagsEvent(tags, _prototype); var ev = new GetAccessTagsEvent(tags, _prototype);

View File

@@ -51,7 +51,7 @@ namespace Content.Shared.Access.Systems
/// Replaces the set of access tags we have with the provided set. /// Replaces the set of access tags we have with the provided set.
/// </summary> /// </summary>
/// <param name="access">The new access tags</param> /// <param name="access">The new access tags</param>
public bool TrySetTags(EntityUid uid, IEnumerable<string> newTags, AccessComponent? access = null) public bool TrySetTags(EntityUid uid, IEnumerable<ProtoId<AccessLevelPrototype>> newTags, AccessComponent? access = null)
{ {
if (!Resolve(uid, ref access)) if (!Resolve(uid, ref access))
return false; return false;
@@ -67,12 +67,12 @@ namespace Content.Shared.Access.Systems
/// Gets the set of access tags. /// Gets the set of access tags.
/// </summary> /// </summary>
/// <param name="access">The new access tags</param> /// <param name="access">The new access tags</param>
public IEnumerable<string>? TryGetTags(EntityUid uid, AccessComponent? access = null) public IEnumerable<ProtoId<AccessLevelPrototype>>? TryGetTags(EntityUid uid, AccessComponent? access = null)
{ {
return !Resolve(uid, ref access) ? null : access.Tags; return !Resolve(uid, ref access) ? null : access.Tags;
} }
public bool TryAddGroups(EntityUid uid, IEnumerable<string> newGroups, AccessComponent? access = null) public bool TryAddGroups(EntityUid uid, IEnumerable<ProtoId<AccessGroupPrototype>> newGroups, AccessComponent? access = null)
{ {
if (!Resolve(uid, ref access)) if (!Resolve(uid, ref access))
return false; return false;

View File

@@ -1,6 +1,9 @@
using Content.Shared.Access.Components; using Content.Shared.Access.Components;
using Content.Shared.Containers.ItemSlots; using Content.Shared.Containers.ItemSlots;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization;
using Robust.Shared.Prototypes;
namespace Content.Shared.Access.Systems namespace Content.Shared.Access.Systems
{ {
@@ -33,5 +36,16 @@ namespace Content.Shared.Access.Systems
_itemSlotsSystem.RemoveItemSlot(uid, component.PrivilegedIdSlot); _itemSlotsSystem.RemoveItemSlot(uid, component.PrivilegedIdSlot);
_itemSlotsSystem.RemoveItemSlot(uid, component.TargetIdSlot); _itemSlotsSystem.RemoveItemSlot(uid, component.TargetIdSlot);
} }
[Serializable, NetSerializable]
private sealed class IdCardConsoleComponentState : ComponentState
{
public List<string> AccessLevels;
public IdCardConsoleComponentState(List<string> accessLevels)
{
AccessLevels = accessLevels;
}
}
} }
} }

View File

@@ -0,0 +1,42 @@
using Robust.Shared.GameStates;
using Robust.Shared.Serialization;
using Robust.Shared.Prototypes;
using Content.Shared.Access;
namespace Content.Shared.Doors.Electronics;
/// <summary>
/// Allows an entity's AccessReader to be configured via UI.
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed partial class DoorElectronicsComponent : Component
{
}
[Serializable, NetSerializable]
public sealed class DoorElectronicsUpdateConfigurationMessage : BoundUserInterfaceMessage
{
public List<ProtoId<AccessLevelPrototype>> AccessList;
public DoorElectronicsUpdateConfigurationMessage(List<ProtoId<AccessLevelPrototype>> accessList)
{
AccessList = accessList;
}
}
[Serializable, NetSerializable]
public sealed class DoorElectronicsConfigurationState : BoundUserInterfaceState
{
public List<ProtoId<AccessLevelPrototype>> AccessList;
public DoorElectronicsConfigurationState(List<ProtoId<AccessLevelPrototype>> accessList)
{
AccessList = accessList;
}
}
[Serializable, NetSerializable]
public enum DoorElectronicsConfigurationUiKey : byte
{
Key
}

View File

@@ -116,8 +116,8 @@ public sealed partial class NearbyAccessRule : RulesRule
[DataField("count")] [DataField("count")]
public int Count = 1; public int Count = 1;
[DataField("access", required: true, customTypeSerializer: typeof(PrototypeIdListSerializer<AccessLevelPrototype>))] [DataField("access", required: true)]
public List<string> Access = new(); public List<ProtoId<AccessLevelPrototype>> Access = new();
[DataField("range")] [DataField("range")]
public float Range = 10f; public float Range = 10f;

View File

@@ -105,17 +105,17 @@ namespace Content.Shared.Roles
[DataField("special", serverOnly: true)] [DataField("special", serverOnly: true)]
public JobSpecial[] Special { get; private set; } = Array.Empty<JobSpecial>(); public JobSpecial[] Special { get; private set; } = Array.Empty<JobSpecial>();
[DataField("access", customTypeSerializer: typeof(PrototypeIdListSerializer<AccessLevelPrototype>))] [DataField("access")]
public IReadOnlyCollection<string> Access { get; private set; } = Array.Empty<string>(); public IReadOnlyCollection<ProtoId<AccessLevelPrototype>> Access { get; private set; } = Array.Empty<ProtoId<AccessLevelPrototype>>();
[DataField("accessGroups", customTypeSerializer: typeof(PrototypeIdListSerializer<AccessGroupPrototype>))] [DataField("accessGroups")]
public IReadOnlyCollection<string> AccessGroups { get; private set; } = Array.Empty<string>(); public IReadOnlyCollection<ProtoId<AccessGroupPrototype>> AccessGroups { get; private set; } = Array.Empty<ProtoId<AccessGroupPrototype>>();
[DataField("extendedAccess", customTypeSerializer: typeof(PrototypeIdListSerializer<AccessLevelPrototype>))] [DataField("extendedAccess")]
public IReadOnlyCollection<string> ExtendedAccess { get; private set; } = Array.Empty<string>(); public IReadOnlyCollection<ProtoId<AccessLevelPrototype>> ExtendedAccess { get; private set; } = Array.Empty<ProtoId<AccessLevelPrototype>>();
[DataField("extendedAccessGroups", customTypeSerializer: typeof(PrototypeIdListSerializer<AccessGroupPrototype>))] [DataField("extendedAccessGroups")]
public IReadOnlyCollection<string> ExtendedAccessGroups { get; private set; } = Array.Empty<string>(); public IReadOnlyCollection<ProtoId<AccessGroupPrototype>> ExtendedAccessGroups { get; private set; } = Array.Empty<ProtoId<AccessGroupPrototype>>();
} }
/// <summary> /// <summary>

View File

@@ -7,9 +7,19 @@
- type: Sprite - type: Sprite
sprite: Objects/Misc/module.rsi sprite: Objects/Misc/module.rsi
state: door_electronics state: door_electronics
- type: AccessReader
- type: Tag - type: Tag
tags: tags:
- DoorElectronics - DoorElectronics
- type: DoorElectronics
- type: StaticPrice - type: StaticPrice
price: 55 price: 55
- type: AccessReader
- type: ActivatableUI
key: enum.DoorElectronicsConfigurationUiKey.Key
allowedItems:
tags:
- Multitool
- type: UserInterface
interfaces:
- key: enum.DoorElectronicsConfigurationUiKey.Key
type: DoorElectronicsBoundUserInterface

View File

@@ -0,0 +1,264 @@
- type: entity
parent: DoorElectronics
id: DoorElectronicsService
suffix: Service, Locked
components:
- type: AccessReader
access: [["Service"]]
- type: entity
parent: DoorElectronics
id: DoorElectronicsTheatre
suffix: Theatre, Locked
components:
- type: AccessReader
access: [["Theatre"]]
- type: entity
parent: DoorElectronics
id: DoorElectronicsChapel
suffix: Chapel, Locked
components:
- type: AccessReader
access: [["Chapel"]]
- type: entity
parent: DoorElectronics
id: DoorElectronicsJanitor
suffix: Janitor, Locked
components:
- type: AccessReader
access: [["Janitor"]]
- type: entity
parent: DoorElectronics
id: DoorElectronicsKitchen
suffix: Kitchen, Locked
components:
- type: AccessReader
access: [["Kitchen"]]
- type: entity
parent: DoorElectronics
id: DoorElectronicsBar
suffix: Bar, Locked
components:
- type: AccessReader
access: [["Bar"]]
- type: entity
parent: DoorElectronics
id: DoorElectronicsHydroponics
suffix: Hydroponics, Locked
components:
- type: AccessReader
access: [["Hydroponics"]]
- type: entity
parent: DoorElectronics
id: DoorElectronicsCaptain
suffix: Captain, Locked
components:
- type: AccessReader
access: [["Captain"]]
- type: entity
parent: DoorElectronics
id: DoorElectronicsExternal
suffix: External, Locked
components:
- type: AccessReader
access: [["External"]]
- type: entity
parent: DoorElectronics
id: DoorElectronicsCargo
suffix: Cargo, Locked
components:
- type: AccessReader
access: [["Cargo"]]
- type: entity
parent: DoorElectronics
id: DoorElectronicsEngineering
suffix: Engineering, Locked
components:
- type: AccessReader
access: [["Engineering"]]
- type: entity
parent: DoorElectronics
id: DoorElectronicsAtmospherics
suffix: Atmospherics, Locked
components:
- type: AccessReader
access: [["Atmospherics"]]
- type: entity
parent: DoorElectronics
id: DoorElectronicsFreezer
suffix: Freezer, Locked
components:
- type: AccessReader
access: [["Kitchen"], ["Hydroponics"]]
- type: entity
parent: DoorElectronics
id: DoorElectronicsSalvage
suffix: Salvage, Locked
components:
- type: AccessReader
access: [["Salvage"]]
- type: entity
parent: DoorElectronics
id: DoorElectronicsMedical
suffix: Medical, Locked
components:
- type: AccessReader
access: [["Medical"]]
- type: entity
parent: DoorElectronics
id: DoorElectronicsChemistry
suffix: Chemistry, Locked
components:
- type: AccessReader
access: [["Chemistry"]]
- type: entity
parent: DoorElectronics
id: DoorElectronicsResearch
suffix: Research, Locked
components:
- type: AccessReader
access: [["Research"]]
- type: entity
parent: DoorElectronics
id: DoorElectronicsScience
suffix: Science, Locked
components:
- type: AccessReader
access: [["Research"], ["Medical"]]
- type: entity
parent: DoorElectronics
id: DoorElectronicsCommand
suffix: Command, Locked
components:
- type: AccessReader
access: [["Command"]]
- type: entity
parent: DoorElectronics
id: DoorElectronicsChiefMedicalOfficer
suffix: ChiefMedicalOfficer, Locked
components:
- type: AccessReader
access: [["ChiefMedicalOfficer"]]
- type: entity
parent: DoorElectronics
id: DoorElectronicsChiefEngineer
suffix: ChiefEngineer, Locked
components:
- type: AccessReader
access: [["ChiefEngineer"]]
- type: entity
parent: DoorElectronics
id: DoorElectronicsHeadOfSecurity
suffix: HeadOfSecurity, Locked
components:
- type: AccessReader
access: [["HeadOfSecurity"]]
- type: entity
parent: DoorElectronics
id: DoorElectronicsResearchDirector
suffix: ResearchDirector, Locked
components:
- type: AccessReader
access: [["ResearchDirector"]]
- type: entity
parent: DoorElectronics
id: DoorElectronicsHeadOfPersonnel
suffix: HeadOfPersonnel, Locked
components:
- type: AccessReader
access: [["HeadOfPersonnel"]]
- type: entity
parent: DoorElectronics
id: DoorElectronicsQuartermaster
suffix: Quartermaster, Locked
components:
- type: AccessReader
access: [["Quartermaster"]]
- type: entity
parent: DoorElectronics
id: DoorElectronicsSecurity
suffix: Security, Locked
components:
- type: AccessReader
access: [["Security"]]
- type: entity
parent: DoorElectronics
id: DoorElectronicsDetective
suffix: Detective, Locked
components:
- type: AccessReader
access: [["Detective"]]
- type: entity
parent: DoorElectronics
id: DoorElectronicsBrig
suffix: Brig, Locked
components:
- type: AccessReader
access: [["Brig"]]
- type: entity
parent: DoorElectronics
id: DoorElectronicsArmory
suffix: Armory, Locked
components:
- type: AccessReader
access: [["Armory"]]
- type: entity
parent: DoorElectronics
id: DoorElectronicsVault
suffix: Vault, Locked
components:
- type: AccessReader
access: [["Security", "Command"]]
- type: entity
parent: DoorElectronics
id: DoorElectronicsMaintenance
suffix: Maintenance, Locked
components:
- type: AccessReader
access: [["Maintenance"]]
- type: entity
parent: DoorElectronics
id: DoorElectronicsSyndicateAgent
suffix: SyndicateAgent, Locked
components:
- type: AccessReader
access: [["SyndicateAgent"]]
- type: entity
parent: DoorElectronics
id: DoorElectronicsRnDMed
suffix: Medical/Science, Locked
components:
- type: AccessReader
access: [["Research"], ["Medical"]]

View File

@@ -48,6 +48,8 @@
- type: ContainerFill - type: ContainerFill
containers: containers:
board: [ DoorElectronics ] board: [ DoorElectronics ]
- type: AccessReader
containerAccessProvider: board
- type: Door - type: Door
crushDamage: crushDamage:
types: types:
@@ -140,7 +142,6 @@
- type: PaintableAirlock - type: PaintableAirlock
group: Standard group: Standard
department: Civilian department: Civilian
- type: AccessReader
- type: StaticPrice - type: StaticPrice
price: 150 price: 150
- type: LightningTarget - type: LightningTarget

View File

@@ -60,6 +60,7 @@
- type: NavMapDoor - type: NavMapDoor
- type: DoorBolt - type: DoorBolt
- type: AccessReader - type: AccessReader
containerAccessProvider: board
- type: Appearance - type: Appearance
- type: WiresVisuals - type: WiresVisuals
- type: ApcPowerReceiver - type: ApcPowerReceiver

View File

@@ -46,7 +46,7 @@
conditions: conditions:
- !type:EntityAnchored {} - !type:EntityAnchored {}
steps: steps:
- tag: DoorElectronics - component: DoorElectronics
store: board store: board
name: "door electronics circuit board" name: "door electronics circuit board"
icon: icon:

View File

@@ -44,7 +44,7 @@
- !type:EntityAnchored - !type:EntityAnchored
anchored: true anchored: true
steps: steps:
- tag: DoorElectronics - component: DoorElectronics
name: Door Electronics name: Door Electronics
icon: icon:
sprite: "Objects/Misc/module.rsi" sprite: "Objects/Misc/module.rsi"

View File

@@ -23,7 +23,7 @@
conditions: conditions:
- !type:EntityAnchored {} - !type:EntityAnchored {}
steps: steps:
- tag: DoorElectronics - component: DoorElectronics
store: board store: board
name: "door electronics circuit board" name: "door electronics circuit board"
icon: icon:

View File

@@ -107,7 +107,7 @@
conditions: conditions:
- !type:EntityAnchored {} - !type:EntityAnchored {}
steps: steps:
- tag: DoorElectronics - component: DoorElectronics
store: board store: board
name: "door electronics circuit board" name: "door electronics circuit board"
icon: icon:
@@ -378,7 +378,7 @@
conditions: conditions:
- !type:EntityAnchored { } - !type:EntityAnchored { }
steps: steps:
- tag: DoorElectronics - component: DoorElectronics
store: board store: board
name: "door electronics circuit board" name: "door electronics circuit board"
icon: icon: