diff --git a/Content.Client/CartridgeLoader/Cartridges/PsiWatchEntryControl.xaml b/Content.Client/CartridgeLoader/Cartridges/PsiWatchEntryControl.xaml
new file mode 100644
index 00000000000..9dafac9caea
--- /dev/null
+++ b/Content.Client/CartridgeLoader/Cartridges/PsiWatchEntryControl.xaml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Content.Client/CartridgeLoader/Cartridges/PsiWatchEntryControl.xaml.cs b/Content.Client/CartridgeLoader/Cartridges/PsiWatchEntryControl.xaml.cs
new file mode 100644
index 00000000000..0951ba55264
--- /dev/null
+++ b/Content.Client/CartridgeLoader/Cartridges/PsiWatchEntryControl.xaml.cs
@@ -0,0 +1,25 @@
+using Content.Shared.CartridgeLoader.Cartridges;
+using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface;
+using Robust.Client.UserInterface.Controls;
+using Robust.Client.UserInterface.XAML;
+
+///
+/// ADAPTED FROM SECWATCH - DELTAV
+///
+
+namespace Content.Client.CartridgeLoader.Cartridges;
+
+[GenerateTypedNameReferences]
+public sealed partial class PsiWatchEntryControl : BoxContainer
+{
+ public PsiWatchEntryControl(PsiWatchEntry entry)
+ {
+ RobustXamlLoader.Load(this);
+
+ Status.Text = Loc.GetString($"psionics-records-status-{entry.Status.ToString().ToLower()}");
+ Title.Text = Loc.GetString("psi-watch-entry", ("name", entry.Name), ("job", entry.Job));
+
+ Reason.Text = entry.Reason ?? Loc.GetString("psi-watch-no-reason");
+ }
+}
diff --git a/Content.Client/CartridgeLoader/Cartridges/PsiWatchUi.cs b/Content.Client/CartridgeLoader/Cartridges/PsiWatchUi.cs
new file mode 100644
index 00000000000..40eb2f19e43
--- /dev/null
+++ b/Content.Client/CartridgeLoader/Cartridges/PsiWatchUi.cs
@@ -0,0 +1,31 @@
+using Content.Client.UserInterface.Fragments;
+using Content.Shared.CartridgeLoader;
+using Content.Shared.CartridgeLoader.Cartridges;
+using Robust.Client.UserInterface;
+
+///
+/// ADAPTED FROM SECWATCH - DELTAV
+///
+
+namespace Content.Client.CartridgeLoader.Cartridges;
+
+public sealed partial class PsiWatchUi : UIFragment
+{
+ private PsiWatchUiFragment? _fragment;
+
+ public override Control GetUIFragmentRoot()
+ {
+ return _fragment!;
+ }
+
+ public override void Setup(BoundUserInterface ui, EntityUid? owner)
+ {
+ _fragment = new PsiWatchUiFragment();
+ }
+
+ public override void UpdateState(BoundUserInterfaceState state)
+ {
+ if (state is PsiWatchUiState cast)
+ _fragment?.UpdateState(cast);
+ }
+}
diff --git a/Content.Client/CartridgeLoader/Cartridges/PsiWatchUiFragment.xaml b/Content.Client/CartridgeLoader/Cartridges/PsiWatchUiFragment.xaml
new file mode 100644
index 00000000000..25181e347b3
--- /dev/null
+++ b/Content.Client/CartridgeLoader/Cartridges/PsiWatchUiFragment.xaml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/Content.Client/CartridgeLoader/Cartridges/PsiWatchUiFragment.xaml.cs b/Content.Client/CartridgeLoader/Cartridges/PsiWatchUiFragment.xaml.cs
new file mode 100644
index 00000000000..e446581317d
--- /dev/null
+++ b/Content.Client/CartridgeLoader/Cartridges/PsiWatchUiFragment.xaml.cs
@@ -0,0 +1,29 @@
+using Content.Shared.CartridgeLoader.Cartridges;
+using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface.Controls;
+using Robust.Client.UserInterface.XAML;
+
+///
+/// ADAPTED FROM SECWATCH - DELTAV
+///
+
+namespace Content.Client.CartridgeLoader.Cartridges;
+
+[GenerateTypedNameReferences]
+public sealed partial class PsiWatchUiFragment : BoxContainer
+{
+ public PsiWatchUiFragment()
+ {
+ RobustXamlLoader.Load(this);
+ }
+
+ public void UpdateState(PsiWatchUiState state)
+ {
+ NoEntries.Visible = state.Entries.Count == 0;
+ Entries.RemoveAllChildren();
+ foreach (var entry in state.Entries)
+ {
+ Entries.AddChild(new PsiWatchEntryControl(entry));
+ }
+ }
+}
diff --git a/Content.Client/Overlays/ShowPsionicsRecordIconsSystem.cs b/Content.Client/Overlays/ShowPsionicsRecordIconsSystem.cs
new file mode 100644
index 00000000000..26f3407adf7
--- /dev/null
+++ b/Content.Client/Overlays/ShowPsionicsRecordIconsSystem.cs
@@ -0,0 +1,32 @@
+using Content.Shared.Overlays;
+using Content.Shared.Psionics.Components;
+using Content.Shared.StatusIcon;
+using Content.Shared.StatusIcon.Components;
+using Robust.Shared.Prototypes;
+
+///
+/// EVERYTHING HERE IS A MODIFIED VERSION OF CRIMINAL RECORDS
+///
+
+namespace Content.Client.Overlays;
+
+public sealed class ShowPsionicsRecordIconsSystem : EquipmentHudSystem
+{
+ [Dependency] private readonly IPrototypeManager _prototype = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent(OnGetStatusIconsEvent);
+ }
+
+ private void OnGetStatusIconsEvent(EntityUid uid, PsionicsRecordComponent component, ref GetStatusIconsEvent ev)
+ {
+ if (!IsActive)
+ return;
+
+ if (_prototype.TryIndex(component.StatusIcon, out var iconPrototype))
+ ev.StatusIcons.Add(iconPrototype);
+ }
+}
diff --git a/Content.Client/PsionicsRecords/Components/PsionicsRecordsConsoleSystem.cs b/Content.Client/PsionicsRecords/Components/PsionicsRecordsConsoleSystem.cs
new file mode 100644
index 00000000000..8f68e38c86d
--- /dev/null
+++ b/Content.Client/PsionicsRecords/Components/PsionicsRecordsConsoleSystem.cs
@@ -0,0 +1,11 @@
+using Content.Shared.PsionicsRecords.Systems;
+
+///
+/// EVERYTHING HERE IS A MODIFIED VERSION OF CRIMINAL RECORDS
+///
+
+namespace Content.Client.PsionicsRecords.Systems;
+
+public sealed class PsionicsRecordsConsoleSystem : SharedPsionicsRecordsConsoleSystem
+{
+}
diff --git a/Content.Client/PsionicsRecords/PsionicsRecordsConsoleBoundUserInterface.cs b/Content.Client/PsionicsRecords/PsionicsRecordsConsoleBoundUserInterface.cs
new file mode 100644
index 00000000000..3d38f6db648
--- /dev/null
+++ b/Content.Client/PsionicsRecords/PsionicsRecordsConsoleBoundUserInterface.cs
@@ -0,0 +1,64 @@
+using Content.Shared.Access.Systems;
+using Content.Shared.PsionicsRecords;
+using Content.Shared.PsionicsRecords.Components;
+using Content.Shared.Psionics;
+using Content.Shared.StationRecords;
+using Robust.Client.Player;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Random;
+
+///
+/// EVERYTHING HERE IS A MODIFIED VERSION OF CRIMINAL RECORDS
+///
+
+namespace Content.Client.PsionicsRecords;
+
+public sealed class PsionicsRecordsConsoleBoundUserInterface : BoundUserInterface
+{
+ [Dependency] private readonly IPrototypeManager _proto = default!;
+ [Dependency] private readonly IRobustRandom _random = default!;
+ [Dependency] private readonly IPlayerManager _playerManager = default!;
+ private readonly AccessReaderSystem _accessReader;
+
+ private PsionicsRecordsConsoleWindow? _window;
+
+ public PsionicsRecordsConsoleBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
+ {
+ _accessReader = EntMan.System();
+ }
+
+ protected override void Open()
+ {
+ base.Open();
+
+ var comp = EntMan.GetComponent(Owner);
+
+ _window = new(Owner, comp.MaxStringLength, _playerManager, _proto, _random, _accessReader);
+ _window.OnKeySelected += key =>
+ SendMessage(new SelectStationRecord(key));
+ _window.OnFiltersChanged += (type, filterValue) =>
+ SendMessage(new SetStationRecordFilter(type, filterValue));
+ _window.OnStatusSelected += status =>
+ SendMessage(new PsionicsRecordChangeStatus(status, null));
+ _window.OnDialogConfirmed += (status, reason) =>
+ SendMessage(new PsionicsRecordChangeStatus(status, reason));
+ _window.OnClose += Close;
+ }
+
+ protected override void UpdateState(BoundUserInterfaceState state)
+ {
+ base.UpdateState(state);
+
+ if (state is not PsionicsRecordsConsoleState cast)
+ return;
+
+ _window?.UpdateState(cast);
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ base.Dispose(disposing);
+
+ _window?.Close();
+ }
+}
diff --git a/Content.Client/PsionicsRecords/PsionicsRecordsConsoleWindow.xaml b/Content.Client/PsionicsRecords/PsionicsRecordsConsoleWindow.xaml
new file mode 100644
index 00000000000..40a3a58b50b
--- /dev/null
+++ b/Content.Client/PsionicsRecords/PsionicsRecordsConsoleWindow.xaml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Content.Client/PsionicsRecords/PsionicsRecordsConsoleWindow.xaml.cs b/Content.Client/PsionicsRecords/PsionicsRecordsConsoleWindow.xaml.cs
new file mode 100644
index 00000000000..2099d0aabe3
--- /dev/null
+++ b/Content.Client/PsionicsRecords/PsionicsRecordsConsoleWindow.xaml.cs
@@ -0,0 +1,256 @@
+using Content.Client.UserInterface.Controls;
+using Content.Shared.Access.Systems;
+using Content.Shared.Administration;
+using Content.Shared.PsionicsRecords;
+using Content.Shared.Dataset;
+using Content.Shared.Psionics;
+using Content.Shared.StationRecords;
+using Robust.Client.AutoGenerated;
+using Robust.Client.Player;
+using Robust.Client.UserInterface.XAML;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Random;
+using Robust.Shared.Utility;
+
+///
+/// EVERYTHING HERE IS A MODIFIED VERSION OF CRIMINAL RECORDS
+///
+
+namespace Content.Client.PsionicsRecords;
+
+// TODO: dedupe shitcode from general records theres a lot
+[GenerateTypedNameReferences]
+public sealed partial class PsionicsRecordsConsoleWindow : FancyWindow
+{
+ private readonly IPlayerManager _player;
+ private readonly IPrototypeManager _proto;
+ private readonly IRobustRandom _random;
+ private readonly AccessReaderSystem _accessReader;
+
+ public readonly EntityUid Console;
+
+ [ValidatePrototypeId]
+ private const string ReasonPlaceholders = "PsionicsRecordsRecordsPlaceholders";
+
+ public Action? OnKeySelected;
+ public Action? OnFiltersChanged;
+ public Action? OnStatusSelected;
+ public Action? OnDialogConfirmed;
+
+ private uint _maxLength;
+ private bool _isPopulating;
+ private bool _access;
+ private uint? _selectedKey;
+ private PsionicsRecord? _selectedRecord;
+
+ private DialogWindow? _reasonDialog;
+
+ private StationRecordFilterType _currentFilterType;
+
+ public PsionicsRecordsConsoleWindow(EntityUid console, uint maxLength, IPlayerManager playerManager, IPrototypeManager prototypeManager, IRobustRandom robustRandom, AccessReaderSystem accessReader)
+ {
+ RobustXamlLoader.Load(this);
+
+ Console = console;
+ _player = playerManager;
+ _proto = prototypeManager;
+ _random = robustRandom;
+ _accessReader = accessReader;
+
+ _maxLength = maxLength;
+ _currentFilterType = StationRecordFilterType.Name;
+
+ OpenCentered();
+
+ foreach (var item in Enum.GetValues())
+ {
+ FilterType.AddItem(GetTypeFilterLocals(item), (int) item);
+ }
+
+ foreach (var status in Enum.GetValues())
+ {
+ AddStatusSelect(status);
+ }
+
+ OnClose += () => _reasonDialog?.Close();
+
+ RecordListing.OnItemSelected += args =>
+ {
+ if (_isPopulating || RecordListing[args.ItemIndex].Metadata is not uint cast)
+ return;
+
+ OnKeySelected?.Invoke(cast);
+ };
+
+ RecordListing.OnItemDeselected += _ =>
+ {
+ if (!_isPopulating)
+ OnKeySelected?.Invoke(null);
+ };
+
+ FilterType.OnItemSelected += eventArgs =>
+ {
+ var type = (StationRecordFilterType) eventArgs.Id;
+
+ if (_currentFilterType != type)
+ {
+ _currentFilterType = type;
+ FilterListingOfRecords(FilterText.Text);
+ }
+ };
+
+ FilterText.OnTextEntered += args =>
+ {
+ FilterListingOfRecords(args.Text);
+ };
+
+ StatusOptionButton.OnItemSelected += args =>
+ {
+ SetStatus((PsionicsStatus) args.Id);
+ };
+ }
+
+ public void UpdateState(PsionicsRecordsConsoleState state)
+ {
+ if (state.Filter != null)
+ {
+ if (state.Filter.Type != _currentFilterType)
+ {
+ _currentFilterType = state.Filter.Type;
+ }
+
+ if (state.Filter.Value != FilterText.Text)
+ {
+ FilterText.Text = state.Filter.Value;
+ }
+ }
+
+ _selectedKey = state.SelectedKey;
+
+ FilterType.SelectId((int) _currentFilterType);
+
+ // set up the records listing panel
+ RecordListing.Clear();
+
+ var hasRecords = state.RecordListing != null && state.RecordListing.Count > 0;
+ NoRecords.Visible = !hasRecords;
+ if (hasRecords)
+ PopulateRecordListing(state.RecordListing!);
+
+ // set up the selected person's record
+ var selected = _selectedKey != null;
+
+ PersonContainer.Visible = selected;
+ RecordUnselected.Visible = !selected;
+
+ _access = _player.LocalSession?.AttachedEntity is {} player
+ && _accessReader.IsAllowed(player, Console);
+
+ // hide access-required editing parts when no access
+ var editing = _access && selected;
+ StatusOptionButton.Disabled = !editing;
+
+ if (state is { PsionicsRecord: not null, StationRecord: not null })
+ {
+ PopulateRecordContainer(state.StationRecord, state.PsionicsRecord);
+ _selectedRecord = state.PsionicsRecord;
+ }
+ else
+ {
+ _selectedRecord = null;
+ }
+ }
+
+ private void PopulateRecordListing(Dictionary listing)
+ {
+ _isPopulating = true;
+
+ foreach (var (key, name) in listing)
+ {
+ var item = RecordListing.AddItem(name);
+ item.Metadata = key;
+ item.Selected = key == _selectedKey;
+ }
+ _isPopulating = false;
+
+ RecordListing.SortItemsByText();
+ }
+
+ private void PopulateRecordContainer(GeneralStationRecord stationRecord, PsionicsRecord psionicsRecord)
+ {
+ var na = Loc.GetString("generic-not-available-shorthand");
+ PersonName.Text = stationRecord.Name;
+
+ StatusOptionButton.SelectId((int) psionicsRecord.Status);
+ if (psionicsRecord.Reason is {} reason)
+ {
+ var message = FormattedMessage.FromMarkup(Loc.GetString("psionics-records-console-wanted-reason"));
+ message.AddText($": {reason}");
+ PsionicsList.SetMessage(message);
+ PsionicsList.Visible = true;
+ }
+ else
+ {
+ PsionicsList.Visible = false;
+ }
+ }
+
+ private void AddStatusSelect(PsionicsStatus status)
+ {
+ var name = Loc.GetString($"psionics-records-status-{status.ToString().ToLower()}");
+ StatusOptionButton.AddItem(name, (int) status);
+ }
+
+ private void FilterListingOfRecords(string text = "")
+ {
+ if (!_isPopulating)
+ {
+ OnFiltersChanged?.Invoke(_currentFilterType, text);
+ }
+ }
+
+ private void SetStatus(PsionicsStatus status)
+ {
+ if (status != PsionicsStatus.None) // All statuses should have a reasoning.
+ {
+ GetReason(status);
+ return;
+ }
+
+ OnStatusSelected?.Invoke(status);
+ }
+
+ private void GetReason(PsionicsStatus status)
+ {
+ if (_reasonDialog != null)
+ {
+ _reasonDialog.MoveToFront();
+ return;
+ }
+
+ var field = "reason";
+ var title = Loc.GetString("psionics-records-status-" + status.ToString().ToLower());
+ var placeholders = _proto.Index(ReasonPlaceholders);
+ var placeholder = Loc.GetString("psionics-records-console-reason-placeholder", ("placeholder", _random.Pick(placeholders.Values))); // just funny it doesn't actually get used
+ var prompt = Loc.GetString("psionics-records-console-reason");
+ var entry = new QuickDialogEntry(field, QuickDialogEntryType.LongText, prompt, placeholder);
+ var entries = new List() { entry };
+ _reasonDialog = new DialogWindow(title, entries);
+
+ _reasonDialog.OnConfirmed += responses =>
+ {
+ var reason = responses[field];
+ if (reason.Length < 1 || reason.Length > _maxLength)
+ return;
+
+ OnDialogConfirmed?.Invoke(status, reason);
+ };
+
+ _reasonDialog.OnClose += () => { _reasonDialog = null; };
+ }
+
+ private string GetTypeFilterLocals(StationRecordFilterType type)
+ {
+ return Loc.GetString($"psionics-records-{type.ToString().ToLower()}-filter");
+ }
+}
diff --git a/Content.Server/CartridgeLoader/Cartridges/PsiWatchCartridgeComponent.cs b/Content.Server/CartridgeLoader/Cartridges/PsiWatchCartridgeComponent.cs
new file mode 100644
index 00000000000..66a8b131d35
--- /dev/null
+++ b/Content.Server/CartridgeLoader/Cartridges/PsiWatchCartridgeComponent.cs
@@ -0,0 +1,28 @@
+using Content.Shared.Psionics;
+
+///
+/// ADAPTED FROM SECWATCH - DELTAV
+///
+
+namespace Content.Server.CartridgeLoader.Cartridges;
+
+[RegisterComponent, Access(typeof(PsiWatchCartridgeSystem))]
+public sealed partial class PsiWatchCartridgeComponent : Component
+{
+ ///
+ /// Only show people with these statuses.
+ ///
+ [DataField]
+ public List Statuses = new()
+ {
+ PsionicsStatus.Abusing,
+ PsionicsStatus.Registered,
+ PsionicsStatus.Suspected
+ };
+
+ ///
+ /// Station entity thats getting its records checked.
+ ///
+ [DataField]
+ public EntityUid? Station;
+}
diff --git a/Content.Server/CartridgeLoader/Cartridges/PsiWatchCartridgeSystem.cs b/Content.Server/CartridgeLoader/Cartridges/PsiWatchCartridgeSystem.cs
new file mode 100644
index 00000000000..c0936e10fdb
--- /dev/null
+++ b/Content.Server/CartridgeLoader/Cartridges/PsiWatchCartridgeSystem.cs
@@ -0,0 +1,77 @@
+using Content.Server.Station.Systems;
+using Content.Server.StationRecords;
+using Content.Server.StationRecords.Systems;
+using Content.Shared.CartridgeLoader;
+using Content.Shared.CartridgeLoader.Cartridges;
+using Content.Shared.PsionicsRecords;
+using Content.Shared.StationRecords;
+
+///
+/// ADAPTED FROM SECWATCH - DELTAV
+///
+
+namespace Content.Server.CartridgeLoader.Cartridges;
+
+public sealed class PsiWatchCartridgeSystem : EntitySystem
+{
+ [Dependency] private readonly CartridgeLoaderSystem _cartridgeLoader = default!;
+ [Dependency] private readonly StationRecordsSystem _records = default!;
+ [Dependency] private readonly StationSystem _station = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent(OnRecordModified);
+
+ SubscribeLocalEvent(OnUiReady);
+ }
+
+ private void OnRecordModified(RecordModifiedEvent args)
+ {
+ // when a record is modified update the ui of every loaded cartridge tuned to the same station
+ var query = EntityQueryEnumerator();
+ while (query.MoveNext(out var uid, out var comp, out var cartridge))
+ {
+ if (cartridge.LoaderUid is not {} loader || comp.Station != args.Station)
+ continue;
+
+ UpdateUI((uid, comp), loader);
+ }
+ }
+
+ private void OnUiReady(Entity ent, ref CartridgeUiReadyEvent args)
+ {
+ UpdateUI(ent, args.Loader);
+ }
+
+ private void UpdateUI(Entity ent, EntityUid loader)
+ {
+ // if the loader is on a grid, update the station
+ // if it is off grid use the cached station
+ if (_station.GetOwningStation(loader) is {} station)
+ ent.Comp.Station = station;
+
+ if (!TryComp(ent.Comp.Station, out var records))
+ return;
+
+ station = ent.Comp.Station.Value;
+
+ var entries = new List();
+ foreach (var (id, criminal) in _records.GetRecordsOfType(station, records))
+ {
+ if (!ent.Comp.Statuses.Contains(criminal.Status))
+ continue;
+
+ var key = new StationRecordKey(id, station);
+ if (!_records.TryGetRecord(key, out var general, records))
+ continue;
+
+ var status = criminal.Status;
+ entries.Add(new PsiWatchEntry(general.Name, general.JobTitle, criminal.Status, criminal.Reason));
+ }
+
+ var state = new PsiWatchUiState(entries);
+ _cartridgeLoader.UpdateCartridgeUiState(loader, state);
+ }
+}
diff --git a/Content.Server/IdentityManagement/IdentitySystem.cs b/Content.Server/IdentityManagement/IdentitySystem.cs
index 1a2cdcce511..e2f57b648a4 100644
--- a/Content.Server/IdentityManagement/IdentitySystem.cs
+++ b/Content.Server/IdentityManagement/IdentitySystem.cs
@@ -1,6 +1,7 @@
using Content.Server.Access.Systems;
using Content.Server.Administration.Logs;
using Content.Server.CriminalRecords.Systems;
+using Content.Server.PsionicsRecords.Systems;
using Content.Server.Humanoid;
using Content.Shared.Clothing;
using Content.Shared.Database;
@@ -27,6 +28,7 @@ public sealed class IdentitySystem : SharedIdentitySystem
[Dependency] private readonly SharedContainerSystem _container = default!;
[Dependency] private readonly HumanoidAppearanceSystem _humanoid = default!;
[Dependency] private readonly CriminalRecordsConsoleSystem _criminalRecordsConsole = default!;
+ [Dependency] private readonly PsionicsRecordsConsoleSystem _psionicsRecordsConsole = default!;
private HashSet _queuedIdentityUpdates = new();
@@ -112,7 +114,7 @@ private void UpdateIdentityInfo(EntityUid uid, IdentityComponent identity)
_adminLog.Add(LogType.Identity, LogImpact.Medium, $"{ToPrettyString(uid)} changed identity to {name}");
var identityChangedEvent = new IdentityChangedEvent(uid, ident);
RaiseLocalEvent(uid, ref identityChangedEvent);
- SetIdentityCriminalIcon(uid);
+ SetIdentityRecordsIcon(uid);
}
private string GetIdentityName(EntityUid target, IdentityRepresentation representation)
@@ -124,13 +126,14 @@ private string GetIdentityName(EntityUid target, IdentityRepresentation represen
}
///
- /// When the identity of a person is changed, searches the criminal records to see if the name of the new identity
- /// has a record. If the new name has a criminal status attached to it, the person will get the criminal status
- /// until they change identity again.
+ /// When the identity of a person is changed, searches the criminal records and psionics records to see if the name
+ /// of the new identity has a record. If the new name has a criminal status or psionics status attached to it, the
+ /// person will get the criminal status and/or psionics status until they change identity again.
///
- private void SetIdentityCriminalIcon(EntityUid uid)
+ private void SetIdentityRecordsIcon(EntityUid uid)
{
_criminalRecordsConsole.CheckNewIdentity(uid);
+ _psionicsRecordsConsole.CheckNewIdentity(uid);
}
///
diff --git a/Content.Server/PsionicsRecords/Systems/PsionicsRecordsConsoleSystem.cs b/Content.Server/PsionicsRecords/Systems/PsionicsRecordsConsoleSystem.cs
new file mode 100644
index 00000000000..a847d45ce96
--- /dev/null
+++ b/Content.Server/PsionicsRecords/Systems/PsionicsRecordsConsoleSystem.cs
@@ -0,0 +1,234 @@
+using Content.Server.Popups;
+using Content.Server.Radio.EntitySystems;
+using Content.Server.Station.Systems;
+using Content.Server.StationRecords;
+using Content.Server.StationRecords.Systems;
+using Content.Shared.Access.Systems;
+using Content.Shared.PsionicsRecords;
+using Content.Shared.PsionicsRecords.Components;
+using Content.Shared.PsionicsRecords.Systems;
+using Content.Shared.Psionics;
+using Content.Shared.StationRecords;
+using Robust.Server.GameObjects;
+using System.Diagnostics.CodeAnalysis;
+using Content.Shared.IdentityManagement;
+using Content.Shared.Psionics.Components;
+
+///
+/// EVERYTHING HERE IS A MODIFIED VERSION OF CRIMINAL RECORDS
+///
+
+namespace Content.Server.PsionicsRecords.Systems;
+
+///
+/// Handles all UI for Psionics records console
+///
+public sealed class PsionicsRecordsConsoleSystem : SharedPsionicsRecordsConsoleSystem
+{
+ [Dependency] private readonly AccessReaderSystem _access = default!;
+ [Dependency] private readonly PsionicsRecordsSystem _psionicsRecords = default!;
+ [Dependency] private readonly PopupSystem _popup = default!;
+ [Dependency] private readonly RadioSystem _radio = default!;
+ [Dependency] private readonly SharedIdCardSystem _idCard = default!;
+ [Dependency] private readonly StationRecordsSystem _stationRecords = default!;
+ [Dependency] private readonly StationSystem _station = default!;
+ [Dependency] private readonly UserInterfaceSystem _ui = default!;
+
+ public override void Initialize()
+ {
+ SubscribeLocalEvent(UpdateUserInterface);
+ SubscribeLocalEvent(UpdateUserInterface);
+
+ Subs.BuiEvents(PsionicsRecordsConsoleKey.Key, subs =>
+ {
+ subs.Event(UpdateUserInterface);
+ subs.Event(OnKeySelected);
+ subs.Event(OnFiltersChanged);
+ subs.Event(OnChangeStatus);
+ });
+ }
+
+ private void UpdateUserInterface(Entity ent, ref T args)
+ {
+ // TODO: this is probably wasteful, maybe better to send a message to modify the exact state?
+ UpdateUserInterface(ent);
+ }
+
+ private void OnKeySelected(Entity ent, ref SelectStationRecord msg)
+ {
+ // no concern of sus client since record retrieval will fail if invalid id is given
+ ent.Comp.ActiveKey = msg.SelectedKey;
+ UpdateUserInterface(ent);
+ }
+
+ private void OnFiltersChanged(Entity ent, ref SetStationRecordFilter msg)
+ {
+ if (ent.Comp.Filter == null ||
+ ent.Comp.Filter.Type != msg.Type || ent.Comp.Filter.Value != msg.Value)
+ {
+ ent.Comp.Filter = new StationRecordsFilter(msg.Type, msg.Value);
+ UpdateUserInterface(ent);
+ }
+ }
+
+ private void OnChangeStatus(Entity ent, ref PsionicsRecordChangeStatus msg)
+ {
+ // prevent malf client violating registered/reason nullability
+ if (msg.Status == PsionicsStatus.Registered != (msg.Reason != null) &&
+ msg.Status == PsionicsStatus.Suspected != (msg.Reason != null) &&
+ msg.Status == PsionicsStatus.Abusing != (msg.Reason != null))
+ return;
+
+ if (!CheckSelected(ent, msg.Actor, out var mob, out var key))
+ return;
+
+ if (!_stationRecords.TryGetRecord(key.Value, out var record) || record.Status == msg.Status)
+ return;
+
+ // validate the reason
+ string? reason = null;
+ if (msg.Reason != null)
+ {
+ reason = msg.Reason.Trim();
+ if (reason.Length < 1 || reason.Length > ent.Comp.MaxStringLength)
+ return;
+ }
+
+ var oldStatus = record.Status;
+
+ // will probably never fail given the checks above
+ _psionicsRecords.TryChangeStatus(key.Value, msg.Status, msg.Reason);
+
+ var name = RecordName(key.Value);
+ var officer = Loc.GetString("psionics-records-console-unknown-officer");
+
+ var tryGetIdentityShortInfoEvent = new TryGetIdentityShortInfoEvent(null, mob.Value);
+ RaiseLocalEvent(tryGetIdentityShortInfoEvent);
+ if (tryGetIdentityShortInfoEvent.Title != null)
+ {
+ officer = tryGetIdentityShortInfoEvent.Title;
+ }
+
+ (string, object)[] args;
+ if (reason != null)
+ args = new (string, object)[] { ("name", name), ("officer", officer), ("reason", reason) };
+ else
+ args = new (string, object)[] { ("name", name), ("officer", officer) };
+
+ // figure out which radio message to send depending on transition
+ var statusString = (oldStatus, msg.Status) switch
+ {
+ // person has been registered
+ (_, PsionicsStatus.Registered) => "registered",
+ // person did something suspicious
+ (_, PsionicsStatus.Suspected) => "suspected",
+ // person is abusing
+ (_, PsionicsStatus.Abusing) => "abusing",
+ // person is no longer suspicious
+ (PsionicsStatus.Suspected, PsionicsStatus.None) => "not-suspected",
+ // person is no longer registered
+ (PsionicsStatus.Registered, PsionicsStatus.None) => "not-registered",
+ // person is no longer abusing
+ (PsionicsStatus.Abusing, PsionicsStatus.None) => "not-abusing",
+ // this is impossible
+ _ => "not-wanted"
+ };
+ _radio.SendRadioMessage(ent, Loc.GetString($"psionics-records-console-{statusString}", args),
+ ent.Comp.RadioChannel, ent);
+
+ UpdateUserInterface(ent);
+ UpdatePsionicsIdentity(name, msg.Status);
+ }
+
+ private void UpdateUserInterface(Entity ent)
+ {
+ var (uid, console) = ent;
+ var owningStation = _station.GetOwningStation(uid);
+
+ if (!TryComp(owningStation, out var stationRecords))
+ {
+ _ui.SetUiState(uid, PsionicsRecordsConsoleKey.Key, new PsionicsRecordsConsoleState());
+ return;
+ }
+
+ var listing = _stationRecords.BuildListing((owningStation.Value, stationRecords), console.Filter);
+
+ var state = new PsionicsRecordsConsoleState(listing, console.Filter);
+ if (console.ActiveKey is { } id)
+ {
+ // get records to display when a crewmember is selected
+ var key = new StationRecordKey(id, owningStation.Value);
+ _stationRecords.TryGetRecord(key, out state.StationRecord, stationRecords);
+ _stationRecords.TryGetRecord(key, out state.PsionicsRecord, stationRecords);
+ state.SelectedKey = id;
+ }
+
+ _ui.SetUiState(uid, PsionicsRecordsConsoleKey.Key, state);
+ }
+
+ ///
+ /// Boilerplate that most actions use, if they require that a record be selected.
+ /// Obviously shouldn't be used for selecting records.
+ ///
+ private bool CheckSelected(Entity ent, EntityUid user,
+ [NotNullWhen(true)] out EntityUid? mob, [NotNullWhen(true)] out StationRecordKey? key)
+ {
+ key = null;
+ mob = null;
+
+ if (!_access.IsAllowed(user, ent))
+ {
+ _popup.PopupEntity(Loc.GetString("psionics-records-permission-denied"), ent, user);
+ return false;
+ }
+
+ if (ent.Comp.ActiveKey is not { } id)
+ return false;
+
+ // checking the console's station since the user might be off-grid using on-grid console
+ if (_station.GetOwningStation(ent) is not { } station)
+ return false;
+
+ key = new StationRecordKey(id, station);
+ mob = user;
+ return true;
+ }
+
+ ///
+ /// Gets the name from a record, or empty string if this somehow fails.
+ ///
+ private string RecordName(StationRecordKey key)
+ {
+ if (!_stationRecords.TryGetRecord(key, out var record))
+ return "";
+
+ return record.Name;
+ }
+
+ ///
+ /// Checks if the new identity's name has a psionics record attached to it, and gives the entity the icon that
+ /// belongs to the status if it does.
+ ///
+ public void CheckNewIdentity(EntityUid uid)
+ {
+ var name = Identity.Name(uid, EntityManager);
+ var xform = Transform(uid);
+
+ // TODO use the entity's station? Not the station of the map that it happens to currently be on?
+ var station = _station.GetStationInMap(xform.MapID);
+
+ if (station != null && _stationRecords.GetRecordByName(station.Value, name) is { } id)
+ {
+ if (_stationRecords.TryGetRecord(new StationRecordKey(id, station.Value),
+ out var record))
+ {
+ if (record.Status != PsionicsStatus.None)
+ {
+ SetPsionicsIcon(name, record.Status, uid);
+ return;
+ }
+ }
+ }
+ RemComp(uid);
+ }
+}
diff --git a/Content.Server/PsionicsRecords/Systems/PsionicsRecordsSystem.cs b/Content.Server/PsionicsRecords/Systems/PsionicsRecordsSystem.cs
new file mode 100644
index 00000000000..c30851316fa
--- /dev/null
+++ b/Content.Server/PsionicsRecords/Systems/PsionicsRecordsSystem.cs
@@ -0,0 +1,59 @@
+using System.Diagnostics.CodeAnalysis;
+using Content.Server.StationRecords.Systems;
+using Content.Shared.PsionicsRecords;
+using Content.Shared.Psionics;
+using Content.Shared.StationRecords;
+using Content.Server.GameTicking;
+
+///
+/// EVERYTHING HERE IS A MODIFIED VERSION OF CRIMINAL RECORDS
+///
+
+namespace Content.Server.PsionicsRecords.Systems;
+
+///
+/// Psionics records
+///
+/// Psionics Records inherit Station Records' core and add role-playing tools for Epistemics:
+/// - Ability to track a person's status (None/Suspected/Registered/Abusing)
+/// - See cataloguers' actions in Psionics Records in the radio
+/// - See reasons for any action with no need to ask the officer personally
+///
+public sealed class PsionicsRecordsSystem : EntitySystem
+{
+ [Dependency] private readonly GameTicker _ticker = default!;
+ [Dependency] private readonly StationRecordsSystem _stationRecords = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent(OnGeneralRecordCreated);
+ }
+
+ private void OnGeneralRecordCreated(AfterGeneralRecordCreatedEvent ev)
+ {
+ _stationRecords.AddRecordEntry(ev.Key, new PsionicsRecord());
+ _stationRecords.Synchronize(ev.Key);
+ }
+
+ ///
+ /// Tries to change the status of the record found by the StationRecordKey.
+ /// Reason should only be passed if status is not None.
+ ///
+ /// True if the status is changed, false if not
+ public bool TryChangeStatus(StationRecordKey key, PsionicsStatus status, string? reason)
+ {
+ // don't do anything if its the same status
+ if (!_stationRecords.TryGetRecord(key, out var record)
+ || status == record.Status)
+ return false;
+
+ record.Status = status;
+ record.Reason = reason;
+
+ _stationRecords.Synchronize(key);
+
+ return true;
+ }
+}
diff --git a/Content.Shared/CartridgeLoader/Cartridges/PsiWatchUiState.cs b/Content.Shared/CartridgeLoader/Cartridges/PsiWatchUiState.cs
new file mode 100644
index 00000000000..7dae7df5a63
--- /dev/null
+++ b/Content.Shared/CartridgeLoader/Cartridges/PsiWatchUiState.cs
@@ -0,0 +1,24 @@
+using Content.Shared.Psionics;
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.CartridgeLoader.Cartridges;
+
+///
+/// Show a list of wanted and suspected people from psionics records.
+///
+[Serializable, NetSerializable]
+public sealed class PsiWatchUiState : BoundUserInterfaceState
+{
+ public readonly List Entries;
+
+ public PsiWatchUiState(List entries)
+ {
+ Entries = entries;
+ }
+}
+
+///
+/// Entry for a person who is suspected, registered, or abusing.
+///
+[Serializable, NetSerializable]
+public record struct PsiWatchEntry(string Name, string Job, PsionicsStatus Status, string? Reason);
diff --git a/Content.Shared/Inventory/InventorySystem.Relay.cs b/Content.Shared/Inventory/InventorySystem.Relay.cs
index 4375f1ab193..6d9523e8511 100644
--- a/Content.Shared/Inventory/InventorySystem.Relay.cs
+++ b/Content.Shared/Inventory/InventorySystem.Relay.cs
@@ -65,6 +65,7 @@ public void InitializeRelay()
SubscribeLocalEvent>(RelayInventoryEvent);
SubscribeLocalEvent>(RelayInventoryEvent);
SubscribeLocalEvent>(RelayInventoryEvent);
+ SubscribeLocalEvent>(RelayInventoryEvent);
SubscribeLocalEvent>(RelayInventoryEvent);
SubscribeLocalEvent>(RelayInventoryEvent);
diff --git a/Content.Shared/Overlays/ShowPsionicsRecordIconsSystem.cs b/Content.Shared/Overlays/ShowPsionicsRecordIconsSystem.cs
new file mode 100644
index 00000000000..3562ea08088
--- /dev/null
+++ b/Content.Shared/Overlays/ShowPsionicsRecordIconsSystem.cs
@@ -0,0 +1,14 @@
+using Robust.Shared.GameStates;
+
+///
+/// EVERYTHING HERE IS A MODIFIED VERSION OF CRIMINAL RECORDS
+///
+
+namespace Content.Shared.Overlays;
+
+///
+/// This component allows you to see Psionics record status of mobs.
+///
+
+[RegisterComponent, NetworkedComponent]
+public sealed partial class ShowPsionicsRecordIconsComponent : Component { }
diff --git a/Content.Shared/Psionics/Components/PsionicsRecordComponent.cs b/Content.Shared/Psionics/Components/PsionicsRecordComponent.cs
new file mode 100644
index 00000000000..5884410ebda
--- /dev/null
+++ b/Content.Shared/Psionics/Components/PsionicsRecordComponent.cs
@@ -0,0 +1,19 @@
+using Content.Shared.StatusIcon;
+using Robust.Shared.GameStates;
+using Robust.Shared.Prototypes;
+
+///
+/// EVERYTHING HERE IS A MODIFIED VERSION OF CRIMINAL RECORDS
+///
+
+namespace Content.Shared.Psionics.Components;
+
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+public sealed partial class PsionicsRecordComponent : Component
+{
+ ///
+ /// The icon that should be displayed based on the psionics status of the entity.
+ ///
+ [DataField, AutoNetworkedField]
+ public ProtoId StatusIcon = "PsionicsIconStatus";
+}
diff --git a/Content.Shared/Psionics/PsionicsStatus.cs b/Content.Shared/Psionics/PsionicsStatus.cs
new file mode 100644
index 00000000000..59530915763
--- /dev/null
+++ b/Content.Shared/Psionics/PsionicsStatus.cs
@@ -0,0 +1,21 @@
+///
+/// EVERYTHING HERE IS A MODIFIED VERSION OF CRIMINAL RECORDS
+///
+
+namespace Content.Shared.Psionics;
+
+///
+/// Status used in Psionics Records.
+///
+/// None - the default value
+/// Suspected - the person is suspected of having psionics
+/// Registered - the person is a registered psionics user
+/// Abusing - the person has been caught abusing their psionics
+///
+public enum PsionicsStatus : byte
+{
+ None,
+ Suspected,
+ Registered,
+ Abusing
+}
diff --git a/Content.Shared/PsionicsRecords/Components/SharedPsionicsRecordsConsoleComponent.cs b/Content.Shared/PsionicsRecords/Components/SharedPsionicsRecordsConsoleComponent.cs
new file mode 100644
index 00000000000..e8f38073fe9
--- /dev/null
+++ b/Content.Shared/PsionicsRecords/Components/SharedPsionicsRecordsConsoleComponent.cs
@@ -0,0 +1,49 @@
+using Content.Shared.PsionicsRecords.Systems;
+using Content.Shared.Radio;
+using Content.Shared.StationRecords;
+using Robust.Shared.Prototypes;
+
+///
+/// EVERYTHING HERE IS A MODIFIED VERSION OF CRIMINAL RECORDS
+///
+
+namespace Content.Shared.PsionicsRecords.Components;
+
+///
+/// A component for Psionics Records Console storing an active station record key and a currently applied filter
+///
+[RegisterComponent]
+[Access(typeof(SharedPsionicsRecordsConsoleSystem))]
+public sealed partial class PsionicsRecordsConsoleComponent : Component
+{
+ ///
+ /// Currently active station record key.
+ /// There is no station parameter as the console uses the current station.
+ ///
+ ///
+ /// TODO: in the future this should be clientside instead of something players can fight over.
+ /// Client selects a record and tells the server the key it wants records for.
+ /// Server then sends a state with just the records, not the listing or filter, and the client updates just that.
+ /// I don't know if it's possible to have multiple bui states right now.
+ ///
+ [DataField]
+ public uint? ActiveKey;
+
+ ///
+ /// Currently applied filter.
+ ///
+ [DataField]
+ public StationRecordsFilter? Filter;
+
+ ///
+ /// Channel to send messages to when someone's status gets changed.
+ ///
+ [DataField]
+ public ProtoId RadioChannel = "Science";
+
+ ///
+ /// Max length of psionics listing strings.
+ ///
+ [DataField]
+ public uint MaxStringLength = 256;
+}
diff --git a/Content.Shared/PsionicsRecords/PsionicsRecord.cs b/Content.Shared/PsionicsRecords/PsionicsRecord.cs
new file mode 100644
index 00000000000..fd68775e410
--- /dev/null
+++ b/Content.Shared/PsionicsRecords/PsionicsRecord.cs
@@ -0,0 +1,29 @@
+using Content.Shared.Psionics;
+using Robust.Shared.Serialization;
+
+///
+/// EVERYTHING HERE IS A MODIFIED VERSION OF CRIMINAL RECORDS
+///
+
+namespace Content.Shared.PsionicsRecords;
+
+///
+/// Psionics record for a crewmember.
+/// Can be viewed and edited in a psionics records console by epistemics.
+///
+[Serializable, NetSerializable, DataRecord]
+public sealed record PsionicsRecord
+{
+ ///
+ /// Status of the person (None, Suspect, Registered, Abusing).
+ ///
+ [DataField]
+ public PsionicsStatus Status = PsionicsStatus.None;
+
+ ///
+ /// When Status is Anything but none, the reason for it.
+ /// Should never be set otherwise.
+ ///
+ [DataField]
+ public string? Reason;
+}
diff --git a/Content.Shared/PsionicsRecords/PsionicsRecordsUi.cs b/Content.Shared/PsionicsRecords/PsionicsRecordsUi.cs
new file mode 100644
index 00000000000..0e69f2bb6c0
--- /dev/null
+++ b/Content.Shared/PsionicsRecords/PsionicsRecordsUi.cs
@@ -0,0 +1,78 @@
+using Content.Shared.Psionics;
+using Content.Shared.StationRecords;
+using Robust.Shared.Serialization;
+
+///
+/// EVERYTHING HERE IS A MODIFIED VERSION OF CRIMINAL RECORDS
+///
+
+namespace Content.Shared.PsionicsRecords;
+
+[Serializable, NetSerializable]
+public enum PsionicsRecordsConsoleKey : byte
+{
+ Key
+}
+
+///
+/// Psionics records console state. There are a few states:
+/// - SelectedKey null, Record null, RecordListing null
+/// - The station record database could not be accessed.
+/// - SelectedKey null, Record null, RecordListing non-null
+/// - Records are populated in the database, or at least the station has
+/// the correct component.
+/// - SelectedKey non-null, Record null, RecordListing non-null
+/// - The selected key does not have a record tied to it.
+/// - SelectedKey non-null, Record non-null, RecordListing non-null
+/// - The selected key has a record tied to it, and the record has been sent.
+///
+/// - there is added new filters and so added new states
+/// -SelectedKey null, Record null, RecordListing null, filters non-null
+/// the station may have data, but they all did not pass through the filters
+///
+/// Other states are erroneous.
+///
+[Serializable, NetSerializable]
+public sealed class PsionicsRecordsConsoleState : BoundUserInterfaceState
+{
+ ///
+ /// Currently selected crewmember record key.
+ ///
+ public uint? SelectedKey = null;
+
+ public PsionicsRecord? PsionicsRecord = null;
+ public GeneralStationRecord? StationRecord = null;
+ public readonly Dictionary? RecordListing;
+ public readonly StationRecordsFilter? Filter;
+
+ public PsionicsRecordsConsoleState(Dictionary? recordListing, StationRecordsFilter? newFilter)
+ {
+ RecordListing = recordListing;
+ Filter = newFilter;
+ }
+
+ ///
+ /// Default state for opening the console
+ ///
+ public PsionicsRecordsConsoleState() : this(null, null)
+ {
+ }
+
+ public bool IsEmpty() => SelectedKey == null && StationRecord == null && PsionicsRecord == null && RecordListing == null;
+}
+
+///
+/// Used to change status, respecting the psionics nullability rules in .
+///
+[Serializable, NetSerializable]
+public sealed class PsionicsRecordChangeStatus : BoundUserInterfaceMessage
+{
+ public readonly PsionicsStatus Status;
+ public readonly string? Reason;
+
+ public PsionicsRecordChangeStatus(PsionicsStatus status, string? reason)
+ {
+ Status = status;
+ Reason = reason;
+ }
+}
diff --git a/Content.Shared/PsionicsRecords/Systems/SharedPsionicsRecordsConsoleSystem.cs b/Content.Shared/PsionicsRecords/Systems/SharedPsionicsRecordsConsoleSystem.cs
new file mode 100644
index 00000000000..21867e65a02
--- /dev/null
+++ b/Content.Shared/PsionicsRecords/Systems/SharedPsionicsRecordsConsoleSystem.cs
@@ -0,0 +1,54 @@
+using Content.Shared.IdentityManagement;
+using Content.Shared.IdentityManagement.Components;
+using Content.Shared.Psionics;
+using Content.Shared.Psionics.Components;
+
+///
+/// EVERYTHING HERE IS A MODIFIED VERSION OF CRIMINAL RECORDS
+///
+
+namespace Content.Shared.PsionicsRecords.Systems;
+
+public abstract class SharedPsionicsRecordsConsoleSystem : EntitySystem
+{
+ ///
+ /// Any entity that has the name of the record that was just changed as their visible name will get their icon
+ /// updated with the new status, if the record got removed their icon will be removed too.
+ ///
+ public void UpdatePsionicsIdentity(string name, PsionicsStatus status)
+ {
+ var query = EntityQueryEnumerator();
+
+ while (query.MoveNext(out var uid, out var identity))
+ {
+ if (!Identity.Name(uid, EntityManager).Equals(name))
+ continue;
+
+ if (status == PsionicsStatus.None)
+ RemComp(uid);
+ else
+ SetPsionicsIcon(name, status, uid);
+ }
+ }
+
+ ///
+ /// Decides the icon that should be displayed on the entity based on the psionics status
+ ///
+ public void SetPsionicsIcon(string name, PsionicsStatus status, EntityUid characterUid)
+ {
+ EnsureComp(characterUid, out var record);
+
+ var previousIcon = record.StatusIcon;
+
+ record.StatusIcon = status switch
+ {
+ PsionicsStatus.Suspected => "PsionicsIconSuspected",
+ PsionicsStatus.Registered => "PsionicsIconRegistered",
+ PsionicsStatus.Abusing => "PsionicsIconAbusing",
+ _ => record.StatusIcon
+ };
+
+ if (previousIcon != record.StatusIcon)
+ Dirty(characterUid, record);
+ }
+}
diff --git a/Content.Shared/StatusIcon/StatusIconPrototype.cs b/Content.Shared/StatusIcon/StatusIconPrototype.cs
index 689bec3882b..a9ec1f7098e 100644
--- a/Content.Shared/StatusIcon/StatusIconPrototype.cs
+++ b/Content.Shared/StatusIcon/StatusIconPrototype.cs
@@ -163,6 +163,22 @@ public sealed partial class SecurityIconPrototype : StatusIconPrototype, IInheri
public bool Abstract { get; }
}
+///
+/// StatusIcons for showing the psionics status on the epi HUD
+///
+[Prototype]
+public sealed partial class PsionicsIconPrototype : StatusIconPrototype, IInheritingPrototype
+{
+ ///
+ [ParentDataField(typeof(AbstractPrototypeIdArraySerializer))]
+ public string[]? Parents { get; }
+
+ ///
+ [NeverPushInheritance]
+ [AbstractDataField]
+ public bool Abstract { get; }
+}
+
///
/// StatusIcons for faction membership
///
diff --git a/Resources/Locale/en-US/cartridge-loader/psiwatch.ftl b/Resources/Locale/en-US/cartridge-loader/psiwatch.ftl
new file mode 100644
index 00000000000..33df99fd44f
--- /dev/null
+++ b/Resources/Locale/en-US/cartridge-loader/psiwatch.ftl
@@ -0,0 +1,5 @@
+psi-watch-program-name = PsiWatch
+psi-watch-title = PsiWatch 1.3
+psi-watch-no-entries = Nobody is registered. Why not enjoy a nice, cold brew?
+psi-watch-entry = {$name}, {$job}
+psi-watch-no-reason = [ERROR] No reason given?
diff --git a/Resources/Locale/en-US/psionics-records/psionics-records.ftl b/Resources/Locale/en-US/psionics-records/psionics-records.ftl
new file mode 100644
index 00000000000..ff2c2b8a4c5
--- /dev/null
+++ b/Resources/Locale/en-US/psionics-records/psionics-records.ftl
@@ -0,0 +1,37 @@
+psionics-records-console-window-title = Psionics Registry Records
+psionics-records-console-records-list-title = Crewmembers
+psionics-records-console-select-record-info = Select a record.
+psionics-records-console-no-records = No records found!
+psionics-records-console-no-record-found = No record was found for the selected person.
+
+## Status
+
+psionics-records-console-status = Status
+psionics-records-status-none = None
+psionics-records-status-registered = Registered Psionic
+psionics-records-status-suspected = Suspected Psionics
+psionics-records-status-abusing = Abusing Psionics
+
+psionics-records-console-wanted-reason = [color=gray]Psionics Listed[/color]
+psionics-records-console-suspected-reason = [color=gray]Suspected Reason[/color]
+psionics-records-console-reason = Psionics/Reason
+psionics-records-console-reason-placeholder = For example: {$placeholder}
+
+psionics-records-permission-denied = Permission denied
+
+## Security channel notifications
+
+psionics-records-console-registered = {$name} is registered as psionic by {$officer}: {$reason}.
+psionics-records-console-suspected = {$officer} marked {$name} as a possible psionic because of: {$reason}.
+psionics-records-console-not-suspected = {$name} is no longer a suspected psionic.
+psionics-records-console-not-registered = {$name} is no longer registered as psionic.
+psionics-records-console-abusing = {$officer} marked {$name} as abusing psionics because of: {$reason}.
+psionics-records-console-not-abusing = {$name} is no longer marked as abusing psionics.
+psionics-records-console-unknown-officer =
+
+## Filters
+
+psionics-records-filter-placeholder = Input text and press "Enter"
+psionics-records-name-filter = Name
+psionics-records-prints-filter = Fingerprints
+psionics-records-dna-filter = DNA
diff --git a/Resources/Maps/arena.yml b/Resources/Maps/arena.yml
index aa61c9bb3af..be3a8428849 100644
--- a/Resources/Maps/arena.yml
+++ b/Resources/Maps/arena.yml
@@ -58311,6 +58311,14 @@ entities:
rot: 1.5707963267948966 rad
pos: 50.5,7.5
parent: 6747
+- proto: ComputerPsionicsRecords
+ entities:
+ - uid: 27824
+ components:
+ - type: Transform
+ rot: -1.5707963267948966 rad
+ pos: 22.5,-62.5
+ parent: 6747
- proto: ComputerRadar
entities:
- uid: 3976
diff --git a/Resources/Maps/asterisk.yml b/Resources/Maps/asterisk.yml
index 41fc0f2e73d..4966832b241 100644
--- a/Resources/Maps/asterisk.yml
+++ b/Resources/Maps/asterisk.yml
@@ -25465,6 +25465,14 @@ entities:
rot: 3.141592653589793 rad
pos: 30.5,6.5
parent: 2
+- proto: ComputerPsionicsRecords
+ entities:
+ - uid: 11188
+ components:
+ - type: Transform
+ rot: -1.5707963267948966 rad
+ pos: -28.5,-18.5
+ parent: 2
- proto: ComputerRadar
entities:
- uid: 10761
diff --git a/Resources/Maps/core.yml b/Resources/Maps/core.yml
index 9685efd2e13..8284ed39610 100644
--- a/Resources/Maps/core.yml
+++ b/Resources/Maps/core.yml
@@ -61680,6 +61680,13 @@ entities:
- type: Transform
pos: -1.5,-11.5
parent: 2
+- proto: ComputerPsionicsRecords
+ entities:
+ - uid: 22546
+ components:
+ - type: Transform
+ pos: 60.5,-11.5
+ parent: 2
- proto: ComputerRadar
entities:
- uid: 4074
diff --git a/Resources/Maps/edge.yml b/Resources/Maps/edge.yml
index f19c2065af2..4c1274f61d1 100644
--- a/Resources/Maps/edge.yml
+++ b/Resources/Maps/edge.yml
@@ -5356,7 +5356,8 @@ entities:
-12,7:
0: 65535
-11,7:
- 0: 65535
+ 0: 57343
+ 3: 8192
-11,-8:
0: 53198
-10,-8:
@@ -5451,11 +5452,11 @@ entities:
0: 65535
1,-12:
0: 61937
- 3: 14
- 4: 3584
+ 4: 14
+ 5: 3584
1,-11:
0: 65521
- 5: 14
+ 6: 14
1,-10:
0: 65535
2,-12:
@@ -5560,15 +5561,15 @@ entities:
0: 63487
0,-14:
0: 65522
- 3: 12
+ 4: 12
0,-13:
0: 65535
1,-14:
0: 65521
1,-13:
0: 61937
- 3: 14
- 6: 3584
+ 4: 14
+ 7: 3584
2,-14:
0: 12848
2,-13:
@@ -5937,7 +5938,7 @@ entities:
0: 1908
-16,7:
0: 40704
- 3: 24576
+ 4: 24576
-15,7:
0: 65280
-12,10:
@@ -5954,7 +5955,7 @@ entities:
0: 65535
-16,9:
0: 65439
- 3: 96
+ 4: 96
-16,10:
0: 231
-15,8:
@@ -6091,17 +6092,17 @@ entities:
0: 4095
5,-12:
0: 883
- 3: 3212
+ 4: 3212
3,-13:
0: 51200
4,-13:
0: 65392
- 3: 128
+ 4: 128
5,-13:
0: 4096
- 3: 59184
+ 4: 59184
6,-12:
- 3: 305
+ 4: 305
uniqueMixes:
- volume: 2500
temperature: 293.15
@@ -6148,6 +6149,21 @@ entities:
- 0
- 0
- 0
+ - volume: 2500
+ temperature: 293.14975
+ moles:
+ - 20.078888
+ - 75.53487
+ - 0
+ - 0
+ - 0
+ - 0
+ - 0
+ - 0
+ - 0
+ - 0
+ - 0
+ - 0
- volume: 2500
temperature: 293.15
moles:
@@ -39459,6 +39475,14 @@ entities:
rot: -1.5707963267948966 rad
pos: 0.5,-25.5
parent: 2
+- proto: ComputerPsionicsRecords
+ entities:
+ - uid: 17482
+ components:
+ - type: Transform
+ rot: 1.5707963267948966 rad
+ pos: -42.5,32.5
+ parent: 2
- proto: ComputerRadar
entities:
- uid: 6157
@@ -40235,6 +40259,24 @@ entities:
- type: Transform
pos: -42.5,31.5
parent: 2
+ - type: EntityStorage
+ air:
+ volume: 200
+ immutable: False
+ temperature: 293.14673
+ moles:
+ - 1.7459903
+ - 6.568249
+ - 0
+ - 0
+ - 0
+ - 0
+ - 0
+ - 0
+ - 0
+ - 0
+ - 0
+ - 0
- proto: CrateScienceSecure
entities:
- uid: 6273
diff --git a/Resources/Maps/europa.yml b/Resources/Maps/europa.yml
index 0d54891dc6a..c642efb66c9 100644
--- a/Resources/Maps/europa.yml
+++ b/Resources/Maps/europa.yml
@@ -34896,12 +34896,6 @@ entities:
rot: 1.5707963267948966 rad
pos: 6.5,33.5
parent: 1
- - uid: 299
- components:
- - type: Transform
- rot: -1.5707963267948966 rad
- pos: 10.5,17.5
- parent: 1
- uid: 455
components:
- type: Transform
@@ -37855,6 +37849,14 @@ entities:
rot: 3.141592653589793 rad
pos: -19.5,-24.5
parent: 1
+- proto: ComputerPsionicsRecords
+ entities:
+ - uid: 299
+ components:
+ - type: Transform
+ rot: -1.5707963267948966 rad
+ pos: 10.5,17.5
+ parent: 1
- proto: ComputerRadar
entities:
- uid: 3026
@@ -65764,7 +65766,7 @@ entities:
pos: -11.5,11.5
parent: 1
- type: Door
- secondsUntilStateChange: -26515.367
+ secondsUntilStateChange: -27843.273
state: Opening
- type: Occluder
enabled: True
diff --git a/Resources/Maps/gaxstation.yml b/Resources/Maps/gaxstation.yml
index f502c4b698d..cada7f3a9d0 100644
--- a/Resources/Maps/gaxstation.yml
+++ b/Resources/Maps/gaxstation.yml
@@ -53573,6 +53573,14 @@ entities:
- type: Transform
pos: -31.5,29.5
parent: 2
+- proto: ComputerPsionicsRecords
+ entities:
+ - uid: 20872
+ components:
+ - type: Transform
+ rot: -1.5707963267948966 rad
+ pos: 50.5,26.5
+ parent: 2
- proto: ComputerRadar
entities:
- uid: 7211
@@ -134450,7 +134458,7 @@ entities:
lastSignals:
DoorStatus: True
- type: Door
- secondsUntilStateChange: -120542.65
+ secondsUntilStateChange: -120749.305
state: Opening
- uid: 18112
components:
diff --git a/Resources/Maps/glacier.yml b/Resources/Maps/glacier.yml
index c0fcbe15321..437e8ede164 100644
--- a/Resources/Maps/glacier.yml
+++ b/Resources/Maps/glacier.yml
@@ -46640,6 +46640,14 @@ entities:
- type: Transform
pos: 35.5,50.5
parent: 2
+- proto: ComputerPsionicsRecords
+ entities:
+ - uid: 17501
+ components:
+ - type: Transform
+ rot: -1.5707963267948966 rad
+ pos: 29.5,-21.5
+ parent: 2
- proto: ComputerRadar
entities:
- uid: 17129
diff --git a/Resources/Maps/hammurabi.yml b/Resources/Maps/hammurabi.yml
index 09f874f94b9..d360afe201c 100644
--- a/Resources/Maps/hammurabi.yml
+++ b/Resources/Maps/hammurabi.yml
@@ -88000,6 +88000,14 @@ entities:
rot: 3.141592653589793 rad
pos: -114.5,-89.5
parent: 1
+- proto: ComputerPsionicsRecords
+ entities:
+ - uid: 42455
+ components:
+ - type: Transform
+ rot: 3.141592653589793 rad
+ pos: -19.5,-41.5
+ parent: 1
- proto: ComputerRadar
entities:
- uid: 8226
diff --git a/Resources/Maps/hive.yml b/Resources/Maps/hive.yml
index ef0019b3454..d24bf87ea5e 100644
--- a/Resources/Maps/hive.yml
+++ b/Resources/Maps/hive.yml
@@ -60757,6 +60757,14 @@ entities:
- type: Transform
pos: -96.5,5.5
parent: 1
+- proto: ComputerPsionicsRecords
+ entities:
+ - uid: 27792
+ components:
+ - type: Transform
+ rot: -1.5707963267948966 rad
+ pos: 1.5,42.5
+ parent: 1
- proto: ComputerRadar
entities:
- uid: 5888
diff --git a/Resources/Maps/lighthouse.yml b/Resources/Maps/lighthouse.yml
index 9921bbb9f6d..0ee3064aba6 100644
--- a/Resources/Maps/lighthouse.yml
+++ b/Resources/Maps/lighthouse.yml
@@ -44908,6 +44908,13 @@ entities:
- type: Transform
pos: -7.5,-39.5
parent: 100
+- proto: ComputerPsionicsRecords
+ entities:
+ - uid: 20901
+ components:
+ - type: Transform
+ pos: -21.5,-6.5
+ parent: 100
- proto: ComputerResearchAndDevelopment
entities:
- uid: 5793
diff --git a/Resources/Maps/meta.yml b/Resources/Maps/meta.yml
index 30d6d9beb60..06f43cb9603 100644
--- a/Resources/Maps/meta.yml
+++ b/Resources/Maps/meta.yml
@@ -10339,7 +10339,7 @@ entities:
pos: -53.5,-12.5
parent: 2
- type: Door
- secondsUntilStateChange: -12876.496
+ secondsUntilStateChange: -13237.977
state: Opening
- type: DeviceLinkSource
lastSignals:
@@ -63769,6 +63769,14 @@ entities:
rot: 3.141592653589793 rad
pos: 110.5,-0.5
parent: 2
+- proto: ComputerPsionicsRecords
+ entities:
+ - uid: 27708
+ components:
+ - type: Transform
+ rot: 3.141592653589793 rad
+ pos: 10.5,-25.5
+ parent: 2
- proto: ComputerRadar
entities:
- uid: 10045
@@ -75688,7 +75696,7 @@ entities:
pos: -3.5,-52.5
parent: 2
- type: Door
- secondsUntilStateChange: -40902.03
+ secondsUntilStateChange: -41263.51
state: Closing
- uid: 11944
components:
diff --git a/Resources/Maps/pebble.yml b/Resources/Maps/pebble.yml
index 9b7c428b513..1d468237f28 100644
--- a/Resources/Maps/pebble.yml
+++ b/Resources/Maps/pebble.yml
@@ -29532,6 +29532,14 @@ entities:
rot: 3.141592653589793 rad
pos: 37.5,14.5
parent: 2
+- proto: ComputerPsionicsRecords
+ entities:
+ - uid: 10854
+ components:
+ - type: Transform
+ rot: -1.5707963267948966 rad
+ pos: -0.5,28.5
+ parent: 2
- proto: ComputerRadar
entities:
- uid: 4588
diff --git a/Resources/Maps/radstation.yml b/Resources/Maps/radstation.yml
index f350c67478e..ed00efa981c 100644
--- a/Resources/Maps/radstation.yml
+++ b/Resources/Maps/radstation.yml
@@ -15129,7 +15129,7 @@ entities:
pos: 34.5,-35.5
parent: 2
- type: Door
- secondsUntilStateChange: -55586.55
+ secondsUntilStateChange: -55771.664
state: Opening
- uid: 383
components:
@@ -62312,6 +62312,14 @@ entities:
- type: Transform
pos: -12.5,-55.5
parent: 2
+- proto: ComputerPsionicsRecords
+ entities:
+ - uid: 22318
+ components:
+ - type: Transform
+ rot: -1.5707963267948966 rad
+ pos: -5.5,-28.5
+ parent: 2
- proto: ComputerRadar
entities:
- uid: 9048
@@ -157782,7 +157790,7 @@ entities:
links:
- 19976
- type: Door
- secondsUntilStateChange: -191878.86
+ secondsUntilStateChange: -192063.97
state: Opening
- uid: 23356
components:
diff --git a/Resources/Maps/saltern.yml b/Resources/Maps/saltern.yml
index e2f8a808534..b681e6923da 100644
--- a/Resources/Maps/saltern.yml
+++ b/Resources/Maps/saltern.yml
@@ -4332,7 +4332,7 @@ entities:
pos: 3.5,22.5
parent: 31
- type: Door
- secondsUntilStateChange: -32634.607
+ secondsUntilStateChange: -32743.814
state: Opening
- type: DeviceLinkSource
lastSignals:
@@ -29782,6 +29782,14 @@ entities:
rot: -1.5707963267948966 rad
pos: 60.5,3.5
parent: 31
+- proto: ComputerPsionicsRecords
+ entities:
+ - uid: 11412
+ components:
+ - type: Transform
+ rot: 3.141592653589793 rad
+ pos: -14.5,-22.5
+ parent: 31
- proto: ComputerRadar
entities:
- uid: 579
diff --git a/Resources/Maps/shoukou.yml b/Resources/Maps/shoukou.yml
index 4cbe47174af..d75e9c6c75b 100644
--- a/Resources/Maps/shoukou.yml
+++ b/Resources/Maps/shoukou.yml
@@ -30445,6 +30445,14 @@ entities:
rot: 1.5707963267948966 rad
pos: 42.5,-35.5
parent: 34
+- proto: ComputerPsionicsRecords
+ entities:
+ - uid: 13763
+ components:
+ - type: Transform
+ rot: 3.141592653589793 rad
+ pos: -30.5,-11.5
+ parent: 34
- proto: ComputerRadar
entities:
- uid: 5579
@@ -36708,7 +36716,7 @@ entities:
pos: -36.5,-22.5
parent: 34
- type: Door
- secondsUntilStateChange: -67401.266
+ secondsUntilStateChange: -68431.75
state: Closing
- uid: 1274
components:
diff --git a/Resources/Maps/submarine.yml b/Resources/Maps/submarine.yml
index 0890fe73aa5..123cc527dc1 100644
--- a/Resources/Maps/submarine.yml
+++ b/Resources/Maps/submarine.yml
@@ -100515,6 +100515,14 @@ entities:
- type: Transform
pos: 87.5,-19.5
parent: 2
+- proto: ComputerPsionicsRecords
+ entities:
+ - uid: 39308
+ components:
+ - type: Transform
+ rot: -1.5707963267948966 rad
+ pos: 32.5,28.5
+ parent: 2
- proto: ComputerRadar
entities:
- uid: 6359
diff --git a/Resources/Maps/tortuga.yml b/Resources/Maps/tortuga.yml
index f9f702c820c..df56bcd11fd 100644
--- a/Resources/Maps/tortuga.yml
+++ b/Resources/Maps/tortuga.yml
@@ -63598,6 +63598,14 @@ entities:
rot: 1.5707963267948966 rad
pos: 48.5,28.5
parent: 33
+- proto: ComputerPsionicsRecords
+ entities:
+ - uid: 28381
+ components:
+ - type: Transform
+ rot: -1.5707963267948966 rad
+ pos: -64.5,-6.5
+ parent: 33
- proto: ComputerRadar
entities:
- uid: 3325
diff --git a/Resources/Prototypes/Catalog/Fills/Lockers/heads.yml b/Resources/Prototypes/Catalog/Fills/Lockers/heads.yml
index 9f27ee5700a..437a12aed06 100644
--- a/Resources/Prototypes/Catalog/Fills/Lockers/heads.yml
+++ b/Resources/Prototypes/Catalog/Fills/Lockers/heads.yml
@@ -242,6 +242,7 @@
- id: Intellicard
- id: LunchboxCommandFilledRandom # Delta-V Lunchboxes!
prob: 0.3
+ - id: PsiWatchCartridge
- type: entity
id: LockerResearchDirectorFilled
@@ -264,6 +265,7 @@
- id: EncryptionKeyBinary
- id: LunchboxCommandFilledRandom # Delta-V Lunchboxes!
prob: 0.3
+ - id: PsiWatchCartridge
- type: entity
id: LockerHeadOfSecurityFilledHardsuit
diff --git a/Resources/Prototypes/CharacterItemGroups/Jobs/Epistemics/cataloger.yml b/Resources/Prototypes/CharacterItemGroups/Jobs/Epistemics/cataloger.yml
index 2e53ad71dac..d94c971e836 100644
--- a/Resources/Prototypes/CharacterItemGroups/Jobs/Epistemics/cataloger.yml
+++ b/Resources/Prototypes/CharacterItemGroups/Jobs/Epistemics/cataloger.yml
@@ -25,11 +25,15 @@
- type: loadout
id: LoadoutCatalogerPillCanisterSpaceDrugs
-#- type: characterItemGroup
-# id: LoadoutCatalogerEyes
-# maxItems: 1
-# items:
-#
+- type: characterItemGroup
+ id: LoadoutCatalogerEyes
+ maxItems: 1
+ items:
+ - type: loadout
+ id: LoadoutCatalogerEyesEpiHUD
+ - type: loadout
+ id: LoadoutCatalogerEyesEpiGlasses
+
#- type: characterItemGroup
# id: LoadoutCatalogerGloves
# maxItems: 1
diff --git a/Resources/Prototypes/CharacterItemGroups/Jobs/Epistemics/chaplain.yml b/Resources/Prototypes/CharacterItemGroups/Jobs/Epistemics/chaplain.yml
index ff3d75be33c..3d44c7211dc 100644
--- a/Resources/Prototypes/CharacterItemGroups/Jobs/Epistemics/chaplain.yml
+++ b/Resources/Prototypes/CharacterItemGroups/Jobs/Epistemics/chaplain.yml
@@ -29,11 +29,15 @@
- type: loadout
id: LoadoutChaplainPillCanisterSpaceDrugs
-#- type: characterItemGroup
-# id: LoadoutChaplainEyes
-# maxItems: 1
-# items:
-#
+- type: characterItemGroup
+ id: LoadoutChaplainEyes
+ maxItems: 1
+ items:
+ - type: loadout
+ id: LoadoutChaplainEyesEpiHUD
+ - type: loadout
+ id: LoadoutChaplainEyesEpiGlasses
+
#- type: characterItemGroup
# id: LoadoutChaplainGloves
# maxItems: 1
diff --git a/Resources/Prototypes/CharacterItemGroups/Jobs/Epistemics/mystagogue.yml b/Resources/Prototypes/CharacterItemGroups/Jobs/Epistemics/mystagogue.yml
index 7e2fbbe4b6a..7bf929a8853 100644
--- a/Resources/Prototypes/CharacterItemGroups/Jobs/Epistemics/mystagogue.yml
+++ b/Resources/Prototypes/CharacterItemGroups/Jobs/Epistemics/mystagogue.yml
@@ -31,11 +31,15 @@
- type: loadout
id: LoadoutMystagoguePillCanisterSpaceDrugs
-#- type: characterItemGroup
-# id: LoadoutMystagogueEyes
-# maxItems: 1
-# items:
-#
+- type: characterItemGroup
+ id: LoadoutMystagogueEyes
+ maxItems: 1
+ items:
+ - type: loadout
+ id: LoadoutMystagogueEyesEpiHUD
+ - type: loadout
+ id: LoadoutMystagogueEyesEpiGlasses
+
#- type: characterItemGroup
# id: LoadoutMystagogueGloves
# maxItems: 1
diff --git a/Resources/Prototypes/CharacterItemGroups/Jobs/Epistemics/psionicMantis.yml b/Resources/Prototypes/CharacterItemGroups/Jobs/Epistemics/psionicMantis.yml
index 32660c5f0bf..38bdd91206f 100644
--- a/Resources/Prototypes/CharacterItemGroups/Jobs/Epistemics/psionicMantis.yml
+++ b/Resources/Prototypes/CharacterItemGroups/Jobs/Epistemics/psionicMantis.yml
@@ -27,11 +27,15 @@
- type: loadout
id: LoadoutPsionicMantisPillCanisterCryptobiolin
-#- type: characterItemGroup
-# id: LoadoutPsionicMantisEyes
-# maxItems: 1
-# items:
-#
+- type: characterItemGroup
+ id: LoadoutPsionicMantisEyes
+ maxItems: 1
+ items:
+ - type: loadout
+ id: LoadoutPsionicMantisEyesEpiHUD
+ - type: loadout
+ id: LoadoutPsionicMantisEyesEpiGlasses
+
#- type: characterItemGroup
# id: LoadoutPsionicMantisGloves
# maxItems: 1
diff --git a/Resources/Prototypes/Datasets/psionic-records.yml b/Resources/Prototypes/Datasets/psionic-records.yml
new file mode 100644
index 00000000000..9333b09831a
--- /dev/null
+++ b/Resources/Prototypes/Datasets/psionic-records.yml
@@ -0,0 +1,15 @@
+# "funny" placeholders of extremely minor/non-crimes for wanted reason dialog
+- type: dataset
+ id: PsionicsRecordsRecordsPlaceholders
+ values:
+ - Mindswapped with a rat 2 billion times
+ - Ascended into the telepathic realm with mind control
+ - Forced a clown to punch himself
+ - Forced a mime to speak in owo accent
+ - Mindcontrolled Captain to say bad words
+ - Pissed off the Oracle
+ - Became a Psionic
+ - Usurped the throne
+ - Mindswapped the Mantis with a shoe
+ - Drew stick figures in the Chaplain's bible
+ - Revived a cockroach
diff --git a/Resources/Prototypes/Entities/Clothing/Eyes/glasses.yml b/Resources/Prototypes/Entities/Clothing/Eyes/glasses.yml
index 6231663ac79..1018f8006ea 100644
--- a/Resources/Prototypes/Entities/Clothing/Eyes/glasses.yml
+++ b/Resources/Prototypes/Entities/Clothing/Eyes/glasses.yml
@@ -231,6 +231,25 @@
- type: IdentityBlocker
coverage: EYES
+- type: entity
+ parent: [ClothingEyesBase, ShowPsionicsIcons]
+ id: ClothingEyesGlassesEpistemics
+ name: epi psionics glasses
+ description: Upgraded sunglasses that provide flash immunity and a epistemics psionics HUD.
+ components:
+ - type: Sprite
+ sprite: Clothing/Eyes/Glasses/epiglasses.rsi
+ - type: Clothing
+ sprite: Clothing/Eyes/Glasses/epiglasses.rsi
+ - type: FlashImmunity
+ - type: EyeProtection
+ protectionTime: 5
+ - type: Tag
+ tags:
+ - WhitelistChameleon
+ - type: IdentityBlocker
+ coverage: EYES
+
- type: entity
parent: ClothingEyesGlassesCheapSunglasses
id: ClothingEyesGlassesCheapSunglassesAviator
diff --git a/Resources/Prototypes/Entities/Clothing/Eyes/hud.yml b/Resources/Prototypes/Entities/Clothing/Eyes/hud.yml
index a4ba51b4133..dcf3c02e424 100644
--- a/Resources/Prototypes/Entities/Clothing/Eyes/hud.yml
+++ b/Resources/Prototypes/Entities/Clothing/Eyes/hud.yml
@@ -15,6 +15,13 @@
- type: ShowHealthBars
- type: ShowHealthIcons
+- type: entity
+ id: ShowPsionicsIcons
+ abstract: true
+ categories: [ HideSpawnMenu ]
+ components:
+ - type: ShowPsionicsRecordIcons
+
- type: entity
parent: ClothingEyesBase
id: ClothingEyesHudDiagnostic
@@ -64,6 +71,17 @@
tags:
- HudSecurity
+- type: entity
+ parent: [ClothingEyesBase, ShowPsionicsIcons]
+ id: ClothingEyesHudEpistemics
+ name: epi psionics hud
+ description: A heads-up display that scans the humanoids in view and provides accurate data about their psionics records.
+ components:
+ - type: Sprite
+ sprite: Clothing/Eyes/Hud/epi.rsi
+ - type: Clothing
+ sprite: Clothing/Eyes/Hud/epi.rsi
+
- type: entity
parent: ClothingEyesBase
id: ClothingEyesHudBeer
@@ -170,7 +188,7 @@
damageContainers:
- Biological
- type: ShowHealthIcons
- damageContainers:
+ damageContainers:
- Biological
- type: entity
diff --git a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/computer.yml b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/computer.yml
index 93fc8128851..520f830e806 100644
--- a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/computer.yml
+++ b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/computer.yml
@@ -76,6 +76,17 @@
- type: ComputerBoard
prototype: ComputerCriminalRecords
+- type: entity
+ parent: BaseComputerCircuitboard
+ id: PsionicsRecordsComputerCircuitboard
+ name: psionics registry computer board
+ description: A computer printed circuit board for a psionics registry computer.
+ components:
+ - type: Sprite
+ state: cpu_science
+ - type: ComputerBoard
+ prototype: ComputerPsionicsRecords
+
- type: entity
parent: BaseComputerCircuitboard
id: StationRecordsComputerCircuitboard
diff --git a/Resources/Prototypes/Entities/Objects/Devices/cartridges.yml b/Resources/Prototypes/Entities/Objects/Devices/cartridges.yml
index e9108fd341b..e8c6816feab 100644
--- a/Resources/Prototypes/Entities/Objects/Devices/cartridges.yml
+++ b/Resources/Prototypes/Entities/Objects/Devices/cartridges.yml
@@ -131,3 +131,25 @@
sprite: Objects/Devices/gps.rsi
state: icon
- type: AstroNavCartridge
+
+
+- type: entity
+ parent: BaseItem
+ id: PsiWatchCartridge
+ name: psi watch cartridge
+ description: A cartridge that tracks the status of currently documented psionics individuals.
+ components:
+ - type: Sprite
+ sprite: Objects/Devices/cartridge.rsi
+ state: cart-psi
+ - type: Icon
+ sprite: Objects/Devices/cartridge.rsi
+ state: cart-psi
+ - type: UIFragment
+ ui: !type:PsiWatchUi
+ - type: Cartridge
+ programName: psi-watch-program-name
+ icon:
+ sprite: DeltaV/Structures/Machines/glimmer_machines.rsi
+ state: prober
+ - type: PsiWatchCartridge
diff --git a/Resources/Prototypes/Entities/Objects/Devices/pda.yml b/Resources/Prototypes/Entities/Objects/Devices/pda.yml
index 21d707c915c..61ab7705135 100644
--- a/Resources/Prototypes/Entities/Objects/Devices/pda.yml
+++ b/Resources/Prototypes/Entities/Objects/Devices/pda.yml
@@ -369,6 +369,14 @@
- type: Icon
sprite: DeltaV/Objects/Devices/pda.rsi # DeltaV - Give Chaplain PDA Epistemics colors
state: pda-chaplain
+ - type: CartridgeLoader # Nyanotrasen - Glimmer Monitor
+ preinstalled:
+ - CrewManifestCartridge
+ - NotekeeperCartridge
+ - NewsReaderCartridge
+ - GlimmerMonitorCartridge
+ - NanoChatCartridge # DeltaV
+ - PsiWatchCartridge
- type: entity
name: logistics officer PDA # DeltaV - Logistics Department replacing Cargo
@@ -500,6 +508,7 @@
- NewsReaderCartridge
- GlimmerMonitorCartridge
- NanoChatCartridge
+ - PsiWatchCartridge
- type: entity
parent: BasePDA
@@ -809,6 +818,7 @@
- NewsReaderCartridge
- GlimmerMonitorCartridge
- NanoChatCartridge # DeltaV
+ - PsiWatchCartridge
- type: entity
parent: BasePDA
@@ -832,6 +842,7 @@
- NewsReaderCartridge
- GlimmerMonitorCartridge
- NanoChatCartridge
+ - PsiWatchCartridge
- type: entity
parent: BasePDA
@@ -1012,6 +1023,7 @@
- SecWatchCartridge # DeltaV: SecWatch replaces WantedList
- StockTradingCartridge # Delta-V
- NanoChatCartridge # DeltaV
+ - PsiWatchCartridge
- type: entity
parent: CentcomPDA
@@ -1387,6 +1399,9 @@
accentVColor: "#8900c9"
- type: Icon
state: pda-seniorresearcher
+ - type: CartridgeLoader
+ preinstalled:
+ - PsiWatchCartridge
- type: entity
parent: BaseMedicalPDA
diff --git a/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml b/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml
index c76dba11385..191f7dba941 100644
--- a/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml
+++ b/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml
@@ -423,6 +423,46 @@
guides:
- CriminalRecords
+- type: entity
+ parent: BaseComputerAiAccess
+ id: ComputerPsionicsRecords
+ name: psionics registry computer
+ description: This can be used to check psionics registry records. Only epistemics can modify them.
+ components:
+ - type: PsionicsRecordsConsole
+ - type: UserInterface
+ interfaces:
+ enum.PsionicsRecordsConsoleKey.Key:
+ type: PsionicsRecordsConsoleBoundUserInterface
+ - type: ActivatableUI
+ key: enum.PsionicsRecordsConsoleKey.Key
+ - type: Sprite
+ layers:
+ - map: ["computerLayerBody"]
+ state: computer
+ - map: ["computerLayerKeyboard"]
+ state: generic_keyboard
+ - map: ["computerLayerScreen"]
+ state: registry
+ - map: ["computerLayerKeys"]
+ state: rd_key
+ - map: [ "enum.WiresVisualLayers.MaintenancePanel" ]
+ state: generic_panel_open
+ - type: PointLight
+ radius: 1.5
+ energy: 1.6
+ color: "#1f8c28"
+ - type: Computer
+ board: PsionicsRecordsComputerCircuitboard
+ - type: AccessReader
+ access:
+ - [ Chapel ]
+ - [ Library ]
+ - [ Mantis ]
+ - [ ResearchDirector ]
+ # - type: GuideHelp
+ # guides: # TODO: Add a guide for Psionics Registry
+
- type: entity
parent: BaseComputerAiAccess
id: ComputerStationRecords
diff --git a/Resources/Prototypes/Loadouts/Jobs/Epistemics/cataloger.yml b/Resources/Prototypes/Loadouts/Jobs/Epistemics/cataloger.yml
index 811ae5e2f0b..4eb0ba6973b 100644
--- a/Resources/Prototypes/Loadouts/Jobs/Epistemics/cataloger.yml
+++ b/Resources/Prototypes/Loadouts/Jobs/Epistemics/cataloger.yml
@@ -60,6 +60,33 @@
- PillCanisterSpaceDrugs
# Eyes
+- type: loadout
+ id: LoadoutCatalogerEyesEpiHUD
+ category: JobsEpistemicsCataloger
+ cost: 0
+ exclusive: true
+ requirements:
+ - !type:CharacterItemGroupRequirement
+ group: LoadoutCatalogerEquipment
+ - !type:CharacterJobRequirement
+ jobs:
+ - Librarian
+ items:
+ - ClothingEyesHudEpistemics
+
+- type: loadout
+ id: LoadoutCatalogerEyesEpiGlasses
+ category: JobsEpistemicsCataloger
+ cost: 3
+ exclusive: true
+ requirements:
+ - !type:CharacterItemGroupRequirement
+ group: LoadoutCatalogerEquipment
+ - !type:CharacterJobRequirement
+ jobs:
+ - Librarian
+ items:
+ - ClothingEyesGlassesEpistemics
# Gloves
diff --git a/Resources/Prototypes/Loadouts/Jobs/Epistemics/chaplain.yml b/Resources/Prototypes/Loadouts/Jobs/Epistemics/chaplain.yml
index 970d697014f..7a4527cd7a5 100644
--- a/Resources/Prototypes/Loadouts/Jobs/Epistemics/chaplain.yml
+++ b/Resources/Prototypes/Loadouts/Jobs/Epistemics/chaplain.yml
@@ -75,6 +75,33 @@
- PillCanisterSpaceDrugs
# Eyes
+- type: loadout
+ id: LoadoutChaplainEyesEpiHUD
+ category: JobsEpistemicsChaplain
+ cost: 0
+ exclusive: true
+ requirements:
+ - !type:CharacterItemGroupRequirement
+ group: LoadoutChaplainEquipment
+ - !type:CharacterJobRequirement
+ jobs:
+ - Chaplain
+ items:
+ - ClothingEyesHudEpistemics
+
+- type: loadout
+ id: LoadoutChaplainEyesEpiGlasses
+ category: JobsEpistemicsChaplain
+ cost: 3
+ exclusive: true
+ requirements:
+ - !type:CharacterItemGroupRequirement
+ group: LoadoutChaplainEquipment
+ - !type:CharacterJobRequirement
+ jobs:
+ - Chaplain
+ items:
+ - ClothingEyesGlassesEpistemics
# Gloves
diff --git a/Resources/Prototypes/Loadouts/Jobs/Epistemics/mystagogue.yml b/Resources/Prototypes/Loadouts/Jobs/Epistemics/mystagogue.yml
index f36ca4f6603..eaa7821a17f 100644
--- a/Resources/Prototypes/Loadouts/Jobs/Epistemics/mystagogue.yml
+++ b/Resources/Prototypes/Loadouts/Jobs/Epistemics/mystagogue.yml
@@ -88,6 +88,33 @@
- PillCanisterSpaceDrugs
# Eyes
+- type: loadout
+ id: LoadoutMystagogueEyesEpiHUD
+ category: JobsEpistemicsMystagogue
+ cost: 0
+ exclusive: true
+ requirements:
+ - !type:CharacterItemGroupRequirement
+ group: LoadoutMystagogueEquipment
+ - !type:CharacterJobRequirement
+ jobs:
+ - ResearchDirector
+ items:
+ - ClothingEyesHudEpistemics
+
+- type: loadout
+ id: LoadoutMystagogueEyesEpiGlasses
+ category: JobsEpistemicsMystagogue
+ cost: 0
+ exclusive: true
+ requirements:
+ - !type:CharacterItemGroupRequirement
+ group: LoadoutMystagogueEquipment
+ - !type:CharacterJobRequirement
+ jobs:
+ - ResearchDirector
+ items:
+ - ClothingEyesGlassesEpistemics
# Gloves
diff --git a/Resources/Prototypes/Loadouts/Jobs/Epistemics/psionicMantis.yml b/Resources/Prototypes/Loadouts/Jobs/Epistemics/psionicMantis.yml
index 6df1d8ef1ff..d5f731c9480 100644
--- a/Resources/Prototypes/Loadouts/Jobs/Epistemics/psionicMantis.yml
+++ b/Resources/Prototypes/Loadouts/Jobs/Epistemics/psionicMantis.yml
@@ -61,6 +61,34 @@
- PillCanisterCryptobiolin
# Eyes
+- type: loadout
+ id: LoadoutPsionicMantisEyesEpiHUD
+ category: JobsEpistemicsPsionicMantis
+ cost: 0
+ exclusive: true
+ requirements:
+ - !type:CharacterItemGroupRequirement
+ group: LoadoutPsionicMantisEquipment
+ - !type:CharacterJobRequirement
+ jobs:
+ - ForensicMantis
+ items:
+ - ClothingEyesHudEpistemics
+
+- type: loadout
+ id: LoadoutPsionicMantisEyesEpiGlasses
+ category: JobsEpistemicsPsionicMantis
+ cost: 3
+ exclusive: true
+ requirements:
+ - !type:CharacterItemGroupRequirement
+ group: LoadoutPsionicMantisEquipment
+ - !type:CharacterJobRequirement
+ jobs:
+ - ForensicMantis
+ items:
+ - ClothingEyesGlassesEpistemics
+
# Gloves
diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Objects/Devices/pda.yml b/Resources/Prototypes/Nyanotrasen/Entities/Objects/Devices/pda.yml
index f2a16126b96..af0eb5f0f53 100644
--- a/Resources/Prototypes/Nyanotrasen/Entities/Objects/Devices/pda.yml
+++ b/Resources/Prototypes/Nyanotrasen/Entities/Objects/Devices/pda.yml
@@ -122,3 +122,4 @@
- NewsReaderCartridge
- GlimmerMonitorCartridge
- NanoChatCartridge
+ - PsiWatchCartridge
diff --git a/Resources/Prototypes/StatusIcon/psionics.yml b/Resources/Prototypes/StatusIcon/psionics.yml
new file mode 100644
index 00000000000..67aa622204f
--- /dev/null
+++ b/Resources/Prototypes/StatusIcon/psionics.yml
@@ -0,0 +1,27 @@
+- type: psionicsIcon
+ id: PsionicsIcon
+ abstract: true
+ offset: 1
+ locationPreference: Left
+ isShaded: true
+
+- type: psionicsIcon
+ parent: PsionicsIcon
+ id: PsionicsIconSuspected
+ icon:
+ sprite: /Textures/Interface/Misc/psionics_icons.rsi
+ state: hud_suspected
+
+- type: psionicsIcon
+ parent: PsionicsIcon
+ id: PsionicsIconRegistered
+ icon:
+ sprite: /Textures/Interface/Misc/psionics_icons.rsi
+ state: hud_registered
+
+- type: psionicsIcon
+ parent: PsionicsIcon
+ id: PsionicsIconAbusing
+ icon:
+ sprite: /Textures/Interface/Misc/psionics_icons.rsi
+ state: hud_abusing
diff --git a/Resources/Textures/Clothing/Eyes/Glasses/epiglasses.rsi/equipped-EYES.png b/Resources/Textures/Clothing/Eyes/Glasses/epiglasses.rsi/equipped-EYES.png
new file mode 100644
index 00000000000..1c1f4a830ce
Binary files /dev/null and b/Resources/Textures/Clothing/Eyes/Glasses/epiglasses.rsi/equipped-EYES.png differ
diff --git a/Resources/Textures/Clothing/Eyes/Glasses/epiglasses.rsi/icon.png b/Resources/Textures/Clothing/Eyes/Glasses/epiglasses.rsi/icon.png
new file mode 100644
index 00000000000..df2d0b77905
Binary files /dev/null and b/Resources/Textures/Clothing/Eyes/Glasses/epiglasses.rsi/icon.png differ
diff --git a/Resources/Textures/Clothing/Eyes/Glasses/epiglasses.rsi/inhand-left.png b/Resources/Textures/Clothing/Eyes/Glasses/epiglasses.rsi/inhand-left.png
new file mode 100644
index 00000000000..9a9fd68d551
Binary files /dev/null and b/Resources/Textures/Clothing/Eyes/Glasses/epiglasses.rsi/inhand-left.png differ
diff --git a/Resources/Textures/Clothing/Eyes/Glasses/epiglasses.rsi/inhand-right.png b/Resources/Textures/Clothing/Eyes/Glasses/epiglasses.rsi/inhand-right.png
new file mode 100644
index 00000000000..1b4867ad28a
Binary files /dev/null and b/Resources/Textures/Clothing/Eyes/Glasses/epiglasses.rsi/inhand-right.png differ
diff --git a/Resources/Textures/Clothing/Eyes/Glasses/epiglasses.rsi/meta.json b/Resources/Textures/Clothing/Eyes/Glasses/epiglasses.rsi/meta.json
new file mode 100644
index 00000000000..663a6be032d
--- /dev/null
+++ b/Resources/Textures/Clothing/Eyes/Glasses/epiglasses.rsi/meta.json
@@ -0,0 +1,26 @@
+{
+ "version": 1,
+ "license": "CC-BY-SA-3.0",
+ "copyright": "Recolored by CerberusWolfie but taken from tgstation at commit https://github.com/tgstation/tgstation/commit/5a73e8f825ff279e82949b9329783a9e3070e2da.",
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "states": [
+ {
+ "name": "icon"
+ },
+ {
+ "name": "equipped-EYES",
+ "directions": 4
+ },
+ {
+ "name": "inhand-left",
+ "directions": 4
+ },
+ {
+ "name": "inhand-right",
+ "directions": 4
+ }
+ ]
+}
diff --git a/Resources/Textures/Clothing/Eyes/Hud/epi.rsi/equipped-EYES.png b/Resources/Textures/Clothing/Eyes/Hud/epi.rsi/equipped-EYES.png
new file mode 100644
index 00000000000..92d769cebfc
Binary files /dev/null and b/Resources/Textures/Clothing/Eyes/Hud/epi.rsi/equipped-EYES.png differ
diff --git a/Resources/Textures/Clothing/Eyes/Hud/epi.rsi/icon.png b/Resources/Textures/Clothing/Eyes/Hud/epi.rsi/icon.png
new file mode 100644
index 00000000000..3d0bc5a2140
Binary files /dev/null and b/Resources/Textures/Clothing/Eyes/Hud/epi.rsi/icon.png differ
diff --git a/Resources/Textures/Clothing/Eyes/Hud/epi.rsi/inhand-left.png b/Resources/Textures/Clothing/Eyes/Hud/epi.rsi/inhand-left.png
new file mode 100644
index 00000000000..81927119bbf
Binary files /dev/null and b/Resources/Textures/Clothing/Eyes/Hud/epi.rsi/inhand-left.png differ
diff --git a/Resources/Textures/Clothing/Eyes/Hud/epi.rsi/inhand-right.png b/Resources/Textures/Clothing/Eyes/Hud/epi.rsi/inhand-right.png
new file mode 100644
index 00000000000..070927cd6e8
Binary files /dev/null and b/Resources/Textures/Clothing/Eyes/Hud/epi.rsi/inhand-right.png differ
diff --git a/Resources/Textures/Clothing/Eyes/Hud/epi.rsi/meta.json b/Resources/Textures/Clothing/Eyes/Hud/epi.rsi/meta.json
new file mode 100644
index 00000000000..920314b00b6
--- /dev/null
+++ b/Resources/Textures/Clothing/Eyes/Hud/epi.rsi/meta.json
@@ -0,0 +1,26 @@
+{
+ "version": 1,
+ "license": "CC-BY-SA-3.0",
+ "copyright": "Recolored by CerberusWolfie but taken from tgstation at commit https://github.com/tgstation/tgstation/commit/5a73e8f825ff279e82949b9329783a9e3070e2da",
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "states": [
+ {
+ "name": "icon"
+ },
+ {
+ "name": "equipped-EYES",
+ "directions": 4
+ },
+ {
+ "name": "inhand-left",
+ "directions": 4
+ },
+ {
+ "name": "inhand-right",
+ "directions": 4
+ }
+ ]
+}
diff --git a/Resources/Textures/Interface/Misc/psionics_icons.rsi/hud_abusing.png b/Resources/Textures/Interface/Misc/psionics_icons.rsi/hud_abusing.png
new file mode 100644
index 00000000000..02c090efe36
Binary files /dev/null and b/Resources/Textures/Interface/Misc/psionics_icons.rsi/hud_abusing.png differ
diff --git a/Resources/Textures/Interface/Misc/psionics_icons.rsi/hud_registered.png b/Resources/Textures/Interface/Misc/psionics_icons.rsi/hud_registered.png
new file mode 100644
index 00000000000..aa8e441c0a4
Binary files /dev/null and b/Resources/Textures/Interface/Misc/psionics_icons.rsi/hud_registered.png differ
diff --git a/Resources/Textures/Interface/Misc/psionics_icons.rsi/hud_suspected.png b/Resources/Textures/Interface/Misc/psionics_icons.rsi/hud_suspected.png
new file mode 100644
index 00000000000..4c723f8a975
Binary files /dev/null and b/Resources/Textures/Interface/Misc/psionics_icons.rsi/hud_suspected.png differ
diff --git a/Resources/Textures/Interface/Misc/psionics_icons.rsi/meta.json b/Resources/Textures/Interface/Misc/psionics_icons.rsi/meta.json
new file mode 100644
index 00000000000..556b11590ea
--- /dev/null
+++ b/Resources/Textures/Interface/Misc/psionics_icons.rsi/meta.json
@@ -0,0 +1,20 @@
+{
+ "version": 1,
+ "license": "CC-BY-SA-3.0",
+ "copyright": "Created by CerberusWolfie",
+ "size": {
+ "x": 8,
+ "y": 8
+ },
+ "states": [
+ {
+ "name": "hud_suspected"
+ },
+ {
+ "name": "hud_registered"
+ },
+ {
+ "name": "hud_abusing"
+ }
+ ]
+ }
diff --git a/Resources/Textures/Objects/Devices/cartridge.rsi/cart-psi.png b/Resources/Textures/Objects/Devices/cartridge.rsi/cart-psi.png
new file mode 100644
index 00000000000..446d937c2a4
Binary files /dev/null and b/Resources/Textures/Objects/Devices/cartridge.rsi/cart-psi.png differ
diff --git a/Resources/Textures/Objects/Devices/cartridge.rsi/meta.json b/Resources/Textures/Objects/Devices/cartridge.rsi/meta.json
index d5b9a8df884..586070e5007 100644
--- a/Resources/Textures/Objects/Devices/cartridge.rsi/meta.json
+++ b/Resources/Textures/Objects/Devices/cartridge.rsi/meta.json
@@ -1,7 +1,7 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
- "copyright": "Taken from vgstation at https://github.com/vgstation-coders/vgstation13/commit/1cdfb0230cc96d0ba751fa002d04f8aa2f25ad7d and tgstation at tgstation at https://github.com/tgstation/tgstation/commit/0c15d9dbcf0f2beb230eba5d9d889ef2d1945bb8, cart-log made by Skarletto (github), cart-sec made by dieselmohawk (discord), cart-nav, cart-med made by ArchRBX (github)",
+ "copyright": "Taken from vgstation at https://github.com/vgstation-coders/vgstation13/commit/1cdfb0230cc96d0ba751fa002d04f8aa2f25ad7d and tgstation at tgstation at https://github.com/tgstation/tgstation/commit/0c15d9dbcf0f2beb230eba5d9d889ef2d1945bb8, cart-log made by Skarletto (github), cart-sec made by dieselmohawk (discord), cart-nav, cart-med made by ArchRBX (github), cart-psi made by CerberusWolfie (github/discord)",
"size": {
"x": 32,
"y": 32
@@ -81,6 +81,9 @@
},
{
"name": "cart-med"
+ },
+ {
+ "name": "cart-psi"
}
]
-}
\ No newline at end of file
+}
diff --git a/Resources/Textures/Structures/Machines/computers.rsi/meta.json b/Resources/Textures/Structures/Machines/computers.rsi/meta.json
index 5355f379ab1..9bc48934ae3 100644
--- a/Resources/Textures/Structures/Machines/computers.rsi/meta.json
+++ b/Resources/Textures/Structures/Machines/computers.rsi/meta.json
@@ -1,7 +1,7 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
- "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/bd6873fd4dd6a61d7e46f1d75cd4d90f64c40894. comm_syndie made by Veritius, based on comm. generic_panel_open made by Errant, commit https://github.com/space-wizards/space-station-14/pull/32273.",
+ "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/bd6873fd4dd6a61d7e46f1d75cd4d90f64c40894. comm_syndie made by Veritius, based on comm. generic_panel_open made by Errant, commit https://github.com/space-wizards/space-station-14/pull/32273. registry made by CerberusWolfie, based on explosive.",
"size": {
"x": 32,
"y": 32
@@ -913,6 +913,56 @@
]
]
},
+ {
+ "name": "registry",
+ "directions": 4,
+ "delays": [
+ [
+ 1,
+ 0.1,
+ 0.1,
+ 1,
+ 0.1,
+ 0.1,
+ 1,
+ 0.1,
+ 0.1
+ ],
+ [
+ 1,
+ 0.1,
+ 0.1,
+ 1,
+ 0.1,
+ 0.1,
+ 1,
+ 0.1,
+ 0.1
+ ],
+ [
+ 1,
+ 0.1,
+ 0.1,
+ 1,
+ 0.1,
+ 0.1,
+ 1,
+ 0.1,
+ 0.1
+ ],
+ [
+ 1,
+ 0.1,
+ 0.1,
+ 1,
+ 0.1,
+ 0.1,
+ 1,
+ 0.1,
+ 0.1
+ ]
+ ]
+ },
{
"name": "forensic",
"directions": 4,
diff --git a/Resources/Textures/Structures/Machines/computers.rsi/registry.png b/Resources/Textures/Structures/Machines/computers.rsi/registry.png
new file mode 100644
index 00000000000..f682c9be743
Binary files /dev/null and b/Resources/Textures/Structures/Machines/computers.rsi/registry.png differ