diff --git a/Content.IntegrationTests/Tests/Access/AccessReaderTest.cs b/Content.IntegrationTests/Tests/Access/AccessReaderTest.cs index b98f030b06..a0c8c775b1 100644 --- a/Content.IntegrationTests/Tests/Access/AccessReaderTest.cs +++ b/Content.IntegrationTests/Tests/Access/AccessReaderTest.cs @@ -54,7 +54,7 @@ namespace Content.IntegrationTests.Tests.Access system.ClearDenyTags(reader); // test one list - system.AddAccess(reader, "A"); + system.TryAddAccess(reader, "A"); Assert.Multiple(() => { Assert.That(system.AreAccessTagsAllowed(new List> { "A" }, reader), Is.True); @@ -62,10 +62,10 @@ namespace Content.IntegrationTests.Tests.Access Assert.That(system.AreAccessTagsAllowed(new List> { "A", "B" }, reader), Is.True); Assert.That(system.AreAccessTagsAllowed(Array.Empty>(), reader), Is.False); }); - system.ClearAccesses(reader); + system.TryClearAccesses(reader); // test one list - two items - system.AddAccess(reader, new HashSet> { "A", "B" }); + system.TryAddAccess(reader, new HashSet> { "A", "B" }); Assert.Multiple(() => { Assert.That(system.AreAccessTagsAllowed(new List> { "A" }, reader), Is.False); @@ -73,14 +73,14 @@ namespace Content.IntegrationTests.Tests.Access Assert.That(system.AreAccessTagsAllowed(new List> { "A", "B" }, reader), Is.True); Assert.That(system.AreAccessTagsAllowed(Array.Empty>(), reader), Is.False); }); - system.ClearAccesses(reader); + system.TryClearAccesses(reader); // test two list var accesses = new List>>() { new HashSet> () { "A" }, new HashSet> () { "B", "C" } }; - system.AddAccesses(reader, accesses); + system.TryAddAccesses(reader, accesses); Assert.Multiple(() => { Assert.That(system.AreAccessTagsAllowed(new List> { "A" }, reader), Is.True); @@ -90,10 +90,10 @@ namespace Content.IntegrationTests.Tests.Access Assert.That(system.AreAccessTagsAllowed(new List> { "C", "B", "A" }, reader), Is.True); Assert.That(system.AreAccessTagsAllowed(Array.Empty>(), reader), Is.False); }); - system.ClearAccesses(reader); + system.TryClearAccesses(reader); // test deny list - system.AddAccess(reader, new HashSet> { "A" }); + system.TryAddAccess(reader, new HashSet> { "A" }); system.AddDenyTag(reader, "B"); Assert.Multiple(() => { @@ -102,7 +102,7 @@ namespace Content.IntegrationTests.Tests.Access Assert.That(system.AreAccessTagsAllowed(new List> { "A", "B" }, reader), Is.False); Assert.That(system.AreAccessTagsAllowed(Array.Empty>(), reader), Is.False); }); - system.ClearAccesses(reader); + system.TryClearAccesses(reader); system.ClearDenyTags(reader); }); await pair.CleanReturnAsync(); diff --git a/Content.Server/Access/Systems/AccessOverriderSystem.cs b/Content.Server/Access/Systems/AccessOverriderSystem.cs index 68bdd6b9a9..4eaf3c0419 100644 --- a/Content.Server/Access/Systems/AccessOverriderSystem.cs +++ b/Content.Server/Access/Systems/AccessOverriderSystem.cs @@ -229,7 +229,7 @@ public sealed class AccessOverriderSystem : SharedAccessOverriderSystem _adminLogger.Add(LogType.Action, LogImpact.High, $"{ToPrettyString(player):player} has modified {ToPrettyString(accessReaderEnt.Value):entity} with the following allowed access level holders: [{string.Join(", ", addedTags.Union(removedTags))}] [{string.Join(", ", newAccessList)}]"); - _accessReader.SetAccesses(accessReaderEnt.Value, newAccessList); + _accessReader.TrySetAccesses(accessReaderEnt.Value, newAccessList); var ev = new OnAccessOverriderAccessUpdatedEvent(player); RaiseLocalEvent(component.TargetAccessReaderId, ref ev); diff --git a/Content.Server/Doors/Electronics/Systems/DoorElectronicsSystem.cs b/Content.Server/Doors/Electronics/Systems/DoorElectronicsSystem.cs index af2738d105..5579bc5988 100644 --- a/Content.Server/Doors/Electronics/Systems/DoorElectronicsSystem.cs +++ b/Content.Server/Doors/Electronics/Systems/DoorElectronicsSystem.cs @@ -48,7 +48,7 @@ public sealed class DoorElectronicsSystem : EntitySystem DoorElectronicsUpdateConfigurationMessage args) { var accessReader = EnsureComp(uid); - _accessReader.SetAccesses((uid, accessReader), args.AccessList); + _accessReader.TrySetAccesses((uid, accessReader), args.AccessList); } private void OnAccessReaderChanged( diff --git a/Content.Shared/Access/Components/AccessReaderComponent.cs b/Content.Shared/Access/Components/AccessReaderComponent.cs index 6c2416fdf4..c261c7deca 100644 --- a/Content.Shared/Access/Components/AccessReaderComponent.cs +++ b/Content.Shared/Access/Components/AccessReaderComponent.cs @@ -34,6 +34,15 @@ public sealed partial class AccessReaderComponent : Component [DataField("access")] public List>> AccessLists = new(); + /// + /// An unmodified copy of the original list of the access groups that grant access to this reader. + /// + /// + /// If null, the access lists of this entity have not been modified yet. + /// + [DataField] + public List>>? AccessListsOriginal = null; + /// /// A list of s that grant access. Only a single matching key is required to gain access. /// @@ -76,6 +85,16 @@ public sealed partial class AccessReaderComponent : Component /// [DataField] public bool BreakOnAccessBreaker = true; + + /// + /// The examination text associated with this component. + /// + /// + /// The text can be supplied with the 'access' variable to populate it + /// with a comma separated list of the access levels contained in . + /// + [DataField] + public LocId ExaminationText = "access-reader-examination"; } [DataDefinition, Serializable, NetSerializable] @@ -96,19 +115,36 @@ public sealed class AccessReaderComponentState : ComponentState public bool Enabled; public HashSet> DenyTags; public List>> AccessLists; + public List>>? AccessListsOriginal; public List<(NetEntity, uint)> AccessKeys; public Queue AccessLog; public int AccessLogLimit; - public AccessReaderComponentState(bool enabled, HashSet> denyTags, List>> accessLists, List<(NetEntity, uint)> accessKeys, Queue accessLog, int accessLogLimit) + public AccessReaderComponentState( + bool enabled, + HashSet> denyTags, + List>> accessLists, + List>>? accessListsOriginal, + List<(NetEntity, uint)> accessKeys, + Queue accessLog, + int accessLogLimit) { Enabled = enabled; DenyTags = denyTags; AccessLists = accessLists; + AccessListsOriginal = accessListsOriginal; AccessKeys = accessKeys; AccessLog = accessLog; AccessLogLimit = accessLogLimit; } } +/// +/// Raised after the settings on the access reader are changed. +/// public sealed class AccessReaderConfigurationChangedEvent : EntityEventArgs; + +/// +/// Raised before the settings on the access reader are changed. Can be cancelled. +/// +public sealed class AccessReaderConfigurationAttemptEvent : CancellableEntityEventArgs; diff --git a/Content.Shared/Access/Components/ShowAccessReaderSettingsComponent.cs b/Content.Shared/Access/Components/ShowAccessReaderSettingsComponent.cs new file mode 100644 index 0000000000..6f72a41f94 --- /dev/null +++ b/Content.Shared/Access/Components/ShowAccessReaderSettingsComponent.cs @@ -0,0 +1,16 @@ +using Content.Shared.Inventory; +using Robust.Shared.GameStates; + +namespace Content.Shared.Access.Components; + +/// +/// This component allows you to see whether an access reader's settings have been modified. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class ShowAccessReaderSettingsComponent : Component, IClothingSlots +{ + /// + /// Determines from which equipment slots this entity can provide its benefits. + /// + public SlotFlags Slots { get; set; } = ~SlotFlags.POCKET; +} diff --git a/Content.Shared/Access/Systems/AccessReaderSystem.cs b/Content.Shared/Access/Systems/AccessReaderSystem.cs index 186aef5305..56aa0550cf 100644 --- a/Content.Shared/Access/Systems/AccessReaderSystem.cs +++ b/Content.Shared/Access/Systems/AccessReaderSystem.cs @@ -1,12 +1,15 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Text; using Content.Shared.Access.Components; using Content.Shared.DeviceLinking.Events; using Content.Shared.Emag.Systems; +using Content.Shared.Examine; using Content.Shared.GameTicking; using Content.Shared.Hands.EntitySystems; using Content.Shared.IdentityManagement; using Content.Shared.Inventory; +using Content.Shared.Localizations; using Content.Shared.NameIdentifier; using Content.Shared.PDA; using Content.Shared.StationRecords; @@ -37,17 +40,74 @@ public sealed class AccessReaderSystem : EntitySystem { base.Initialize(); + SubscribeLocalEvent(OnExamined); SubscribeLocalEvent(OnEmagged); SubscribeLocalEvent(OnLinkAttempt); + SubscribeLocalEvent(OnConfigurationAttempt); SubscribeLocalEvent(OnGetState); SubscribeLocalEvent(OnHandleState); } + private void OnExamined(Entity ent, ref ExaminedEvent args) + { + if (!GetMainAccessReader(ent, out var mainAccessReader)) + return; + + mainAccessReader.Value.Comp.AccessListsOriginal ??= new(mainAccessReader.Value.Comp.AccessLists); + + var accessHasBeenModified = mainAccessReader.Value.Comp.AccessLists.Count != mainAccessReader.Value.Comp.AccessListsOriginal.Count; + + if (!accessHasBeenModified) + { + foreach (var accessSubgroup in mainAccessReader.Value.Comp.AccessLists) + { + if (!mainAccessReader.Value.Comp.AccessListsOriginal.Any(y => y.SetEquals(accessSubgroup))) + { + accessHasBeenModified = true; + break; + } + } + } + + var canSeeAccessModification = accessHasBeenModified && + (HasComp(ent) || + _inventorySystem.TryGetInventoryEntity(args.Examiner, out _)); + + if (canSeeAccessModification) + { + var localizedCurrentNames = GetLocalizedAccessNames(mainAccessReader.Value.Comp.AccessLists); + var accessesFormatted = ContentLocalizationManager.FormatListToOr(localizedCurrentNames); + var currentSettingsMessage = localizedCurrentNames.Count > 0 + ? Loc.GetString("access-reader-access-settings-modified-message", ("access", accessesFormatted)) + : Loc.GetString("access-reader-access-settings-removed-message"); + + args.PushMarkup(currentSettingsMessage); + + return; + } + + var localizedOriginalNames = GetLocalizedAccessNames(mainAccessReader.Value.Comp.AccessListsOriginal); + + // If the string list is empty either there were no access restrictions or the localized names were invalid + if (localizedOriginalNames.Count == 0) + return; + + var originalAccessesFormatted = ContentLocalizationManager.FormatListToOr(localizedOriginalNames); + var originalSettingsMessage = Loc.GetString(mainAccessReader.Value.Comp.ExaminationText, ("access", originalAccessesFormatted)); + args.PushMarkup(originalSettingsMessage); + } + private void OnGetState(EntityUid uid, AccessReaderComponent component, ref ComponentGetState args) { - args.State = new AccessReaderComponentState(component.Enabled, component.DenyTags, component.AccessLists, - _recordsSystem.Convert(component.AccessKeys), component.AccessLog, component.AccessLogLimit); + args.State = new AccessReaderComponentState( + component.Enabled, + component.DenyTags, + component.AccessLists, + component.AccessListsOriginal, + _recordsSystem.Convert(component.AccessKeys), + component.AccessLog, + component.AccessLogLimit); } private void OnHandleState(EntityUid uid, AccessReaderComponent component, ref ComponentHandleState args) @@ -66,6 +126,7 @@ public sealed class AccessReaderSystem : EntitySystem } component.AccessLists = new(state.AccessLists); + component.AccessListsOriginal = state.AccessListsOriginal == null ? null : new(state.AccessListsOriginal); component.DenyTags = new(state.DenyTags); component.AccessLog = new(state.AccessLog); component.AccessLogLimit = state.AccessLogLimit; @@ -100,6 +161,13 @@ public sealed class AccessReaderSystem : EntitySystem Dirty(uid, reader); } + private void OnConfigurationAttempt(Entity ent, ref AccessReaderConfigurationAttemptEvent args) + { + // The first time that the access list of the reader is modified, + // make a copy of the original settings + ent.Comp.AccessListsOriginal ??= new(ent.Comp.AccessLists); + } + /// /// Searches the source for access tags /// then compares it with the all targets accesses to see if it is allowed. @@ -348,11 +416,23 @@ public sealed class AccessReaderSystem : EntitySystem #region: AccessLists API + /// + /// Tries to clear the entity's . + /// + /// The access reader entity which is having its access permissions cleared. + public void TryClearAccesses(Entity ent) + { + if (CanConfigureAccessReader(ent)) + { + ClearAccesses(ent); + } + } + /// /// Clears the entity's . /// /// The access reader entity which is having its access permissions cleared. - public void ClearAccesses(Entity ent) + private void ClearAccesses(Entity ent) { ent.Comp.AccessLists.Clear(); @@ -360,32 +440,65 @@ public sealed class AccessReaderSystem : EntitySystem RaiseLocalEvent(ent, new AccessReaderConfigurationChangedEvent()); } + /// + /// Tries to replace the access permissions in an entity's with a supplied list. + /// + /// The access reader entity which is having its list of access permissions replaced. + /// The list of access permissions replacing the original one. + public void TrySetAccesses(Entity ent, List>> accesses) + { + if (CanConfigureAccessReader(ent)) + { + SetAccesses(ent, accesses); + } + } + /// /// Replaces the access permissions in an entity's with a supplied list. /// /// The access reader entity which is having its list of access permissions replaced. /// The list of access permissions replacing the original one. - public void SetAccesses(Entity ent, List>> accesses) + private void SetAccesses(Entity ent, List>> accesses) { ent.Comp.AccessLists.Clear(); - AddAccesses(ent, accesses); } + /// + public void TrySetAccesses(Entity ent, List> accesses) + { + if (CanConfigureAccessReader(ent)) + { + SetAccesses(ent, accesses); + } + } + /// - public void SetAccesses(Entity ent, List> accesses) + private void SetAccesses(Entity ent, List> accesses) { ent.Comp.AccessLists.Clear(); - AddAccesses(ent, accesses); } + /// + /// Tries to add a collection of access permissions to an access reader entity's + /// + /// The access reader entity to which the new access permissions are being added. + /// The list of access permissions being added. + public void TryAddAccesses(Entity ent, List>> accesses) + { + if (CanConfigureAccessReader(ent)) + { + AddAccesses(ent, accesses); + } + } + /// /// Adds a collection of access permissions to an access reader entity's /// /// The access reader entity to which the new access permissions are being added. /// The list of access permissions being added. - public void AddAccesses(Entity ent, List>> accesses) + private void AddAccesses(Entity ent, List>> accesses) { foreach (var access in accesses) { @@ -396,8 +509,17 @@ public sealed class AccessReaderSystem : EntitySystem RaiseLocalEvent(ent, new AccessReaderConfigurationChangedEvent()); } + /// + public void TryAddAccesses(Entity ent, List> accesses) + { + if (CanConfigureAccessReader(ent)) + { + AddAccesses(ent, accesses); + } + } + /// - public void AddAccesses(Entity ent, List> accesses) + private void AddAccesses(Entity ent, List> accesses) { foreach (var access in accesses) { @@ -408,13 +530,27 @@ public sealed class AccessReaderSystem : EntitySystem RaiseLocalEvent(ent, new AccessReaderConfigurationChangedEvent()); } + /// + /// Tries to add an access permission to an access reader entity's + /// + /// The access reader entity to which the access permission is being added. + /// The access permission being added. + /// If true, the component will be marked as changed afterward. + public void TryAddAccess(Entity ent, HashSet> access) + { + if (CanConfigureAccessReader(ent)) + { + AddAccess(ent, access); + } + } + /// /// Adds an access permission to an access reader entity's /// /// The access reader entity to which the access permission is being added. /// The access permission being added. /// If true, the component will be marked as changed afterward. - public void AddAccess(Entity ent, HashSet> access, bool dirty = true) + private void AddAccess(Entity ent, HashSet> access, bool dirty = true) { ent.Comp.AccessLists.Add(access); @@ -425,18 +561,40 @@ public sealed class AccessReaderSystem : EntitySystem RaiseLocalEvent(ent, new AccessReaderConfigurationChangedEvent()); } + /// + public void TryAddAccess(Entity ent, ProtoId access) + { + if (CanConfigureAccessReader(ent)) + { + AddAccess(ent, access); + } + } + /// - public void AddAccess(Entity ent, ProtoId access, bool dirty = true) + private void AddAccess(Entity ent, ProtoId access, bool dirty = true) { AddAccess(ent, new HashSet>() { access }, dirty); } + /// + /// Tries to remove a collection of access permissions from an access reader entity's + /// + /// The access reader entity from which the access permissions are being removed. + /// The list of access permissions being removed. + public void TryRemoveAccesses(Entity ent, List>> accesses) + { + if (CanConfigureAccessReader(ent)) + { + RemoveAccesses(ent, accesses); + } + } + /// /// Removes a collection of access permissions from an access reader entity's /// /// The access reader entity from which the access permissions are being removed. /// The list of access permissions being removed. - public void RemoveAccesses(Entity ent, List>> accesses) + private void RemoveAccesses(Entity ent, List>> accesses) { foreach (var access in accesses) { @@ -447,8 +605,17 @@ public sealed class AccessReaderSystem : EntitySystem RaiseLocalEvent(ent, new AccessReaderConfigurationChangedEvent()); } + /// + public void TryRemoveAccesses(Entity ent, List> accesses) + { + if (CanConfigureAccessReader(ent)) + { + RemoveAccesses(ent, accesses); + } + } + /// - public void RemoveAccesses(Entity ent, List> accesses) + private void RemoveAccesses(Entity ent, List> accesses) { foreach (var access in accesses) { @@ -459,13 +626,27 @@ public sealed class AccessReaderSystem : EntitySystem RaiseLocalEvent(ent, new AccessReaderConfigurationChangedEvent()); } + /// + /// Tries to removes an access permission from an access reader entity's + /// + /// The access reader entity from which the access permission is being removed. + /// The access permission being removed. + /// If true, the component will be marked as changed afterward. + public void TryRemoveAccess(Entity ent, HashSet> access) + { + if (CanConfigureAccessReader(ent)) + { + RemoveAccess(ent, access); + } + } + /// /// Removes an access permission from an access reader entity's /// /// The access reader entity from which the access permission is being removed. /// The access permission being removed. /// If true, the component will be marked as changed afterward. - public void RemoveAccess(Entity ent, HashSet> access, bool dirty = true) + private void RemoveAccess(Entity ent, HashSet> access, bool dirty = true) { for (int i = ent.Comp.AccessLists.Count - 1; i >= 0; i--) { @@ -482,12 +663,29 @@ public sealed class AccessReaderSystem : EntitySystem RaiseLocalEvent(ent, new AccessReaderConfigurationChangedEvent()); } + /// + public void TryRemoveAccess(Entity ent, ProtoId access) + { + if (CanConfigureAccessReader(ent)) + { + RemoveAccess(ent, new HashSet>() { access }); + } + } + /// - public void RemoveAccess(Entity ent, ProtoId access, bool dirty = true) + private void RemoveAccess(Entity ent, ProtoId access, bool dirty = true) { RemoveAccess(ent, new HashSet>() { access }, dirty); } + private bool CanConfigureAccessReader(Entity ent) + { + var ev = new AccessReaderConfigurationAttemptEvent(); + RaiseLocalEvent(ent, ev); + + return !ev.Cancelled; + } + #endregion #region: AccessKeys API @@ -727,4 +925,38 @@ public sealed class AccessReaderSystem : EntitySystem Dirty(ent); } + + private List GetLocalizedAccessNames(List>> accessLists) + { + var localizedNames = new List(); + string? andSeparator = null; + + foreach (var accessHashSet in accessLists) + { + var sb = new StringBuilder(); + var accessSubset = accessHashSet.ToList(); + + // Combine the names of all access levels in the subset into a single string + foreach (var access in accessSubset) + { + var accessName = Loc.GetString("access-reader-unknown-id"); + + if (_prototype.Resolve(access, out var accessProto) && !string.IsNullOrWhiteSpace(accessProto.Name)) + accessName = Loc.GetString(accessProto.Name); + + sb.Append(Loc.GetString("access-reader-access-label", ("access", accessName))); + + if (accessSubset.IndexOf(access) < (accessSubset.Count - 1)) + { + andSeparator ??= " " + Loc.GetString("generic-and") + " "; + sb.Append(andSeparator); + } + } + + // Add this string to the list + localizedNames.Add(sb.ToString()); + } + + return localizedNames; + } } diff --git a/Content.Shared/Localizations/ContentLocalizationManager.cs b/Content.Shared/Localizations/ContentLocalizationManager.cs index 3cfe3f2a3d..7af34f01d8 100644 --- a/Content.Shared/Localizations/ContentLocalizationManager.cs +++ b/Content.Shared/Localizations/ContentLocalizationManager.cs @@ -132,7 +132,7 @@ namespace Content.Shared.Localizations <= 0 => string.Empty, 1 => list[0], 2 => $"{list[0]} or {list[1]}", - _ => $"{string.Join(" or ", list)}" + _ => $"{string.Join(", ", list.GetRange(0, list.Count - 1))}, or {list[^1]}" }; } diff --git a/Resources/Locale/en-US/access/systems/access-reader-system.ftl b/Resources/Locale/en-US/access/systems/access-reader-system.ftl index d66989f6cf..bf3bfe4d96 100644 --- a/Resources/Locale/en-US/access/systems/access-reader-system.ftl +++ b/Resources/Locale/en-US/access/systems/access-reader-system.ftl @@ -1 +1,6 @@ access-reader-unknown-id = Unknown +access-reader-access-label = [color=yellow]{$access}[/color] +access-reader-examination = Access is generally restricted to personnel with {$access} access. +access-reader-examination-functionality-restricted = {$access} access may be required to use certain functions. +access-reader-access-settings-modified-message = [italic]The access reader has been modified to accept personnel with {$access} access.[/italic] +access-reader-access-settings-removed-message = [italic]The settings on the access reader have been deleted.[/italic] \ No newline at end of file diff --git a/Resources/Locale/en-US/generic.ftl b/Resources/Locale/en-US/generic.ftl index 3504097885..cdca0f2493 100644 --- a/Resources/Locale/en-US/generic.ftl +++ b/Resources/Locale/en-US/generic.ftl @@ -4,6 +4,9 @@ generic-not-available-shorthand = N/A generic-article-a = a generic-article-an = an +generic-and = and +generic-or = or + generic-unknown = unknown generic-unknown-title = Unknown generic-error = error diff --git a/Resources/Prototypes/Entities/Clothing/Eyes/hud.yml b/Resources/Prototypes/Entities/Clothing/Eyes/hud.yml index b35109ce17..5ecabc41eb 100644 --- a/Resources/Prototypes/Entities/Clothing/Eyes/hud.yml +++ b/Resources/Prototypes/Entities/Clothing/Eyes/hud.yml @@ -30,6 +30,7 @@ damageContainers: - Inorganic - Silicon + - type: ShowAccessReaderSettings - type: entity parent: [ClothingEyesBase, ShowMedicalIcons] diff --git a/Resources/Prototypes/Entities/Structures/Doors/Firelocks/firelock.yml b/Resources/Prototypes/Entities/Structures/Doors/Firelocks/firelock.yml index 489411bc28..bde406f5cb 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Firelocks/firelock.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Firelocks/firelock.yml @@ -132,6 +132,7 @@ price: 150 - type: AccessReader access: [ [ "Engineering" ] ] + examinationText: access-reader-examination-functionality-restricted - type: PryUnpowered pryModifier: 0.5 - type: PointLight diff --git a/Resources/Prototypes/Entities/Structures/Machines/holopad.yml b/Resources/Prototypes/Entities/Structures/Machines/holopad.yml index 95a1fba489..8c68710d76 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/holopad.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/holopad.yml @@ -76,6 +76,7 @@ speakerVolume: Speak - type: AccessReader access: [[ "Command" ]] + examinationText: access-reader-examination-functionality-restricted - type: ActivatableUI key: enum.HolopadUiKey.InteractionWindow - type: ActivatableUIRequiresPower diff --git a/Resources/Prototypes/Entities/Structures/Storage/Closets/Lockers/lockers.yml b/Resources/Prototypes/Entities/Structures/Storage/Closets/Lockers/lockers.yml index 52f29168fd..0fd9f1fab5 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/Closets/Lockers/lockers.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/Closets/Lockers/lockers.yml @@ -416,6 +416,7 @@ type: GenpopLockerBoundUserInterface - type: AccessReader # note! this access is for the UI, not the door. door access is handled on GenpopLocker access: [["Security"]] + examinationText: access-reader-examination-functionality-restricted - type: Lock locked: false useAccess: false diff --git a/Resources/Prototypes/Entities/Structures/cryogenic_sleep_unit.yml b/Resources/Prototypes/Entities/Structures/cryogenic_sleep_unit.yml index 7458cd2b69..131caec4a3 100644 --- a/Resources/Prototypes/Entities/Structures/cryogenic_sleep_unit.yml +++ b/Resources/Prototypes/Entities/Structures/cryogenic_sleep_unit.yml @@ -23,6 +23,7 @@ - type: AccessReader breakOnAccessBreaker: false access: [["Cryogenics"]] + examinationText: access-reader-examination-functionality-restricted - type: InteractionOutline - type: Cryostorage - type: Fixtures