+++ /dev/null
-using Content.Shared.IdentityManagement;
-
-namespace Content.Client.IdentityManagement;
-
-public sealed class IdentitySystem : SharedIdentitySystem
-{
-}
using System.Linq;
using Content.Server.Emp;
-using Content.Server.IdentityManagement;
using Content.Shared.Clothing.Components;
using Content.Shared.Clothing.EntitySystems;
using Content.Shared.Emp;
+using Content.Shared.IdentityManagement;
using Content.Shared.IdentityManagement.Components;
using Content.Shared.Inventory;
using Content.Shared.Prototypes;
mob = user;
return true;
}
-
- /// <summary>
- /// Checks if the new identity's name has a criminal record attached to it, and gives the entity the icon that
- /// belongs to the status if it does.
- /// </summary>
- public void CheckNewIdentity(EntityUid uid)
- {
- var name = Identity.Name(uid, EntityManager);
- var xform = Transform(uid);
-
- // TODO use the entity's station? Not the station of the map that it happens to currently be on?
- var station = _station.GetStationInMap(xform.MapID);
-
- if (station != null && _records.GetRecordByName(station.Value, name) is { } id)
- {
- if (_records.TryGetRecord<CriminalRecord>(new StationRecordKey(id, station.Value),
- out var record))
- {
- if (record.Status != SecurityStatus.None)
- {
- _criminalRecords.SetCriminalIcon(name, record.Status, uid);
- return;
- }
- }
- }
- RemComp<CriminalRecordComponent>(uid);
- }
}
using Content.Shared.Delivery;
using Content.Shared.Power.EntitySystems;
-using Content.Server.StationRecords;
using Content.Shared.EntityTable;
+using Content.Shared.StationRecords;
using Robust.Shared.Random;
using Robust.Shared.Timing;
+++ /dev/null
-using Content.Server.Access.Systems;
-using Content.Server.Administration.Logs;
-using Content.Server.CriminalRecords.Systems;
-using Content.Server.Humanoid;
-using Content.Shared.Clothing;
-using Content.Shared.Database;
-using Content.Shared.Hands;
-using Content.Shared.Humanoid;
-using Content.Shared.IdentityManagement;
-using Content.Shared.IdentityManagement.Components;
-using Content.Shared.Inventory;
-using Content.Shared.Inventory.Events;
-using Robust.Shared.Containers;
-using Robust.Shared.Enums;
-using Robust.Shared.GameObjects.Components.Localization;
-
-namespace Content.Server.IdentityManagement;
-
-/// <summary>
-/// Responsible for updating the identity of an entity on init or clothing equip/unequip.
-/// </summary>
-public sealed class IdentitySystem : SharedIdentitySystem
-{
- [Dependency] private readonly IdCardSystem _idCard = default!;
- [Dependency] private readonly IAdminLogManager _adminLog = default!;
- [Dependency] private readonly MetaDataSystem _metaData = default!;
- [Dependency] private readonly SharedContainerSystem _container = default!;
- [Dependency] private readonly HumanoidAppearanceSystem _humanoid = default!;
- [Dependency] private readonly CriminalRecordsConsoleSystem _criminalRecordsConsole = default!;
- [Dependency] private readonly GrammarSystem _grammarSystem = default!;
-
- private HashSet<EntityUid> _queuedIdentityUpdates = new();
-
- public override void Initialize()
- {
- base.Initialize();
-
- SubscribeLocalEvent<IdentityComponent, DidEquipEvent>((uid, _, _) => QueueIdentityUpdate(uid));
- SubscribeLocalEvent<IdentityComponent, DidEquipHandEvent>((uid, _, _) => QueueIdentityUpdate(uid));
- SubscribeLocalEvent<IdentityComponent, DidUnequipEvent>((uid, _, _) => QueueIdentityUpdate(uid));
- SubscribeLocalEvent<IdentityComponent, DidUnequipHandEvent>((uid, _, _) => QueueIdentityUpdate(uid));
- SubscribeLocalEvent<IdentityComponent, WearerMaskToggledEvent>((uid, _, _) => QueueIdentityUpdate(uid));
- SubscribeLocalEvent<IdentityComponent, EntityRenamedEvent>((uid, _, _) => QueueIdentityUpdate(uid));
- SubscribeLocalEvent<IdentityComponent, MapInitEvent>(OnMapInit);
- }
-
- public override void Update(float frameTime)
- {
- base.Update(frameTime);
-
- foreach (var ent in _queuedIdentityUpdates)
- {
- if (!TryComp<IdentityComponent>(ent, out var identity))
- continue;
-
- UpdateIdentityInfo(ent, identity);
- }
-
- _queuedIdentityUpdates.Clear();
- }
-
- // This is where the magic happens
- private void OnMapInit(EntityUid uid, IdentityComponent component, MapInitEvent args)
- {
- var ident = Spawn(null, Transform(uid).Coordinates);
-
- _metaData.SetEntityName(ident, "identity");
- QueueIdentityUpdate(uid);
- _container.Insert(ident, component.IdentityEntitySlot);
- }
-
- /// <summary>
- /// Queues an identity update to the start of the next tick.
- /// </summary>
- public override void QueueIdentityUpdate(EntityUid uid)
- {
- _queuedIdentityUpdates.Add(uid);
- }
-
- #region Private API
-
- /// <summary>
- /// Updates the metadata name for the id(entity) from the current state of the character.
- /// </summary>
- private void UpdateIdentityInfo(EntityUid uid, IdentityComponent identity)
- {
- if (identity.IdentityEntitySlot.ContainedEntity is not { } ident)
- return;
-
- var representation = GetIdentityRepresentation(uid);
- var name = GetIdentityName(uid, representation);
-
- // Clone the old entity's grammar to the identity entity, for loc purposes.
- if (TryComp<GrammarComponent>(uid, out var grammar))
- {
- var identityGrammar = EnsureComp<GrammarComponent>(ident);
- identityGrammar.Attributes.Clear();
-
- foreach (var (k, v) in grammar.Attributes)
- {
- identityGrammar.Attributes.Add(k, v);
- }
-
- // If presumed name is null and we're using that, we set proper noun to be false ("the old woman")
- if (name != representation.TrueName && representation.PresumedName == null)
- _grammarSystem.SetProperNoun((ident, identityGrammar), false);
-
- Dirty(ident, identityGrammar);
- }
-
- if (name == Name(ident))
- return;
-
- _metaData.SetEntityName(ident, name);
-
- _adminLog.Add(LogType.Identity, LogImpact.Medium, $"{ToPrettyString(uid)} changed identity to {name}");
- var identityChangedEvent = new IdentityChangedEvent(uid, ident);
- RaiseLocalEvent(uid, ref identityChangedEvent);
- SetIdentityCriminalIcon(uid);
- }
-
- private string GetIdentityName(EntityUid target, IdentityRepresentation representation)
- {
- var ev = new SeeIdentityAttemptEvent();
-
- RaiseLocalEvent(target, ev);
- return representation.ToStringKnown(!ev.Cancelled);
- }
-
- /// <summary>
- /// When the identity of a person is changed, searches the criminal records to see if the name of the new identity
- /// has a record. If the new name has a criminal status attached to it, the person will get the criminal status
- /// until they change identity again.
- /// </summary>
- private void SetIdentityCriminalIcon(EntityUid uid)
- {
- _criminalRecordsConsole.CheckNewIdentity(uid);
- }
-
- /// <summary>
- /// Gets an 'identity representation' of an entity, with their true name being the entity name
- /// and their 'presumed name' and 'presumed job' being the name/job on their ID card, if they have one.
- /// </summary>
- private IdentityRepresentation GetIdentityRepresentation(EntityUid target,
- InventoryComponent? inventory=null,
- HumanoidAppearanceComponent? appearance=null)
- {
- int age = 18;
- Gender gender = Gender.Epicene;
- string species = SharedHumanoidAppearanceSystem.DefaultSpecies;
-
- // Always use their actual age and gender, since that can't really be changed by an ID.
- if (Resolve(target, ref appearance, false))
- {
- gender = appearance.Gender;
- age = appearance.Age;
- species = appearance.Species;
- }
-
- var ageString = _humanoid.GetAgeRepresentation(species, age);
- var trueName = Name(target);
- if (!Resolve(target, ref inventory, false))
- return new(trueName, gender, ageString, string.Empty);
-
- string? presumedJob = null;
- string? presumedName = null;
-
- // Get their name and job from their ID for their presumed name.
- if (_idCard.TryFindIdCard(target, out var id))
- {
- presumedName = string.IsNullOrWhiteSpace(id.Comp.FullName) ? null : id.Comp.FullName;
- presumedJob = id.Comp.LocalizedJobTitle?.ToLowerInvariant();
- }
-
- // If it didn't find a job, that's fine.
- return new(trueName, gender, ageString, presumedName, presumedJob);
- }
-
- #endregion
-}
using Content.Server.Access.Systems;
using Content.Server.Humanoid;
-using Content.Server.IdentityManagement;
using Content.Server.Mind;
using Content.Server.PDA;
using Content.Server.Station.Components;
using Content.Shared.DetailExaminable;
using Content.Shared.Humanoid;
using Content.Shared.Humanoid.Prototypes;
+using Content.Shared.IdentityManagement;
using Content.Shared.PDA;
using Content.Shared.Preferences;
using Content.Shared.Preferences.Loadouts;
return false;
}
- /// <summary>
- /// Try to get a record from this station's record entries,
- /// from the provided station record key. Will always return
- /// null if the key does not match the station.
- /// </summary>
- /// <param name="key">Station and key to try and index from the record set.</param>
- /// <param name="entry">The resulting entry.</param>
- /// <param name="records">Station record component.</param>
- /// <typeparam name="T">Type to get from the record set.</typeparam>
- /// <returns>True if the record was obtained, false otherwise.</returns>
- public bool TryGetRecord<T>(StationRecordKey key, [NotNullWhen(true)] out T? entry, StationRecordsComponent? records = null)
- {
- entry = default;
-
- if (!Resolve(key.OriginStation, ref records))
- return false;
-
- return records.Records.TryGetRecordEntry(key.Id, out entry);
- }
-
/// <summary>
/// Gets a random record from the station's record entries.
/// </summary>
return ent.Comp.Records.TryGetRecordEntry(key, out entry);
}
- /// <summary>
- /// Returns an id if a record with the same name exists.
- /// </summary>
- /// <remarks>
- /// Linear search so O(n) time complexity.
- /// </remarks>
- public uint? GetRecordByName(EntityUid station, string name, StationRecordsComponent? records = null)
- {
- if (!Resolve(station, ref records, false))
- return null;
-
- foreach (var (id, record) in GetRecordsOfType<GeneralStationRecord>(station, records))
- {
- if (record.Name == name)
- return id;
- }
-
- return null;
- }
-
/// <summary>
/// Get the name for a record, or an empty string if it has no record.
/// </summary>
return record.Name;
}
- /// <summary>
- /// Gets all records of a specific type from a station.
- /// </summary>
- /// <param name="station">The station to get the records from.</param>
- /// <param name="records">Station records component.</param>
- /// <typeparam name="T">Type of record to fetch</typeparam>
- /// <returns>Enumerable of pairs with a station record key, and the entry in question of type T.</returns>
- public IEnumerable<(uint, T)> GetRecordsOfType<T>(EntityUid station, StationRecordsComponent? records = null)
- {
- if (!Resolve(station, ref records))
- return Array.Empty<(uint, T)>();
-
- return records.Records.GetRecordsOfType<T>();
- }
-
/// <summary>
/// Adds a new record entry to a station's record set.
/// </summary>
using Content.Server.Ghost;
using Content.Server.Ghost.Roles.Components;
using Content.Server.Humanoid;
-using Content.Server.IdentityManagement;
using Content.Server.Inventory;
using Content.Server.Mind;
using Content.Server.NPC;
using Content.Shared.Traits.Assorted;
using Robust.Shared.Audio.Systems;
using Content.Shared.Ghost.Roles.Components;
+using Content.Shared.IdentityManagement;
using Content.Shared.Tag;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
+using Content.Shared.IdentityManagement;
+using Content.Shared.Security;
+using Content.Shared.Security.Components;
+using Content.Shared.Station;
+using Content.Shared.StationRecords;
+
namespace Content.Shared.CriminalRecords.Systems;
/// <summary>
/// Station records aren't predicted, just exists for access.
/// </summary>
-public abstract class SharedCriminalRecordsConsoleSystem : EntitySystem;
+public abstract class SharedCriminalRecordsConsoleSystem : EntitySystem
+{
+ [Dependency] private readonly SharedCriminalRecordsSystem _criminalRecords = default!;
+ [Dependency] private readonly SharedStationRecordsSystem _records = default!;
+ [Dependency] private readonly SharedStationSystem _station = default!;
+
+ /// <summary>
+ /// Checks if the new identity's name has a criminal record attached to it, and gives the entity the icon that
+ /// belongs to the status if it does.
+ /// </summary>
+ public void CheckNewIdentity(EntityUid uid)
+ {
+ var name = Identity.Name(uid, EntityManager);
+ var xform = Transform(uid);
+
+ // TODO use the entity's station? Not the station of the map that it happens to currently be on?
+ var station = _station.GetStationInMap(xform.MapID);
+
+ if (station != null && _records.GetRecordByName(station.Value, name) is { } id)
+ {
+ if (_records.TryGetRecord<CriminalRecord>(new StationRecordKey(id, station.Value),
+ out var record))
+ {
+ if (record.Status != SecurityStatus.None)
+ {
+ _criminalRecords.SetCriminalIcon(name, record.Status, uid);
+ return;
+ }
+ }
+ }
+ RemComp<CriminalRecordComponent>(uid);
+ }
+}
[Dependency] private readonly ISerializationManager _serManager = default!;
[Dependency] private readonly MarkingManager _markingManager = default!;
[Dependency] private readonly GrammarSystem _grammarSystem = default!;
- [Dependency] private readonly SharedIdentitySystem _identity = default!;
+ [Dependency] private readonly IdentitySystem _identity = default!;
public static readonly ProtoId<SpeciesPrototype> DefaultSpecies = "Human";
using Content.Shared.Inventory;
using Robust.Shared.GameStates;
+using Robust.Shared.Serialization;
namespace Content.Shared.IdentityManagement.Components;
-[RegisterComponent, NetworkedComponent]
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class IdentityBlockerComponent : Component
{
- [DataField]
+ [DataField, AutoNetworkedField]
public bool Enabled = true;
/// <summary>
public IdentityBlockerCoverage Coverage = IdentityBlockerCoverage.FULL;
}
+[Flags]
+[Serializable, NetSerializable]
public enum IdentityBlockerCoverage
{
NONE = 0,
using Robust.Shared.Containers;
using Robust.Shared.Enums;
+using Robust.Shared.GameStates;
namespace Content.Shared.IdentityManagement.Components;
/// <remarks>
/// This is a <see cref="ContainerSlot"/> and not just a datum entity because we do sort of care that it gets deleted and sent with the user.
/// </remarks>
-[RegisterComponent]
+[RegisterComponent, NetworkedComponent]
public sealed partial class IdentityComponent : Component
{
[ViewVariables]
--- /dev/null
+using Content.Shared.Access.Systems;
+using Content.Shared.Administration.Logs;
+using Content.Shared.Clothing;
+using Content.Shared.CriminalRecords.Systems;
+using Content.Shared.Database;
+using Content.Shared.Hands;
+using Content.Shared.Humanoid;
+using Content.Shared.IdentityManagement.Components;
+using Content.Shared.Inventory;
+using Content.Shared.Inventory.Events;
+using Robust.Shared.Containers;
+using Robust.Shared.Enums;
+using Robust.Shared.GameObjects.Components.Localization;
+using Robust.Shared.Timing;
+
+namespace Content.Shared.IdentityManagement;
+
+/// <summary>
+/// Responsible for updating the identity of an entity on init or clothing equip/unequip.
+/// </summary>
+public sealed class IdentitySystem : EntitySystem
+{
+ [Dependency] private readonly GrammarSystem _grammarSystem = default!;
+ [Dependency] private readonly IGameTiming _timing = default!;
+ [Dependency] private readonly ISharedAdminLogManager _adminLog = default!;
+ [Dependency] private readonly MetaDataSystem _metaData = default!;
+ [Dependency] private readonly SharedContainerSystem _container = default!;
+ [Dependency] private readonly SharedCriminalRecordsConsoleSystem _criminalRecordsConsole = default!;
+ [Dependency] private readonly SharedHumanoidAppearanceSystem _humanoid = default!;
+ [Dependency] private readonly SharedIdCardSystem _idCard = default!;
+
+ // The name of the container holding the identity entity
+ private const string SlotName = "identity";
+
+ // Recycled hashset for tracking identities each tick that need to update
+ private readonly HashSet<EntityUid> _queuedIdentityUpdates = new();
+
+ /// <inheritdoc />
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent<IdentityBlockerComponent, SeeIdentityAttemptEvent>(OnSeeIdentity);
+ SubscribeLocalEvent<IdentityBlockerComponent, InventoryRelayedEvent<SeeIdentityAttemptEvent>>(OnRelaySeeIdentity);
+ SubscribeLocalEvent<IdentityBlockerComponent, ItemMaskToggledEvent>(OnMaskToggled);
+
+ SubscribeLocalEvent<IdentityComponent, MapInitEvent>(OnMapInit);
+ SubscribeLocalEvent<IdentityComponent, ComponentInit>(OnComponentInit);
+
+ SubscribeLocalEvent<IdentityComponent, DidEquipEvent>((uid, _, _) => QueueIdentityUpdate(uid));
+ SubscribeLocalEvent<IdentityComponent, DidEquipHandEvent>((uid, _, _) => QueueIdentityUpdate(uid));
+ SubscribeLocalEvent<IdentityComponent, DidUnequipEvent>((uid, _, _) => QueueIdentityUpdate(uid));
+ SubscribeLocalEvent<IdentityComponent, DidUnequipHandEvent>((uid, _, _) => QueueIdentityUpdate(uid));
+ SubscribeLocalEvent<IdentityComponent, WearerMaskToggledEvent>((uid, _, _) => QueueIdentityUpdate(uid));
+ SubscribeLocalEvent<IdentityComponent, EntityRenamedEvent>((uid, _, _) => QueueIdentityUpdate(uid));
+ }
+
+ /// <summary>
+ /// Iterates through all identities that need to be updated.
+ /// </summary>
+ public override void Update(float frameTime)
+ {
+ base.Update(frameTime);
+
+ foreach (var ent in _queuedIdentityUpdates)
+ {
+ if (!TryComp<IdentityComponent>(ent, out var identity))
+ continue;
+
+ UpdateIdentityInfo((ent, identity));
+ }
+
+ _queuedIdentityUpdates.Clear();
+ }
+
+ #region Event Handlers
+
+ // Creates an identity entity, and store it in the identity container
+ private void OnMapInit(Entity<IdentityComponent> ent, ref MapInitEvent args)
+ {
+ var ident = Spawn(null, Transform(ent).Coordinates);
+
+ _metaData.SetEntityName(ident, "identity");
+ QueueIdentityUpdate(ent);
+ _container.Insert(ident, ent.Comp.IdentityEntitySlot);
+ }
+
+ private void OnComponentInit(Entity<IdentityComponent> ent, ref ComponentInit args)
+ {
+ ent.Comp.IdentityEntitySlot = _container.EnsureContainer<ContainerSlot>(ent, SlotName);
+ }
+
+ // Adds an identity blocker's coverage, and cancels the event if coverage is complete.
+ private void OnSeeIdentity(Entity<IdentityBlockerComponent> ent, ref SeeIdentityAttemptEvent args)
+ {
+ if (ent.Comp.Enabled)
+ {
+ args.TotalCoverage |= ent.Comp.Coverage;
+ if (args.TotalCoverage == IdentityBlockerCoverage.FULL)
+ args.Cancel();
+ }
+ }
+
+ private void OnRelaySeeIdentity(Entity<IdentityBlockerComponent> ent, ref InventoryRelayedEvent<SeeIdentityAttemptEvent> args)
+ {
+ OnSeeIdentity(ent, ref args.Args);
+ }
+
+ // Toggles if a mask is hiding the identity.
+ private void OnMaskToggled(Entity<IdentityBlockerComponent> ent, ref ItemMaskToggledEvent args)
+ {
+ ent.Comp.Enabled = !args.Mask.Comp.IsToggled;
+ Dirty(ent);
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Queues an identity update to the start of the next tick.
+ /// </summary>
+ public void QueueIdentityUpdate(EntityUid uid)
+ {
+ if (_timing.ApplyingState)
+ return;
+
+ _queuedIdentityUpdates.Add(uid);
+ }
+ #region Private API
+
+ /// <summary>
+ /// Updates the metadata name for the id(entity) from the current state of the character.
+ /// </summary>
+ private void UpdateIdentityInfo(Entity<IdentityComponent> ent)
+ {
+ if (ent.Comp.IdentityEntitySlot.ContainedEntity is not { } ident)
+ return;
+
+ var representation = GetIdentityRepresentation(ent.Owner);
+ var name = GetIdentityName(ent, representation);
+
+ // Clone the old entity's grammar to the identity entity, for loc purposes.
+ if (TryComp<GrammarComponent>(ent, out var grammar))
+ {
+ var identityGrammar = EnsureComp<GrammarComponent>(ident);
+ identityGrammar.Attributes.Clear();
+
+ foreach (var (k, v) in grammar.Attributes)
+ {
+ identityGrammar.Attributes.Add(k, v);
+ }
+
+ // If presumed name is null and we're using that, we set proper noun to be false ("the old woman")
+ if (name != representation.TrueName && representation.PresumedName == null)
+ _grammarSystem.SetProperNoun((ident, identityGrammar), false);
+
+ Dirty(ident, identityGrammar);
+ }
+
+ if (name == Name(ident))
+ return;
+
+ _metaData.SetEntityName(ident, name);
+
+ _adminLog.Add(LogType.Identity, LogImpact.Medium, $"{ToPrettyString(ent)} changed identity to {name}");
+ var identityChangedEvent = new IdentityChangedEvent(ent, ident);
+ RaiseLocalEvent(ent, ref identityChangedEvent);
+ SetIdentityCriminalIcon(ent);
+ }
+
+ /// <summary>
+ /// When the identity of a person is changed, searches the criminal records to see if the name of the new identity
+ /// has a record. If the new name has a criminal status attached to it, the person will get the criminal status
+ /// until they change identity again.
+ /// </summary>
+ private void SetIdentityCriminalIcon(EntityUid uid)
+ {
+ _criminalRecordsConsole.CheckNewIdentity(uid);
+ }
+
+ /// <summary>
+ /// Attempts to get an entity's name. Cancelled if the entity has full coverage from <see cref="IdentityBlockerComponent"/>.
+ /// </summary>
+ /// <param name="target">The entity being targeted.</param>
+ /// <param name="representation">The data structure containing an entity's identities.</param>
+ /// <returns>
+ /// An entity's real name if <see cref="SeeIdentityAttemptEvent"/> isn't cancelled,
+ /// or a hidden identity such as a fake ID or fully hidden identity like "middle-aged man".
+ /// </returns>
+ private string GetIdentityName(EntityUid target, IdentityRepresentation representation)
+ {
+ var ev = new SeeIdentityAttemptEvent();
+
+ RaiseLocalEvent(target, ev);
+ return representation.ToStringKnown(!ev.Cancelled);
+ }
+
+ /// <summary>
+ /// Gets an 'identity representation' of an entity, with their true name being the entity name
+ /// and their 'presumed name' and 'presumed job' being the name/job on their ID card, if they have one.
+ /// </summary>
+ private IdentityRepresentation GetIdentityRepresentation(Entity<InventoryComponent?, HumanoidAppearanceComponent?> target)
+ {
+ var age = 18;
+ var gender = Gender.Epicene;
+ var species = SharedHumanoidAppearanceSystem.DefaultSpecies;
+
+ // Always use their actual age and gender, since that can't really be changed by an ID.
+ if (Resolve(target, ref target.Comp2, false))
+ {
+ gender = target.Comp2.Gender;
+ age = target.Comp2.Age;
+ species = target.Comp2.Species;
+ }
+
+ var ageString = _humanoid.GetAgeRepresentation(species, age);
+ var trueName = Name(target);
+ if (!Resolve(target, ref target.Comp1, false))
+ return new(trueName, gender, ageString, string.Empty);
+
+ string? presumedJob = null;
+ string? presumedName = null;
+
+ // Get their name and job from their ID for their presumed name.
+ if (_idCard.TryFindIdCard(target, out var id))
+ {
+ presumedName = string.IsNullOrWhiteSpace(id.Comp.FullName) ? null : id.Comp.FullName;
+ presumedJob = id.Comp.LocalizedJobTitle?.ToLowerInvariant();
+ }
+
+ // If it didn't find a job, that's fine.
+ return new(trueName, gender, ageString, presumedName, presumedJob);
+ }
+
+ #endregion
+}
+
+/// <summary>
+/// Gets called whenever an entity changes their identity.
+/// </summary>
+[ByRefEvent]
+public record struct IdentityChangedEvent(EntityUid CharacterEntity, EntityUid IdentityEntity);
+++ /dev/null
-using Content.Shared.Clothing;
-using Content.Shared.IdentityManagement.Components;
-using Content.Shared.Inventory;
-using Robust.Shared.Containers;
-
-namespace Content.Shared.IdentityManagement;
-
-public abstract class SharedIdentitySystem : EntitySystem
-{
- [Dependency] private readonly SharedContainerSystem _container = default!;
- private static string SlotName = "identity";
-
- public override void Initialize()
- {
- base.Initialize();
-
- SubscribeLocalEvent<IdentityComponent, ComponentInit>(OnComponentInit);
- SubscribeLocalEvent<IdentityBlockerComponent, SeeIdentityAttemptEvent>(OnSeeIdentity);
- SubscribeLocalEvent<IdentityBlockerComponent, InventoryRelayedEvent<SeeIdentityAttemptEvent>>((e, c, ev) => OnSeeIdentity(e, c, ev.Args));
- SubscribeLocalEvent<IdentityBlockerComponent, ItemMaskToggledEvent>(OnMaskToggled);
- }
-
- private void OnSeeIdentity(EntityUid uid, IdentityBlockerComponent component, SeeIdentityAttemptEvent args)
- {
- if (component.Enabled)
- {
- args.TotalCoverage |= component.Coverage;
- if(args.TotalCoverage == IdentityBlockerCoverage.FULL)
- args.Cancel();
- }
- }
-
- protected virtual void OnComponentInit(EntityUid uid, IdentityComponent component, ComponentInit args)
- {
- component.IdentityEntitySlot = _container.EnsureContainer<ContainerSlot>(uid, SlotName);
- }
-
- private void OnMaskToggled(Entity<IdentityBlockerComponent> ent, ref ItemMaskToggledEvent args)
- {
- ent.Comp.Enabled = !args.Mask.Comp.IsToggled;
- }
-
- /// <summary>
- /// Queues an identity update to the start of the next tick.
- /// </summary>
- public virtual void QueueIdentityUpdate(EntityUid uid) { }
-}
-/// <summary>
-/// Gets called whenever an entity changes their identity.
-/// </summary>
-[ByRefEvent]
-public record struct IdentityChangedEvent(EntityUid CharacterEntity, EntityUid IdentityEntity);
+using System.Diagnostics.CodeAnalysis;
+
namespace Content.Shared.StationRecords;
public abstract class SharedStationRecordsSystem : EntitySystem
}
return result;
}
+
+ /// <summary>
+ /// Try to get a record from this station's record entries,
+ /// from the provided station record key. Will always return
+ /// null if the key does not match the station.
+ /// </summary>
+ /// <param name="key">Station and key to try and index from the record set.</param>
+ /// <param name="entry">The resulting entry.</param>
+ /// <param name="records">Station record component.</param>
+ /// <typeparam name="T">Type to get from the record set.</typeparam>
+ /// <returns>True if the record was obtained, false otherwise. Always false on client.</returns>
+ public bool TryGetRecord<T>(StationRecordKey key, [NotNullWhen(true)] out T? entry, StationRecordsComponent? records = null)
+ {
+ entry = default;
+
+ if (!Resolve(key.OriginStation, ref records))
+ return false;
+
+ return records.Records.TryGetRecordEntry(key.Id, out entry);
+ }
+
+ /// <summary>
+ /// Gets all records of a specific type from a station.
+ /// </summary>
+ /// <param name="station">The station to get the records from.</param>
+ /// <param name="records">Station records component.</param>
+ /// <typeparam name="T">Type of record to fetch</typeparam>
+ /// <returns>Enumerable of pairs with a station record key, and the entry in question of type T. Always empty on client.</returns>
+ public IEnumerable<(uint, T)> GetRecordsOfType<T>(EntityUid station, StationRecordsComponent? records = null)
+ {
+ if (!Resolve(station, ref records))
+ return Array.Empty<(uint, T)>();
+
+ return records.Records.GetRecordsOfType<T>();
+ }
+
+ /// <summary>
+ /// Returns an id if a record with the same name exists.
+ /// </summary>
+ /// <remarks>
+ /// Linear search so O(n) time complexity.
+ /// </remarks>
+ /// <returns>Returns a station record id. Always null on client.</returns>
+ public uint? GetRecordByName(EntityUid station, string name, StationRecordsComponent? records = null)
+ {
+ if (!Resolve(station, ref records, false))
+ return null;
+
+ foreach (var (id, record) in GetRecordsOfType<GeneralStationRecord>(station, records))
+ {
+ if (record.Name == name)
+ return id;
+ }
+
+ return null;
+ }
}
using System.Diagnostics.CodeAnalysis;
using System.Linq;
-using Content.Shared.StationRecords;
using Robust.Shared.Utility;
-namespace Content.Server.StationRecords;
+namespace Content.Shared.StationRecords;
/// <summary>
/// Set of station records for a single station. StationRecordsComponent stores these.
-using Content.Server.StationRecords.Systems;
+namespace Content.Shared.StationRecords;
-namespace Content.Server.StationRecords;
-
-[Access(typeof(StationRecordsSystem))]
+[Access(typeof(SharedStationRecordsSystem))]
[RegisterComponent]
public sealed partial class StationRecordsComponent : Component
{
{
[Dependency] private readonly MetaDataSystem _metaData = default!;
[Dependency] private readonly SharedHumanoidAppearanceSystem _humanoidAppearance = default!;
- [Dependency] private readonly SharedIdentitySystem _identity = default!;
+ [Dependency] private readonly IdentitySystem _identity = default!;
[Dependency] private readonly SharedForensicsSystem _forensics = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
[Dependency] private readonly INetManager _net = default!;