diff --git a/Content.Client/_White/SecurityHud/SecurityHudBUI.cs b/Content.Client/_White/SecurityHud/SecurityHudBUI.cs new file mode 100644 index 0000000000..9e6e1de437 --- /dev/null +++ b/Content.Client/_White/SecurityHud/SecurityHudBUI.cs @@ -0,0 +1,122 @@ +using System.Linq; +using Content.Client._White.UserInterface.Radial; +using Content.Shared._White.SecurityHud; +using Content.Shared.Security; +using Content.Shared.StatusIcon; +using Robust.Shared.Prototypes; + +namespace Content.Client._White.SecurityHud; + +public sealed class SecurityHudBUI : BoundUserInterface +{ + private RadialContainer? _radialContainer; + + private bool _updated; + + private readonly Dictionary _names = new() + { + { "SecurityIconDischarged", Loc.GetString("criminal-records-status-discharged")}, + { "SecurityIconParoled", Loc.GetString("criminal-records-status-paroled")}, + { "SecurityIconSuspected", Loc.GetString("criminal-records-status-suspected")}, + { "SecurityIconWanted", Loc.GetString("criminal-records-status-wanted")}, + { "SecurityIconIncarcerated", Loc.GetString("criminal-records-status-detained")}, + { "CriminalRecordIconRemove", Loc.GetString("security-hud-remove-status") } + }; + + private readonly Dictionary _icons = new() + { + { "SecurityIconDischarged", "/Textures/White/Interface/securityhud.rsi/discharged.png" }, + { "SecurityIconParoled", "/Textures/White/Interface/securityhud.rsi/paroled.png" }, + { "SecurityIconSuspected", "/Textures/White/Interface/securityhud.rsi/suspected.png" }, + { "SecurityIconWanted", "/Textures/White/Interface/securityhud.rsi/wanted.png" }, + { "SecurityIconIncarcerated", "/Textures/White/Interface/securityhud.rsi/incarcerated.png" }, + { "CriminalRecordIconRemove", "/Textures/White/Interface/securityhud.rsi/remove.png" } + }; + + private readonly Dictionary _status = new() + { + { "SecurityIconDischarged", SecurityStatus.Discharged }, + { "SecurityIconParoled", SecurityStatus.Paroled }, + { "SecurityIconSuspected", SecurityStatus.Suspected }, + { "SecurityIconWanted", SecurityStatus.Wanted }, + { "SecurityIconIncarcerated", SecurityStatus.Detained }, + { "CriminalRecordIconRemove", SecurityStatus.None } + }; + + + public SecurityHudBUI(EntityUid owner, Enum uiKey) : base(owner, uiKey) + { + } + + protected override void Open() + { + base.Open(); + + if (_radialContainer != null) + UIReset(); + + _radialContainer = new RadialContainer(); + + _radialContainer.Closed += Close; + + if (State != null) + UpdateState(State); + } + + private void UIReset() + { + _radialContainer?.Close(); + _radialContainer = null; + _updated = false; + } + + private void PopulateRadial(IReadOnlyCollection ids, NetEntity user, NetEntity target) + { + foreach (var id in ids) + { + if (_radialContainer == null) + continue; + + if(!_names.TryGetValue(id, out var name) || !_icons.TryGetValue(id, out var icon) || !_status.TryGetValue(id, out var status)) + return; + + var button = _radialContainer.AddButton(name, icon); + button.Controller.OnPressed += _ => + { + Select(status, user, target); + }; + } + } + + private void Select(SecurityStatus status, NetEntity user, NetEntity target) + { + SendMessage(new SecurityHudStatusSelectedMessage(status, user, target)); + UIReset(); + Close(); + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + UIReset(); + } + + protected override void UpdateState(BoundUserInterfaceState state) + { + base.UpdateState(state); + + if (_updated) + return; + + if (state is SecurityHudBUIState newState) + { + PopulateRadial(newState.Ids, newState.User, newState.Target); + } + + if (_radialContainer == null) + return; + + _radialContainer?.OpenAttachedLocalPlayer(); + _updated = true; + } +} diff --git a/Content.Server/_White/SecurityHud/SecurityHudComponent.cs b/Content.Server/_White/SecurityHud/SecurityHudComponent.cs new file mode 100644 index 0000000000..f68034ffab --- /dev/null +++ b/Content.Server/_White/SecurityHud/SecurityHudComponent.cs @@ -0,0 +1,20 @@ +using Content.Shared.Radio; +using Content.Shared.StatusIcon; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; + +namespace Content.Server._White.SecurityHud; + +[RegisterComponent] +public sealed partial class SecurityHudComponent : Component +{ + [ViewVariables(VVAccess.ReadOnly)] + [DataField("criminalrecords", customTypeSerializer: typeof(PrototypeIdListSerializer))] + public IReadOnlyCollection Status = ArraySegment.Empty; + + [ViewVariables(VVAccess.ReadOnly)] + public ProtoId SecurityChannel = "Security"; + + [ViewVariables(VVAccess.ReadOnly)] + public string Reason = "Изменено с помощью визора"; +} diff --git a/Content.Server/_White/SecurityHud/SecurityHudSystem.cs b/Content.Server/_White/SecurityHud/SecurityHudSystem.cs new file mode 100644 index 0000000000..22c36b4fd6 --- /dev/null +++ b/Content.Server/_White/SecurityHud/SecurityHudSystem.cs @@ -0,0 +1,153 @@ +using Content.Server.Access.Systems; +using Content.Server.CriminalRecords.Systems; +using Content.Server.Popups; +using Content.Server.Radio.EntitySystems; +using Content.Server.StationRecords.Systems; +using Content.Shared._Miracle.Components; +using Content.Shared._White.SecurityHud; +using Content.Shared.Access.Components; +using Content.Shared.Access.Systems; +using Content.Shared.CriminalRecords; +using Content.Shared.Humanoid; +using Content.Shared.Inventory; +using Content.Shared.Popups; +using Content.Shared.Security; +using Content.Shared.Security.Components; +using Content.Shared.StationRecords; +using Content.Shared.Verbs; +using Robust.Server.GameObjects; +using Robust.Shared.Player; + +namespace Content.Server._White.SecurityHud; + +public sealed class SecurityHudSystem : EntitySystem +{ + [Dependency] private readonly UserInterfaceSystem _ui = default!; + [Dependency] private readonly CriminalRecordsSystem _criminalRecordsSystem = default!; + [Dependency] private readonly CriminalRecordsConsoleSystem _criminalRecordsConsoleSystem = default!; + [Dependency] private readonly StationRecordsSystem _stationRecordsSystem = default!; + [Dependency] private readonly IdCardSystem _idCardSystem = default!; + [Dependency] private readonly RadioSystem _radio = default!; + [Dependency] private readonly InventorySystem _invSlotsSystem = default!; + [Dependency] private readonly PopupSystem _popupSystem = default!; + [Dependency] private readonly AccessReaderSystem _accessReaderSystem = default!; + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent>(OnAltVerb); + SubscribeLocalEvent(OnStatusSelected); + } + + private void OnAltVerb(GetVerbsEvent args) + { + if(!HasComp(args.Target)) + return; + + if(!_invSlotsSystem.TryGetSlotEntity(args.User, "eyes", out var ent)) + return; + + if(!TryComp(ent, out var component)) + return; + + if(!TryComp(ent, out var accessReaderComponent)) + return; + + if (!_accessReaderSystem.IsAllowed(args.User, (EntityUid) ent, accessReaderComponent)) + { + _popupSystem.PopupEntity(Loc.GetString("security-hud-not-allowed"), args.User, args.User, PopupType.Medium); + return; + } + + AlternativeVerb verb = new() + { + Act = () => + { + SetWanted(args.User, args.Target, ent.Value, component); + }, + Disabled = false, + Priority = 0, + Text = Loc.GetString("security-hud-verb"), + }; + + args.Verbs.Add(verb); + } + + private void SetWanted(EntityUid uid, EntityUid target, EntityUid hud, SecurityHudComponent component) + { + if (!TryComp(uid, out var actor)) + return; + + if (_ui.TryGetUi(hud, SecurityHudUiKey.Key, out var bui)) + { + _ui.SetUiState(bui, new SecurityHudBUIState(component.Status, GetNetEntity(uid), GetNetEntity(target))); + _ui.OpenUi(bui, actor.PlayerSession); + } + } + + private void OnStatusSelected(EntityUid uid, SecurityHudComponent component, SecurityHudStatusSelectedMessage args) + { + var user = GetEntity(args.User); + var target = GetEntity(args.Target); + + if (!_idCardSystem.TryFindIdCard(target, out var idCard)) + { + _popupSystem.PopupEntity(Loc.GetString("security-hud-id-unknown"), user, user, PopupType.Medium); + return; + } + + if(!TryComp(idCard, out var stationRecordKeyComp)) + return; + + if (stationRecordKeyComp.Key == null) + { + _popupSystem.PopupEntity(Loc.GetString("security-hud-key-null"), user, user, PopupType.Medium); + return; + } + + var key = stationRecordKeyComp.Key.Value; + + if (!SetCriminalStatus(key, args.Status, uid, user, idCard.Comp, component.Reason, component.SecurityChannel)) + { + _popupSystem.PopupEntity(Loc.GetString("security-hud-cant-set-status"), user, user, PopupType.Medium); + } + } + + private bool SetCriminalStatus(StationRecordKey key, SecurityStatus status, EntityUid hud, EntityUid officer, + IdCardComponent idCard, string reason, string securityChannel) + { + if (!_stationRecordsSystem.TryGetRecord(key, out var generalRecord)) + return false; + + if (!_stationRecordsSystem.TryGetRecord(key, out var record) || record.Status == status) + return false; + + var name = generalRecord.Name; + var officerName = Loc.GetString("criminal-records-console-unknown-officer"); + if (_idCardSystem.TryFindIdCard(officer, out var id) && id.Comp.FullName is { } fullName) + officerName = fullName; + + _criminalRecordsSystem.TryChangeStatus(key, status, reason); + + var locArgs = new (string, object)[] { ("name", name), ("officer", officerName), ("reason", reason) }; + + var statusString = (record.Status, status) switch + { + (_, SecurityStatus.Detained) => "detained", + (_, SecurityStatus.Suspected) => "suspected", + (_, SecurityStatus.Paroled) => "paroled", + (_, SecurityStatus.Discharged) => "released", + (_, SecurityStatus.Wanted) => "wanted", + (SecurityStatus.Suspected, SecurityStatus.None) => "not-suspected", + (SecurityStatus.Wanted, SecurityStatus.None) => "not-wanted", + (SecurityStatus.Detained, SecurityStatus.None) => "released", + (SecurityStatus.Paroled, SecurityStatus.None) => "not-parole", + _ => "not-wanted" + }; + + _radio.SendRadioMessage(hud, Loc.GetString($"criminal-records-console-{statusString}", locArgs), securityChannel, hud); + _criminalRecordsConsoleSystem.UpdateCriminalIdentity(name, status); + + return true; + } +} diff --git a/Content.Shared/_White/SecurityHud/SecurityHudBUIState.cs b/Content.Shared/_White/SecurityHud/SecurityHudBUIState.cs new file mode 100644 index 0000000000..5ec4dc9a4e --- /dev/null +++ b/Content.Shared/_White/SecurityHud/SecurityHudBUIState.cs @@ -0,0 +1,44 @@ +using Content.Shared.Security; +using Robust.Shared.Serialization; + +namespace Content.Shared._White.SecurityHud; + +[Serializable, NetSerializable] +public enum SecurityHudUiKey +{ + Key +} + +[Serializable, NetSerializable] +public sealed class SecurityHudBUIState : BoundUserInterfaceState +{ + public IReadOnlyCollection Ids { get; set; } + + public NetEntity User { get; set; } + + public NetEntity Target { get; private set; } + + public SecurityHudBUIState(IReadOnlyCollection ids, NetEntity user, NetEntity target) + { + Ids = ids; + User = user; + Target = target; + } +} + +[Serializable, NetSerializable] +public class SecurityHudStatusSelectedMessage : BoundUserInterfaceMessage +{ + public SecurityStatus Status { get; private set; } + + public NetEntity User { get; private set; } + + public NetEntity Target { get; private set; } + + public SecurityHudStatusSelectedMessage(SecurityStatus status, NetEntity user, NetEntity target) + { + Status = status; + User = user; + Target = target; + } +} diff --git a/Resources/Locale/ru-RU/criminal-records/criminal-records.ftl b/Resources/Locale/ru-RU/criminal-records/criminal-records.ftl index 78f3c1e5c7..698d9ba4d8 100644 --- a/Resources/Locale/ru-RU/criminal-records/criminal-records.ftl +++ b/Resources/Locale/ru-RU/criminal-records/criminal-records.ftl @@ -10,11 +10,12 @@ criminal-records-console-status = Статус criminal-records-status-none = Нет criminal-records-status-wanted = В розыске criminal-records-status-detained = В заключении - criminal-records-status-suspected = Подозреваемый -criminal-records-status-released = Отпущен +criminal-records-status-discharged = Освобождён +criminal-records-status-paroled = Досрочно освобождён criminal-records-console-wanted-reason = [color=gray]Причина розыска[/color] +criminal-records-console-suspected-reason = [color=gray]Причина подозрения[/color] criminal-records-console-reason = Причина criminal-records-console-reason-placeholder = Например: {$placeholder} @@ -31,12 +32,15 @@ criminal-records-permission-denied = Доступ воспрещен ## Security channel notifications criminal-records-console-wanted = {$name} отправлен в розыск по указу {$officer} по причине: {$reason}. +criminal-records-console-suspected = {$name} подозревается по указу {$officer} по причине: {$reason}. +criminal-records-console-not-suspected = {$name} больше не под подозрением. criminal-records-console-detained = {$name} был задержан {$officer}. -criminal-records-console-released = {$name} был задержан {$officer}. +criminal-records-console-released = {$name} был освобожден {$officer}. criminal-records-console-not-wanted = {$name} больше не в розыске. +criminal-records-console-paroled = {$name} был отпущен условно-досрочно {$officer}. +criminal-records-console-not-parole = {$name} больше не условно-досрочно освобождённый. criminal-records-console-unknown-officer = <неизвестный офицер> -criminal-records-console-suspected = {$name} подозревается по указу {$officer} по причине: {$reason}. ## Filters diff --git a/Resources/Locale/ru-RU/white/securityhud/securityhud.ftl b/Resources/Locale/ru-RU/white/securityhud/securityhud.ftl new file mode 100644 index 0000000000..33d6592335 --- /dev/null +++ b/Resources/Locale/ru-RU/white/securityhud/securityhud.ftl @@ -0,0 +1,6 @@ +security-hud-key-null = Визор не может установить личность человека! +security-hud-id-unknown = Визор не может установить идентификационную карту человека! +security-hud-verb = Изменить статус +security-hud-cant-set-status = Произошла ошибка при попытке установить статус! +security-hud-remove-status = Убрать статус. +security-hud-not-allowed = Недостаточный доступ для взаимодействия. \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Clothing/Eyes/hud.yml b/Resources/Prototypes/Entities/Clothing/Eyes/hud.yml index b53ef94bef..3fcad2d428 100644 --- a/Resources/Prototypes/Entities/Clothing/Eyes/hud.yml +++ b/Resources/Prototypes/Entities/Clothing/Eyes/hud.yml @@ -44,6 +44,20 @@ - type: Clothing sprite: Clothing/Eyes/Hud/sec.rsi - type: ShowSecurityIcons + - type: AccessReader + access: [["Security"]] + - type: SecurityHud + criminalrecords: + - SecurityIconDischarged + - SecurityIconParoled + - SecurityIconSuspected + - SecurityIconWanted + - SecurityIconIncarcerated + - CriminalRecordIconRemove + - type: UserInterface + interfaces: + - key: enum.SecurityHudUiKey.Key + type: SecurityHudBUI - type: Tag tags: - HudSecurity diff --git a/Resources/Prototypes/_White/StatusEffects/criminal_records.yml b/Resources/Prototypes/_White/StatusEffects/criminal_records.yml deleted file mode 100644 index 108360cd17..0000000000 --- a/Resources/Prototypes/_White/StatusEffects/criminal_records.yml +++ /dev/null @@ -1,47 +0,0 @@ -- type: statusIcon - id: CriminalRecordIcon - abstract: true - priority: 2 - locationPreference: Right - -- type: statusIcon - parent: CriminalRecordIcon - id: CriminalRecordIconReleased - icon: - sprite: /Textures/White/Interface/records.rsi - state: released - -- type: statusIcon - parent: CriminalRecordIcon - id: CriminalRecordIconDischarged - icon: - sprite: /Textures/White/Interface/records.rsi - state: discharged - -- type: statusIcon - parent: CriminalRecordIcon - id: CriminalRecordIconParolled - icon: - sprite: /Textures/White/Interface/records.rsi - state: parolled - -- type: statusIcon - parent: CriminalRecordIcon - id: CriminalRecordIconSuspected - icon: - sprite: /Textures/White/Interface/records.rsi - state: suspected - -- type: statusIcon - parent: CriminalRecordIcon - id: CriminalRecordIconWanted - icon: - sprite: /Textures/White/Interface/records.rsi - state: wanted - -- type: statusIcon - parent: CriminalRecordIcon - id: CriminalRecordIconIncarcerated - icon: - sprite: /Textures/White/Interface/records.rsi - state: incarcerated diff --git a/Resources/Textures/Interface/Misc/security_icons.rsi/hud_suspected.png b/Resources/Textures/Interface/Misc/security_icons.rsi/hud_suspected.png index cfb34742bf..8e04eb21f4 100644 Binary files a/Resources/Textures/Interface/Misc/security_icons.rsi/hud_suspected.png and b/Resources/Textures/Interface/Misc/security_icons.rsi/hud_suspected.png differ diff --git a/Resources/Textures/White/Interface/securityhud.rsi/discharged.png b/Resources/Textures/White/Interface/securityhud.rsi/discharged.png new file mode 100644 index 0000000000..3206bf0693 Binary files /dev/null and b/Resources/Textures/White/Interface/securityhud.rsi/discharged.png differ diff --git a/Resources/Textures/White/Interface/securityhud.rsi/incarcerated.png b/Resources/Textures/White/Interface/securityhud.rsi/incarcerated.png new file mode 100644 index 0000000000..f6a3464fea Binary files /dev/null and b/Resources/Textures/White/Interface/securityhud.rsi/incarcerated.png differ diff --git a/Resources/Textures/White/Interface/securityhud.rsi/meta.json b/Resources/Textures/White/Interface/securityhud.rsi/meta.json new file mode 100644 index 0000000000..f5ffc7b5b7 --- /dev/null +++ b/Resources/Textures/White/Interface/securityhud.rsi/meta.json @@ -0,0 +1,23 @@ +{ + "license": "CC-BY-SA-3.0", + "copyright": "https://github.com/tgstation/tgstation/blob/8e49222b72f6fdcbe741d2a6ce0a8425d95010b7/icons/mob/huds/hud.dmi", + "version": 1, + "size": { "y": 32, "x": 32 }, + "states": [ + { + "name": "wanted" + }, + { + "name": "suspected" + }, + { + "name": "released" + }, + { + "name": "incarcerated" + }, + { + "name": "remove" + } + ] +} diff --git a/Resources/Textures/White/Interface/securityhud.rsi/paroled.png b/Resources/Textures/White/Interface/securityhud.rsi/paroled.png new file mode 100644 index 0000000000..df2f1898de Binary files /dev/null and b/Resources/Textures/White/Interface/securityhud.rsi/paroled.png differ diff --git a/Resources/Textures/White/Interface/securityhud.rsi/released.png b/Resources/Textures/White/Interface/securityhud.rsi/released.png new file mode 100644 index 0000000000..3b58b7ccd0 Binary files /dev/null and b/Resources/Textures/White/Interface/securityhud.rsi/released.png differ diff --git a/Resources/Textures/White/Interface/securityhud.rsi/remove.png b/Resources/Textures/White/Interface/securityhud.rsi/remove.png new file mode 100644 index 0000000000..bd84443aad Binary files /dev/null and b/Resources/Textures/White/Interface/securityhud.rsi/remove.png differ diff --git a/Resources/Textures/White/Interface/securityhud.rsi/suspected.png b/Resources/Textures/White/Interface/securityhud.rsi/suspected.png new file mode 100644 index 0000000000..4e28e53563 Binary files /dev/null and b/Resources/Textures/White/Interface/securityhud.rsi/suspected.png differ diff --git a/Resources/Textures/White/Interface/securityhud.rsi/wanted.png b/Resources/Textures/White/Interface/securityhud.rsi/wanted.png new file mode 100644 index 0000000000..8bf6edba39 Binary files /dev/null and b/Resources/Textures/White/Interface/securityhud.rsi/wanted.png differ