Devices with access restrictions list those restrictions in their examination description (#37712)

This commit is contained in:
chromiumboy
2025-09-15 02:19:25 -05:00
committed by GitHub
parent ff94d3e7ad
commit 02061592dd
14 changed files with 324 additions and 27 deletions

View File

@@ -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<ProtoId<AccessLevelPrototype>> { "A" }, reader), Is.True);
@@ -62,10 +62,10 @@ namespace Content.IntegrationTests.Tests.Access
Assert.That(system.AreAccessTagsAllowed(new List<ProtoId<AccessLevelPrototype>> { "A", "B" }, reader), Is.True);
Assert.That(system.AreAccessTagsAllowed(Array.Empty<ProtoId<AccessLevelPrototype>>(), reader), Is.False);
});
system.ClearAccesses(reader);
system.TryClearAccesses(reader);
// test one list - two items
system.AddAccess(reader, new HashSet<ProtoId<AccessLevelPrototype>> { "A", "B" });
system.TryAddAccess(reader, new HashSet<ProtoId<AccessLevelPrototype>> { "A", "B" });
Assert.Multiple(() =>
{
Assert.That(system.AreAccessTagsAllowed(new List<ProtoId<AccessLevelPrototype>> { "A" }, reader), Is.False);
@@ -73,14 +73,14 @@ namespace Content.IntegrationTests.Tests.Access
Assert.That(system.AreAccessTagsAllowed(new List<ProtoId<AccessLevelPrototype>> { "A", "B" }, reader), Is.True);
Assert.That(system.AreAccessTagsAllowed(Array.Empty<ProtoId<AccessLevelPrototype>>(), reader), Is.False);
});
system.ClearAccesses(reader);
system.TryClearAccesses(reader);
// test two list
var accesses = new List<HashSet<ProtoId<AccessLevelPrototype>>>() {
new HashSet<ProtoId<AccessLevelPrototype>> () { "A" },
new HashSet<ProtoId<AccessLevelPrototype>> () { "B", "C" }
};
system.AddAccesses(reader, accesses);
system.TryAddAccesses(reader, accesses);
Assert.Multiple(() =>
{
Assert.That(system.AreAccessTagsAllowed(new List<ProtoId<AccessLevelPrototype>> { "A" }, reader), Is.True);
@@ -90,10 +90,10 @@ namespace Content.IntegrationTests.Tests.Access
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);
});
system.ClearAccesses(reader);
system.TryClearAccesses(reader);
// test deny list
system.AddAccess(reader, new HashSet<ProtoId<AccessLevelPrototype>> { "A" });
system.TryAddAccess(reader, new HashSet<ProtoId<AccessLevelPrototype>> { "A" });
system.AddDenyTag(reader, "B");
Assert.Multiple(() =>
{
@@ -102,7 +102,7 @@ namespace Content.IntegrationTests.Tests.Access
Assert.That(system.AreAccessTagsAllowed(new List<ProtoId<AccessLevelPrototype>> { "A", "B" }, reader), Is.False);
Assert.That(system.AreAccessTagsAllowed(Array.Empty<ProtoId<AccessLevelPrototype>>(), reader), Is.False);
});
system.ClearAccesses(reader);
system.TryClearAccesses(reader);
system.ClearDenyTags(reader);
});
await pair.CleanReturnAsync();

View File

@@ -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);

View File

@@ -48,7 +48,7 @@ public sealed class DoorElectronicsSystem : EntitySystem
DoorElectronicsUpdateConfigurationMessage args)
{
var accessReader = EnsureComp<AccessReaderComponent>(uid);
_accessReader.SetAccesses((uid, accessReader), args.AccessList);
_accessReader.TrySetAccesses((uid, accessReader), args.AccessList);
}
private void OnAccessReaderChanged(

View File

@@ -34,6 +34,15 @@ public sealed partial class AccessReaderComponent : Component
[DataField("access")]
public List<HashSet<ProtoId<AccessLevelPrototype>>> AccessLists = new();
/// <summary>
/// An unmodified copy of the original list of the access groups that grant access to this reader.
/// </summary>
/// <remarks>
/// If null, the access lists of this entity have not been modified yet.
/// </remarks>
[DataField]
public List<HashSet<ProtoId<AccessLevelPrototype>>>? AccessListsOriginal = null;
/// <summary>
/// A list of <see cref="StationRecordKey"/>s that grant access. Only a single matching key is required to gain access.
/// </summary>
@@ -76,6 +85,16 @@ public sealed partial class AccessReaderComponent : Component
/// </summary>
[DataField]
public bool BreakOnAccessBreaker = true;
/// <summary>
/// The examination text associated with this component.
/// </summary>
/// <remarks>
/// The text can be supplied with the 'access' variable to populate it
/// with a comma separated list of the access levels contained in <see cref="AccessLists"/>.
/// </remarks>
[DataField]
public LocId ExaminationText = "access-reader-examination";
}
[DataDefinition, Serializable, NetSerializable]
@@ -96,19 +115,36 @@ public sealed class AccessReaderComponentState : ComponentState
public bool Enabled;
public HashSet<ProtoId<AccessLevelPrototype>> DenyTags;
public List<HashSet<ProtoId<AccessLevelPrototype>>> AccessLists;
public List<HashSet<ProtoId<AccessLevelPrototype>>>? AccessListsOriginal;
public List<(NetEntity, uint)> AccessKeys;
public Queue<AccessRecord> AccessLog;
public int AccessLogLimit;
public AccessReaderComponentState(bool enabled, HashSet<ProtoId<AccessLevelPrototype>> denyTags, List<HashSet<ProtoId<AccessLevelPrototype>>> accessLists, List<(NetEntity, uint)> accessKeys, Queue<AccessRecord> accessLog, int accessLogLimit)
public AccessReaderComponentState(
bool enabled,
HashSet<ProtoId<AccessLevelPrototype>> denyTags,
List<HashSet<ProtoId<AccessLevelPrototype>>> accessLists,
List<HashSet<ProtoId<AccessLevelPrototype>>>? accessListsOriginal,
List<(NetEntity, uint)> accessKeys,
Queue<AccessRecord> accessLog,
int accessLogLimit)
{
Enabled = enabled;
DenyTags = denyTags;
AccessLists = accessLists;
AccessListsOriginal = accessListsOriginal;
AccessKeys = accessKeys;
AccessLog = accessLog;
AccessLogLimit = accessLogLimit;
}
}
/// <summary>
/// Raised after the settings on the access reader are changed.
/// </summary>
public sealed class AccessReaderConfigurationChangedEvent : EntityEventArgs;
/// <summary>
/// Raised before the settings on the access reader are changed. Can be cancelled.
/// </summary>
public sealed class AccessReaderConfigurationAttemptEvent : CancellableEntityEventArgs;

View File

@@ -0,0 +1,16 @@
using Content.Shared.Inventory;
using Robust.Shared.GameStates;
namespace Content.Shared.Access.Components;
/// <summary>
/// This component allows you to see whether an access reader's settings have been modified.
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed partial class ShowAccessReaderSettingsComponent : Component, IClothingSlots
{
/// <summary>
/// Determines from which equipment slots this entity can provide its benefits.
/// </summary>
public SlotFlags Slots { get; set; } = ~SlotFlags.POCKET;
}

View File

@@ -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<AccessReaderComponent, ExaminedEvent>(OnExamined);
SubscribeLocalEvent<AccessReaderComponent, GotEmaggedEvent>(OnEmagged);
SubscribeLocalEvent<AccessReaderComponent, LinkAttemptEvent>(OnLinkAttempt);
SubscribeLocalEvent<AccessReaderComponent, AccessReaderConfigurationAttemptEvent>(OnConfigurationAttempt);
SubscribeLocalEvent<AccessReaderComponent, ComponentGetState>(OnGetState);
SubscribeLocalEvent<AccessReaderComponent, ComponentHandleState>(OnHandleState);
}
private void OnExamined(Entity<AccessReaderComponent> 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<ShowAccessReaderSettingsComponent>(ent) ||
_inventorySystem.TryGetInventoryEntity<ShowAccessReaderSettingsComponent>(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<AccessReaderComponent> 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);
}
/// <summary>
/// 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
/// <summary>
/// Tries to clear the entity's <see cref="AccessReaderComponent.AccessLists"/>.
/// </summary>
/// <param name="ent">The access reader entity which is having its access permissions cleared.</param>
public void TryClearAccesses(Entity<AccessReaderComponent> ent)
{
if (CanConfigureAccessReader(ent))
{
ClearAccesses(ent);
}
}
/// <summary>
/// Clears the entity's <see cref="AccessReaderComponent.AccessLists"/>.
/// </summary>
/// <param name="ent">The access reader entity which is having its access permissions cleared.</param>
public void ClearAccesses(Entity<AccessReaderComponent> ent)
private void ClearAccesses(Entity<AccessReaderComponent> ent)
{
ent.Comp.AccessLists.Clear();
@@ -360,32 +440,65 @@ public sealed class AccessReaderSystem : EntitySystem
RaiseLocalEvent(ent, new AccessReaderConfigurationChangedEvent());
}
/// <summary>
/// Tries to replace the access permissions in an entity's <see cref="AccessReaderComponent.AccessLists"/> with a supplied list.
/// </summary>
/// <param name="ent">The access reader entity which is having its list of access permissions replaced.</param>
/// <param name="accesses">The list of access permissions replacing the original one.</param>
public void TrySetAccesses(Entity<AccessReaderComponent> ent, List<HashSet<ProtoId<AccessLevelPrototype>>> accesses)
{
if (CanConfigureAccessReader(ent))
{
SetAccesses(ent, accesses);
}
}
/// <summary>
/// Replaces the access permissions in an entity's <see cref="AccessReaderComponent.AccessLists"/> with a supplied list.
/// </summary>
/// <param name="ent">The access reader entity which is having its list of access permissions replaced.</param>
/// <param name="accesses">The list of access permissions replacing the original one.</param>
public void SetAccesses(Entity<AccessReaderComponent> ent, List<HashSet<ProtoId<AccessLevelPrototype>>> accesses)
private void SetAccesses(Entity<AccessReaderComponent> ent, List<HashSet<ProtoId<AccessLevelPrototype>>> accesses)
{
ent.Comp.AccessLists.Clear();
AddAccesses(ent, accesses);
}
/// <inheritdoc cref = "TrySetAccesses"/>
public void TrySetAccesses(Entity<AccessReaderComponent> ent, List<ProtoId<AccessLevelPrototype>> accesses)
{
if (CanConfigureAccessReader(ent))
{
SetAccesses(ent, accesses);
}
}
/// <inheritdoc cref = "SetAccesses"/>
public void SetAccesses(Entity<AccessReaderComponent> ent, List<ProtoId<AccessLevelPrototype>> accesses)
private void SetAccesses(Entity<AccessReaderComponent> ent, List<ProtoId<AccessLevelPrototype>> accesses)
{
ent.Comp.AccessLists.Clear();
AddAccesses(ent, accesses);
}
/// <summary>
/// Tries to add a collection of access permissions to an access reader entity's <see cref="AccessReaderComponent.AccessLists"/>
/// </summary>
/// <param name="ent">The access reader entity to which the new access permissions are being added.</param>
/// <param name="accesses">The list of access permissions being added.</param>
public void TryAddAccesses(Entity<AccessReaderComponent> ent, List<HashSet<ProtoId<AccessLevelPrototype>>> accesses)
{
if (CanConfigureAccessReader(ent))
{
AddAccesses(ent, accesses);
}
}
/// <summary>
/// Adds a collection of access permissions to an access reader entity's <see cref="AccessReaderComponent.AccessLists"/>
/// </summary>
/// <param name="ent">The access reader entity to which the new access permissions are being added.</param>
/// <param name="accesses">The list of access permissions being added.</param>
public void AddAccesses(Entity<AccessReaderComponent> ent, List<HashSet<ProtoId<AccessLevelPrototype>>> accesses)
private void AddAccesses(Entity<AccessReaderComponent> ent, List<HashSet<ProtoId<AccessLevelPrototype>>> accesses)
{
foreach (var access in accesses)
{
@@ -396,8 +509,17 @@ public sealed class AccessReaderSystem : EntitySystem
RaiseLocalEvent(ent, new AccessReaderConfigurationChangedEvent());
}
/// <inheritdoc cref = "TryAddAccesses"/>
public void TryAddAccesses(Entity<AccessReaderComponent> ent, List<ProtoId<AccessLevelPrototype>> accesses)
{
if (CanConfigureAccessReader(ent))
{
AddAccesses(ent, accesses);
}
}
/// <inheritdoc cref = "AddAccesses"/>
public void AddAccesses(Entity<AccessReaderComponent> ent, List<ProtoId<AccessLevelPrototype>> accesses)
private void AddAccesses(Entity<AccessReaderComponent> ent, List<ProtoId<AccessLevelPrototype>> accesses)
{
foreach (var access in accesses)
{
@@ -408,13 +530,27 @@ public sealed class AccessReaderSystem : EntitySystem
RaiseLocalEvent(ent, new AccessReaderConfigurationChangedEvent());
}
/// <summary>
/// Tries to add an access permission to an access reader entity's <see cref="AccessReaderComponent.AccessLists"/>
/// </summary>
/// <param name="ent">The access reader entity to which the access permission is being added.</param>
/// <param name="access">The access permission being added.</param>
/// <param name="dirty">If true, the component will be marked as changed afterward.</param>
public void TryAddAccess(Entity<AccessReaderComponent> ent, HashSet<ProtoId<AccessLevelPrototype>> access)
{
if (CanConfigureAccessReader(ent))
{
AddAccess(ent, access);
}
}
/// <summary>
/// Adds an access permission to an access reader entity's <see cref="AccessReaderComponent.AccessLists"/>
/// </summary>
/// <param name="ent">The access reader entity to which the access permission is being added.</param>
/// <param name="access">The access permission being added.</param>
/// <param name="dirty">If true, the component will be marked as changed afterward.</param>
public void AddAccess(Entity<AccessReaderComponent> ent, HashSet<ProtoId<AccessLevelPrototype>> access, bool dirty = true)
private void AddAccess(Entity<AccessReaderComponent> ent, HashSet<ProtoId<AccessLevelPrototype>> access, bool dirty = true)
{
ent.Comp.AccessLists.Add(access);
@@ -425,18 +561,40 @@ public sealed class AccessReaderSystem : EntitySystem
RaiseLocalEvent(ent, new AccessReaderConfigurationChangedEvent());
}
/// <inheritdoc cref = "TryAddAccess"/>
public void TryAddAccess(Entity<AccessReaderComponent> ent, ProtoId<AccessLevelPrototype> access)
{
if (CanConfigureAccessReader(ent))
{
AddAccess(ent, access);
}
}
/// <inheritdoc cref = "AddAccess"/>
public void AddAccess(Entity<AccessReaderComponent> ent, ProtoId<AccessLevelPrototype> access, bool dirty = true)
private void AddAccess(Entity<AccessReaderComponent> ent, ProtoId<AccessLevelPrototype> access, bool dirty = true)
{
AddAccess(ent, new HashSet<ProtoId<AccessLevelPrototype>>() { access }, dirty);
}
/// <summary>
/// Tries to remove a collection of access permissions from an access reader entity's <see cref="AccessReaderComponent.AccessLists"/>
/// </summary>
/// <param name="ent">The access reader entity from which the access permissions are being removed.</param>
/// <param name="accesses">The list of access permissions being removed.</param>
public void TryRemoveAccesses(Entity<AccessReaderComponent> ent, List<HashSet<ProtoId<AccessLevelPrototype>>> accesses)
{
if (CanConfigureAccessReader(ent))
{
RemoveAccesses(ent, accesses);
}
}
/// <summary>
/// Removes a collection of access permissions from an access reader entity's <see cref="AccessReaderComponent.AccessLists"/>
/// </summary>
/// <param name="ent">The access reader entity from which the access permissions are being removed.</param>
/// <param name="accesses">The list of access permissions being removed.</param>
public void RemoveAccesses(Entity<AccessReaderComponent> ent, List<HashSet<ProtoId<AccessLevelPrototype>>> accesses)
private void RemoveAccesses(Entity<AccessReaderComponent> ent, List<HashSet<ProtoId<AccessLevelPrototype>>> accesses)
{
foreach (var access in accesses)
{
@@ -447,8 +605,17 @@ public sealed class AccessReaderSystem : EntitySystem
RaiseLocalEvent(ent, new AccessReaderConfigurationChangedEvent());
}
/// <inheritdoc cref = "TryRemoveAccesses"/>
public void TryRemoveAccesses(Entity<AccessReaderComponent> ent, List<ProtoId<AccessLevelPrototype>> accesses)
{
if (CanConfigureAccessReader(ent))
{
RemoveAccesses(ent, accesses);
}
}
/// <inheritdoc cref = "RemoveAccesses"/>
public void RemoveAccesses(Entity<AccessReaderComponent> ent, List<ProtoId<AccessLevelPrototype>> accesses)
private void RemoveAccesses(Entity<AccessReaderComponent> ent, List<ProtoId<AccessLevelPrototype>> accesses)
{
foreach (var access in accesses)
{
@@ -459,13 +626,27 @@ public sealed class AccessReaderSystem : EntitySystem
RaiseLocalEvent(ent, new AccessReaderConfigurationChangedEvent());
}
/// <summary>
/// Tries to removes an access permission from an access reader entity's <see cref="AccessReaderComponent.AccessLists"/>
/// </summary>
/// <param name="ent">The access reader entity from which the access permission is being removed.</param>
/// <param name="access">The access permission being removed.</param>
/// <param name="dirty">If true, the component will be marked as changed afterward.</param>
public void TryRemoveAccess(Entity<AccessReaderComponent> ent, HashSet<ProtoId<AccessLevelPrototype>> access)
{
if (CanConfigureAccessReader(ent))
{
RemoveAccess(ent, access);
}
}
/// <summary>
/// Removes an access permission from an access reader entity's <see cref="AccessReaderComponent.AccessLists"/>
/// </summary>
/// <param name="ent">The access reader entity from which the access permission is being removed.</param>
/// <param name="access">The access permission being removed.</param>
/// <param name="dirty">If true, the component will be marked as changed afterward.</param>
public void RemoveAccess(Entity<AccessReaderComponent> ent, HashSet<ProtoId<AccessLevelPrototype>> access, bool dirty = true)
private void RemoveAccess(Entity<AccessReaderComponent> ent, HashSet<ProtoId<AccessLevelPrototype>> 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());
}
/// <inheritdoc cref = "TryRemoveAccess"/>
public void TryRemoveAccess(Entity<AccessReaderComponent> ent, ProtoId<AccessLevelPrototype> access)
{
if (CanConfigureAccessReader(ent))
{
RemoveAccess(ent, new HashSet<ProtoId<AccessLevelPrototype>>() { access });
}
}
/// <inheritdoc cref = "RemoveAccess"/>
public void RemoveAccess(Entity<AccessReaderComponent> ent, ProtoId<AccessLevelPrototype> access, bool dirty = true)
private void RemoveAccess(Entity<AccessReaderComponent> ent, ProtoId<AccessLevelPrototype> access, bool dirty = true)
{
RemoveAccess(ent, new HashSet<ProtoId<AccessLevelPrototype>>() { access }, dirty);
}
private bool CanConfigureAccessReader(Entity<AccessReaderComponent> 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<string> GetLocalizedAccessNames(List<HashSet<ProtoId<AccessLevelPrototype>>> accessLists)
{
var localizedNames = new List<string>();
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;
}
}

View File

@@ -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]}"
};
}

View File

@@ -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]

View File

@@ -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

View File

@@ -30,6 +30,7 @@
damageContainers:
- Inorganic
- Silicon
- type: ShowAccessReaderSettings
- type: entity
parent: [ClothingEyesBase, ShowMedicalIcons]

View File

@@ -132,6 +132,7 @@
price: 150
- type: AccessReader
access: [ [ "Engineering" ] ]
examinationText: access-reader-examination-functionality-restricted
- type: PryUnpowered
pryModifier: 0.5
- type: PointLight

View File

@@ -76,6 +76,7 @@
speakerVolume: Speak
- type: AccessReader
access: [[ "Command" ]]
examinationText: access-reader-examination-functionality-restricted
- type: ActivatableUI
key: enum.HolopadUiKey.InteractionWindow
- type: ActivatableUIRequiresPower

View File

@@ -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

View File

@@ -23,6 +23,7 @@
- type: AccessReader
breakOnAccessBreaker: false
access: [["Cryogenics"]]
examinationText: access-reader-examination-functionality-restricted
- type: InteractionOutline
- type: Cryostorage
- type: Fixtures