]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Add door electronics access configuration menu (#17778)
authorc4llv07e <38111072+c4llv07e@users.noreply.github.com>
Mon, 1 Apr 2024 06:06:13 +0000 (09:06 +0300)
committerGitHub <noreply@github.com>
Mon, 1 Apr 2024 06:06:13 +0000 (17:06 +1100)
* 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>
40 files changed:
Content.Client/Access/UI/AccessLevelControl.xaml [new file with mode: 0644]
Content.Client/Access/UI/AccessLevelControl.xaml.cs [new file with mode: 0644]
Content.Client/Access/UI/AccessOverriderBoundUserInterface.cs
Content.Client/Access/UI/AccessOverriderWindow.xaml.cs
Content.Client/Access/UI/IdCardConsoleBoundUserInterface.cs
Content.Client/Access/UI/IdCardConsoleWindow.xaml
Content.Client/Access/UI/IdCardConsoleWindow.xaml.cs
Content.Client/Doors/Electronics/DoorElectronicsBoundUserInterface.cs [new file with mode: 0644]
Content.Client/Doors/Electronics/DoorElectronicsConfigurationMenu.xaml [new file with mode: 0644]
Content.Client/Doors/Electronics/DoorElectronicsConfigurationMenu.xaml.cs [new file with mode: 0644]
Content.IntegrationTests/Tests/Access/AccessReaderTest.cs
Content.Server/Access/Systems/AccessOverriderSystem.cs
Content.Server/Access/Systems/IdCardConsoleSystem.cs
Content.Server/Administration/Systems/AdminVerbSystem.Tools.cs
Content.Server/Doors/Electronics/Systems/DoorElectronicsSystem.cs [new file with mode: 0644]
Content.Server/Sandbox/SandboxSystem.cs
Content.Server/Storage/EntitySystems/BluespaceLockerSystem.cs
Content.Server/UserInterface/ActivatableUIComponent.cs
Content.Server/UserInterface/ActivatableUISystem.cs
Content.Shared/Access/AccessGroupPrototype.cs
Content.Shared/Access/AccessLevelPrototype.cs
Content.Shared/Access/Components/AccessComponent.cs
Content.Shared/Access/Components/AccessOverriderComponent.cs
Content.Shared/Access/Components/AccessReaderComponent.cs
Content.Shared/Access/Components/IdCardConsoleComponent.cs
Content.Shared/Access/Systems/AccessReaderSystem.cs
Content.Shared/Access/Systems/SharedAccessSystem.cs
Content.Shared/Access/Systems/SharedIdCardConsoleSystem.cs
Content.Shared/Doors/Electronics/DoorElectronicsComponent.cs [new file with mode: 0644]
Content.Shared/Random/RulesPrototype.cs
Content.Shared/Roles/JobPrototype.cs
Resources/Prototypes/Entities/Objects/Devices/Electronics/door.yml
Resources/Prototypes/Entities/Objects/Devices/Electronics/door_access.yml [new file with mode: 0644]
Resources/Prototypes/Entities/Structures/Doors/Airlocks/access.yml
Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml
Resources/Prototypes/Entities/Structures/Doors/Airlocks/highsec.yml
Resources/Prototypes/Recipes/Construction/Graphs/structures/airlock.yml
Resources/Prototypes/Recipes/Construction/Graphs/structures/shutter.yml
Resources/Prototypes/Recipes/Construction/Graphs/structures/shuttle.yml
Resources/Prototypes/Recipes/Construction/Graphs/structures/windoor.yml

diff --git a/Content.Client/Access/UI/AccessLevelControl.xaml b/Content.Client/Access/UI/AccessLevelControl.xaml
new file mode 100644 (file)
index 0000000..56968d8
--- /dev/null
@@ -0,0 +1,4 @@
+<GridContainer xmlns="https://spacestation14.io"
+               Columns="5"
+               HorizontalAlignment="Center">
+</GridContainer>
diff --git a/Content.Client/Access/UI/AccessLevelControl.xaml.cs b/Content.Client/Access/UI/AccessLevelControl.xaml.cs
new file mode 100644 (file)
index 0000000..34db80b
--- /dev/null
@@ -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);
+        }
+    }
+}
index 0c23542f798bffe62454199833750bc3cd1252bd..c1b63dc4d05895d002c4c9ef5880005494f36cb3 100644 (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));
         }
index 2fd005712159cf4ad6b34ed36a5d21cb46b5d94c..6025c3b551fd6623e4065c5152ebeed5ea45229f 100644 (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());
         }
     }
 }
index 898792aa030b0fe7dd2bde1e9b318e4194bbcc65..5b7011c195ab914d197725845c3225de98d7825b 100644 (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];
index c29adc8ebd3f2f07adccb5505e238639b3f1a690..a2f5f3382bbcadcd667f089c2480e44874d24279 100644 (file)
             <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>
index bf5984e8096b2d1f8f087c4d8a02d26486db5371..298912e7d536e5cbcb1cee040ed01da4082ec2c6 100644 (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)
-            {
-                if (!prototypeManager.TryIndex<AccessLevelPrototype>(access, out var accessLevel))
-                {
-                    _logMill.Error($"Unable to find accesslevel for {access}");
-                    continue;
-                }
+            _accessButtons.Populate(accessLevels, prototypeManager);
+            AccessLevelControlContainer.AddChild(_accessButtons);
 
-                var newButton = new Button
-                {
-                    Text = GetAccessLevelName(accessLevel),
-                    ToggleMode = true,
-                };
-                AccessLevelGrid.AddChild(newButton);
-                _accessButtons.Add(accessLevel.ID, newButton);
-                newButton.OnPressed += _ => SubmitData();
+            foreach (var (id, button) in _accessButtons.ButtonsList)
+            {
+                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);
         }
     }
diff --git a/Content.Client/Doors/Electronics/DoorElectronicsBoundUserInterface.cs b/Content.Client/Doors/Electronics/DoorElectronicsBoundUserInterface.cs
new file mode 100644 (file)
index 0000000..cd7ea71
--- /dev/null
@@ -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));
+    }
+}
diff --git a/Content.Client/Doors/Electronics/DoorElectronicsConfigurationMenu.xaml b/Content.Client/Doors/Electronics/DoorElectronicsConfigurationMenu.xaml
new file mode 100644 (file)
index 0000000..4cd59f3
--- /dev/null
@@ -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>
diff --git a/Content.Client/Doors/Electronics/DoorElectronicsConfigurationMenu.xaml.cs b/Content.Client/Doors/Electronics/DoorElectronicsConfigurationMenu.xaml.cs
new file mode 100644 (file)
index 0000000..c01f13a
--- /dev/null
@@ -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);
+    }
+}
index 7d86fc2f6c2ca8df8410cce49363918d90792e6f..895bb2cf878faf15ad916ca97c7b429631a71566 100644 (file)
@@ -57,9 +57,9 @@ namespace Content.IntegrationTests.Tests.Access
                 var reader = new AccessReaderComponent();
                 Assert.Multiple(() =>
                 {
-                    Assert.That(system.AreAccessTagsAllowed(new[] { "Foo" }, reader), Is.True);
-                    Assert.That(system.AreAccessTagsAllowed(new[] { "Bar" }, reader), Is.True);
-                    Assert.That(system.AreAccessTagsAllowed(Array.Empty<string>(), reader), Is.True);
+                    Assert.That(system.AreAccessTagsAllowed(new List<ProtoId<AccessLevelPrototype>> { "Foo" }, reader), Is.True);
+                    Assert.That(system.AreAccessTagsAllowed(new List<ProtoId<AccessLevelPrototype>> { "Bar" }, reader), Is.True);
+                    Assert.That(system.AreAccessTagsAllowed(Array.Empty<ProtoId<AccessLevelPrototype>>(), reader), Is.True);
                 });
 
                 // test deny
@@ -67,58 +67,58 @@ namespace Content.IntegrationTests.Tests.Access
                 reader.DenyTags.Add("A");
                 Assert.Multiple(() =>
                 {
-                    Assert.That(system.AreAccessTagsAllowed(new[] { "Foo" }, reader), Is.True);
-                    Assert.That(system.AreAccessTagsAllowed(new[] { "A" }, reader), Is.False);
-                    Assert.That(system.AreAccessTagsAllowed(new[] { "A", "Foo" }, reader), Is.False);
-                    Assert.That(system.AreAccessTagsAllowed(Array.Empty<string>(), reader), Is.True);
+                    Assert.That(system.AreAccessTagsAllowed(new List<ProtoId<AccessLevelPrototype>> { "Foo" }, reader), Is.True);
+                    Assert.That(system.AreAccessTagsAllowed(new List<ProtoId<AccessLevelPrototype>> { "A" }, reader), Is.False);
+                    Assert.That(system.AreAccessTagsAllowed(new List<ProtoId<AccessLevelPrototype>> { "A", "Foo" }, reader), Is.False);
+                    Assert.That(system.AreAccessTagsAllowed(Array.Empty<ProtoId<AccessLevelPrototype>>(), reader), Is.True);
                 });
 
                 // test one list
                 reader = new AccessReaderComponent();
-                reader.AccessLists.Add(new HashSet<string> { "A" });
+                reader.AccessLists.Add(new HashSet<ProtoId<AccessLevelPrototype>> { "A" });
                 Assert.Multiple(() =>
                 {
-                    Assert.That(system.AreAccessTagsAllowed(new[] { "A" }, reader), Is.True);
-                    Assert.That(system.AreAccessTagsAllowed(new[] { "B" }, reader), Is.False);
-                    Assert.That(system.AreAccessTagsAllowed(new[] { "A", "B" }, reader), Is.True);
-                    Assert.That(system.AreAccessTagsAllowed(Array.Empty<string>(), reader), Is.False);
+                    Assert.That(system.AreAccessTagsAllowed(new List<ProtoId<AccessLevelPrototype>> { "A" }, reader), Is.True);
+                    Assert.That(system.AreAccessTagsAllowed(new List<ProtoId<AccessLevelPrototype>> { "B" }, reader), Is.False);
+                    Assert.That(system.AreAccessTagsAllowed(new List<ProtoId<AccessLevelPrototype>> { "A", "B" }, reader), Is.True);
+                    Assert.That(system.AreAccessTagsAllowed(Array.Empty<ProtoId<AccessLevelPrototype>>(), reader), Is.False);
                 });
 
                 // test one list - two items
                 reader = new AccessReaderComponent();
-                reader.AccessLists.Add(new HashSet<string> { "A", "B" });
+                reader.AccessLists.Add(new HashSet<ProtoId<AccessLevelPrototype>> { "A", "B" });
                 Assert.Multiple(() =>
                 {
-                    Assert.That(system.AreAccessTagsAllowed(new[] { "A" }, reader), Is.False);
-                    Assert.That(system.AreAccessTagsAllowed(new[] { "B" }, reader), Is.False);
-                    Assert.That(system.AreAccessTagsAllowed(new[] { "A", "B" }, reader), Is.True);
-                    Assert.That(system.AreAccessTagsAllowed(Array.Empty<string>(), reader), Is.False);
+                    Assert.That(system.AreAccessTagsAllowed(new List<ProtoId<AccessLevelPrototype>> { "A" }, reader), Is.False);
+                    Assert.That(system.AreAccessTagsAllowed(new List<ProtoId<AccessLevelPrototype>> { "B" }, reader), Is.False);
+                    Assert.That(system.AreAccessTagsAllowed(new List<ProtoId<AccessLevelPrototype>> { "A", "B" }, reader), Is.True);
+                    Assert.That(system.AreAccessTagsAllowed(Array.Empty<ProtoId<AccessLevelPrototype>>(), reader), Is.False);
                 });
 
                 // test two list
                 reader = new AccessReaderComponent();
-                reader.AccessLists.Add(new HashSet<string> { "A" });
-                reader.AccessLists.Add(new HashSet<string> { "B", "C" });
+                reader.AccessLists.Add(new HashSet<ProtoId<AccessLevelPrototype>> { "A" });
+                reader.AccessLists.Add(new HashSet<ProtoId<AccessLevelPrototype>> { "B", "C" });
                 Assert.Multiple(() =>
                 {
-                    Assert.That(system.AreAccessTagsAllowed(new[] { "A" }, reader), Is.True);
-                    Assert.That(system.AreAccessTagsAllowed(new[] { "B" }, reader), Is.False);
-                    Assert.That(system.AreAccessTagsAllowed(new[] { "A", "B" }, reader), Is.True);
-                    Assert.That(system.AreAccessTagsAllowed(new[] { "C", "B" }, reader), Is.True);
-                    Assert.That(system.AreAccessTagsAllowed(new[] { "C", "B", "A" }, reader), Is.True);
-                    Assert.That(system.AreAccessTagsAllowed(Array.Empty<string>(), reader), Is.False);
+                    Assert.That(system.AreAccessTagsAllowed(new List<ProtoId<AccessLevelPrototype>> { "A" }, reader), Is.True);
+                    Assert.That(system.AreAccessTagsAllowed(new List<ProtoId<AccessLevelPrototype>> { "B" }, reader), Is.False);
+                    Assert.That(system.AreAccessTagsAllowed(new List<ProtoId<AccessLevelPrototype>> { "A", "B" }, reader), Is.True);
+                    Assert.That(system.AreAccessTagsAllowed(new List<ProtoId<AccessLevelPrototype>> { "C", "B" }, reader), Is.True);
+                    Assert.That(system.AreAccessTagsAllowed(new List<ProtoId<AccessLevelPrototype>> { "C", "B", "A" }, reader), Is.True);
+                    Assert.That(system.AreAccessTagsAllowed(Array.Empty<ProtoId<AccessLevelPrototype>>(), reader), Is.False);
                 });
 
                 // test deny list
                 reader = new AccessReaderComponent();
-                reader.AccessLists.Add(new HashSet<string> { "A" });
+                reader.AccessLists.Add(new HashSet<ProtoId<AccessLevelPrototype>> { "A" });
                 reader.DenyTags.Add("B");
                 Assert.Multiple(() =>
                 {
-                    Assert.That(system.AreAccessTagsAllowed(new[] { "A" }, reader), Is.True);
-                    Assert.That(system.AreAccessTagsAllowed(new[] { "B" }, reader), Is.False);
-                    Assert.That(system.AreAccessTagsAllowed(new[] { "A", "B" }, reader), Is.False);
-                    Assert.That(system.AreAccessTagsAllowed(Array.Empty<string>(), reader), Is.False);
+                    Assert.That(system.AreAccessTagsAllowed(new List<ProtoId<AccessLevelPrototype>> { "A" }, reader), Is.True);
+                    Assert.That(system.AreAccessTagsAllowed(new List<ProtoId<AccessLevelPrototype>> { "B" }, reader), Is.False);
+                    Assert.That(system.AreAccessTagsAllowed(new List<ProtoId<AccessLevelPrototype>> { "A", "B" }, reader), Is.False);
+                    Assert.That(system.AreAccessTagsAllowed(Array.Empty<ProtoId<AccessLevelPrototype>>(), reader), Is.False);
                 });
             });
             await pair.CleanReturnAsync();
index 684312c7c11ab50f01a8031fcc91e244f1db7a75..25f2e4c1b04dacdb6838f65568fa5fb9694bd0c3 100644 (file)
@@ -1,5 +1,6 @@
 using System.Linq;
 using Content.Server.Popups;
+using Content.Shared.Access;
 using Content.Shared.Access.Components;
 using Content.Shared.Access.Systems;
 using Content.Shared.Administration.Logs;
@@ -12,6 +13,7 @@ using Robust.Shared.Audio;
 using Robust.Shared.Audio.Systems;
 using Robust.Shared.Containers;
 using Robust.Shared.Player;
+using Robust.Shared.Prototypes;
 using static Content.Shared.Access.Components.AccessOverriderComponent;
 
 namespace Content.Server.Access.Systems;
@@ -26,6 +28,7 @@ public sealed class AccessOverriderSystem : SharedAccessOverriderSystem
     [Dependency] private readonly PopupSystem _popupSystem = default!;
     [Dependency] private readonly SharedAudioSystem _audioSystem = default!;
     [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
+    [Dependency] private readonly SharedContainerSystem _containerSystem = default!;
 
     public override void Initialize()
     {
@@ -108,17 +111,20 @@ public sealed class AccessOverriderSystem : SharedAccessOverriderSystem
         var targetLabel = Loc.GetString("access-overrider-window-no-target");
         var targetLabelColor = Color.Red;
 
-        string[]? possibleAccess = null;
-        string[]? currentAccess = null;
-        string[]? missingAccess = null;
+        ProtoId<AccessLevelPrototype>[]? possibleAccess = null;
+        ProtoId<AccessLevelPrototype>[]? currentAccess = null;
+        ProtoId<AccessLevelPrototype>[]? missingAccess = null;
 
         if (component.TargetAccessReaderId is { Valid: true } accessReader)
         {
             targetLabel = Loc.GetString("access-overrider-window-target-label") + " " + EntityManager.GetComponent<MetaDataComponent>(component.TargetAccessReaderId).EntityName;
             targetLabelColor = Color.White;
 
-            List<HashSet<string>> currentAccessHashsets = EntityManager.GetComponent<AccessReaderComponent>(accessReader).AccessLists;
-            currentAccess = ConvertAccessHashSetsToList(currentAccessHashsets)?.ToArray();
+            if (!_accessReader.GetMainAccessReader(accessReader, out var accessReaderComponent))
+                return;
+
+            var currentAccessHashsets = accessReaderComponent.AccessLists;
+            currentAccess = ConvertAccessHashSetsToList(currentAccessHashsets).ToArray();
         }
 
         if (component.PrivilegedIdSlot.Item is { Valid: true } idCard)
@@ -151,15 +157,15 @@ public sealed class AccessOverriderSystem : SharedAccessOverriderSystem
         _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())
         {
-            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);
                 }
@@ -169,15 +175,15 @@ public sealed class AccessOverriderSystem : SharedAccessOverriderSystem
         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())
         {
-            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.
     /// </summary>
     private void TryWriteToTargetAccessReaderId(EntityUid uid,
-        List<string> newAccessList,
+        List<ProtoId<AccessLevelPrototype>> newAccessList,
         EntityUid player,
         AccessOverriderComponent? component = null)
     {
@@ -211,9 +217,7 @@ public sealed class AccessOverriderSystem : SharedAccessOverriderSystem
             return;
         }
 
-        TryComp(component.TargetAccessReaderId, out AccessReaderComponent? accessReader);
-
-        if (accessReader == null)
+        if (!_accessReader.GetMainAccessReader(component.TargetAccessReaderId, out var accessReader))
             return;
 
         var oldTags = ConvertAccessHashSetsToList(accessReader.AccessLists);
@@ -262,10 +266,10 @@ public sealed class AccessOverriderSystem : SharedAccessOverriderSystem
         if (!Resolve(uid, ref component))
             return true;
 
-        if (!EntityManager.TryGetComponent<AccessReaderComponent>(uid, out var reader))
+        if (_accessReader.GetMainAccessReader(uid, out var accessReader))
             return true;
 
         var privilegedId = component.PrivilegedIdSlot.Item;
-        return privilegedId != null && _accessReader.IsAllowed(privilegedId.Value, uid, reader);
+        return privilegedId != null && _accessReader.IsAllowed(privilegedId.Value, uid, accessReader);
     }
 }
index 791159f972b072dea4d3dc442675d5aab0c2c94d..db8b9d036e863d9909432254e0916280792f5a6a 100644 (file)
@@ -12,6 +12,7 @@ using Robust.Shared.Containers;
 using Robust.Shared.Prototypes;
 using System.Linq;
 using static Content.Shared.Access.Components.IdCardConsoleComponent;
+using Content.Shared.Access;
 
 namespace Content.Server.Access.Systems;
 
@@ -54,11 +55,11 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem
             return;
 
         var privilegedIdName = string.Empty;
-        string[]? possibleAccess = null;
+        List<ProtoId<AccessLevelPrototype>>? possibleAccess = null;
         if (component.PrivilegedIdSlot.Item is { Valid: true } item)
         {
             privilegedIdName = EntityManager.GetComponent<MetaDataComponent>(item).EntityName;
-            possibleAccess = _accessReader.FindAccessTags(item).ToArray();
+            possibleAccess = _accessReader.FindAccessTags(item).ToList();
         }
 
         IdCardConsoleBoundUserInterfaceState newState;
@@ -82,7 +83,7 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem
             var targetIdComponent = EntityManager.GetComponent<IdCardComponent>(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)
                 && keyStorage.Key is {} key
                 && _record.TryGetRecord<GeneralStationRecord>(key, out var record))
@@ -96,7 +97,7 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem
                 true,
                 targetIdComponent.FullName,
                 targetIdComponent.JobTitle,
-                targetAccessComponent.Tags.ToArray(),
+                targetAccessComponent.Tags.ToList(),
                 possibleAccess,
                 jobProto,
                 privilegedIdName,
@@ -113,8 +114,8 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem
     private void TryWriteToTargetId(EntityUid uid,
         string newFullName,
         string newJobTitle,
-        List<string> newAccessList,
-        string newJobProto,
+        List<ProtoId<AccessLevelPrototype>> newAccessList,
+        ProtoId<AccessLevelPrototype> newJobProto,
         EntityUid player,
         IdCardConsoleComponent? component = null)
     {
@@ -140,7 +141,7 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem
             return;
         }
 
-        var oldTags = _access.TryGetTags(targetId) ?? new List<string>();
+        var oldTags = _access.TryGetTags(targetId) ?? new List<ProtoId<AccessLevelPrototype>>();
         oldTags = oldTags.ToList();
 
         var privilegedId = component.PrivilegedIdSlot.Item;
@@ -189,7 +190,7 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem
         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)
             || keyStorage.Key is not { } key
index 9d66338c8bf0bb1ac7b939cb86bb8a1ddb6ae061..328fa74484644fbeb74b2ddd36bd6202194debfb 100644 (file)
@@ -35,6 +35,7 @@ using Robust.Shared.Map.Components;
 using Robust.Shared.Physics;
 using Robust.Shared.Physics.Components;
 using Robust.Shared.Player;
+using Robust.Shared.Prototypes;
 using Robust.Shared.Utility;
 
 namespace Content.Server.Administration.Systems;
@@ -844,14 +845,14 @@ public sealed partial class AdminVerbSystem
     {
         var allAccess = _prototypeManager
             .EnumeratePrototypes<AccessLevelPrototype>()
-            .Select(p => p.ID).ToArray();
+            .Select(p => new ProtoId<AccessLevelPrototype>(p.ID)).ToArray();
 
         _accessSystem.TrySetTags(entity, allAccess);
     }
 
     private void RevokeAllAccess(EntityUid entity)
     {
-        _accessSystem.TrySetTags(entity, Array.Empty<string>());
+        _accessSystem.TrySetTags(entity, new List<ProtoId<AccessLevelPrototype>>());
     }
 
     public enum TricksVerbPriorities
diff --git a/Content.Server/Doors/Electronics/Systems/DoorElectronicsSystem.cs b/Content.Server/Doors/Electronics/Systems/DoorElectronicsSystem.cs
new file mode 100644 (file)
index 0000000..56e8bd5
--- /dev/null
@@ -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);
+    }
+}
index b8bf1230902db8cdc1f96dfeb3461b3ceaacb1e4..30fe4e0fe95e348ab21111ad32a4b7f723fdfc86 100644 (file)
@@ -14,6 +14,7 @@ using Robust.Server.Placement;
 using Robust.Server.Player;
 using Robust.Shared.Enums;
 using Robust.Shared.Player;
+using Robust.Shared.Prototypes;
 
 namespace Content.Server.Sandbox
 {
@@ -121,7 +122,7 @@ namespace Content.Server.Sandbox
 
             var allAccess = PrototypeManager
                 .EnumeratePrototypes<AccessLevelPrototype>()
-                .Select(p => p.ID).ToArray();
+                .Select(p => new ProtoId<AccessLevelPrototype>(p.ID)).ToList();
 
             if (_inventory.TryGetSlotEntity(attached, "id", out var slotEntity))
             {
index 356768769bb830586d9f02c1ca0ac1d9eb3d11fd..838311c1aacc51565c6e6bff143d52e8fdc29d52 100644 (file)
@@ -3,6 +3,7 @@ using Content.Server.Explosion.EntitySystems;
 using Content.Server.Resist;
 using Content.Server.Station.Components;
 using Content.Server.Storage.Components;
+using Content.Shared.Access;
 using Content.Shared.Access.Components;
 using Content.Shared.Coordinates;
 using Content.Shared.DoAfter;
@@ -14,6 +15,7 @@ using Content.Shared.Tools.Systems;
 using Robust.Shared.Containers;
 using Robust.Shared.Random;
 using Robust.Shared.Timing;
+using Robust.Shared.Prototypes;
 
 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>
-    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))
             return true;
index 54639dd2b0527f250ab1d7662e5bb74d5334b660..cc0e5008e4763309946bd331efcba9ad6d570dc5 100644 (file)
@@ -1,3 +1,6 @@
+using Content.Shared.Whitelist;
+using Robust.Server.GameObjects;
+using Robust.Server.Player;
 using Robust.Shared.Player;
 using Robust.Shared.Serialization.TypeSerializers.Implementations;
 
@@ -34,12 +37,19 @@ namespace Content.Server.UserInterface
         [DataField]
         public bool RequireHands = true;
 
+        /// <summary>
+        ///     Entities that are required to open this UI.
+        /// </summary>
+        [DataField("allowedItems")]
+        [ViewVariables(VVAccess.ReadWrite)]
+        public EntityWhitelist? AllowedItems = null;
+
         /// <summary>
         ///     Whether you can activate this ui with activateinhand or not
         /// </summary>
         [ViewVariables(VVAccess.ReadWrite)]
         [DataField]
-        public bool rightClickOnly = false;
+        public bool RightClickOnly;
 
         /// <summary>
         ///     Whether spectators (non-admin ghosts) should be allowed to view this UI.
@@ -63,4 +73,3 @@ namespace Content.Server.UserInterface
         public ICommonSession? CurrentSingleUser;
     }
 }
-
index 387221e00ca1be92fa7af2b98b72c0689d19da40..e3a11af4295c43ae32c81e1156e7aa885c37ecf0 100644 (file)
@@ -26,6 +26,7 @@ public sealed partial class ActivatableUISystem : EntitySystem
 
         SubscribeLocalEvent<ActivatableUIComponent, ActivateInWorldEvent>(OnActivate);
         SubscribeLocalEvent<ActivatableUIComponent, UseInHandEvent>(OnUseInHand);
+        SubscribeLocalEvent<ActivatableUIComponent, InteractUsingEvent>(OnInteractUsing);
         SubscribeLocalEvent<ActivatableUIComponent, HandDeselectedEvent>(OnHandDeselected);
         SubscribeLocalEvent<ActivatableUIComponent, GotUnequippedHandEvent>((uid, aui, _) => CloseAll(uid, aui));
         // *THIS IS A BLATANT WORKAROUND!* RATIONALE: Microwaves need it
@@ -100,12 +101,20 @@ public sealed partial class ActivatableUISystem : EntitySystem
         if (args.Handled)
             return;
 
-        if (component.rightClickOnly)
+        if (component.RightClickOnly)
             return;
 
         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)
     {
         CloseAll(uid, aui);
index 8d3ed8feea1f42331309be003f71bbbb8a676959..323fdb91eda547cfe2ada9f4e70475793ffbd76d 100644 (file)
@@ -14,6 +14,6 @@ public sealed partial class AccessGroupPrototype : IPrototype
     [IdDataField]
     public string ID { get; private set; } = default!;
 
-    [DataField("tags", required: true, customTypeSerializer: typeof(PrototypeIdHashSetSerializer<AccessLevelPrototype>))]
-    public HashSet<string> Tags = default!;
+    [DataField("tags", required: true)]
+    public HashSet<ProtoId<AccessLevelPrototype>> Tags = default!;
 }
index 8cc59271585480aa959856add4bc8cd6dc0e5564..e3a3b426b09b88be34a7378b9832a7c03ab6ed48 100644 (file)
@@ -17,5 +17,13 @@ namespace Content.Shared.Access
         /// </summary>
         [DataField("name")]
         public string? Name { get; set; }
+
+        public string GetAccessLevelName()
+        {
+            if (Name is { } name)
+                return Loc.GetString(name);
+
+            return ID;
+        }
     }
 }
index 2eacf2aa67b02d4dc24e309979721d6cd632aec4..00ee87b3b67fa4c47cd2b7b4788eae4e78d5b988 100644 (file)
@@ -20,17 +20,17 @@ public sealed partial class AccessComponent : Component
     [AutoNetworkedField]
     public bool Enabled = true;
 
-    [DataField(customTypeSerializer: typeof(PrototypeIdHashSetSerializer<AccessLevelPrototype>))]
+    [DataField]
     [Access(typeof(SharedAccessSystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends
     [AutoNetworkedField]
-    public HashSet<string> Tags = new();
+    public HashSet<ProtoId<AccessLevelPrototype>> Tags = new();
 
     /// <summary>
     /// Access Groups. These are added to the tags during map init. After map init this will have no effect.
     /// </summary>
-    [DataField(readOnly: true, customTypeSerializer: typeof(PrototypeIdHashSetSerializer<AccessGroupPrototype>))]
+    [DataField(readOnly: true)]
     [AutoNetworkedField]
-    public HashSet<string> Groups = new();
+    public HashSet<ProtoId<AccessGroupPrototype>> Groups = new();
 }
 
 /// <summary>
@@ -47,9 +47,9 @@ public struct GetAdditionalAccessEvent
 }
 
 [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))
             return;
index 92c09f1ab7178181675b3e1d40bf83ed6c8b3a0b..6a1bf2c831a42fbc5611f96a7f9dada3a3767a0d 100644 (file)
@@ -25,9 +25,9 @@ public sealed partial class AccessOverriderComponent : Component
     [Serializable, NetSerializable]
     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;
         }
@@ -48,15 +48,15 @@ public sealed partial class AccessOverriderComponent : Component
         public readonly string PrivilegedIdName;
         public readonly bool IsPrivilegedIdPresent;
         public readonly bool IsPrivilegedIdAuthorized;
-        public readonly string[]? TargetAccessReaderIdAccessList;
-        public readonly string[]? AllowedModifyAccessList;
-        public readonly string[]? MissingPrivilegesList;
+        public readonly ProtoId<AccessLevelPrototype>[]? TargetAccessReaderIdAccessList;
+        public readonly ProtoId<AccessLevelPrototype>[]? AllowedModifyAccessList;
+        public readonly ProtoId<AccessLevelPrototype>[]? MissingPrivilegesList;
 
         public AccessOverriderBoundUserInterfaceState(bool isPrivilegedIdPresent,
             bool isPrivilegedIdAuthorized,
-            string[]? targetAccessReaderIdAccessList,
-            string[]? allowedModifyAccessList,
-            string[]? missingPrivilegesList,
+            ProtoId<AccessLevelPrototype>[]? targetAccessReaderIdAccessList,
+            ProtoId<AccessLevelPrototype>[]? allowedModifyAccessList,
+            ProtoId<AccessLevelPrototype>[]? missingPrivilegesList,
             string privilegedIdName,
             string targetLabel,
             Color targetLabelColor)
index b157797922362a22e3b59b025b2579a7a2f058e6..cbd3f5acd6770bd11e3388783078732fd21a2532 100644 (file)
@@ -1,5 +1,6 @@
 using Content.Shared.StationRecords;
 using Robust.Shared.GameStates;
+using Robust.Shared.Prototypes;
 using Robust.Shared.Serialization;
 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.
     /// </summary>
     [ViewVariables(VVAccess.ReadWrite)]
-    [DataField(customTypeSerializer: typeof(PrototypeIdHashSetSerializer<AccessLevelPrototype>))]
-    public HashSet<string> DenyTags = new();
+    [DataField]
+    public HashSet<ProtoId<AccessLevelPrototype>> DenyTags = new();
 
     /// <summary>
     /// 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.
     /// </summary>
     [DataField("access")] [ViewVariables(VVAccess.ReadWrite)]
-    public List<HashSet<string>> AccessLists = new();
+    public List<HashSet<ProtoId<AccessLevelPrototype>>> AccessLists = new();
 
     /// <summary>
     /// 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 HashSet<string> DenyTags;
+    public HashSet<ProtoId<AccessLevelPrototype>> DenyTags;
 
-    public List<HashSet<string>> AccessLists;
+    public List<HashSet<ProtoId<AccessLevelPrototype>>> AccessLists;
 
     public List<(NetEntity, uint)> AccessKeys;
 
@@ -98,7 +99,7 @@ public sealed class AccessReaderComponentState : ComponentState
 
     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;
         DenyTags = denyTags;
@@ -108,3 +109,10 @@ public sealed class AccessReaderComponentState : ComponentState
         AccessLogLimit = accessLogLimit;
     }
 }
+
+public sealed class AccessReaderConfigurationChangedEvent : EntityEventArgs
+{
+    public AccessReaderConfigurationChangedEvent()
+    {
+    }
+}
index 387ca8a01386b7b1c202adcca3cd41c0ee722063..deeafe27936c08df5a1a7b1ada9f4ca193c038da 100644 (file)
@@ -3,6 +3,8 @@ using Content.Shared.Containers.ItemSlots;
 using Robust.Shared.GameStates;
 using Robust.Shared.Prototypes;
 using Robust.Shared.Serialization;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
+using Robust.Shared.Prototypes;
 
 namespace Content.Shared.Access.Components;
 
@@ -27,10 +29,10 @@ public sealed partial class IdCardConsoleComponent : Component
     {
         public readonly string FullName;
         public readonly string JobTitle;
-        public readonly List<string> AccessList;
-        public readonly string JobPrototype;
+        public readonly List<ProtoId<AccessLevelPrototype>> AccessList;
+        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;
             JobTitle = jobTitle;
@@ -86,18 +88,18 @@ public sealed partial class IdCardConsoleComponent : Component
         public readonly string TargetIdName;
         public readonly string? TargetIdFullName;
         public readonly string? TargetIdJobTitle;
-        public readonly string[]? TargetIdAccessList;
-        public readonly string[]? AllowedModifyAccessList;
-        public readonly string TargetIdJobPrototype;
+        public readonly List<ProtoId<AccessLevelPrototype>>? TargetIdAccessList;
+        public readonly List<ProtoId<AccessLevelPrototype>>? AllowedModifyAccessList;
+        public readonly ProtoId<AccessLevelPrototype> TargetIdJobPrototype;
 
         public IdCardConsoleBoundUserInterfaceState(bool isPrivilegedIdPresent,
             bool isPrivilegedIdAuthorized,
             bool isTargetIdPresent,
             string? targetIdFullName,
             string? targetIdJobTitle,
-            string[]? targetIdAccessList,
-            string[]? allowedModifyAccessList,
-            string targetIdJobPrototype,
+            List<ProtoId<AccessLevelPrototype>>? targetIdAccessList,
+            List<ProtoId<AccessLevelPrototype>>? allowedModifyAccessList,
+            ProtoId<AccessLevelPrototype> targetIdJobPrototype,
             string privilegedIdName,
             string targetIdName)
         {
index 812a8e048706f9369fd2dcdaa80647965b73275c..4c12622ab56b2cde02e05daf5561c881304f1c58 100644 (file)
@@ -112,11 +112,36 @@ public sealed class AccessReaderSystem : EntitySystem
         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>
     /// Check whether the given access permissions satisfy an access reader's requirements.
     /// </summary>
     public bool IsAllowed(
-        ICollection<string> access,
+        ICollection<ProtoId<AccessLevelPrototype>> access,
         ICollection<StationRecordKey> stationKeys,
         EntityUid target,
         AccessReaderComponent reader)
@@ -142,7 +167,7 @@ public sealed class AccessReaderSystem : EntitySystem
         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
                || AreAccessTagsAllowed(access, reader)
@@ -154,7 +179,7 @@ public sealed class AccessReaderSystem : EntitySystem
     /// </summary>
     /// <param name="accessTags">A list of access tags</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))
         {
@@ -218,9 +243,9 @@ public sealed class AccessReaderSystem : EntitySystem
     /// </summary>
     /// <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>
-    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;
 
         items ??= FindPotentialAccessItems(uid);
@@ -230,7 +255,7 @@ public sealed class AccessReaderSystem : EntitySystem
             FindAccessTagsItem(ent, ref tags, ref owned);
         }
 
-        return (ICollection<string>?) tags ?? Array.Empty<string>();
+        return (ICollection<ProtoId<AccessLevelPrototype>>?) tags ?? Array.Empty<ProtoId<AccessLevelPrototype>>();
     }
 
     /// <summary>
@@ -260,7 +285,7 @@ public sealed class AccessReaderSystem : EntitySystem
     ///     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).
     /// </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))
         {
@@ -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)
     {
         items = new();
@@ -308,7 +343,7 @@ public sealed class AccessReaderSystem : EntitySystem
     ///     Try to find <see cref="AccessComponent"/> on this item
     ///     or inside this item (if it's pda)
     /// </summary>
-    private bool FindAccessTagsItem(EntityUid uid, out HashSet<string> tags)
+    private bool FindAccessTagsItem(EntityUid uid, out HashSet<ProtoId<AccessLevelPrototype>> tags)
     {
         tags = new();
         var ev = new GetAccessTagsEvent(tags, _prototype);
index 965ccb24128e426b48e78d85953523d4c79cdab1..a4b04c35597faf3c93fca193d987055329ef081d 100644 (file)
@@ -51,7 +51,7 @@ namespace Content.Shared.Access.Systems
         ///     Replaces the set of access tags we have with the provided set.
         /// </summary>
         /// <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))
                 return false;
@@ -67,12 +67,12 @@ namespace Content.Shared.Access.Systems
         ///     Gets the set of access tags.
         /// </summary>
         /// <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;
         }
 
-        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))
                 return false;
index 12e25e1e229b4845a907ed87400526ee1c40442e..81843b5e2d245a57bbe3623cf29f5776337eabec 100644 (file)
@@ -1,6 +1,9 @@
 using Content.Shared.Access.Components;
 using Content.Shared.Containers.ItemSlots;
 using JetBrains.Annotations;
+using Robust.Shared.GameStates;
+using Robust.Shared.Serialization;
+using Robust.Shared.Prototypes;
 
 namespace Content.Shared.Access.Systems
 {
@@ -33,5 +36,16 @@ namespace Content.Shared.Access.Systems
             _itemSlotsSystem.RemoveItemSlot(uid, component.PrivilegedIdSlot);
             _itemSlotsSystem.RemoveItemSlot(uid, component.TargetIdSlot);
         }
+
+        [Serializable, NetSerializable]
+        private sealed class IdCardConsoleComponentState : ComponentState
+        {
+            public List<string> AccessLevels;
+
+            public IdCardConsoleComponentState(List<string> accessLevels)
+            {
+                AccessLevels = accessLevels;
+            }
+        }
     }
 }
diff --git a/Content.Shared/Doors/Electronics/DoorElectronicsComponent.cs b/Content.Shared/Doors/Electronics/DoorElectronicsComponent.cs
new file mode 100644 (file)
index 0000000..3e9f279
--- /dev/null
@@ -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
+}
index 6bbc3a68f8f56460be38761b16adaf0ba3f4c974..20961af8e72fc728e30445632518c2411dcac19e 100644 (file)
@@ -116,8 +116,8 @@ public sealed partial class NearbyAccessRule : RulesRule
     [DataField("count")]
     public int Count = 1;
 
-    [DataField("access", required: true, customTypeSerializer: typeof(PrototypeIdListSerializer<AccessLevelPrototype>))]
-    public List<string> Access = new();
+    [DataField("access", required: true)]
+    public List<ProtoId<AccessLevelPrototype>> Access = new();
 
     [DataField("range")]
     public float Range = 10f;
index 0064fcdf17ee2a99f0b8a3b1306732d15ca32729..34a8ce64bffb1ae734b455d4cb361252618fd876 100644 (file)
@@ -105,17 +105,17 @@ namespace Content.Shared.Roles
         [DataField("special", serverOnly: true)]
         public JobSpecial[] Special { get; private set; } = Array.Empty<JobSpecial>();
 
-        [DataField("access", customTypeSerializer: typeof(PrototypeIdListSerializer<AccessLevelPrototype>))]
-        public IReadOnlyCollection<string> Access { get; private set; } = Array.Empty<string>();
+        [DataField("access")]
+        public IReadOnlyCollection<ProtoId<AccessLevelPrototype>> Access { get; private set; } = Array.Empty<ProtoId<AccessLevelPrototype>>();
 
-        [DataField("accessGroups", customTypeSerializer: typeof(PrototypeIdListSerializer<AccessGroupPrototype>))]
-        public IReadOnlyCollection<string> AccessGroups { get; private set; } = Array.Empty<string>();
+        [DataField("accessGroups")]
+        public IReadOnlyCollection<ProtoId<AccessGroupPrototype>> AccessGroups { get; private set; } = Array.Empty<ProtoId<AccessGroupPrototype>>();
 
-        [DataField("extendedAccess", customTypeSerializer: typeof(PrototypeIdListSerializer<AccessLevelPrototype>))]
-        public IReadOnlyCollection<string> ExtendedAccess { get; private set; } = Array.Empty<string>();
+        [DataField("extendedAccess")]
+        public IReadOnlyCollection<ProtoId<AccessLevelPrototype>> ExtendedAccess { get; private set; } = Array.Empty<ProtoId<AccessLevelPrototype>>();
 
-        [DataField("extendedAccessGroups", customTypeSerializer: typeof(PrototypeIdListSerializer<AccessGroupPrototype>))]
-        public IReadOnlyCollection<string> ExtendedAccessGroups { get; private set; } = Array.Empty<string>();
+        [DataField("extendedAccessGroups")]
+        public IReadOnlyCollection<ProtoId<AccessGroupPrototype>> ExtendedAccessGroups { get; private set; } = Array.Empty<ProtoId<AccessGroupPrototype>>();
     }
 
     /// <summary>
index 275a61d8216f22ead5165fe1f8eb4faaf5613aa3..16713a6692f51d5f47109d9a000d808b6eca2813 100644 (file)
@@ -7,9 +7,19 @@
     - type: Sprite
       sprite: Objects/Misc/module.rsi
       state: door_electronics
-    - type: AccessReader
     - type: Tag
       tags:
         - DoorElectronics
+    - type: DoorElectronics
     - type: StaticPrice
       price: 55
+    - type: AccessReader
+    - type: ActivatableUI
+      key: enum.DoorElectronicsConfigurationUiKey.Key
+      allowedItems:
+        tags:
+        - Multitool
+    - type: UserInterface
+      interfaces:
+      - key: enum.DoorElectronicsConfigurationUiKey.Key
+        type: DoorElectronicsBoundUserInterface
diff --git a/Resources/Prototypes/Entities/Objects/Devices/Electronics/door_access.yml b/Resources/Prototypes/Entities/Objects/Devices/Electronics/door_access.yml
new file mode 100644 (file)
index 0000000..fc6d8e2
--- /dev/null
@@ -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"]]
index 7eed29ad56ba562ecfc5c8e14c595b58eaa04019..8ffd18e7e294b6050cfa4b38d0e10d460f1a9b42 100644 (file)
@@ -4,10 +4,9 @@
   id: AirlockServiceLocked
   suffix: Service, Locked
   components:
-  - type: AccessReader
-    access: [["Service"]]
-  - type: Wires
-    layoutId: AirlockService
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsService ]
 
 - type: entity
   parent: Airlock
   id: AirlockTheatreLocked
   suffix: Theatre, Locked
   components:
-  - type: AccessReader
-    access: [["Theatre"]]
-  - type: Wires
-    layoutId: AirlockService
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsTheatre ]
 
 - type: entity
   parent: Airlock
   id: AirlockChapelLocked
   suffix: Chapel, Locked
   components:
-  - type: AccessReader
-    access: [["Chapel"]]
-  - type: Wires
-    layoutId: AirlockService
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsChapel ]
 
 - type: entity
   parent: Airlock
   id: AirlockJanitorLocked
   suffix: Janitor, Locked
   components:
-  - type: AccessReader
-    access: [["Janitor"]]
-  - type: Wires
-    layoutId: AirlockService
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsJanitor ]
 
 - type: entity
   parent: Airlock
   id: AirlockKitchenLocked
   suffix: Kitchen, Locked
   components:
-  - type: AccessReader
-    access: [["Kitchen"]]
-  - type: Wires
-    layoutId: AirlockService
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsKitchen ]
 
 - type: entity
   parent: Airlock
   id: AirlockBarLocked
   suffix: Bar, Locked
   components:
-  - type: AccessReader
-    access: [["Bar"]]
-  - type: Wires
-    layoutId: AirlockService
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsBar ]
 
 - type: entity
   parent: Airlock
   id: AirlockHydroponicsLocked
   suffix: Hydroponics, Locked
   components:
-  - type: AccessReader
-    access: [["Hydroponics"]]
-  - type: Wires
-    layoutId: AirlockService
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsHydroponics ]
 
 - type: entity
   parent: Airlock
   id: AirlockServiceCaptainLocked
   suffix: Captain, Locked
   components:
-  - type: AccessReader
-    access: [["Captain"]]
-  - type: Wires
-    layoutId: AirlockCommand
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsCaptain ]
 
 - type: entity
   parent: AirlockExternal
   id: AirlockExternalLocked
   suffix: External, Locked
   components:
-  - type: AccessReader
-    access: [["External"]]
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsExternal ]
 
 - type: entity
   parent: AirlockExternal
   id: AirlockExternalCargoLocked
   suffix: External, Cargo, Locked
   components:
-  - type: AccessReader
-    access: [["Cargo"]]
-  - type: Wires
-    layoutId: AirlockCargo
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsCargo ]
 
 - type: entity
   parent: AirlockExternal
   id: AirlockExternalEngineeringLocked
   suffix: External, Engineering, Locked
   components:
-  - type: AccessReader
-    access: [["Engineering"]]
-  - type: Wires
-    layoutId: AirlockEngineering
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsEngineering ]
 
 - type: entity
   parent: AirlockExternal
   id: AirlockExternalAtmosphericsLocked
   suffix: External, Atmospherics, Locked
   components:
-  - type: AccessReader
-    access: [["Atmospherics"]]
-  - type: Wires
-    layoutId: AirlockEngineering
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsAtmospherics ]
 
 - type: entity
   parent: AirlockExternal
   id: AirlockFreezerLocked
   suffix: Kitchen, Locked
   components:
-  - type: AccessReader
-    access: [["Kitchen"]]
-  - type: Wires
-    layoutId: AirlockService
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsKitchen ]
 
 - type: entity
   parent: AirlockFreezer
   id: AirlockFreezerKitchenHydroLocked
   suffix: Kitchen/Hydroponics, Locked
   components:
-  - type: AccessReader
-    access: [["Kitchen"], ["Hydroponics"]]
-  - type: Wires
-    layoutId: AirlockService
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsFreezer ]
 
 - type: entity
   parent: AirlockFreezer
   id: AirlockEngineeringLocked
   suffix: Engineering, Locked
   components:
-  - type: AccessReader
-    access: [["Engineering"]]
-  - type: Wires
-    layoutId: AirlockEngineering
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsEngineering ]
 
 - type: entity
   parent: AirlockAtmospherics
   id: AirlockAtmosphericsLocked
   suffix: Atmospherics, Locked
   components:
-  - type: AccessReader
-    access: [["Atmospherics"]]
-  - type: Wires
-    layoutId: AirlockEngineering
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsAtmospherics ]
 
 - type: entity
   parent: AirlockCargo
   id: AirlockCargoLocked
   suffix: Cargo, Locked
   components:
-  - type: AccessReader
-    access: [["Cargo"]]
-  - type: Wires
-    layoutId: AirlockCargo
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsCargo ]
 
 - type: entity
   parent: AirlockCargo
   id: AirlockSalvageLocked
   suffix: Salvage, Locked
   components:
-  - type: AccessReader
-    access: [["Salvage"]]
-  - type: Wires
-    layoutId: AirlockCargo
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsSalvage ]
 
 - type: entity
   parent: AirlockMining
   id: AirlockMedicalLocked
   suffix: Medical, Locked
   components:
-  - type: AccessReader
-    access: [["Medical"]]
-  - type: Wires
-    layoutId: AirlockMedical
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsMedical ]
 
 - type: entity
   parent: AirlockVirology
   id: AirlockVirologyLocked
   suffix: Virology, Locked
   components:
-  - type: AccessReader
-    access: [["Medical"]]
-  - type: Wires
-    layoutId: AirlockMedical
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsMedical ]
 
 - type: entity
   parent: AirlockChemistry
   id: AirlockChemistryLocked
   suffix: Chemistry, Locked
   components:
-  - type: AccessReader
-    access: [["Chemistry"]]
-  - type: Wires
-    layoutId: AirlockMedical
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsChemistry ]
 
 - type: entity
   parent: AirlockScience
   id: AirlockScienceLocked
   suffix: Science, Locked
   components:
-  - type: AccessReader
-    access: [["Research"]]
-  - type: Wires
-    layoutId: AirlockScience
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsResearch ]
 
 - type: entity
   parent: AirlockScience
   id: AirlockMedicalScienceLocked
   suffix: Medical/Science, Locked
   components:
-  - type: AccessReader
-    access: [["Research"], ["Medical"]]
-  - type: Wires
-    layoutId: AirlockScience
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsScience ]
 
 - type: entity
   parent: AirlockCentralCommand
   id: AirlockCommandLocked
   suffix: Command, Locked
   components:
-  - type: AccessReader
-    access: [["Command"]]
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsCommand ]
   - type: Wires
     layoutId: AirlockCommand
 
   id: AirlockCaptainLocked
   suffix: Captain, Locked
   components:
-  - type: AccessReader
-    access: [["Captain"]]
-  - type: Wires
-    layoutId: AirlockCommand
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsCaptain ]
 
 - type: entity
   parent: AirlockCommand
   id: AirlockChiefMedicalOfficerLocked
   suffix: ChiefMedicalOfficer, Locked
   components:
-  - type: AccessReader
-    access: [["ChiefMedicalOfficer"]]
-  - type: Wires
-    layoutId: AirlockCommand
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsChiefMedicalOfficer ]
 
 - type: entity
   parent: AirlockCommand
   id: AirlockChiefEngineerLocked
   suffix: ChiefEngineer, Locked
   components:
-  - type: AccessReader
-    access: [["ChiefEngineer"]]
-  - type: Wires
-    layoutId: AirlockCommand
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsChiefEngineer ]
 
 - type: entity
   parent: AirlockCommand
   id: AirlockHeadOfSecurityLocked
   suffix: HeadOfSecurity, Locked
   components:
-  - type: AccessReader
-    access: [["HeadOfSecurity"]]
-  - type: Wires
-    layoutId: AirlockCommand
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsHeadOfSecurity ]
 
 - type: entity
   parent: AirlockCommand
   id: AirlockResearchDirectorLocked
   suffix: ResearchDirector, Locked
   components:
-  - type: AccessReader
-    access: [["ResearchDirector"]]
-  - type: Wires
-    layoutId: AirlockCommand
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsResearchDirector ]
 
 - type: entity
   parent: AirlockCommand
   id: AirlockHeadOfPersonnelLocked
   suffix: HeadOfPersonnel, Locked
   components:
-  - type: AccessReader
-    access: [["HeadOfPersonnel"]]
-  - type: Wires
-    layoutId: AirlockCommand
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsHeadOfPersonnel ]
 
 - type: entity
   parent: AirlockCommand
   id: AirlockQuartermasterLocked
   suffix: Quartermaster, Locked
   components:
-  - type: AccessReader
-    access: [["Quartermaster"]]
-  - type: Wires
-    layoutId: AirlockCommand
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsQuartermaster ]
 
 - type: entity
   parent: AirlockSecurity
   id: AirlockSecurityLocked
   suffix: Security, Locked
   components:
-  - type: AccessReader
-    access: [["Security"]]
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsSecurity ]
   - type: Wires
     layoutId: AirlockSecurity
 
   id: AirlockDetectiveLocked
   suffix: Detective, Locked
   components:
-  - type: AccessReader
-    access: [["Detective"]]
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsDetective ]
   - type: Wires
     layoutId: AirlockSecurity
 
   id: AirlockBrigLocked
   suffix: Brig, Locked
   components:
-  - type: AccessReader
-    access: [["Brig"]]
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsBrig ]
   - type: Wires
     layoutId: AirlockSecurity
 
   id: AirlockArmoryLocked
   suffix: Armory, Locked
   components:
-  - type: AccessReader
-    access: [["Armory"]]
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsArmory ]
   - type: Wires
     layoutId: AirlockArmory
 
   id: AirlockVaultLocked
   suffix: Vault, Locked
   components:
-  - type: AccessReader
-    access: [["Security", "Command"]]
-  - type: Wires
-    layoutId: AirlockSecurity
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsVault ]
 
 - type: entity
   parent: AirlockCommand
   id: AirlockEVALocked
   suffix: EVA, Locked
   components:
-  - type: AccessReader
-    access: [["External"]]
-  - type: Wires
-    layoutId: AirlockCommand
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsExternal ]
 
 # Glass Airlocks
 - type: entity
   id: AirlockServiceGlassLocked
   suffix: Service, Locked
   components:
-  - type: AccessReader
-    access: [["Service"]]
-  - type: Wires
-    layoutId: AirlockService
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsService ]
 
 - type: entity
   parent: AirlockGlass
   id: AirlockBarGlassLocked
   suffix: Bar, Locked
   components:
-  - type: AccessReader
-    access: [["Bar"]]
-  - type: Wires
-    layoutId: AirlockService
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsBar ]
 
 - type: entity
   parent: AirlockExternalGlass
   id: AirlockExternalGlassLocked
   suffix: External, Glass, Locked
   components:
-  - type: AccessReader
-    access: [["External"]]
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsExternal ]
 
 - type: entity
   parent: AirlockExternalGlass
   id: AirlockExternalGlassCargoLocked
   suffix: External, Glass, Cargo, Locked
   components:
-  - type: AccessReader
-    access: [["Cargo"]]
-  - type: Wires
-    layoutId: AirlockService
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsCargo ]
 
 - type: entity
   parent: AirlockExternalGlass
   id: AirlockExternalGlassEngineeringLocked
   suffix: External, Glass, Engineering, Locked
   components:
-  - type: AccessReader
-    access: [["Engineering"]]
-  - type: Wires
-    layoutId: AirlockEngineering
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsEngineering ]
 
 - type: entity
   parent: AirlockExternalGlass
   id: AirlockExternalGlassAtmosphericsLocked
   suffix: External, Glass, Atmospherics, Locked
   components:
-  - type: AccessReader
-    access: [["Atmospherics"]]
-  - type: Wires
-    layoutId: AirlockEngineering
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsAtmospherics ]
 
 - type: entity
   parent: AirlockGlass
   id: AirlockKitchenGlassLocked
   suffix: Kitchen, Locked
   components:
-  - type: AccessReader
-    access: [["Kitchen"]]
-  - type: Wires
-    layoutId: AirlockService
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsKitchen ]
 
 - type: entity
   parent: AirlockGlass
   id: AirlockHydroGlassLocked
   suffix: Hydroponics, Locked
   components:
-  - type: AccessReader
-    access: [["Hydroponics"]]
-  - type: Wires
-    layoutId: AirlockService
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsHydroponics ]
 
 - type: entity
   parent: AirlockGlass
   id: AirlockChapelGlassLocked
   suffix: Chapel, Locked
   components:
-  - type: AccessReader
-    access: [["Chapel"]]
-  - type: Wires
-    layoutId: AirlockService
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsChapel ]
 
 - type: entity
   parent: AirlockEngineeringGlass
   id: AirlockEngineeringGlassLocked
   suffix: Engineering, Locked
   components:
-  - type: AccessReader
-    access: [["Engineering"]]
-  - type: Wires
-    layoutId: AirlockEngineering
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsEngineering ]
 
 - type: entity
   parent: AirlockAtmosphericsGlass
   id: AirlockAtmosphericsGlassLocked
   suffix: Atmospherics, Locked
   components:
-  - type: AccessReader
-    access: [["Atmospherics"]]
-  - type: Wires
-    layoutId: AirlockEngineering
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsAtmospherics ]
 
 - type: entity
   parent: AirlockCargoGlass
   id: AirlockCargoGlassLocked
   suffix: Cargo, Locked
   components:
-  - type: AccessReader
-    access: [["Cargo"]]
-  - type: Wires
-    layoutId: AirlockCargo
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsCargo ]
 
 - type: entity
   parent: AirlockCargoGlass
   id: AirlockSalvageGlassLocked
   suffix: Salvage, Locked
   components:
-  - type: AccessReader
-    access: [["Salvage"]]
-  - type: Wires
-    layoutId: AirlockCargo
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsSalvage ]
 
 - type: entity
   parent: AirlockMiningGlass
   id: AirlockMedicalGlassLocked
   suffix: Medical, Locked
   components:
-  - type: AccessReader
-    access: [["Medical"]]
-  - type: Wires
-    layoutId: AirlockMedical
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsMedical ]
 
 - type: entity
   parent: AirlockVirologyGlass
   id: AirlockVirologyGlassLocked
   suffix: Virology, Locked
   components:
-  - type: AccessReader
-    access: [["Medical"]]
-  - type: Wires
-    layoutId: AirlockMedical
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsMedical ]
 
 - type: entity
   parent: AirlockScienceGlass
   id: AirlockScienceGlassLocked
   suffix: Science, Locked
   components:
-  - type: AccessReader
-    access: [["Research"]]
-  - type: Wires
-    layoutId: AirlockScience
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsResearch ]
 
 - type: entity
   parent: AirlockScienceGlass
   id: AirlockMedicalScienceGlassLocked
   suffix: Medical/Science, Locked
   components:
-  - type: AccessReader
-    access: [["Research"], ["Medical"]]
-  - type: Wires
-    layoutId: AirlockScience
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsScience ]
 
 - type: entity
   parent: AirlockCentralCommandGlass
   id: AirlockCommandGlassLocked
   suffix: Command, Locked
   components:
-  - type: AccessReader
-    access: [["Command"]]
-  - type: Wires
-    layoutId: AirlockCommand
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsCommand ]
 
 - type: entity
   parent: AirlockCommandGlass
   id: AirlockCaptainGlassLocked
   suffix: Captain, Locked
   components:
-  - type: AccessReader
-    access: [["Captain"]]
-  - type: Wires
-    layoutId: AirlockCommand
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsCaptain ]
 
 - type: entity
   parent: AirlockCommandGlass
   id: AirlockChiefMedicalOfficerGlassLocked
   suffix: ChiefMedicalOfficer, Locked
   components:
-  - type: AccessReader
-    access: [["ChiefMedicalOfficer"]]
-  - type: Wires
-    layoutId: AirlockCommand
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsChiefMedicalOfficer ]
 
 - type: entity
   parent: AirlockCommandGlass
   id: AirlockChiefEngineerGlassLocked
   suffix: ChiefEngineer, Locked
   components:
-  - type: AccessReader
-    access: [["ChiefEngineer"]]
-  - type: Wires
-    layoutId: AirlockCommand
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsChiefEngineer ]
 
 - type: entity
   parent: AirlockCommandGlass
   id: AirlockHeadOfSecurityGlassLocked
   suffix: HeadOfSecurity, Locked
   components:
-  - type: AccessReader
-    access: [["HeadOfSecurity"]]
-  - type: Wires
-    layoutId: AirlockCommand
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsHeadOfSecurity ]
 
 - type: entity
   parent: AirlockCommandGlass
   id: AirlockResearchDirectorGlassLocked
   suffix: ResearchDirector, Locked
   components:
-  - type: AccessReader
-    access: [["ResearchDirector"]]
-  - type: Wires
-    layoutId: AirlockCommand
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsResearchDirector ]
 
 - type: entity
   parent: AirlockCommandGlass
   id: AirlockHeadOfPersonnelGlassLocked
   suffix: HeadOfPersonnel, Locked
   components:
-  - type: AccessReader
-    access: [["HeadOfPersonnel"]]
-  - type: Wires
-    layoutId: AirlockCommand
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsHeadOfPersonnel ]
 
 - type: entity
   parent: AirlockCommandGlass
   id: AirlockQuartermasterGlassLocked
   suffix: Quartermaster, Locked
   components:
-  - type: AccessReader
-    access: [["Quartermaster"]]
-  - type: Wires
-    layoutId: AirlockCommand
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsQuartermaster ]
 
 - type: entity
   parent: AirlockSecurityGlass
   id: AirlockSecurityGlassLocked
   suffix: Security, Locked
   components:
-  - type: AccessReader
-    access: [["Security"]]
-  - type: Wires
-    layoutId: AirlockCommand
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsSecurity ]
 
 - type: entity
   parent: AirlockSecurityGlass
   id: AirlockDetectiveGlassLocked
   suffix: Detective, Locked
   components:
-  - type: AccessReader
-    access: [["Detective"]]
-  - type: Wires
-    layoutId: AirlockCommand
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsDetective ]
 
 - type: entity
   parent: AirlockSecurityGlass
   id: AirlockBrigGlassLocked
   suffix: Brig, Locked
   components:
-  - type: AccessReader
-    access: [["Brig"]]
-  - type: Wires
-    layoutId: AirlockCommand
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsBrig ]
 
 - type: entity
   parent: AirlockSecurityGlass
   id: AirlockArmoryGlassLocked
   suffix: Armory, Locked
   components:
-  - type: AccessReader
-    access: [["Armory"]]
-  - type: Wires
-    layoutId: AirlockSecurity
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsArmory ]
 
 - type: entity
   parent: AirlockCommandGlassLocked
   id: AirlockEVAGlassLocked
   suffix: EVA, Locked
   components:
-  - type: AccessReader
-    access: [["External"]]
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsExternal ]
 
 - type: entity
   parent: AirlockSyndicateGlass
   id: AirlockMaintLocked
   suffix: Locked
   components:
-  - type: AccessReader
-    access: [["Maintenance"]]
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsMaintenance ]
 
 - type: entity
   parent: AirlockMaintGlass
   id: AirlockMaintGlassLocked
   suffix: Locked
   components:
-  - type: AccessReader
-    access: [["Maintenance"]]
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsMaintenance ]
 
 - type: entity
   parent: AirlockMaint
   id: AirlockMaintSalvageLocked
   suffix: Salvage, Locked
   components:
-  - type: AccessReader
-    access: [["Salvage"]]
-  - type: Wires
-    layoutId: AirlockCargo
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsSalvage ]
 
 - type: entity
   parent: AirlockMaint
   id: AirlockMaintCargoLocked
   suffix: Cargo, Locked
   components:
-  - type: AccessReader
-    access: [["Cargo"]]
-  - type: Wires
-    layoutId: AirlockCargo
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsCargo ]
 
 - type: entity
   parent: AirlockMaint
   id: AirlockMaintCommandLocked
   suffix: Command, Locked
   components:
-  - type: AccessReader
-    access: [["Command"]]
-  - type: Wires
-    layoutId: AirlockCommand
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsCommand ]
 
 - type: entity
   parent: AirlockMaint
   id: AirlockMaintCommonLocked
   suffix: Common, Locked
   components:
-  - type: AccessReader
-    access: [["Maintenance"]]
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsMaintenance ]
 
 - type: entity
   parent: AirlockMaint
   id: AirlockMaintEngiLocked
   suffix: Engineering, Locked
   components:
-  - type: AccessReader
-    access: [["Engineering"]]
-  - type: Wires
-    layoutId: AirlockEngineering
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsEngineering ]
 
 - type: entity
   parent: AirlockMaint
   id: AirlockMaintAtmoLocked
   suffix: Atmospherics, Locked
   components:
-  - type: AccessReader
-    access: [["Atmospherics"]]
-  - type: Wires
-    layoutId: AirlockEngineering
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsAtmospherics ]
 
 - type: entity
   parent: AirlockMaint
   id: AirlockMaintBarLocked
   suffix: Bar, Locked
   components:
-  - type: AccessReader
-    access: [["Bar"]]
-  - type: Wires
-    layoutId: AirlockService
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsBar ]
 
 - type: entity
   parent: AirlockMaint
   id: AirlockMaintChapelLocked
   suffix: Chapel, Locked
   components:
-  - type: AccessReader
-    access: [["Chapel"]]
-  - type: Wires
-    layoutId: AirlockService
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsChapel ]
 
 - type: entity
   parent: AirlockMaint
   id: AirlockMaintHydroLocked
   suffix: Hydroponics, Locked
   components:
-  - type: AccessReader
-    access: [["Hydroponics"]]
-  - type: Wires
-    layoutId: AirlockService
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsHydroponics ]
 
 - type: entity
   parent: AirlockMaint
   id: AirlockMaintJanitorLocked
   suffix: Janitor, Locked
   components:
-  - type: AccessReader
-    access: [["Janitor"]]
-  - type: Wires
-    layoutId: AirlockService
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsJanitor ]
 
 - type: entity
   parent: AirlockMaint
   id: AirlockMaintTheatreLocked
   suffix: Theatre, Locked
   components:
-  - type: AccessReader
-    access: [["Theatre"]]
-  - type: Wires
-    layoutId: AirlockService
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsTheatre ]
 
 - type: entity
   parent: AirlockMaint
   id: AirlockMaintKitchenLocked
   suffix: Kitchen, Locked
   components:
-  - type: AccessReader
-    access: [["Kitchen"]]
-  - type: Wires
-    layoutId: AirlockService
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsKitchen ]
 
 - type: entity
   parent: AirlockMaint
   id: AirlockMaintIntLocked
   suffix: Interior, Locked
   components:
-  - type: AccessReader
-    access: [["Maintenance"]]
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsMaintenance ]
 
 - type: entity
   parent: AirlockMaint
   id: AirlockMaintMedLocked
   suffix: Medical, Locked
   components:
-  - type: AccessReader
-    access: [["Medical"]]
-  - type: Wires
-    layoutId: AirlockMedical
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsMedical ]
 
 - type: entity
   parent: AirlockMaint
   id: AirlockMaintChemLocked
   suffix: Chemistry, Locked
   components:
-  - type: AccessReader
-    access: [["Chemistry"]]
-  - type: Wires
-    layoutId: AirlockMedical
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsChemistry ]
 
 - type: entity
   parent: AirlockMaint
   id: AirlockMaintRnDLocked
   suffix: Science, Locked
   components:
-  - type: AccessReader
-    access: [["Research"]]
-  - type: Wires
-    layoutId: AirlockScience
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsResearch ]
 
 - type: entity
   parent: AirlockMaint
   id: AirlockMaintRnDMedLocked
   suffix: Medical/Science, Locked
   components:
-  - type: AccessReader
-    access: [["Research"], ["Medical"]]
-  - type: Wires
-    layoutId: AirlockScience
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsRnDMed ]
 
 - type: entity
   parent: AirlockMaint
   id: AirlockMaintSecLocked
   suffix: Security, Locked
   components:
-  - type: AccessReader
-    access: [["Security"]]
-  - type: Wires
-    layoutId: AirlockSecurity
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsSecurity ]
 
 - type: entity
   parent: AirlockMaint
   id: AirlockMaintDetectiveLocked
   suffix: Detective, Locked
   components:
-  - type: AccessReader
-    access: [["Detective"]]
-  - type: Wires
-    layoutId: AirlockSecurity
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsDetective ]
 
 - type: entity
   parent: AirlockMaint
   id: AirlockMaintHOPLocked
   suffix: HeadOfPersonnel, Locked
   components:
-  - type: AccessReader
-    access: [["HeadOfPersonnel"]]
-  - type: Wires
-    layoutId: AirlockCommand
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsHeadOfPersonnel ]
 
 - type: entity
   parent: AirlockMaint
   id: AirlockMaintCaptainLocked
   suffix: Captain, Locked
   components:
-  - type: AccessReader
-    access: [["Captain"]]
-  - type: Wires
-    layoutId: AirlockCommand
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsCaptain ]
 
 - type: entity
   parent: AirlockMaint
   id: AirlockExternalShuttleLocked
   suffix: External, Docking, Locked
   components:
-  - type: AccessReader
-    access: [["External"]]
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsExternal ]
 
 - type: entity
   parent: AirlockShuttleSyndicate
   id: AirlockExternalGlassShuttleLocked
   suffix: External, Glass, Docking, Locked
   components:
-  - type: AccessReader
-    access: [["External"]]
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsExternal ]
 
 - type: entity
   parent: AirlockGlassShuttleSyndicate
   components:
     - type: PriorityDock
       tag: DockEmergency
-    - type: AccessReader
-      access: [["External"]]
+    - type: ContainerFill
+      containers:
+        board: [ DoorElectronicsExternal ]
 
 - type: entity
   parent: AirlockGlassShuttle
   id: HighSecCommandLocked
   suffix: Command, Locked
   components:
-  - type: AccessReader
-    access: [["Command"]]
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsCommand ]
 
 - type: entity
   parent: HighSecDoor
   id: HighSecCaptainLocked
   suffix: Captain, Locked
   components:
-  - type: AccessReader
-    access: [["Captain"]]
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsCaptain ]
 
 - type: entity
   parent: HighSecDoor
   id: HighSecArmoryLocked
   suffix: Armory, Locked
   components:
-  - type: AccessReader
-    access: [["Armory"]]
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsArmory ]
 
 #Airtight hatch
 
   id: AirlockHatchSyndicate
   suffix: Syndicate, Locked
   components:
-  - type: AccessReader
-    access: [["SyndicateAgent"]]
-
+  - type: ContainerFill
+    containers:
+      board: [ DoorElectronicsSyndicateAgent ]
index abc86b2be8e25c35443f1983539d7102cb46ed31..4ca7df6482efaef57ab5b68d67861e06021fe45b 100644 (file)
@@ -48,6 +48,8 @@
   - type: ContainerFill
     containers:
       board: [ DoorElectronics ]
+  - type: AccessReader
+    containerAccessProvider: board
   - type: Door
     crushDamage:
       types:
   - type: PaintableAirlock
     group: Standard
     department: Civilian
-  - type: AccessReader
   - type: StaticPrice
     price: 150
   - type: LightningTarget
index a26226c957899d6c18449db9d4265fb222fcf457..e9ea05a1c3f04db9ab566ce719073ac72a4f1d58 100644 (file)
@@ -60,6 +60,7 @@
   - type: NavMapDoor
   - type: DoorBolt
   - type: AccessReader
+    containerAccessProvider: board
   - type: Appearance
   - type: WiresVisuals
   - type: ApcPowerReceiver
index 3cd22bbfc72ba4375e996d43cce4a2e7f8b07a31..5529514fdcbc410c8693aced2999af1360e1472a 100644 (file)
@@ -46,7 +46,7 @@
       conditions:
       - !type:EntityAnchored {}
       steps:
-      - tag: DoorElectronics
+      - component: DoorElectronics
         store: board
         name: "door electronics circuit board"
         icon:
index 70d1c12d8454ee05888dc394c5b1fbd70f36f785..db53de288a4a6a1c134d5abc9ba96ee9d7bccac8 100644 (file)
@@ -44,7 +44,7 @@
             - !type:EntityAnchored
               anchored: true
           steps:
-            - tag: DoorElectronics
+            - component: DoorElectronics
               name: Door Electronics
               icon:
                 sprite: "Objects/Misc/module.rsi"
index 457fbfede562f6a10a64b49b474cafe1ec30e087..f29629043df5136baae940915ea4e609bd6be788 100644 (file)
@@ -23,7 +23,7 @@
       conditions:
       - !type:EntityAnchored {}
       steps:
-      - tag: DoorElectronics
+      - component: DoorElectronics
         store: board
         name: "door electronics circuit board"
         icon:
index 64809ee1dbc9682cb8d30d1bd6632b3ddf95da54..de896634670a576f0042859c75df618f6391759c 100644 (file)
       conditions:
       - !type:EntityAnchored {}
       steps:
-      - tag: DoorElectronics
+      - component: DoorElectronics
         store: board
         name: "door electronics circuit board"
         icon:
       conditions:
       - !type:EntityAnchored { }
       steps:
-      - tag: DoorElectronics
+      - component: DoorElectronics
         store: board
         name: "door electronics circuit board"
         icon: