diff --git a/Content.Server/Access/Systems/IdCardSystem.cs b/Content.Server/Access/Systems/IdCardSystem.cs index b49bc55d1b..aeb4cc163f 100644 --- a/Content.Server/Access/Systems/IdCardSystem.cs +++ b/Content.Server/Access/Systems/IdCardSystem.cs @@ -24,6 +24,7 @@ public sealed class IdCardSystem : SharedIdCardSystem public override void Initialize() { base.Initialize(); + SubscribeLocalEvent(OnMicrowaved); } diff --git a/Content.Server/Cargo/Systems/CargoSystem.Orders.cs b/Content.Server/Cargo/Systems/CargoSystem.Orders.cs index f68fe0fa63..e4bd951513 100644 --- a/Content.Server/Cargo/Systems/CargoSystem.Orders.cs +++ b/Content.Server/Cargo/Systems/CargoSystem.Orders.cs @@ -8,6 +8,8 @@ using Content.Shared.Cargo.Components; using Content.Shared.Cargo.Events; using Content.Shared.Cargo.Prototypes; using Content.Shared.Database; +using Content.Shared.Emag.Components; +using Content.Shared.IdentityManagement; using Content.Shared.Interaction; using Content.Shared.Paper; using Robust.Shared.Map; @@ -175,10 +177,6 @@ namespace Content.Server.Cargo.Systems RaiseLocalEvent(ref ev); ev.FulfillmentEntity ??= station.Value; - _idCardSystem.TryFindIdCard(player, out var idCard); - // ReSharper disable once ConditionalAccessQualifierIsNonNullableAccordingToAPIContract - order.SetApproverData(idCard.Comp?.FullName, idCard.Comp?.JobTitle); - if (!ev.Handled) { ev.FulfillmentEntity = TryFulfillOrder((station.Value, stationData), order, orderDatabase); @@ -194,12 +192,20 @@ namespace Content.Server.Cargo.Systems order.Approved = true; _audio.PlayPvs(component.ConfirmSound, uid); - var message = Loc.GetString("cargo-console-unlock-approved-order-broadcast", - ("productName", Loc.GetString(order.ProductName)), - ("orderAmount", order.OrderQuantity), - ("approver", order.Approver ?? string.Empty), - ("cost", cost)); - _radio.SendRadioMessage(uid, message, component.AnnouncementChannel, uid, escapeMarkup: false); + if (!HasComp(uid)) + { + var tryGetIdentityShortInfoEvent = new TryGetIdentityShortInfoEvent(uid, player); + RaiseLocalEvent(tryGetIdentityShortInfoEvent); + order.SetApproverData(tryGetIdentityShortInfoEvent.Title); + + var message = Loc.GetString("cargo-console-unlock-approved-order-broadcast", + ("productName", Loc.GetString(order.ProductName)), + ("orderAmount", order.OrderQuantity), + ("approver", order.Approver ?? string.Empty), + ("cost", cost)); + _radio.SendRadioMessage(uid, message, component.AnnouncementChannel, uid, escapeMarkup: false); + } + ConsolePopup(args.Actor, Loc.GetString("cargo-console-trade-station", ("destination", MetaData(ev.FulfillmentEntity.Value).EntityName))); // Log order approval diff --git a/Content.Server/Cargo/Systems/CargoSystem.cs b/Content.Server/Cargo/Systems/CargoSystem.cs index 0d205d762b..b93a0f3315 100644 --- a/Content.Server/Cargo/Systems/CargoSystem.cs +++ b/Content.Server/Cargo/Systems/CargoSystem.cs @@ -1,4 +1,3 @@ -using Content.Server.Access.Systems; using Content.Server.Cargo.Components; using Content.Server.DeviceLinking.Systems; using Content.Server.Popups; @@ -31,7 +30,6 @@ public sealed partial class CargoSystem : SharedCargoSystem [Dependency] private readonly AccessReaderSystem _accessReaderSystem = default!; [Dependency] private readonly DeviceLinkSystem _linker = default!; [Dependency] private readonly EntityLookupSystem _lookup = default!; - [Dependency] private readonly IdCardSystem _idCardSystem = default!; [Dependency] private readonly ItemSlotsSystem _slots = default!; [Dependency] private readonly PaperSystem _paperSystem = default!; [Dependency] private readonly PopupSystem _popup = default!; diff --git a/Content.Server/Communications/CommunicationsConsoleSystem.cs b/Content.Server/Communications/CommunicationsConsoleSystem.cs index 1791fb421e..6c320edb23 100644 --- a/Content.Server/Communications/CommunicationsConsoleSystem.cs +++ b/Content.Server/Communications/CommunicationsConsoleSystem.cs @@ -1,15 +1,11 @@ -using System.Globalization; -using Content.Server.Access.Systems; using Content.Server.Administration.Logs; using Content.Server.AlertLevel; using Content.Server.Chat.Systems; -using Content.Server.DeviceNetwork; using Content.Server.DeviceNetwork.Components; using Content.Server.DeviceNetwork.Systems; using Content.Server.Interaction; using Content.Server.Popups; using Content.Server.RoundEnd; -using Content.Server.Screens; using Content.Server.Screens.Components; using Content.Server.Shuttles.Systems; using Content.Server.Station.Systems; @@ -21,8 +17,8 @@ using Content.Shared.Communications; using Content.Shared.Database; using Content.Shared.DeviceNetwork; using Content.Shared.Emag.Components; +using Content.Shared.IdentityManagement; using Content.Shared.Popups; -using Content.Shared.Silicons.Borgs.Components; using Robust.Server.GameObjects; using Robust.Shared.Configuration; @@ -35,7 +31,6 @@ namespace Content.Server.Communications [Dependency] private readonly ChatSystem _chatSystem = default!; [Dependency] private readonly DeviceNetworkSystem _deviceNetworkSystem = default!; [Dependency] private readonly EmergencyShuttleSystem _emergency = default!; - [Dependency] private readonly IdCardSystem _idCardSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly RoundEndSystem _roundEndSystem = default!; [Dependency] private readonly StationSystem _stationSystem = default!; @@ -251,17 +246,9 @@ namespace Content.Server.Communications return; } - // User has an id - if (_idCardSystem.TryFindIdCard(mob, out var id)) - { - author = $"{id.Comp.FullName} ({CultureInfo.CurrentCulture.TextInfo.ToTitleCase(id.Comp.JobTitle ?? string.Empty)})".Trim(); - } - - // User does not have an id and is a borg, so use the borg's name - if (HasComp(mob)) - { - author = Name(mob).Trim(); - } + var tryGetIdentityShortInfoEvent = new TryGetIdentityShortInfoEvent(uid, mob); + RaiseLocalEvent(tryGetIdentityShortInfoEvent); + author = tryGetIdentityShortInfoEvent.Title; } comp.AnnouncementCooldownRemaining = comp.Delay; diff --git a/Content.Server/CriminalRecords/Systems/CriminalRecordsConsoleSystem.cs b/Content.Server/CriminalRecords/Systems/CriminalRecordsConsoleSystem.cs index bbd402b9fa..c5f1d159f3 100644 --- a/Content.Server/CriminalRecords/Systems/CriminalRecordsConsoleSystem.cs +++ b/Content.Server/CriminalRecords/Systems/CriminalRecordsConsoleSystem.cs @@ -10,7 +10,6 @@ using Content.Shared.CriminalRecords.Systems; using Content.Shared.Security; using Content.Shared.StationRecords; using Robust.Server.GameObjects; -using Robust.Shared.Player; using System.Diagnostics.CodeAnalysis; using Content.Shared.IdentityManagement; using Content.Shared.Security.Components; @@ -26,7 +25,6 @@ public sealed class CriminalRecordsConsoleSystem : SharedCriminalRecordsConsoleS [Dependency] private readonly CriminalRecordsSystem _criminalRecords = default!; [Dependency] private readonly PopupSystem _popup = default!; [Dependency] private readonly RadioSystem _radio = default!; - [Dependency] private readonly SharedIdCardSystem _idCard = default!; [Dependency] private readonly StationRecordsSystem _records = default!; [Dependency] private readonly StationSystem _station = default!; [Dependency] private readonly UserInterfaceSystem _ui = default!; @@ -108,8 +106,13 @@ public sealed class CriminalRecordsConsoleSystem : SharedCriminalRecordsConsoleS var name = _records.RecordName(key.Value); var officer = Loc.GetString("criminal-records-console-unknown-officer"); - if (_idCard.TryFindIdCard(mob.Value, out var id) && id.Comp.FullName is { } fullName) - officer = fullName; + + var tryGetIdentityShortInfoEvent = new TryGetIdentityShortInfoEvent(null, mob.Value); + RaiseLocalEvent(tryGetIdentityShortInfoEvent); + if (tryGetIdentityShortInfoEvent.Title != null) + { + officer = tryGetIdentityShortInfoEvent.Title; + } (string, object)[] args; if (reason != null) diff --git a/Content.Server/MassMedia/Systems/NewsSystem.cs b/Content.Server/MassMedia/Systems/NewsSystem.cs index b46cacfcac..fd49ebf188 100644 --- a/Content.Server/MassMedia/Systems/NewsSystem.cs +++ b/Content.Server/MassMedia/Systems/NewsSystem.cs @@ -1,5 +1,4 @@ using System.Diagnostics.CodeAnalysis; -using Content.Server.Access.Systems; using Content.Server.Administration.Logs; using Content.Server.CartridgeLoader; using Content.Server.CartridgeLoader.Cartridges; @@ -19,6 +18,7 @@ using Content.Shared.MassMedia.Systems; using Content.Shared.Popups; using Robust.Server.GameObjects; using Robust.Shared.Audio.Systems; +using Content.Shared.IdentityManagement; using Robust.Shared.Timing; namespace Content.Server.MassMedia.Systems; @@ -35,7 +35,7 @@ public sealed class NewsSystem : SharedNewsSystem [Dependency] private readonly PopupSystem _popup = default!; [Dependency] private readonly StationSystem _station = default!; [Dependency] private readonly GameTicker _ticker = default!; - [Dependency] private readonly IdCardSystem _idCardSystem = default!; + [Dependency] private readonly AccessReaderSystem _accessReader = default!; [Dependency] private readonly IChatManager _chatManager = default!; public override void Initialize() @@ -140,9 +140,9 @@ public sealed class NewsSystem : SharedNewsSystem ent.Comp.PublishEnabled = false; ent.Comp.NextPublish = _timing.CurTime + TimeSpan.FromSeconds(ent.Comp.PublishCooldown); - string? authorName = null; - if (_idCardSystem.TryFindIdCard(msg.Actor, out var idCard)) - authorName = idCard.Comp.FullName; + var tryGetIdentityShortInfoEvent = new TryGetIdentityShortInfoEvent(ent, msg.Actor); + RaiseLocalEvent(tryGetIdentityShortInfoEvent); + string? authorName = tryGetIdentityShortInfoEvent.Title; var title = msg.Title.Trim(); var content = msg.Content.Trim(); diff --git a/Content.Server/Research/Systems/ResearchSystem.Console.cs b/Content.Server/Research/Systems/ResearchSystem.Console.cs index 5358ddefcd..127b80a631 100644 --- a/Content.Server/Research/Systems/ResearchSystem.Console.cs +++ b/Content.Server/Research/Systems/ResearchSystem.Console.cs @@ -2,6 +2,8 @@ using Content.Server.Power.EntitySystems; using Content.Server.Research.Components; using Content.Shared.UserInterface; using Content.Shared.Access.Components; +using Content.Shared.Emag.Components; +using Content.Shared.IdentityManagement; using Content.Shared.Research.Components; using Content.Shared.Research.Prototypes; @@ -37,10 +39,20 @@ public sealed partial class ResearchSystem if (!UnlockTechnology(uid, args.Id, act)) return; - var message = Loc.GetString("research-console-unlock-technology-radio-broadcast", - ("technology", Loc.GetString(technologyPrototype.Name)), - ("amount", technologyPrototype.Cost)); - _radio.SendRadioMessage(uid, message, component.AnnouncementChannel, uid, escapeMarkup: false); + if (!HasComp(uid)) + { + var getIdentityEvent = new TryGetIdentityShortInfoEvent(uid, act); + RaiseLocalEvent(getIdentityEvent); + + var message = Loc.GetString( + "research-console-unlock-technology-radio-broadcast", + ("technology", Loc.GetString(technologyPrototype.Name)), + ("amount", technologyPrototype.Cost), + ("approver", getIdentityEvent.Title ?? string.Empty) + ); + _radio.SendRadioMessage(uid, message, component.AnnouncementChannel, uid, escapeMarkup: false); + } + SyncClientWithServer(uid); UpdateConsoleInterface(uid, component); } @@ -87,4 +99,5 @@ public sealed partial class ResearchSystem { UpdateConsoleInterface(uid, component); } + } diff --git a/Content.Shared/Access/Systems/AccessReaderSystem.cs b/Content.Shared/Access/Systems/AccessReaderSystem.cs index 2e0737c6ab..f670ac860b 100644 --- a/Content.Shared/Access/Systems/AccessReaderSystem.cs +++ b/Content.Shared/Access/Systems/AccessReaderSystem.cs @@ -8,11 +8,11 @@ using Content.Shared.Hands.EntitySystems; using Content.Shared.Inventory; using Content.Shared.NameIdentifier; using Content.Shared.PDA; -using Content.Shared.Silicons.Borgs.Components; using Content.Shared.StationRecords; using Robust.Shared.Containers; using Robust.Shared.GameStates; using Content.Shared.GameTicking; +using Content.Shared.IdentityManagement; using Robust.Shared.Collections; using Robust.Shared.Prototypes; using Robust.Shared.Timing; @@ -26,7 +26,6 @@ public sealed class AccessReaderSystem : EntitySystem [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly SharedGameTicker _gameTicker = default!; [Dependency] private readonly SharedHandsSystem _handsSystem = default!; - [Dependency] private readonly SharedIdCardSystem _idCardSystem = default!; [Dependency] private readonly SharedContainerSystem _containerSystem = default!; [Dependency] private readonly SharedStationRecordsSystem _recordsSystem = default!; @@ -402,9 +401,12 @@ public sealed class AccessReaderSystem : EntitySystem // TODO pass the ID card on IsAllowed() instead of using this expensive method // Set name if the accessor has a card and that card has a name and allows itself to be recorded - if (_idCardSystem.TryFindIdCard(accessor, out var idCard) - && idCard.Comp is { BypassLogging: false, FullName: not null }) - name = idCard.Comp.FullName; + var getIdentityShortInfoEvent = new TryGetIdentityShortInfoEvent(ent, accessor, true); + RaiseLocalEvent(getIdentityShortInfoEvent); + if (getIdentityShortInfoEvent.Title != null) + { + name = getIdentityShortInfoEvent.Title; + } LogAccess(ent, name ?? Loc.GetString("access-reader-unknown-id")); } diff --git a/Content.Shared/Access/Systems/SharedIdCardSystem.cs b/Content.Shared/Access/Systems/SharedIdCardSystem.cs index 06f9d66a61..5a90d4ea35 100644 --- a/Content.Shared/Access/Systems/SharedIdCardSystem.cs +++ b/Content.Shared/Access/Systems/SharedIdCardSystem.cs @@ -1,7 +1,9 @@ +using System.Globalization; using Content.Shared.Access.Components; using Content.Shared.Administration.Logs; using Content.Shared.Database; using Content.Shared.Hands.Components; +using Content.Shared.IdentityManagement; using Content.Shared.Inventory; using Content.Shared.PDA; using Content.Shared.Roles; @@ -20,7 +22,9 @@ public abstract class SharedIdCardSystem : EntitySystem public override void Initialize() { base.Initialize(); + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnTryGetIdentityShortInfo); } private void OnMapInit(EntityUid uid, IdCardComponent id, MapInitEvent args) @@ -28,6 +32,23 @@ public abstract class SharedIdCardSystem : EntitySystem UpdateEntityName(uid, id); } + private void OnTryGetIdentityShortInfo(TryGetIdentityShortInfoEvent ev) + { + if (ev.Handled) + { + return; + } + + string? title = null; + if (TryFindIdCard(ev.ForActor, out var idCard) && !(ev.RequestForAccessLogging && idCard.Comp.BypassLogging)) + { + title = ExtractFullTitle(idCard); + } + + ev.Title = title; + ev.Handled = true; + } + /// /// Attempt to find an ID card on an entity. This will look in the entity itself, in the entity's hands, and /// in the entity's inventory. @@ -214,4 +235,10 @@ public abstract class SharedIdCardSystem : EntitySystem ("jobSuffix", jobSuffix)); _metaSystem.SetEntityName(uid, val); } + + private static string ExtractFullTitle(IdCardComponent idCardComponent) + { + return $"{idCardComponent.FullName} ({CultureInfo.CurrentCulture.TextInfo.ToTitleCase(idCardComponent.JobTitle ?? string.Empty)})" + .Trim(); + } } diff --git a/Content.Shared/Cargo/CargoOrderData.cs b/Content.Shared/Cargo/CargoOrderData.cs index 5645cc454d..9813457910 100644 --- a/Content.Shared/Cargo/CargoOrderData.cs +++ b/Content.Shared/Cargo/CargoOrderData.cs @@ -63,6 +63,11 @@ namespace Content.Shared.Cargo Reason = reason; } + public void SetApproverData(string? approver) + { + Approver = approver; + } + public void SetApproverData(string? fullName, string? jobTitle) { var sb = new StringBuilder(); diff --git a/Content.Shared/IdentityManagement/TryGetIdentityShortInfoEvent.cs b/Content.Shared/IdentityManagement/TryGetIdentityShortInfoEvent.cs new file mode 100644 index 0000000000..1303896e7b --- /dev/null +++ b/Content.Shared/IdentityManagement/TryGetIdentityShortInfoEvent.cs @@ -0,0 +1,32 @@ +namespace Content.Shared.IdentityManagement; + +/// +/// Event of attempt to collect actor full title - full name + job from id card for employee or entity name for borgs. +/// +public sealed class TryGetIdentityShortInfoEvent(EntityUid? whileInteractingWith, EntityUid forActor, bool forLogging = false) : HandledEntityEventArgs +{ + /// + /// Full name of , with JobTitle. + /// Can be null if no system could find actor name / job. + /// + public string? Title; + + /// + /// Entity for interacting with which title should be collected. + /// Could be used to black-out name of people when announcing actions + /// on e-magged devices. + /// + public readonly EntityUid? WhileInteractingWith = whileInteractingWith; + + /// + /// Actor for whom title should be collected. + /// + public readonly EntityUid ForActor = forActor; + + /// + /// Marker that title info was requested for access logging. + /// Is required as event handlers can determine, if they don't need + /// to place title info due to access logging restrictions. + /// + public readonly bool RequestForAccessLogging = forLogging; +} diff --git a/Content.Shared/Silicons/Borgs/SharedBorgSystem.cs b/Content.Shared/Silicons/Borgs/SharedBorgSystem.cs index 48d2357836..c62e63481d 100644 --- a/Content.Shared/Silicons/Borgs/SharedBorgSystem.cs +++ b/Content.Shared/Silicons/Borgs/SharedBorgSystem.cs @@ -1,5 +1,5 @@ -using Content.Shared.Access.Components; using Content.Shared.Containers.ItemSlots; +using Content.Shared.IdentityManagement; using Content.Shared.Item.ItemToggle; using Content.Shared.Movement.Components; using Content.Shared.Movement.Systems; @@ -34,10 +34,27 @@ public abstract partial class SharedBorgSystem : EntitySystem SubscribeLocalEvent(OnRemoved); SubscribeLocalEvent(OnRefreshMovementSpeedModifiers); SubscribeLocalEvent(OnUIOpenAttempt); - + SubscribeLocalEvent(OnTryGetIdentityShortInfo); + InitializeRelay(); } + private void OnTryGetIdentityShortInfo(TryGetIdentityShortInfoEvent args) + { + if (args.Handled) + { + return; + } + + if (!HasComp(args.ForActor)) + { + return; + } + + args.Title = Name(args.ForActor).Trim(); + args.Handled = true; + } + private void OnItemSlotInsertAttempt(EntityUid uid, BorgChassisComponent component, ref ItemSlotInsertAttemptEvent args) { if (args.Cancelled) diff --git a/Resources/Locale/en-US/research/components/research-console-component.ftl b/Resources/Locale/en-US/research/components/research-console-component.ftl index 33070480e5..5a1e074f4d 100644 --- a/Resources/Locale/en-US/research/components/research-console-component.ftl +++ b/Resources/Locale/en-US/research/components/research-console-component.ftl @@ -18,4 +18,4 @@ research-console-prereqs-list-start = Requires: research-console-prereqs-list-entry = - [color=orchid]{$text}[/color] research-console-no-access-popup = No access! -research-console-unlock-technology-radio-broadcast = Unlocked [bold]{$technology}[/bold] for [bold]{$amount}[/bold] research. +research-console-unlock-technology-radio-broadcast = Unlocked [bold]{$technology}[/bold] for [bold]{$amount}[/bold] research by [bold]{$approver}[/bold].