From 5f51cb81de2aac2fd6aa83a30f3dc208334714d4 Mon Sep 17 00:00:00 2001 From: RavMorgan <48182970+RavMorgan@users.noreply.github.com> Date: Thu, 15 Jun 2023 13:17:04 +0300 Subject: [PATCH] [Feat] War crime console (#144) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ADD: Icons, New component. Надо будет рефакторнуть худы в одну систему * Some govno ebanoe * ui * Some govno * UI and UI lol * Dermo again * ы * Добавлена система консоли. Надо добавить манипуляцию с рекордами и сохранение крим. записей на сервер. Я пометил в туду * Added functional for Criminal Records UI * Дропаю это говно * Рабочая версия крим консоли * Fuull functional * Added radio * Arrest info feature * improve ui * another names * New texturem Sprite viewer * fix small names * Added login menu * Final fix. * ох * Убрал логгеры * fix Comments and Access to proto * moved dummy code, removed qustyions * added disposer() when window was close * Small fixes * Removed comments. Added DNA check for CriminalityHud * Small lol --------- Co-authored-by: DocNITE --- .../Overlays/ShowSecurityIconsSystem.cs | 84 ++++- .../UI/Controls/RecordCard.xaml | 74 +++++ .../UI/Controls/RecordCard.xaml.cs | 88 ++++++ .../UI/Controls/RecordIconButton.xaml | 28 ++ .../UI/Controls/RecordIconButton.xaml.cs | 36 +++ .../UI/Controls/RecordItem.xaml | 31 ++ .../UI/Controls/RecordItem.xaml.cs | 14 + .../UI/CriminalRecordsBoundUserInterface.cs | 75 +++++ .../UI/CriminalRecordsWindow.xaml | 99 ++++++ .../UI/CriminalRecordsWindow.xaml.cs | 291 ++++++++++++++++++ .../EntityCrimeRecordsOverlay.cs | 183 +++++++++++ .../ShowCrimeRecordsSystem.cs | 104 +++++++ .../Administration/Systems/AdminSystem.cs | 4 +- .../CrewManifest/CrewManifestSystem.cs | 12 +- Content.Server/Mind/Commands/RenameCommand.cs | 4 +- .../StationRecords/StationRecordSet.cs | 2 +- .../Systems/StationRecordsSystem.cs | 2 +- .../CriminalRecordsConsoleSystem.cs | 244 +++++++++++++++ .../Access/Systems/AccessReaderSystem.cs | 2 +- .../SharedStationRecordsSystem.cs | 4 +- .../StationRecords/StationRecordKey.cs | 7 +- .../CriminalRecordsConsoleComponent.cs | 110 +++++++ .../CriminalRecordsServerComponent.cs | 52 ++++ .../CriminalRecordsServerSystem.cs | 133 ++++++++ .../ShowCrimeRecordsComponent.cs | 10 + .../Locale/ru-RU/white/criminal-console.ftl | 39 +++ .../Machines/Computers/computers.yml | 27 ++ .../Prototypes/Recipes/Lathes/electronics.yml | 10 - .../Machines/criminal_records_server.yml | 73 +++++ .../White/StatusEffects/criminal_records.yml | 47 +++ .../Interface/records.rsi/discharged.png | Bin 0 -> 144 bytes .../Interface/records.rsi/incarcerated.png | Bin 0 -> 139 bytes .../White/Interface/records.rsi/meta.json | 27 ++ .../White/Interface/records.rsi/parolled.png | Bin 0 -> 150 bytes .../White/Interface/records.rsi/released.png | Bin 0 -> 143 bytes .../White/Interface/records.rsi/suspected.png | Bin 0 -> 141 bytes .../White/Interface/records.rsi/wanted.png | Bin 0 -> 147 bytes .../criminal_record_server.rsi/icon.png | Bin 0 -> 416 bytes .../criminal_record_server.rsi/meta.json | 34 ++ .../criminal_record_server.rsi/panel.png | Bin 0 -> 860 bytes .../criminal_record_server.rsi/unlit.png | Bin 0 -> 1885 bytes 41 files changed, 1922 insertions(+), 28 deletions(-) create mode 100644 Content.Client/White/CriminalRecords/UI/Controls/RecordCard.xaml create mode 100644 Content.Client/White/CriminalRecords/UI/Controls/RecordCard.xaml.cs create mode 100644 Content.Client/White/CriminalRecords/UI/Controls/RecordIconButton.xaml create mode 100644 Content.Client/White/CriminalRecords/UI/Controls/RecordIconButton.xaml.cs create mode 100644 Content.Client/White/CriminalRecords/UI/Controls/RecordItem.xaml create mode 100644 Content.Client/White/CriminalRecords/UI/Controls/RecordItem.xaml.cs create mode 100644 Content.Client/White/CriminalRecords/UI/CriminalRecordsBoundUserInterface.cs create mode 100644 Content.Client/White/CriminalRecords/UI/CriminalRecordsWindow.xaml create mode 100644 Content.Client/White/CriminalRecords/UI/CriminalRecordsWindow.xaml.cs create mode 100644 Content.Client/White/EntityCrimeRecords/EntityCrimeRecordsOverlay.cs create mode 100644 Content.Client/White/EntityCrimeRecords/ShowCrimeRecordsSystem.cs create mode 100644 Content.Server/White/CriminalRecords/CriminalRecordsConsoleSystem.cs create mode 100644 Content.Shared/White/CriminalRecords/CriminalRecordsConsoleComponent.cs create mode 100644 Content.Shared/White/CriminalRecords/CriminalRecordsServerComponent.cs create mode 100644 Content.Shared/White/CriminalRecords/CriminalRecordsServerSystem.cs create mode 100644 Content.Shared/White/EntityCrimeRecords/ShowCrimeRecordsComponent.cs create mode 100644 Resources/Locale/ru-RU/white/criminal-console.ftl create mode 100644 Resources/Prototypes/White/Entities/Structures/Machines/criminal_records_server.yml create mode 100644 Resources/Prototypes/White/StatusEffects/criminal_records.yml create mode 100644 Resources/Textures/White/Interface/records.rsi/discharged.png create mode 100644 Resources/Textures/White/Interface/records.rsi/incarcerated.png create mode 100644 Resources/Textures/White/Interface/records.rsi/meta.json create mode 100644 Resources/Textures/White/Interface/records.rsi/parolled.png create mode 100644 Resources/Textures/White/Interface/records.rsi/released.png create mode 100644 Resources/Textures/White/Interface/records.rsi/suspected.png create mode 100644 Resources/Textures/White/Interface/records.rsi/wanted.png create mode 100644 Resources/Textures/White/Structures/Machines/criminal_record_server.rsi/icon.png create mode 100644 Resources/Textures/White/Structures/Machines/criminal_record_server.rsi/meta.json create mode 100644 Resources/Textures/White/Structures/Machines/criminal_record_server.rsi/panel.png create mode 100644 Resources/Textures/White/Structures/Machines/criminal_record_server.rsi/unlit.png diff --git a/Content.Client/Overlays/ShowSecurityIconsSystem.cs b/Content.Client/Overlays/ShowSecurityIconsSystem.cs index 77c14c5ef0..f374f58760 100644 --- a/Content.Client/Overlays/ShowSecurityIconsSystem.cs +++ b/Content.Client/Overlays/ShowSecurityIconsSystem.cs @@ -1,10 +1,13 @@ +using Content.Client.White.EntityCrimeRecords; using Content.Shared.Access.Components; using Content.Shared.Access.Systems; +using Content.Shared.Inventory; using Content.Shared.Mindshield.Components; using Content.Shared.Overlays; using Content.Shared.PDA; using Content.Shared.StatusIcon; using Content.Shared.StatusIcon.Components; +using Content.Shared.White.CriminalRecords; using Robust.Shared.Prototypes; namespace Content.Client.Overlays; @@ -13,6 +16,11 @@ public sealed class ShowSecurityIconsSystem : EquipmentHudSystem] private const string JobIconForNoId = "JobIconNoId"; @@ -74,8 +82,82 @@ public sealed class ShowSecurityIconsSystem : EquipmentHudSystem "CriminalRecordIconDischarged", + EnumCriminalRecordType.Incarcerated => "CriminalRecordIconIncarcerated", + EnumCriminalRecordType.Parolled => "CriminalRecordIconParolled", + EnumCriminalRecordType.Suspected => "CriminalRecordIconSuspected", + EnumCriminalRecordType.Wanted => "CriminalRecordIconWanted", + _ => "CriminalRecordIconReleased" + }; + + if (_prototypeMan.TryIndex(protoId, out var recordIcon)) + result.Add(recordIcon); + // WD EDIT END return result; } + + // WD EDIT START + private bool GetRecord(EntityUid uid, out EnumCriminalRecordType type) + { + if (!_entManager.TryGetComponent(uid, out MetaDataComponent? meta)) + { + type = EnumCriminalRecordType.Released; + return false; + } + + var serverList = _entManager.EntityQuery(); + foreach (var server in serverList) + { + // if all good - check avaible records + foreach (var (key, info) in server.Cache) + { + // Check id + if (_inventorySystem.TryGetSlotEntity(uid, "id", out var idUid)) + { + // PDA + if (_entManager.TryGetComponent(idUid, out PdaComponent? pda) && + _entManager.TryGetComponent(pda.ContainedId, out IdCardComponent? idCard)) + { + if (idCard.FullName == info.StationRecord.Name && + idCard.JobTitle == info.StationRecord.JobTitle) + { + type = info.CriminalType; + return true; + } + } + // ID Card + if (_entManager.TryGetComponent(idUid, out IdCardComponent? id)) + { + idCard = id; + if (idCard.FullName == info.StationRecord.Name && + idCard.JobTitle == info.StationRecord.JobTitle) + { + type = info.CriminalType; + return true; + } + } + } + // Check DNA (Dirty Nanotrasen tehnology lol) + // And yeah, he can't check - is pulled mask or not + // it's only Content.Server logic, idk hot it impl to Content.Client + if (_parentSystem.CanIdentityName(uid) != meta.EntityName) + continue; + if (meta.EntityName != info.StationRecord.Name) + continue; + type = info.CriminalType; + return true; + } + } + + type = EnumCriminalRecordType.Released; + return false; + } + // WD EDIT END } diff --git a/Content.Client/White/CriminalRecords/UI/Controls/RecordCard.xaml b/Content.Client/White/CriminalRecords/UI/Controls/RecordCard.xaml new file mode 100644 index 0000000000..c6d65aba54 --- /dev/null +++ b/Content.Client/White/CriminalRecords/UI/Controls/RecordCard.xaml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Content.Client/White/CriminalRecords/UI/Controls/RecordCard.xaml.cs b/Content.Client/White/CriminalRecords/UI/Controls/RecordCard.xaml.cs new file mode 100644 index 0000000000..9b80eb2962 --- /dev/null +++ b/Content.Client/White/CriminalRecords/UI/Controls/RecordCard.xaml.cs @@ -0,0 +1,88 @@ +using Content.Shared.StationRecords; +using Content.Shared.White.CriminalRecords; +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.XAML; +using Robust.Shared.Input; +using Robust.Shared.Utility; + +namespace Content.Client.White.CriminalRecords.UI.Controls; + +[GenerateTypedNameReferences, Virtual] +public partial class RecordCard : Control +{ + private FormattedMessage _description = new FormattedMessage(); + public bool _canDoEvent = false; + + public Action? OnTextBindDown; + + public RecordCard() + { + RobustXamlLoader.Load(this); + + StatusOption.AddItem(Loc.GetString("criminal-status-released"), (int)EnumCriminalRecordType.Released); + StatusOption.AddItem(Loc.GetString("criminal-status-discharged"), (int)EnumCriminalRecordType.Discharged); + StatusOption.AddItem(Loc.GetString("criminal-status-parolled"), (int)EnumCriminalRecordType.Parolled); + StatusOption.AddItem(Loc.GetString("criminal-status-suspected"), (int)EnumCriminalRecordType.Suspected); + StatusOption.AddItem(Loc.GetString("criminal-status-wanted"), (int)EnumCriminalRecordType.Wanted); + StatusOption.AddItem(Loc.GetString("criminal-status-incarcerated"), (int)EnumCriminalRecordType.Incarcerated); + + Input.OnKeyBindDown += args => + { + if (args.Function == EngineKeyFunctions.TextSubmit) + { + UpdateReasonController(); + args.Handle(); + } + }; + + EditReason.OnPressed += args => + { + UpdateReasonController(); + }; + } + + public void InitializeStatusOption(StationRecordKey key, Dictionary cache) + { + if (cache != null) + { + foreach (var (kkey, info) in cache) + { + if (kkey.Id == key.Id) + { + StatusOption.SelectId((int) info.CriminalType); + break; + } + } + } + } + + private void UpdateReasonController() + { + if (ReasonWritten.Visible) + { + var text = ReasonWritten.GetMessage(); + ReasonWritten.SetMessage(""); + ReasonWritten.Visible = false; + if (text != null) + { + Input.CursorPosition = new TextEdit.CursorPos(); + Input.InsertAtCursor(text); + } + InputContainer.Visible = true; + } + else + { + var text = Rope.Collapse(Input.TextRope); + OnTextBindDown?.Invoke(text); + Input.TextRope = Rope.Leaf.Empty; + Input.CursorPosition = new TextEdit.CursorPos(0, TextEdit.LineBreakBias.Top); + // close input and open label + InputContainer.Visible = false; + // for label + ReasonWritten.SetMessage(text); + ReasonWritten.Visible = true; + } + } +} diff --git a/Content.Client/White/CriminalRecords/UI/Controls/RecordIconButton.xaml b/Content.Client/White/CriminalRecords/UI/Controls/RecordIconButton.xaml new file mode 100644 index 0000000000..2f07f8ab37 --- /dev/null +++ b/Content.Client/White/CriminalRecords/UI/Controls/RecordIconButton.xaml @@ -0,0 +1,28 @@ + + + + + + + diff --git a/Content.Client/White/CriminalRecords/UI/Controls/RecordIconButton.xaml.cs b/Content.Client/White/CriminalRecords/UI/Controls/RecordIconButton.xaml.cs new file mode 100644 index 0000000000..8f1b995c83 --- /dev/null +++ b/Content.Client/White/CriminalRecords/UI/Controls/RecordIconButton.xaml.cs @@ -0,0 +1,36 @@ +using Robust.Client.AutoGenerated; +using Robust.Client.ResourceManagement; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.XAML; +using Robust.Client.Utility; +using Robust.Shared.Utility; + +namespace Content.Client.White.CriminalRecords.UI.Controls; + +[GenerateTypedNameReferences, Virtual] +public partial class RecordIconButton : Control +{ + [Dependency] private readonly IResourceCache _resourceCache = default!; + + public string Icon + { + set + { + var path = new ResPath(value); // /Textures/Interface/VerbIcons/close.svg.192dpi.png + var specifier = new SpriteSpecifier.Texture(path); + LIcon.Texture = specifier.Frame0(); + } + } + + public string Label + { + set => LLabel.SetMessage(value); + } + + public RecordIconButton() + { + RobustXamlLoader.Load(this); + IoCManager.InjectDependencies(this); + } +} diff --git a/Content.Client/White/CriminalRecords/UI/Controls/RecordItem.xaml b/Content.Client/White/CriminalRecords/UI/Controls/RecordItem.xaml new file mode 100644 index 0000000000..23196cb9fe --- /dev/null +++ b/Content.Client/White/CriminalRecords/UI/Controls/RecordItem.xaml @@ -0,0 +1,31 @@ + + + + + + + diff --git a/Content.Client/White/CriminalRecords/UI/Controls/RecordItem.xaml.cs b/Content.Client/White/CriminalRecords/UI/Controls/RecordItem.xaml.cs new file mode 100644 index 0000000000..ac91be8c09 --- /dev/null +++ b/Content.Client/White/CriminalRecords/UI/Controls/RecordItem.xaml.cs @@ -0,0 +1,14 @@ +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.XAML; + +namespace Content.Client.White.CriminalRecords.UI.Controls; + +[GenerateTypedNameReferences, Virtual] +public partial class RecordItem : Control +{ + public RecordItem() + { + RobustXamlLoader.Load(this); + } +} diff --git a/Content.Client/White/CriminalRecords/UI/CriminalRecordsBoundUserInterface.cs b/Content.Client/White/CriminalRecords/UI/CriminalRecordsBoundUserInterface.cs new file mode 100644 index 0000000000..f780c59c9d --- /dev/null +++ b/Content.Client/White/CriminalRecords/UI/CriminalRecordsBoundUserInterface.cs @@ -0,0 +1,75 @@ +using Content.Shared.Containers.ItemSlots; +using Content.Shared.StationRecords; +using Content.Shared.White.CriminalRecords; +using Robust.Client.GameObjects; +using Robust.Shared.Input; +using Robust.Shared.Utility; + +namespace Content.Client.White.CriminalRecords.UI; + +public sealed class CriminalRecordsBoundUserInterface : BoundUserInterface +{ + private CriminalRecordsWindow? _window = default!; + + public CriminalRecordsBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) + {} + + protected override void Open() + { + base.Open(); + + _window = new(); + _window.OnKeySelected += OnKeySelected; + _window.OnStatusSelected += OnStatusSelected; + _window.OnTextBindDown += OnTextEntered; + _window.LogOutButton.Controller.OnPressed += _ => + { + SendMessage(new ItemSlotButtonPressedEvent(CriminalRecordsConsoleComponent.IdSlotId)); + }; + _window.LogInButton.Controller.OnPressed += _ => + { + SendMessage(new ItemSlotButtonPressedEvent(CriminalRecordsConsoleComponent.IdSlotId)); + }; + _window.OnClose += Close; + + _window.OpenCentered(); + } + + private void OnKeySelected(StationRecordKey key) + { + SendMessage(new SelectCriminalRecord(key)); + } + + private void OnStatusSelected(StationRecordKey key, CriminalRecordInfo status) + { + SendMessage(new SelectCriminalStatus(key, status)); + } + + private void OnTextEntered(StationRecordKey key, string text) + { + if (!string.IsNullOrEmpty(text)) + { + SendMessage(new SelectCriminalReason(key, text)); + } + } + + protected override void UpdateState(BoundUserInterfaceState state) + { + base.UpdateState(state); + + if (state is not CriminalRecordsConsoleBuiState cast) + { + return; + } + + _window?.UpdateState(cast); + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + + _window?.Close(); + _window?.Dispose(); + } +} diff --git a/Content.Client/White/CriminalRecords/UI/CriminalRecordsWindow.xaml b/Content.Client/White/CriminalRecords/UI/CriminalRecordsWindow.xaml new file mode 100644 index 0000000000..e1ee972723 --- /dev/null +++ b/Content.Client/White/CriminalRecords/UI/CriminalRecordsWindow.xaml @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Content.Client/White/CriminalRecords/UI/CriminalRecordsWindow.xaml.cs b/Content.Client/White/CriminalRecords/UI/CriminalRecordsWindow.xaml.cs new file mode 100644 index 0000000000..654e3330ac --- /dev/null +++ b/Content.Client/White/CriminalRecords/UI/CriminalRecordsWindow.xaml.cs @@ -0,0 +1,291 @@ +using System.Linq; +using Content.Client.Humanoid; +using Content.Client.Inventory; +using Content.Client.White.CriminalRecords.UI.Controls; +using Content.Shared.Access.Systems; +using Content.Shared.CrewManifest; +using Content.Shared.GameTicking; +using Content.Shared.Humanoid.Prototypes; +using Content.Shared.Preferences; +using Content.Shared.Radio.Components; +using Content.Shared.Roles; +using Content.Shared.StationRecords; +using Content.Shared.White.CriminalRecords; +using Robust.Client.AutoGenerated; +using Robust.Client.GameObjects; +using Robust.Client.ResourceManagement; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.CustomControls; +using Robust.Client.UserInterface.XAML; +using Robust.Client.Utility; +using Robust.Shared.Console; +using Robust.Shared.Input; +using Robust.Shared.Map; +using Robust.Shared.Prototypes; +using Robust.Shared.Utility; + +namespace Content.Client.White.CriminalRecords.UI; + +[GenerateTypedNameReferences] +public sealed partial class CriminalRecordsWindow : DefaultWindow +{ + [Dependency] private readonly IResourceCache _resourceCache = default!; + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + + public Action? OnKeySelected; + public Action? OnTextBindDown; + public Action? OnStatusSelected; + + public CriminalRecordsWindow() : base() + { + RobustXamlLoader.Load(this); + IoCManager.InjectDependencies(this); + } + + public void UpdateState(CriminalRecordsConsoleBuiState state) + { + // Check access AllAccess Security + var jobs = _prototypeManager.EnumeratePrototypes(); + var canAccess = state.IsAllowed; + // Check if exists card or not + if (state.ContainedId == null || !canAccess) + { + var messageHint = new FormattedMessage(); + messageHint.AddMarkup(Loc.GetString("criminal-login-hint", ("name", Loc.GetString("criminal-login-in")))); + messageHint.AddMarkup("\n\n"); + messageHint.AddMarkup(Loc.GetString("criminal-login-warn")); + LoginHint.SetMessage(messageHint); + MainContent.Visible = false; + NonAccessContent.Visible = true; + return; + } + + MainContent.Visible = true; + NonAccessContent.Visible = false; + + // Init header panel + UserLabel.SetMessage(Loc.GetString("criminal-login-info", + ("user", (state.ContainedId.FullName ?? string.Empty) + ", " + + (state.ContainedId.JobTitle ?? string.Empty) ))); + + // Make crew list + Populate(state, state.RecordListing); + + // Make card + if (state is { SelectedKey: not null, Record: not null }) + { + CreateRecordCard(state, state.SelectedKey.Value, state.Record); + } + } + + public void Populate(CriminalRecordsConsoleBuiState State, Dictionary? RecordListing) + { + if (RecordListing == null) + return; + + // clear govno from list + RecordsListContainer.RemoveAllChildren(); + + foreach (var (recordKey, name) in RecordListing) + { + var element = CreateRecordItem(State, recordKey, name); + element.ButtonElement.OnPressed += _ => + { + OnKeySelected?.Invoke(recordKey); + }; + } + } + + private CriminalRecordInfo? GetRecord(StationRecordKey Key, Dictionary Cache) + { + foreach (var (key, info) in Cache) + { + if (Key.Id == key.Id) + { + return info; + } + } + return null; + } + + private Color GetColor(StationRecordKey Key, Dictionary Cache) + { + var info = GetRecord(Key, Cache); + if (info == null) + return new Color(0,0,0); + + switch (info.CriminalType) + { + case EnumCriminalRecordType.Released: + return new Color(14, 106, 254); + case EnumCriminalRecordType.Discharged: + return new Color(14,106,254); + case EnumCriminalRecordType.Parolled: + return new Color(151,196,66); + case EnumCriminalRecordType.Suspected: + return new Color(217,126,35); + case EnumCriminalRecordType.Wanted: + return new Color(190, 50 ,50); + case EnumCriminalRecordType.Incarcerated: + return new Color(196,164,114); + } + return new Color(0,0,0); + } + + private RecordItem CreateRecordItem(CriminalRecordsConsoleBuiState State, StationRecordKey Key, string Name) + { + var record = new RecordItem(); + record.VerticalAlignment = Control.VAlignment.Top; + if (State.Cache != null && GetRecord(Key, State.Cache) != null) + { + record.SideLineElement.ModulateSelfOverride = GetColor(Key, State.Cache); + } + else + { + record.SideLineElement.ModulateSelfOverride = new Color(0, 0 ,0); + } + record.NameLabel.SetMessage(Name); + // append element into list + RecordsListContainer.AddChild(record); + // result + return record; + } + + private RecordCard CreateRecordCard(CriminalRecordsConsoleBuiState State, StationRecordKey Key, GeneralStationRecord Record) + { + var card = new RecordCard(); + // set color + if (State.Cache != null && GetRecord(Key, State.Cache) != null) + { + card.SideLineElement.ModulateSelfOverride = GetColor(Key, State.Cache); + } + else + { + card.SideLineElement.ModulateSelfOverride = new Color(0, 0 ,0); + } + // name + card.CharacterNameLabel.Text = Record.Name + ", " + Record.JobTitle; + // job icon + var path = new ResPath("/Textures/Interface/Misc/job_icons.rsi"); + _resourceCache.TryGetResource(path, out RSIResource? rsi); + + if (rsi != null) + { + if (rsi.RSI.TryGetState(Record.JobIcon, out _)) + { + var specifier = new SpriteSpecifier.Rsi(path, Record.JobIcon); + card.JobIcon.Texture = specifier.Frame0(); + } + else if (rsi.RSI.TryGetState("Unknown", out _)) + { + var specifier = new SpriteSpecifier.Rsi(path, "Unknown"); + card.JobIcon.Texture = specifier.Frame0(); + } + } + // status icon + card.ViewIcon.SetEntity(CreateCharacterDummy(Record)); + // info + var dnaInfo = ""; + if (Record.DNA != null) + dnaInfo = Record.DNA; + + var fingerprintInfo = ""; + if (Record.Fingerprint != null) + fingerprintInfo = Record.Fingerprint; + + var message = new FormattedMessage(); + message.AddMarkup(Loc.GetString("criminal-dna-name")); + message.AddMarkup("\n"); + message.AddMarkup(Loc.GetString("criminal-dna-desc", + ("color", new Color(171,129,222).ToHex()), ("info", dnaInfo) )); + message.AddMarkup("\n"); + message.AddMarkup(Loc.GetString("criminal-fingerprint-name")); + message.AddMarkup("\n"); + message.AddMarkup(Loc.GetString("criminal-fingerprint-desc", + ("color", new Color(171,129,222).ToHex()), ("info", fingerprintInfo) )); + card.DetailLabel.SetMessage(message); + // clear and aapend + RecordCardContainer.DisposeAllChildren(); + RecordCardContainer.AddChild(card); + // some shit (like change status) + card.StatusOption.OnItemSelected += eventArgs => + { + if (!card._canDoEvent) + return; + + var record = new CriminalRecordInfo(Record, (EnumCriminalRecordType)eventArgs.Id, card.ReasonWritten.GetMessage() ?? string.Empty); + OnStatusSelected?.Invoke(Key, record); + card.StatusOption.SelectId(eventArgs.Id); + }; + // init status option + if (State.Cache != null) + { + card.InitializeStatusOption(Key, State.Cache); + card._canDoEvent = true; + } + // init reason text + if (State.Cache != null) + { + var criminalInfo = GetRecord(Key, State.Cache); + if (criminalInfo != null) + card.ReasonWritten.SetMessage(criminalInfo.Reason); + } + + card.OnTextBindDown += args => + { + OnTextBindDown?.Invoke(Key, args); + }; + // rsult + return card; + } + + private EntityUid CreateCharacterDummy(GeneralStationRecord Record) + { + IEntityManager entityManager = IoCManager.Resolve(); + IPrototypeManager prototypeManager = IoCManager.Resolve(); + HumanoidAppearanceSystem appearanceSystem = IoCManager.Resolve().GetEntitySystem(); + + var profile = Record.Profile ?? new HumanoidCharacterProfile(); + var _previewDummy = entityManager.SpawnEntity(prototypeManager.Index(profile.Species).DollPrototype, MapCoordinates.Nullspace); + appearanceSystem.LoadProfile(_previewDummy, profile); + GiveDummyJobClothes(_previewDummy, Record.JobPrototype, profile); + + return _previewDummy; + } + + private void GiveDummyJobClothes(EntityUid dummy, string jobPrototype, HumanoidCharacterProfile profile) + { + IEntityManager entityManager = IoCManager.Resolve(); + IPrototypeManager prototypeManager = IoCManager.Resolve(); + ClientInventorySystem inventorySystem = IoCManager.Resolve().GetEntitySystem(); + + // ReSharper disable once NullCoalescingConditionIsAlwaysNotNullAccordingToAPIContract (what is resharper smoking?) + var job = prototypeManager.Index(jobPrototype ?? SharedGameTicker.FallbackOverflowJob); + + if (job.StartingGear != null && inventorySystem.TryGetSlots(dummy, out var slots)) + { + var gear = prototypeManager.Index(job.StartingGear); + + foreach (var slot in slots) + { + var itemType = gear.GetGear(slot.Name, profile); + if (inventorySystem.TryUnequip(dummy, slot.Name, out var unequippedItem, true, true)) + { + entityManager.DeleteEntity(unequippedItem.Value); + } + + if (itemType != string.Empty) + { + var item = entityManager.SpawnEntity(itemType, MapCoordinates.Nullspace); + inventorySystem.TryEquip(dummy, item, slot.Name, true, true); + } + } + } + } + + private void GetAccess() + { + //_accessReader.FindAccessTags(item).ToArray(); + } +} diff --git a/Content.Client/White/EntityCrimeRecords/EntityCrimeRecordsOverlay.cs b/Content.Client/White/EntityCrimeRecords/EntityCrimeRecordsOverlay.cs new file mode 100644 index 0000000000..7a9a15ed8b --- /dev/null +++ b/Content.Client/White/EntityCrimeRecords/EntityCrimeRecordsOverlay.cs @@ -0,0 +1,183 @@ +using Content.Shared.Humanoid; +using Content.Shared.Inventory; +using Content.Shared.PDA; +using Robust.Client.GameObjects; +using Robust.Client.Graphics; +using Robust.Shared.Enums; +using Robust.Shared.Prototypes; +using Robust.Shared.Utility; +using Content.Shared.Access.Components; +using Content.Shared.Roles; +using System.Diagnostics.CodeAnalysis; +using System.Numerics; +using Content.Shared.White.CriminalRecords; +using Robust.Shared.Map; + +namespace Content.Client.White.EntityCrimeRecords; + +public sealed class EntityCrimeRecordsOverlay : Overlay +{ + private readonly IEntityManager _entManager; + private readonly SharedTransformSystem _transform; + private readonly IPrototypeManager _prototypeManager; + private readonly InventorySystem _inventorySystem; + private readonly ShaderInstance _shader; + private readonly ShowCrimeRecordsSystem _parentSystem; + public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowFOV; + + public EntityCrimeRecordsOverlay( + IEntityManager entManager, + IPrototypeManager protoManager, + InventorySystem inventorySystem, + ShowCrimeRecordsSystem showCrimSystem) + { + _entManager = entManager; + _prototypeManager = protoManager; + _inventorySystem = inventorySystem; + _parentSystem = showCrimSystem; + _transform = _entManager.EntitySysManager.GetEntitySystem(); + _shader = protoManager.Index("unshaded").Instance(); + } + + protected override void Draw(in OverlayDrawArgs args) + { + var handle = args.WorldHandle; + var rotation = args.Viewport.Eye?.Rotation ?? Angle.Zero; + var spriteQuery = _entManager.GetEntityQuery(); + var xformQuery = _entManager.GetEntityQuery(); + + const float scale = 1f; + var scaleMatrix = Matrix3.CreateScale(new Vector2(scale, scale)); + var rotationMatrix = Matrix3.CreateRotation(-rotation); + handle.UseShader(_shader); + // da pizda + this.ZIndex = this.ZIndex -= 1; + + foreach (var hum in _entManager.EntityQuery(true)) + { + if (!xformQuery.TryGetComponent(hum.Owner, out var xform) || + xform.MapID != args.MapId) + { + continue; + } + + var worldPosition = _transform.GetWorldPosition(xform); + var worldMatrix = Matrix3.CreateTranslation(worldPosition); + + Matrix3.Multiply(scaleMatrix, worldMatrix, out var scaledWorld); + Matrix3.Multiply(rotationMatrix, scaledWorld, out var matty); + + handle.SetTransform(matty); + + if (GetRecord(hum.Owner, args.MapId, out var criminalType)) + { + var icon = "released"; + switch (criminalType) + { + case EnumCriminalRecordType.Released: + icon = "released"; + break; + case EnumCriminalRecordType.Discharged: + icon = "discharged"; + break; + case EnumCriminalRecordType.Parolled: + icon = "parolled"; + break; + case EnumCriminalRecordType.Suspected: + icon = "suspected"; + break; + case EnumCriminalRecordType.Wanted: + icon = "wanted"; + break; + case EnumCriminalRecordType.Incarcerated: + icon = "incarcerated"; + break; + } + + var sprite_icon = new SpriteSpecifier.Rsi(new ResPath("/Textures/White/Interface/records.rsi"), icon); + var _iconTexture = _entManager.EntitySysManager.GetEntitySystem().Frame0(sprite_icon); + + float yOffset; + float xOffset; + if (spriteQuery.TryGetComponent(hum.Owner, out var sprite)) + { + yOffset = sprite.Bounds.Height + 7f - 7f; //sprite.Bounds.Height + 7f; + xOffset = sprite.Bounds.Width - 17f; //sprite.Bounds.Width + 7f; + } + else + { + yOffset = 1f; + xOffset = 1f; + } + + // Position above the entity (we've already applied the matrix transform to the entity itself) + // Offset by the texture size for every do_after we have. + var position = new Vector2(xOffset / EyeManager.PixelsPerMeter, + yOffset / EyeManager.PixelsPerMeter); + + // Draw the underlying bar texture + if (sprite != null && !sprite.ContainerOccluded) + handle.DrawTexture(_iconTexture, position); + } + } + + handle.UseShader(null); + handle.SetTransform(Matrix3.Identity); + } + + private bool GetRecord(EntityUid uid, MapId mapId, out EnumCriminalRecordType type) + { + if (!_entManager.TryGetComponent(uid, out MetaDataComponent? meta)) + { + type = EnumCriminalRecordType.Released; + return false; + } + + var serverList = _entManager.EntityQuery(); + foreach (var server in serverList) + { + // if all good - check avaible records + foreach (var (key, info) in server.Cache) + { + // Check id + if (_inventorySystem.TryGetSlotEntity(uid, "id", out var idUid)) + { + // PDA + if (_entManager.TryGetComponent(idUid, out PdaComponent? pda) && + _entManager.TryGetComponent(pda.ContainedId, out IdCardComponent? idCard)) + { + if (idCard.FullName == info.StationRecord.Name && + idCard.JobTitle == info.StationRecord.JobTitle) + { + type = info.CriminalType; + return true; + } + } + // ID Card + if (_entManager.TryGetComponent(idUid, out IdCardComponent? id)) + { + idCard = id; + if (idCard.FullName == info.StationRecord.Name && + idCard.JobTitle == info.StationRecord.JobTitle) + { + type = info.CriminalType; + return true; + } + } + } + // Check DNA (Dirty Nanotrasen tehnology lol) + // And yeah, he can't check - is pulled mask or not + // it's only Content.Server logic, idk hot it impl to Content.Client + if (_parentSystem.CanIdentityName(uid) != meta.EntityName) + continue; + if (meta.EntityName != info.StationRecord.Name) + continue; + type = info.CriminalType; + return true; + } + } + + type = EnumCriminalRecordType.Released; + return false; + } +} diff --git a/Content.Client/White/EntityCrimeRecords/ShowCrimeRecordsSystem.cs b/Content.Client/White/EntityCrimeRecords/ShowCrimeRecordsSystem.cs new file mode 100644 index 0000000000..e3c3e1b5da --- /dev/null +++ b/Content.Client/White/EntityCrimeRecords/ShowCrimeRecordsSystem.cs @@ -0,0 +1,104 @@ +using Content.Shared.White.EntityCrimeRecords; +using Content.Shared.GameTicking; +using Content.Shared.Humanoid; +using Content.Shared.IdentityManagement.Components; +using Robust.Client.Player; +using Robust.Client.Graphics; +using Robust.Client.GameObjects; +using Robust.Shared.Prototypes; +using Content.Shared.Inventory; +using Robust.Shared.Enums; +using Robust.Shared.Player; + +namespace Content.Client.White.EntityCrimeRecords +{ + public sealed class ShowCrimeRecordsSystem : EntitySystem + { + [Dependency] private readonly IPlayerManager _player = default!; + [Dependency] private readonly IPrototypeManager _protoMan = default!; + [Dependency] private readonly IOverlayManager _overlayMan = default!; + [Dependency] private readonly InventorySystem _inventorySystem = default!; + + private EntityCrimeRecordsOverlay _overlay = default!; + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnRemove); + SubscribeLocalEvent(OnPlayerAttached); + SubscribeLocalEvent(OnPlayerDetached); + SubscribeLocalEvent(OnRoundRestart); + + _overlay = new(EntityManager, _protoMan, _inventorySystem, this); + } + + private void OnInit(EntityUid uid, ShowCrimeRecordsComponent component, ComponentInit args) + { + if (_player.LocalPlayer?.ControlledEntity == uid) + { + _overlayMan.AddOverlay(_overlay); + } + } + private void OnRemove(EntityUid uid, ShowCrimeRecordsComponent component, ComponentRemove args) + { + if (_player.LocalPlayer?.ControlledEntity == uid) + { + _overlayMan.RemoveOverlay(_overlay); + } + } + + private void OnPlayerAttached(EntityUid uid, ShowCrimeRecordsComponent component, PlayerAttachedEvent args) + { + _overlayMan.AddOverlay(_overlay); + } + + private void OnPlayerDetached(EntityUid uid, ShowCrimeRecordsComponent component, PlayerDetachedEvent args) + { + _overlayMan.RemoveOverlay(_overlay); + } + + private void OnRoundRestart(RoundRestartCleanupEvent args) + { + _overlayMan.RemoveOverlay(_overlay); + } + + public string CanIdentityName(EntityUid target) + { + var representation = GetIdentityRepresentation(target); + var ev = new SeeIdentityAttemptEvent(); + + RaiseLocalEvent(target, ev); + return representation.ToStringKnown(!ev.Cancelled); + } + + /// + /// 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. + /// + private IdentityRepresentation GetIdentityRepresentation(EntityUid target, + InventoryComponent? inventory=null, + HumanoidAppearanceComponent? appearance=null) + { + var age = 18; + var gender = Gender.Epicene; + + // 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; + } + + var trueName = Name(target); + if (!Resolve(target, ref inventory, false)) + return new(trueName, gender, age.ToString(), string.Empty); + + string? presumedJob = null; + string? presumedName = null; + + // If it didn't find a job, that's fine. + return new IdentityRepresentation(trueName, gender, age.ToString(), presumedName, presumedJob); + } + } +} diff --git a/Content.Server/Administration/Systems/AdminSystem.cs b/Content.Server/Administration/Systems/AdminSystem.cs index 966bff2f71..94f5c2e164 100644 --- a/Content.Server/Administration/Systems/AdminSystem.cs +++ b/Content.Server/Administration/Systems/AdminSystem.cs @@ -349,7 +349,7 @@ namespace Content.Server.Administration.Systems if (TryComp(item, out PdaComponent? pda) && TryComp(pda.ContainedId, out StationRecordKeyStorageComponent? keyStorage) && keyStorage.Key is { } key && - _stationRecords.TryGetRecord(key.OriginStation, key, out GeneralStationRecord? record)) + _stationRecords.TryGetRecord(GetEntity(key.OriginStation), key, out GeneralStationRecord? record)) { if (TryComp(entity, out DnaComponent? dna) && dna.DNA != record.DNA) @@ -363,7 +363,7 @@ namespace Content.Server.Administration.Systems continue; } - _stationRecords.RemoveRecord(key.OriginStation, key); + _stationRecords.RemoveRecord(GetEntity(key.OriginStation), key); Del(item); } } diff --git a/Content.Server/CrewManifest/CrewManifestSystem.cs b/Content.Server/CrewManifest/CrewManifestSystem.cs index 4c4f17f61d..2499647386 100644 --- a/Content.Server/CrewManifest/CrewManifestSystem.cs +++ b/Content.Server/CrewManifest/CrewManifestSystem.cs @@ -73,20 +73,20 @@ public sealed class CrewManifestSystem : EntitySystem // wrt the amount of players readied up. private void AfterGeneralRecordCreated(AfterGeneralRecordCreatedEvent ev) { - BuildCrewManifest(ev.Key.OriginStation); - UpdateEuis(ev.Key.OriginStation); + BuildCrewManifest(GetEntity(ev.Key.OriginStation)); + UpdateEuis(GetEntity(ev.Key.OriginStation)); } private void OnRecordModified(RecordModifiedEvent ev) { - BuildCrewManifest(ev.Key.OriginStation); - UpdateEuis(ev.Key.OriginStation); + BuildCrewManifest(GetEntity(ev.Key.OriginStation)); + UpdateEuis(GetEntity(ev.Key.OriginStation)); } private void OnRecordRemoved(RecordRemovedEvent ev) { - BuildCrewManifest(ev.Key.OriginStation); - UpdateEuis(ev.Key.OriginStation); + BuildCrewManifest(GetEntity(ev.Key.OriginStation)); + UpdateEuis(GetEntity(ev.Key.OriginStation)); } private void OnBoundUiClose(EntityUid uid, CrewManifestViewerComponent component, BoundUIClosedEvent ev) diff --git a/Content.Server/Mind/Commands/RenameCommand.cs b/Content.Server/Mind/Commands/RenameCommand.cs index bb7d89ddf5..f97e98e6eb 100644 --- a/Content.Server/Mind/Commands/RenameCommand.cs +++ b/Content.Server/Mind/Commands/RenameCommand.cs @@ -72,14 +72,14 @@ public sealed class RenameCommand : IConsoleCommand { var origin = keyStorage.Key.Value.OriginStation; - if (recordsSystem.TryGetRecord(origin, + if (recordsSystem.TryGetRecord(_entManager.GetEntity(origin), keyStorage.Key.Value, out var generalRecord)) { generalRecord.Name = name; } - recordsSystem.Synchronize(origin); + recordsSystem.Synchronize(_entManager.GetEntity(origin)); } } } diff --git a/Content.Server/StationRecords/StationRecordSet.cs b/Content.Server/StationRecords/StationRecordSet.cs index 2f6b220a78..1c8d1dbb41 100644 --- a/Content.Server/StationRecords/StationRecordSet.cs +++ b/Content.Server/StationRecords/StationRecordSet.cs @@ -56,7 +56,7 @@ public sealed partial class StationRecordSet /// /// Entry to add. /// Type of the entry that's being added. - public StationRecordKey AddRecordEntry(EntityUid station, T entry) + public StationRecordKey AddRecordEntry(NetEntity station, T entry) { if (entry == null) return StationRecordKey.Invalid; diff --git a/Content.Server/StationRecords/Systems/StationRecordsSystem.cs b/Content.Server/StationRecords/Systems/StationRecordsSystem.cs index 97026c0215..457e95463f 100644 --- a/Content.Server/StationRecords/Systems/StationRecordsSystem.cs +++ b/Content.Server/StationRecords/Systems/StationRecordsSystem.cs @@ -224,7 +224,7 @@ public sealed class StationRecordsSystem : SharedStationRecordsSystem if (!Resolve(station, ref records)) return StationRecordKey.Invalid; - return records.Records.AddRecordEntry(station, record); + return records.Records.AddRecordEntry(GetNetEntity(station), record); } /// diff --git a/Content.Server/White/CriminalRecords/CriminalRecordsConsoleSystem.cs b/Content.Server/White/CriminalRecords/CriminalRecordsConsoleSystem.cs new file mode 100644 index 0000000000..a6149a9469 --- /dev/null +++ b/Content.Server/White/CriminalRecords/CriminalRecordsConsoleSystem.cs @@ -0,0 +1,244 @@ +using System.Linq; +using Content.Server.Radio.EntitySystems; +using Content.Server.Station.Systems; +using Content.Server.StationRecords; +using Content.Server.StationRecords.Systems; +using Content.Shared.Access.Components; +using Content.Shared.Access.Systems; +using Content.Shared.Containers.ItemSlots; +using Content.Shared.PDA; +using Content.Shared.Radio; +using Content.Shared.StationRecords; +using Content.Shared.White.CriminalRecords; +using Robust.Server.GameObjects; +using Robust.Shared.Containers; +using Robust.Shared.Prototypes; + +namespace Content.Server.White.CriminalRecords; + +public sealed class CriminalRecordsConsoleSystem : EntitySystem +{ + [Dependency] private readonly IPrototypeManager _prototype = default!; + + [Dependency] private readonly ItemSlotsSystem _itemSlotsSystem = default!; + [Dependency] private readonly UserInterfaceSystem _userInterface = default!; + [Dependency] private readonly StationSystem _stationSystem = default!; + [Dependency] private readonly StationRecordsSystem _stationRecordsSystem = default!; + [Dependency] private readonly AccessReaderSystem _accessReader = default!; + [Dependency] private readonly RadioSystem _radioSystem = default!; + + public override void Initialize() + { + SubscribeLocalEvent(OnComponentInit); + SubscribeLocalEvent(OnComponentRemove); + SubscribeLocalEvent(UpdateUserInterface); + SubscribeLocalEvent(OnItemInserted); + SubscribeLocalEvent(OnItemRemoved); + SubscribeLocalEvent(OnKeySelected); + SubscribeLocalEvent(OnStatusSelected); + SubscribeLocalEvent(OnReasonSelected); + SubscribeLocalEvent(UpdateUserInterface); + SubscribeLocalEvent(UpdateUserInterface); + } + + private void OnComponentInit(EntityUid uid, CriminalRecordsConsoleComponent component, ComponentInit args) + { + _itemSlotsSystem.AddItemSlot(uid, CriminalRecordsConsoleComponent.IdSlotId, component.IdSlot); + } + + private void OnComponentRemove(EntityUid uid, CriminalRecordsConsoleComponent component, ComponentRemove args) + { + _itemSlotsSystem.RemoveItemSlot(uid, component.IdSlot); + } + + private void UpdateUserInterface(EntityUid uid, CriminalRecordsConsoleComponent component, T ev) + { + UpdateUserInterface(uid, component); + } + + private void OnItemInserted(EntityUid uid, CriminalRecordsConsoleComponent component, EntInsertedIntoContainerMessage args) + { + if (args.Container.ID == CriminalRecordsConsoleComponent.IdSlotId) + component.ContainedID = CompOrNull(args.Entity); + + UpdateUserInterface(uid, component); + } + + private void OnItemRemoved(EntityUid uid, CriminalRecordsConsoleComponent component, EntRemovedFromContainerMessage args) + { + if (args.Container.ID == component.IdSlot.ID) + component.ContainedID = null; + + UpdateUserInterface(uid, component); + } + + private void OnKeySelected(EntityUid uid, CriminalRecordsConsoleComponent component, + SelectCriminalRecord msg) + { + component.ActiveKey = msg.SelectedKey; + UpdateUserInterface(uid, component); + } + + private void OnReasonSelected(EntityUid uid, CriminalRecordsConsoleComponent component, + SelectCriminalReason msg) + { + var hasServer = new EventCheckServer(); + RaiseLocalEvent(hasServer); + + if (!hasServer.Result) + return; + + var ev = new EventChangeReason(msg.SelectedKey, msg.Text); + RaiseLocalEvent(ev); + + UpdateUserInterface(uid, component); + } + + private void OnStatusSelected(EntityUid uid, CriminalRecordsConsoleComponent component, + SelectCriminalStatus msg) + { + if (msg.SelectedStatus == null) + return; + + var hasServer = new EventCheckServer(); + RaiseLocalEvent(hasServer); + + if (!hasServer.Result) + return; + + var messageId = "null"; + switch (msg.SelectedStatus.CriminalType) + { + case EnumCriminalRecordType.Released: + messageId = "criminal-targetchannel-set-released"; + break; + case EnumCriminalRecordType.Discharged: + messageId = "criminal-targetchannel-set-discharged"; + break; + case EnumCriminalRecordType.Parolled: + messageId = "criminal-targetchannel-set-parolled"; + break; + case EnumCriminalRecordType.Suspected: + messageId = "criminal-targetchannel-set-suspected"; + break; + case EnumCriminalRecordType.Wanted: + messageId = "criminal-targetchannel-set-wanted"; + break; + case EnumCriminalRecordType.Incarcerated: + messageId = "criminal-targetchannel-set-incarcerated"; + break; + } + + var message = ""; + + if (msg.SelectedStatus.Reason != string.Empty) + { + messageId += "-reason"; + message = Loc.GetString(messageId, + ("target", msg.SelectedStatus.StationRecord.Name), + ("reason", msg.SelectedStatus.Reason)); + } + else + { + message = Loc.GetString(messageId, + ("target", msg.SelectedStatus.StationRecord.Name)); + } + + _radioSystem.SendRadioMessage(uid, message, _prototype.Index(component.TargetChannel), uid); + + var ev = new EventChangeCache(msg.SelectedKey, msg.SelectedStatus); + RaiseLocalEvent(ev); + + UpdateUserInterface(uid, component); + } + + private void UpdateUserInterface(EntityUid uid, + CriminalRecordsConsoleComponent? console = null) + { + if (!Resolve(uid, ref console)) + { + return; + } + + Dirty(console); + + var owningStation = _stationSystem.GetOwningStation(uid); + + if (!TryComp(owningStation, out var stationRecordsComponent)) + { + CriminalRecordsConsoleBuiState state = new(null, null, null, null, null, false); //null + SetStateForInterface(uid, state); + return; + } + + var consoleRecords = + _stationRecordsSystem.GetRecordsOfType(owningStation.Value, stationRecordsComponent); + + var listing = new Dictionary(); + + foreach (var pair in consoleRecords) + { + listing.Add(pair.Item1, pair.Item2.Name); + } + + if (listing.Count == 0) + { + CriminalRecordsConsoleBuiState state = new(null, null, null, null, null, false); //console!.Filter + SetStateForInterface(uid, state); + return; + } + else if (listing.Count == 1) + { + console!.ActiveKey = listing.Keys.First(); + } + + GeneralStationRecord? record = null; + if (console!.ActiveKey != null) + { + _stationRecordsSystem.TryGetRecord(owningStation.Value, console.ActiveKey.Value, out record, + stationRecordsComponent); + } + + var serverEv = new EventGetCache(); + RaiseLocalEvent(serverEv); + + var idCardInfo = console.ContainedID != null ? new IdCardNetInfo(console.ContainedID.FullName, console.ContainedID.JobTitle) : null; + + CriminalRecordsConsoleBuiState newState = new(console.ActiveKey, record, listing, serverEv.Cache, idCardInfo, AccessCheck(console.ContainedID)); //console.Filter + SetStateForInterface(uid, newState); + } + + private void SetStateForInterface(EntityUid uid, CriminalRecordsConsoleBuiState newState) + { + var ui = _userInterface.GetUiOrNull(uid, CriminalRecordsConsoleKey.Key); + if (ui != null) + _userInterface.SetUiState(ui, newState); + } + + private bool AccessCheck(IdCardComponent? component) + { + if (component is null) + return false; + + var uid = component.Owner; + + if (!EntityManager.TryGetComponent(uid, out AccessComponent? reader)) + return false; + + foreach (var tag in reader.Tags) + { + var proto = _prototype.Index("ComputerCriminalRecords"); + proto.TryGetComponent(out AccessReaderComponent? access); + + if (access == null) + continue; + + if (access.AccessLists.SelectMany(list => list).Any(entry => entry == tag)) + { + return true; + } + } + + return false; + } +} diff --git a/Content.Shared/Access/Systems/AccessReaderSystem.cs b/Content.Shared/Access/Systems/AccessReaderSystem.cs index c5bceb4899..884e373e77 100644 --- a/Content.Shared/Access/Systems/AccessReaderSystem.cs +++ b/Content.Shared/Access/Systems/AccessReaderSystem.cs @@ -57,7 +57,7 @@ public sealed class AccessReaderSystem : EntitySystem if (!id.IsValid()) continue; - component.AccessKeys.Add(new StationRecordKey(key.Item2, id)); + component.AccessKeys.Add(new StationRecordKey(key.Item2, GetNetEntity(id))); } component.AccessLists = new(state.AccessLists); diff --git a/Content.Shared/StationRecords/SharedStationRecordsSystem.cs b/Content.Shared/StationRecords/SharedStationRecordsSystem.cs index c2cc418f54..bec1b8c732 100644 --- a/Content.Shared/StationRecords/SharedStationRecordsSystem.cs +++ b/Content.Shared/StationRecords/SharedStationRecordsSystem.cs @@ -14,11 +14,11 @@ public abstract class SharedStationRecordsSystem : EntitySystem public StationRecordKey Convert((NetEntity, uint) input) { - return new StationRecordKey(input.Item2, GetEntity(input.Item1)); + return new StationRecordKey(input.Item2, input.Item1); } public (NetEntity, uint) Convert(StationRecordKey input) { - return (GetNetEntity(input.OriginStation), input.Id); + return (input.OriginStation, input.Id); } public List<(NetEntity, uint)> Convert(ICollection input) diff --git a/Content.Shared/StationRecords/StationRecordKey.cs b/Content.Shared/StationRecords/StationRecordKey.cs index 937c3aa3ef..34f840de57 100644 --- a/Content.Shared/StationRecords/StationRecordKey.cs +++ b/Content.Shared/StationRecords/StationRecordKey.cs @@ -1,18 +1,21 @@ +using Robust.Shared.Serialization; + namespace Content.Shared.StationRecords; // Station record keys. These should be stored somewhere, // preferably within an ID card. +[Serializable, NetSerializable] public readonly struct StationRecordKey : IEquatable { [DataField("id")] public readonly uint Id; [DataField("station")] - public readonly EntityUid OriginStation; + public readonly NetEntity OriginStation; public static StationRecordKey Invalid = default; - public StationRecordKey(uint id, EntityUid originStation) + public StationRecordKey(uint id, NetEntity originStation) { Id = id; OriginStation = originStation; diff --git a/Content.Shared/White/CriminalRecords/CriminalRecordsConsoleComponent.cs b/Content.Shared/White/CriminalRecords/CriminalRecordsConsoleComponent.cs new file mode 100644 index 0000000000..1ad7f4a3cc --- /dev/null +++ b/Content.Shared/White/CriminalRecords/CriminalRecordsConsoleComponent.cs @@ -0,0 +1,110 @@ +using Content.Shared.Access.Components; +using Content.Shared.Containers.ItemSlots; +using Content.Shared.Radio; +using Content.Shared.StationRecords; +using Robust.Shared.GameStates; +using Robust.Shared.Serialization; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; + +namespace Content.Shared.White.CriminalRecords; + +[RegisterComponent, NetworkedComponent] +public sealed partial class CriminalRecordsConsoleComponent : Component +{ + public const string IdSlotId = "id-slot"; + + [DataField("idSlot")] + public ItemSlot IdSlot = new(); + + [DataField("TargetChannel", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string TargetChannel = "Security"; + + [ViewVariables] public IdCardComponent? ContainedID; + [ViewVariables] public StationRecordKey? ActiveKey { get; set; } +} + +[Serializable, NetSerializable] +public sealed class IdCardNetInfo +{ + public string? FullName { get; } + public string? JobTitle { get; } + + public IdCardNetInfo(string? fullName, string? jobTitle) + { + FullName = fullName; + JobTitle = jobTitle; + } +} + +[Serializable, NetSerializable] +public sealed class CriminalRecordsConsoleBuiState : BoundUserInterfaceState +{ + /// + /// Current selected key. + /// + public StationRecordKey? SelectedKey { get; } + public GeneralStationRecord? Record { get; } + public Dictionary? RecordListing { get; } + public Dictionary? Cache { get; } + public IdCardNetInfo? ContainedId { get; } + public bool IsAllowed { get; } + //public GeneralStationRecordsFilter? Filter { get; } + public CriminalRecordsConsoleBuiState(StationRecordKey? key, GeneralStationRecord? record, + Dictionary? recordListing, Dictionary? cache + , IdCardNetInfo? containedId, bool isAllowed) //GeneralStationRecordsFilter? newFilter + { + SelectedKey = key; + Record = record; + RecordListing = recordListing; + Cache = cache; + ContainedId = containedId; + IsAllowed = isAllowed; + //Filter = newFilter; + } + + public bool IsEmpty() => SelectedKey == null + && Record == null && RecordListing == null; +} + +[Serializable, NetSerializable] +public sealed class SelectCriminalRecord : BoundUserInterfaceMessage +{ + public StationRecordKey? SelectedKey { get; } +// + public SelectCriminalRecord(StationRecordKey? selectedKey) + { + SelectedKey = selectedKey; + } +} + +[Serializable, NetSerializable] +public sealed class SelectCriminalStatus : BoundUserInterfaceMessage +{ + public StationRecordKey SelectedKey { get; } + public CriminalRecordInfo? SelectedStatus { get; } + + public SelectCriminalStatus(StationRecordKey selectedKey, CriminalRecordInfo? selectedStatus) + { + SelectedKey = selectedKey; + SelectedStatus = selectedStatus; + } +} + +[Serializable, NetSerializable] +public sealed class SelectCriminalReason : BoundUserInterfaceMessage +{ + public StationRecordKey SelectedKey { get; } + public string Text { get; } + + public SelectCriminalReason(StationRecordKey key, string text) + { + SelectedKey = key; + Text = text; + } +} + +[Serializable, NetSerializable] +public enum CriminalRecordsConsoleKey : byte +{ + Key +} diff --git a/Content.Shared/White/CriminalRecords/CriminalRecordsServerComponent.cs b/Content.Shared/White/CriminalRecords/CriminalRecordsServerComponent.cs new file mode 100644 index 0000000000..eb0a412d5c --- /dev/null +++ b/Content.Shared/White/CriminalRecords/CriminalRecordsServerComponent.cs @@ -0,0 +1,52 @@ +using Content.Shared.StationRecords; +using Robust.Shared.GameStates; +using Robust.Shared.Serialization; + +namespace Content.Shared.White.CriminalRecords; + +[Serializable, NetSerializable] +public enum EnumCriminalRecordType +{ + Released = 0, + Discharged = 1, + Parolled = 2, + Suspected = 3, + Wanted = 4, + Incarcerated = 5 +} + +[Serializable, NetSerializable] +public sealed class CriminalRecordInfo +{ + + // Main data + [DataField("StationRecord")] public GeneralStationRecord StationRecord { get; set; } + + [DataField("CriminalType")] public EnumCriminalRecordType CriminalType { get; set; } + [DataField("Reason")] public string Reason { get; set; } + + public CriminalRecordInfo(GeneralStationRecord stationRecord, EnumCriminalRecordType criminalType, string reason) + { + this.StationRecord = stationRecord; + this.CriminalType = criminalType; + this.Reason = reason; + } +} + +[RegisterComponent] +[NetworkedComponent] +public sealed partial class CriminalRecordsServerComponent : Component +{ + [DataField("Cache")] public Dictionary Cache = new(); + + [Serializable, NetSerializable] + public sealed class CriminalRecordsServerComponentState : ComponentState + { + public Dictionary Cache { get; init; } + + public CriminalRecordsServerComponentState(Dictionary cache) + { + Cache = cache; + } + } +} diff --git a/Content.Shared/White/CriminalRecords/CriminalRecordsServerSystem.cs b/Content.Shared/White/CriminalRecords/CriminalRecordsServerSystem.cs new file mode 100644 index 0000000000..5e8286cdbe --- /dev/null +++ b/Content.Shared/White/CriminalRecords/CriminalRecordsServerSystem.cs @@ -0,0 +1,133 @@ +using Content.Shared.StationRecords; +using Robust.Shared.GameStates; + +namespace Content.Shared.White.CriminalRecords; + +public sealed class CriminalRecordsServerSystem: EntitySystem +{ + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnGetCache); + SubscribeLocalEvent(OnChangeCache); + SubscribeLocalEvent(OnChangeReason); + SubscribeLocalEvent(OnCheckServer); + SubscribeLocalEvent(OnGetState); + SubscribeLocalEvent(OnHandleState); + } + + private void OnGetState(EntityUid uid, CriminalRecordsServerComponent component, ref ComponentGetState args) + { + args.State = new CriminalRecordsServerComponent.CriminalRecordsServerComponentState(component.Cache); + } + + private void OnHandleState(EntityUid uid, CriminalRecordsServerComponent component, ref ComponentHandleState args) + { + if (args.Current is not CriminalRecordsServerComponent.CriminalRecordsServerComponentState state) + return; + + component.Cache = state.Cache; + } + + private void OnGetCache(EventGetCache ev) + { + var serverList = EntityQuery(); + foreach (var server in serverList) + { + ev.Cache = server.Cache; + break; + } + } + + private void OnChangeCache(EventChangeCache ev) + { + var serverList = EntityQuery(); + foreach (var server in serverList) + { + foreach (var (key, info) in server.Cache) + { + if (key.Id == ev.Key.Id) + { + info.Reason = ev.Record.Reason; + info.CriminalType = ev.Record.CriminalType; + Dirty(server); + return; + } + } + server.Cache.Add(ev.Key, ev.Record); + Dirty(server); + return; + } + } + + private void OnChangeReason(EventChangeReason ev) + { + var serverList = EntityQuery(); + foreach (var server in serverList) + { + foreach (var (key, info) in server.Cache) + { + if (key.Id == ev.Key.Id) + { + info.Reason = ev.Text; + Dirty(server); + } + } + return; + } + } + + private void OnCheckServer(EventCheckServer ev) + { + var serverList = EntityQuery(); + foreach (var server in serverList) + { + ev.Result = true; + return; + } + ev.Result = false; + } +} + +// Events +public sealed class EventGetCache +{ + public Dictionary Cache = new(); + + public EventGetCache() + { + } +} + +public sealed class EventChangeCache +{ + public CriminalRecordInfo Record { get; } + public StationRecordKey Key { get; } + + public EventChangeCache(StationRecordKey key, CriminalRecordInfo record) + { + Key = key; + Record = record; + } +} + +public sealed class EventChangeReason +{ + public string Text { get; } + public StationRecordKey Key { get; } + + public EventChangeReason(StationRecordKey key, string text) + { + Key = key; + Text = text; + } +} + +public sealed class EventCheckServer +{ + public bool Result { get; set; } + + public EventCheckServer() + { + } +} diff --git a/Content.Shared/White/EntityCrimeRecords/ShowCrimeRecordsComponent.cs b/Content.Shared/White/EntityCrimeRecords/ShowCrimeRecordsComponent.cs new file mode 100644 index 0000000000..76c1544562 --- /dev/null +++ b/Content.Shared/White/EntityCrimeRecords/ShowCrimeRecordsComponent.cs @@ -0,0 +1,10 @@ +namespace Content.Shared.White.EntityCrimeRecords +{ + /// + /// Allow to see crime records info for character + /// + [RegisterComponent] + public sealed partial class ShowCrimeRecordsComponent : Component + { + } +} diff --git a/Resources/Locale/ru-RU/white/criminal-console.ftl b/Resources/Locale/ru-RU/white/criminal-console.ftl new file mode 100644 index 0000000000..c63a7f9dad --- /dev/null +++ b/Resources/Locale/ru-RU/white/criminal-console.ftl @@ -0,0 +1,39 @@ +criminal-console-list = Список экипажа +criminal-console-name = консоль криминальных записей +criminal-grant-status-button-name = Статус + +criminal-login-out = Log Out +criminal-login-in = Log In +criminal-login-info = ID: {$user} +criminal-login-hint = Вставьте ID карту в консоль, нажав на "{$name}" (нужно держать ID карту в руке) +criminal-login-warn = WARNING: Доступ к системе осуществляется уровнем доступа "службы безопасности" + +criminal-status-released = Освобожден +criminal-status-discharged = Выписан +criminal-status-parolled = Закодирован +criminal-status-suspected = Подозреваемый +criminal-status-wanted = В розыске +criminal-status-incarcerated = Заключенный + +criminal-targetchannel-set-released = {$target} освобожден(а). +criminal-targetchannel-set-released-reason = {$target} освобожден(а). Заметка: {$reason}. +criminal-targetchannel-set-discharged = {$target} выписан(а). +criminal-targetchannel-set-discharged-reason = {$target} выписан(а). Заметка: {$reason}. +criminal-targetchannel-set-parolled = {$target} закодирован(а). +criminal-targetchannel-set-parolled-reason = {$target} закодирован(а). Заметка: {$reason}. +criminal-targetchannel-set-suspected = {$target} под подозрением. +criminal-targetchannel-set-suspected-reason = {$target} под подозрением. Заметка: {$reason}. +criminal-targetchannel-set-wanted = {$target} объявлен(а) в розыск. +criminal-targetchannel-set-wanted-reason = {$target} объявлен(а) в розыск. Заметка: {$reason}. +criminal-targetchannel-set-incarcerated = {$target} был(а) заключен(а) под стражу. +criminal-targetchannel-set-incarcerated-reason = {$target} был(а) заключен(а) под стражу. Заметка: {$reason}. + +criminal-dna-name = ДНК: +criminal-dna-desc = - [color={$color}]{$info}[/color] +criminal-fingerprint-name = Отпечатки пальцев: +criminal-fingerprint-desc = - [color={$color}]{$info}[/color] + +criminal-detail-info = Заметка: + +ent-CriminalRecordsServer = сервер криминальных записей + .desc = Содержит все преступные записи об экипажа на станции. Не дайте злоумышлиникам уничтожить его! diff --git a/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml b/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml index d42127b132..fd7f720068 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml @@ -281,12 +281,23 @@ - type: Computer board: MedicalRecordsComputerCircuitboard +# mark this is needed - type: entity parent: BaseComputer id: ComputerCriminalRecords name: criminal records computer description: This can be used to check criminal records. components: + - type: CriminalRecordsConsole # my own impl + idSlot: + name: ID Card + ejectSound: /Audio/Machines/id_swipe.ogg + insertSound: /Audio/Weapons/Guns/MagIn/batrifle_magin.ogg + ejectOnBreak: true + swap: false + whitelist: + components: + - IdCard - type: Sprite layers: - map: ["computerLayerBody"] @@ -297,12 +308,28 @@ state: explosive - map: ["computerLayerKeys"] state: security_key + - type: UserInterface + interfaces: + - key: enum.CriminalRecordsConsoleKey.Key + type: CriminalRecordsBoundUserInterface + - type: ActivatableUI + key: enum.CriminalRecordsConsoleKey.Key - type: PointLight radius: 1.5 energy: 1.6 color: "#1f8c28" - type: Computer board: CriminalRecordsComputerCircuitboard + - type: Tag + tags: + - EmagImmune + - type: ItemSlots + - type: ContainerContainer + containers: + board: !type:Container + id-slot: !type:ContainerSlot + - type: AccessReader + access: [["Security"]] - type: entity parent: BaseComputer diff --git a/Resources/Prototypes/Recipes/Lathes/electronics.yml b/Resources/Prototypes/Recipes/Lathes/electronics.yml index fab6db7cea..3b5c04f4fe 100644 --- a/Resources/Prototypes/Recipes/Lathes/electronics.yml +++ b/Resources/Prototypes/Recipes/Lathes/electronics.yml @@ -781,13 +781,3 @@ Steel: 100 Glass: 900 Gold: 100 - -- type: latheRecipe - id: ClothingEyesHudMedical - icon: { sprite: Clothing/Eyes/Hud/med.rsi, state: icon } - result: ClothingEyesHudMedical - completetime: 4 - materials: - Steel: 100 - Glass: 300 - Plasma: 200 diff --git a/Resources/Prototypes/White/Entities/Structures/Machines/criminal_records_server.yml b/Resources/Prototypes/White/Entities/Structures/Machines/criminal_records_server.yml new file mode 100644 index 0000000000..f80d3ee0ff --- /dev/null +++ b/Resources/Prototypes/White/Entities/Structures/Machines/criminal_records_server.yml @@ -0,0 +1,73 @@ +- type: entity + parent: [ BaseMachinePowered, ConstructibleMachine ] + id: CriminalRecordsServer + name: criminal records server + description: When powered and filled with encryption keys it allows radio headset communication. + components: + - type: Sprite + sprite: White/Structures/Machines/criminal_record_server.rsi + snapCardinals: true + netsync: false + layers: + - state: icon + - state: unlit + shader: unshaded + map: ["enum.PowerDeviceVisualLayers.Powered"] + - state: panel + map: ["enum.WiresVisualLayers.MaintenancePanel"] + - type: GenericVisualizer + visuals: + enum.PowerDeviceVisuals.Powered: + enum.PowerDeviceVisualLayers.Powered: + True: { visible: true } + False: { visible: false } + - type: Appearance + - type: Physics + bodyType: Static + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeAabb + bounds: "-0.4,-0.4,0.4,0.4" + density: 190 + mask: + - MachineMask + layer: + - MachineLayer + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 100 + behaviors: + - !type:ChangeConstructionNodeBehavior + node: machineFrame + - !type:DoActsBehavior + acts: ["Destruction"] + - type: Machine + board: CriminalRecordsServerCircuitboard + - type: WiresPanel + - type: Wires + boardName: "CriminalRecordsServer" + layoutId: CriminalRecordsServer + - type: Transform + anchored: true + - type: Pullable + - type: CriminalRecordsServer + - type: ContainerContainer + containers: + machine_board: !type:Container + machine_parts: !type:Container + +- type: entity + id: CriminalRecordsServerCircuitboard + parent: BaseMachineCircuitboard + name: criminal records server machine board + description: A machine printed circuit board for an telecommunication server. + components: + - type: MachineBoard + prototype: CriminalRecordsServer + materialRequirements: + Steel: 1 + Cable: 2 diff --git a/Resources/Prototypes/White/StatusEffects/criminal_records.yml b/Resources/Prototypes/White/StatusEffects/criminal_records.yml new file mode 100644 index 0000000000..9ff35b60b0 --- /dev/null +++ b/Resources/Prototypes/White/StatusEffects/criminal_records.yml @@ -0,0 +1,47 @@ +- type: statusIcon + id: CriminalRecordIcon + abstract: true + priority: 3 + locationPreference: Right + +- type: statusIcon + parent: CriminalRecordIcon + id: CriminalRecordIconReleased + icon: + sprite: White/Interface/records.rsi + state: released + +- type: statusIcon + parent: CriminalRecordIcon + id: CriminalRecordIconDischarged + icon: + sprite: White/Interface/records.rsi + state: discharged + +- type: statusIcon + parent: CriminalRecordIcon + id: CriminalRecordIconParolled + icon: + sprite: White/Interface/records.rsi + state: parolled + +- type: statusIcon + parent: CriminalRecordIcon + id: CriminalRecordIconSuspected + icon: + sprite: White/Interface/records.rsi + state: suspected + +- type: statusIcon + parent: CriminalRecordIcon + id: CriminalRecordIconWanted + icon: + sprite: White/Interface/records.rsi + state: wanted + +- type: statusIcon + parent: CriminalRecordIcon + id: CriminalRecordIconIncarcerated + icon: + sprite: White/Interface/records.rsi + state: incarcerated diff --git a/Resources/Textures/White/Interface/records.rsi/discharged.png b/Resources/Textures/White/Interface/records.rsi/discharged.png new file mode 100644 index 0000000000000000000000000000000000000000..b150fafdc2894758a16cb3abf93d70ffc0db5268 GIT binary patch literal 144 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFqjKx9jP7LeL$-D$|ygXeTLnNjq zr}X^!KA(|4Au%B#A;3KKh*st2FZCTwuUltKn)TO1Z2|Wo6GK-+1A~W~=QlQTuxf-U o>}E}qRb~bPz4ia%&mG}lu)M>6s`LEWIH0Kvp00i_>zopr0D9LhY5)KL literal 0 HcmV?d00001 diff --git a/Resources/Textures/White/Interface/records.rsi/incarcerated.png b/Resources/Textures/White/Interface/records.rsi/incarcerated.png new file mode 100644 index 0000000000000000000000000000000000000000..5401f58d4abd88a3d68bb021c3d3b055c7c90e8f GIT binary patch literal 139 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFqjKx9jP7LeL$-D$|Ts>VJLnNjq zuj#t@|9k_xfuVtc!Hn!qqb+*-8XOOcK41+s{Pn-Eah1g1|2{lGF#A@l(UHx3vIVCg!0FU%6jQ{`u literal 0 HcmV?d00001 diff --git a/Resources/Textures/White/Interface/records.rsi/meta.json b/Resources/Textures/White/Interface/records.rsi/meta.json new file mode 100644 index 0000000000..7a924eb5a9 --- /dev/null +++ b/Resources/Textures/White/Interface/records.rsi/meta.json @@ -0,0 +1,27 @@ +{ + "license": "CC-BY-SA-3.0", + "copyright": "https://github.com/tgstation/tgstation/blob/8e49222b72f6fdcbe741d2a6ce0a8425d95010b7/icons/mob/huds/hud.dmi", + "version": 1, + "size": { "y": 8, "x": 8 }, + "states": [ + { + "name": "wanted" + }, + { + "name": "suspected" + }, + { + "name": "released" + }, + { + "name": "parolled" + }, + { + "name": "incarcerated" + }, + { + "name": "discharged" + } + + ] +} diff --git a/Resources/Textures/White/Interface/records.rsi/parolled.png b/Resources/Textures/White/Interface/records.rsi/parolled.png new file mode 100644 index 0000000000000000000000000000000000000000..fe78d0e96721c89a176d726b2455b23334245f8f GIT binary patch literal 150 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFqjKx9jP7LeL$-D$|0z6$DLnNjq z^Sl-KbH0Ing8>j^yxg2Pbx+wxaTmwt$a+hm1!K)~lklL6-pH8BxZ54D3Li?kZJ rk}QN~v+x)g0X04N^y*dD_Z|lCC8FOr3|1ro&1Ud)^>bP0l+XkKk~A!+ literal 0 HcmV?d00001 diff --git a/Resources/Textures/White/Interface/records.rsi/released.png b/Resources/Textures/White/Interface/records.rsi/released.png new file mode 100644 index 0000000000000000000000000000000000000000..a83267bbcf0af8d1846afb9854fe7b9714cf98a4 GIT binary patch literal 143 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFqjKx9jP7LeL$-D$|JUv|;LnNjq zr}X^!KA(|4Au%B#A;3KKh*st2FZCTwuUltKn)O#;7H6!08sp_7O=}o#sx0zN0I8Or nuPiAkAt6yv`SpL1nj6FA-4aF*+*RX%hBA1%`njxgN@xNAnsYCY literal 0 HcmV?d00001 diff --git a/Resources/Textures/White/Interface/records.rsi/suspected.png b/Resources/Textures/White/Interface/records.rsi/suspected.png new file mode 100644 index 0000000000000000000000000000000000000000..8e04eb21f43459330ca7109ee238472ef61f0f7a GIT binary patch literal 141 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFqjKx9jP7LeL$-D$|+&x_!LnNjq z=S)oab-sapg8>k*xD_N!joJT*omqHOL)iatwzdD(Yjg#OBrvmxZcr#*CtCE_oN>0a lgrtOoL_y`&XGiVb7>?Z+*i}}{^d4v=gQu&X%Q~loCICj$D&hbD literal 0 HcmV?d00001 diff --git a/Resources/Textures/White/Interface/records.rsi/wanted.png b/Resources/Textures/White/Interface/records.rsi/wanted.png new file mode 100644 index 0000000000000000000000000000000000000000..f66b1d82beeea13c00d92f2edc27ecd5e5ea2a47 GIT binary patch literal 147 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFqjKx9jP7LeL$-D$|d_7$pLnNlw zo@%S;NZ4S3Tq)3bHh<$LfH>pI=;fHbK4Nv>1gufAT85fMd*09AEuU^Ns uuVEFNa?&pMBWpgNunzg%us+=U5zD1g(bb0w{9gbKX7F_Nb6Mw<&;$V0nKcXm literal 0 HcmV?d00001 diff --git a/Resources/Textures/White/Structures/Machines/criminal_record_server.rsi/icon.png b/Resources/Textures/White/Structures/Machines/criminal_record_server.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..c14e87778d5f9953f808cc82f06a8ac53e7c4da1 GIT binary patch literal 416 zcmV;R0bl-!P)8ZB7>3`JLR?(bP$P78=_*bxq0q@+Q|LeF+&|F2k*Q0^fJ>*Ym#M}Q6~kdcGNelt zHP`057DcG<^4)jbd-r(WeH<|AziU!YOw&B{v1k-U)w)%~G|hwW`$*H2G|BCB+Q=V@ zg5=XQ4a?=Sf;c0&I#eeBRx9#iwr!sV007r@f1D@?f~sv81{pvk zApm0xVHgSxmSr9FU07Fm4_{A4G5`R0o+s=V9n>t#NKpzv=0+4BZ=^^DARj~kj^mKW ziazQHK=x2wf7OFWi8@eM^&(PLy_e@$DbAA#P}>2{lZC44&-1xXn&75Yil2z3c(X~s zd9o<;;Z;IKQAoy1Rc-n>{}sUSc3{2!{?68QPaG!&P(K`_`qC$_4v*$#T0;Z?0000< KMNUMnLSTYni?x9O literal 0 HcmV?d00001 diff --git a/Resources/Textures/White/Structures/Machines/criminal_record_server.rsi/meta.json b/Resources/Textures/White/Structures/Machines/criminal_record_server.rsi/meta.json new file mode 100644 index 0000000000..d26455cea6 --- /dev/null +++ b/Resources/Textures/White/Structures/Machines/criminal_record_server.rsi/meta.json @@ -0,0 +1,34 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/blob/9c3494fd79e6bf8dc532300b9de4f688ff276ac9/icons/obj/machines/telecomms.dmi", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "unlit", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "icon" + }, + { + "name": "panel" + } + ] +} \ No newline at end of file diff --git a/Resources/Textures/White/Structures/Machines/criminal_record_server.rsi/panel.png b/Resources/Textures/White/Structures/Machines/criminal_record_server.rsi/panel.png new file mode 100644 index 0000000000000000000000000000000000000000..1ab3cfb29ca1384da618a4e162889cf88380ceaa GIT binary patch literal 860 zcmV-i1Ec(jP)xukZV{egQD5XdpT{ zcmn_c010qNS#tmY4#WTe4#WYKD-Ig~00ERqL_t(o31eUw1tUEK7?A@&LP7$WOEdP1 z7cbyu!yN#!+{ed<;oZA;a7kJ)goK0`wrttL0CNNrEi56$%uxr7hQMeDu%KrdP(Gz6 zfIHyp*WV2H?%kt{-5|SQV8GJ=JOp4adh_NDjER>P5fK5)-@0`RuR3H&H8nM4E|`ts z0FWqiF7tn|?6fQ}1tadI2+SccHKfwmqX1+v2xm=W09o3~x*LpDR5}<| zYC2=sAugp279&JsXaoviguts;uYk4UNr>8$#tg>c#tiGAI*l1l0@-{FueQ8mK=%2E z4euP$&!sfMh|r;n}liC<8F|axSByVG5gQN_> zM9-=wCMF0;0xWdBAmun6fK4$r^NCf5O)p3co|<`ig~4jLxmj_kMG;3XOi(0<FX zg-?Wih72fttMo*T-xOzR{H554M&`k;dU`TVoM>V#YvhH;guVG(NvqGJzQmO(EG6((77Cj|TjxCk@ z|2)m~)MTx@D*(<{%laIFu6?MKE$jExvMOibc!BFe!@nUp&3ubqZfM?PaPPq7O+!l@ zz|}4cKI?HKUX>iO?dddpTtHt8`tPA*>U6Z zdsgn7<^215_xbvKw)0-16h0sT0w4eau@bnGZTQN{N_Muf>64Qa@3CXlHwNg&@9gZT z^Ye2roKOoF7Z=t3{=TwJh*ke?D6lxrYy@DyY}|tF1p|Viob5m`U^Z^S_JRSyP|kKB z7%&^RV0*!UU?^uh&^>!MeV;D*iA~=aaC!Mh_4|Dm_FBvS#Hx==kbNdF&zl}Vx~He7 zG+A0MEiI+%kB^T_x67_vTU*P{dZ%O4cMQ-}wzjssaA@J-;UP_`#E5IvO~#FJ@(zhg zv(}j~@1C4BnFTTFdkxTI!C)}RAel_|dcDbvo=Tv1UVTwl?_Q|R%Ma@6^;326A70&t*c*yAL`=SUOJ!Ov3Rbo{&IIU)s@n}P5ZqDXf)f~+g?J>K%JcV-#&g;>%;y{ znauw@I#7?2y+@-_dJLNP9H5#)eS2?K<{lPnA%5<(>m5CTuKFn~x1l`udEJjucUA|X^d zUrELmpKo*LZoe@g*7$rfK&t(?1d#(mrRf2rUo|X{BG`~0P%l5iF+dv;Q+z(1c-b;| z`zLu5=G`0Vc?}31pWk_NpsscwljhX2uKsP}^L>@6tDnnH7!PmqCz8Ag-^Xi!-Y{f* z{@3?=X}*B30!7Q}=-(zjzX|Ibjx5ERu!V;-XZP7Od=!(S{pHGHJwI5HySqB8B zoiQM`_0YpNmgaJa}NfrhW384}O2!SVA7(gV1 zO6My{?)Z4S-LdH#1Ek{P$$(h(aS5U`K&Ui5fb^?|B~k<(@&k|`(Ek4~;LZ=QHiCyL0DI}>OicwMScMC1N3_c~VRS#GG=Fs(qX6+UPUlKE|Y)(Wpo(U!2kxxWfCx;j1I#(7{CCz zOaca!(P3Bz0~jEeNjP6gO2_AyRdycN83Ux_^XCz6l{VoLRE1K5+n63e8dbv*DS|9? zegM@YmeMgmH=OhL^J4*B)K0GfLF4nM#K#v^ESBY719anq#ph3nkB_dHaZAq`1Ek~gt@_RVEP8;j#m*SOQ6CJz9F(B}0w4eaQwjV7 Xw8emBZZj!{00000NkvXXu0mjfK@x89 literal 0 HcmV?d00001