mirror of
https://github.com/WWhiteDreamProject/wwdpublic.git
synced 2026-04-24 17:18:14 +03:00
## Mirror of PR #25192: [Criminal record hud icons](https://github.com/space-wizards/space-station-14/pull/25192) from <img src="https://avatars.githubusercontent.com/u/10567778?v=4" alt="space-wizards" width="22"/> [space-wizards](https://github.com/space-wizards)/[space-station-14](https://github.com/space-wizards/space-station-14) ###### `60b9d89e4dbdd8aaad4992a105628297d9480617` PR opened by <img src="https://avatars.githubusercontent.com/u/137322659?v=4" width="16"/><a href="https://github.com/Arendian"> Arendian</a> at 2024-02-13 19:39:59 UTC PR merged by <img src="https://avatars.githubusercontent.com/u/19864447?v=4" width="16"/><a href="https://github.com/web-flow"> web-flow</a> at 2024-03-11 03:12:52 UTC --- PR changed 19 files with 230 additions and 40 deletions. The PR had the following labels: - Changes: UI - Changes: Sprites - Status: Needs Review --- <details open="true"><summary><h1>Original Body</h1></summary> > <!-- Please read these guidelines before opening your PR: https://docs.spacestation14.io/en/getting-started/pr-guideline --> > <!-- The text between the arrows are comments - they will not be visible on your PR. --> > > ## About the PR > <!-- What did you change in this PR? --> > Added three more statuses to the criminal record console: Paroled, Suspect, Discharged. > Entities now have a hud icon based on the criminal record linked to the name they are currently showing. > If "Urist McHands" is wanted, anyone that currently shows as being "Urist McHands", even when their real name is not Urist McHands, will show the relevant hud icon to entities wearing a security hud. If Urist McHands is disguised as someone without a criminal record or has no name(middle-aged captain man etc.), they won't show an icon. > > ## Why / Balance > <!-- Why was it changed? Link any discussions or issues here. Please discuss how this would affect game balance. --> > Icons showing on sechuds will make sechuds more usefull, it will (hopefully) improve security coordination by allowing officers to more easily see who they need to keep track of. > It also gives warden something extra to do. > > ## Technical details > <!-- If this is a code change, summarize at high level how your new code works. This makes it easier to review. --> > > When a criminal record is changed, every entity with an IdentityComponent will have their name checked to see if it equals the name of the changed criminal record, if it's equal the person will get the CriminalRecordComponent which holds the icon that needs to be shown on the entity. > If a criminal record is removed, the CriminalRecordComponent will be removed from all entities that currently have the visible name associated with the criminal record. > > If an entity changes identity, all criminal records will be searched to see if the name has a criminal record attached to it. If the name has a record attached to it, the entity will get the CriminalRecordComponent and the corresponding criminal record hud icon until they change their identity again or the criminal record gets removed. > > ## Media > <!-- > PRs which make ingame changes (adding clothing, items, new features, etc) are required to have media attached that showcase the changes. > Small fixes/refactors are exempt. > Any media may be used in SS14 progress reports, with clear credit given. > > If you're unsure whether your PR will require media, ask a maintainer. > > Check the box below to confirm that you have in fact seen this (put an X in the brackets, like [X]): > --> > > https://github.com/space-wizards/space-station-14/assets/137322659/3c9422e6-897f-456f-b54b-8fdc053cb4e1 > > > - [X] I have added screenshots/videos to this PR showcasing its changes ingame, **or** this PR does not require an ingame showcase > > **Changelog** > <!-- > Make players aware of new features and changes that could affect how they play the game by adding a Changelog entry. Please read the Changelog guidelines located at: https://docs.spacestation14.io/en/getting-started/pr-guideline#changelog > --> > > 🆑 Dygon > - add: The following criminal record statuses have been added: Suspect, Discharged, Paroled. > - add: Security huds now show an icon on entities that have a visible name linked to a criminal record. > </details> Co-authored-by: Arendian <137322659+Arendian@users.noreply.github.com>
273 lines
11 KiB
C#
273 lines
11 KiB
C#
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.CriminalRecords;
|
|
using Content.Shared.CriminalRecords.Components;
|
|
using Content.Shared.CriminalRecords.Systems;
|
|
using Content.Shared.Security;
|
|
using Content.Shared.StationRecords;
|
|
using Robust.Server.GameObjects;
|
|
using Robust.Shared.Player;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using Content.Shared.IdentityManagement;
|
|
using Content.Shared.Security.Components;
|
|
|
|
namespace Content.Server.CriminalRecords.Systems;
|
|
|
|
/// <summary>
|
|
/// Handles all UI for criminal records console
|
|
/// </summary>
|
|
public sealed class CriminalRecordsConsoleSystem : SharedCriminalRecordsConsoleSystem
|
|
{
|
|
[Dependency] private readonly AccessReaderSystem _access = default!;
|
|
[Dependency] private readonly CriminalRecordsSystem _criminalRecords = default!;
|
|
[Dependency] private readonly PopupSystem _popup = default!;
|
|
[Dependency] private readonly RadioSystem _radio = default!;
|
|
[Dependency] private readonly SharedIdCardSystem _idCard = default!;
|
|
[Dependency] private readonly StationRecordsSystem _stationRecords = default!;
|
|
[Dependency] private readonly StationSystem _station = default!;
|
|
[Dependency] private readonly UserInterfaceSystem _ui = default!;
|
|
|
|
public override void Initialize()
|
|
{
|
|
SubscribeLocalEvent<CriminalRecordsConsoleComponent, RecordModifiedEvent>(UpdateUserInterface);
|
|
SubscribeLocalEvent<CriminalRecordsConsoleComponent, AfterGeneralRecordCreatedEvent>(UpdateUserInterface);
|
|
|
|
Subs.BuiEvents<CriminalRecordsConsoleComponent>(CriminalRecordsConsoleKey.Key, subs =>
|
|
{
|
|
subs.Event<BoundUIOpenedEvent>(UpdateUserInterface);
|
|
subs.Event<SelectStationRecord>(OnKeySelected);
|
|
subs.Event<SetStationRecordFilter>(OnFiltersChanged);
|
|
subs.Event<CriminalRecordChangeStatus>(OnChangeStatus);
|
|
subs.Event<CriminalRecordAddHistory>(OnAddHistory);
|
|
subs.Event<CriminalRecordDeleteHistory>(OnDeleteHistory);
|
|
});
|
|
}
|
|
|
|
private void UpdateUserInterface<T>(Entity<CriminalRecordsConsoleComponent> 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<CriminalRecordsConsoleComponent> 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<CriminalRecordsConsoleComponent> 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<CriminalRecordsConsoleComponent> ent, ref CriminalRecordChangeStatus msg)
|
|
{
|
|
// prevent malf client violating wanted/reason nullability
|
|
if (msg.Status == SecurityStatus.Wanted != (msg.Reason != null) &&
|
|
msg.Status == SecurityStatus.Suspected != (msg.Reason != null))
|
|
return;
|
|
|
|
if (!CheckSelected(ent, msg.Session, out var mob, out var key))
|
|
return;
|
|
|
|
if (!_stationRecords.TryGetRecord<CriminalRecord>(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;
|
|
}
|
|
|
|
// when arresting someone add it to history automatically
|
|
// fallback exists if the player was not set to wanted beforehand
|
|
if (msg.Status == SecurityStatus.Detained)
|
|
{
|
|
var oldReason = record.Reason ?? Loc.GetString("criminal-records-console-unspecified-reason");
|
|
var history = Loc.GetString("criminal-records-console-auto-history", ("reason", oldReason));
|
|
_criminalRecords.TryAddHistory(key.Value, history);
|
|
}
|
|
|
|
var oldStatus = record.Status;
|
|
|
|
// will probably never fail given the checks above
|
|
_criminalRecords.TryChangeStatus(key.Value, msg.Status, msg.Reason);
|
|
|
|
var name = RecordName(key.Value);
|
|
var officer = Loc.GetString("criminal-records-console-unknown-officer");
|
|
if (_idCard.TryFindIdCard(mob.Value, out var id) && id.Comp.FullName is { } fullName)
|
|
officer = fullName;
|
|
|
|
(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 detained
|
|
(_, SecurityStatus.Detained) => "detained",
|
|
// person did something sus
|
|
(_, SecurityStatus.Suspected) => "suspected",
|
|
// released on parole
|
|
(_, SecurityStatus.Paroled) => "paroled",
|
|
// prisoner did their time
|
|
(_, SecurityStatus.Discharged) => "released",
|
|
// going from any other state to wanted, AOS or prisonbreak / lazy secoff never set them to released and they reoffended
|
|
(_, SecurityStatus.Wanted) => "wanted",
|
|
// person is no longer sus
|
|
(SecurityStatus.Suspected, SecurityStatus.None) => "not-suspected",
|
|
// going from wanted to none, must have been a mistake
|
|
(SecurityStatus.Wanted, SecurityStatus.None) => "not-wanted",
|
|
// criminal status removed
|
|
(SecurityStatus.Detained, SecurityStatus.None) => "released",
|
|
// criminal is no longer on parole
|
|
(SecurityStatus.Paroled, SecurityStatus.None) => "not-parole",
|
|
// this is impossible
|
|
_ => "not-wanted"
|
|
};
|
|
_radio.SendRadioMessage(ent, Loc.GetString($"criminal-records-console-{statusString}", args),
|
|
ent.Comp.SecurityChannel, ent);
|
|
|
|
UpdateUserInterface(ent);
|
|
UpdateCriminalIdentity(name, msg.Status);
|
|
}
|
|
|
|
private void OnAddHistory(Entity<CriminalRecordsConsoleComponent> ent, ref CriminalRecordAddHistory msg)
|
|
{
|
|
if (!CheckSelected(ent, msg.Session, out _, out var key))
|
|
return;
|
|
|
|
var line = msg.Line.Trim();
|
|
if (line.Length < 1 || line.Length > ent.Comp.MaxStringLength)
|
|
return;
|
|
|
|
if (!_criminalRecords.TryAddHistory(key.Value, line))
|
|
return;
|
|
|
|
// no radio message since its not crucial to officers patrolling
|
|
|
|
UpdateUserInterface(ent);
|
|
}
|
|
|
|
private void OnDeleteHistory(Entity<CriminalRecordsConsoleComponent> ent, ref CriminalRecordDeleteHistory msg)
|
|
{
|
|
if (!CheckSelected(ent, msg.Session, out _, out var key))
|
|
return;
|
|
|
|
if (!_criminalRecords.TryDeleteHistory(key.Value, msg.Index))
|
|
return;
|
|
|
|
// a bit sus but not crucial to officers patrolling
|
|
|
|
UpdateUserInterface(ent);
|
|
}
|
|
|
|
private void UpdateUserInterface(Entity<CriminalRecordsConsoleComponent> ent)
|
|
{
|
|
var (uid, console) = ent;
|
|
var owningStation = _station.GetOwningStation(uid);
|
|
|
|
if (!TryComp<StationRecordsComponent>(owningStation, out var stationRecords))
|
|
{
|
|
_ui.TrySetUiState(uid, CriminalRecordsConsoleKey.Key, new CriminalRecordsConsoleState());
|
|
return;
|
|
}
|
|
|
|
var listing = _stationRecords.BuildListing((owningStation.Value, stationRecords), console.Filter);
|
|
|
|
var state = new CriminalRecordsConsoleState(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.CriminalRecord, stationRecords);
|
|
state.SelectedKey = id;
|
|
}
|
|
|
|
_ui.TrySetUiState(uid, CriminalRecordsConsoleKey.Key, state);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Boilerplate that most actions use, if they require that a record be selected.
|
|
/// Obviously shouldn't be used for selecting records.
|
|
/// </summary>
|
|
private bool CheckSelected(Entity<CriminalRecordsConsoleComponent> ent, ICommonSession session,
|
|
[NotNullWhen(true)] out EntityUid? mob, [NotNullWhen(true)] out StationRecordKey? key)
|
|
{
|
|
key = null;
|
|
mob = null;
|
|
if (session.AttachedEntity is not { } user)
|
|
return false;
|
|
|
|
if (!_access.IsAllowed(user, ent))
|
|
{
|
|
_popup.PopupEntity(Loc.GetString("criminal-records-permission-denied"), ent, session);
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the name from a record, or empty string if this somehow fails.
|
|
/// </summary>
|
|
private string RecordName(StationRecordKey key)
|
|
{
|
|
if (!_stationRecords.TryGetRecord<GeneralStationRecord>(key, out var record))
|
|
return "";
|
|
|
|
return record.Name;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks if the new identity's name has a criminal record attached to it, and gives the entity the icon that
|
|
/// belongs to the status if it does.
|
|
/// </summary>
|
|
public void CheckNewIdentity(EntityUid uid)
|
|
{
|
|
var name = Identity.Name(uid, EntityManager);
|
|
var xform = Transform(uid);
|
|
var station = _station.GetStationInMap(xform.MapID);
|
|
|
|
if (station != null && _stationRecords.GetRecordByName(station.Value, name) is { } id)
|
|
{
|
|
if (_stationRecords.TryGetRecord<CriminalRecord>(new StationRecordKey(id, station.Value),
|
|
out var record))
|
|
{
|
|
if (record.Status != SecurityStatus.None)
|
|
{
|
|
SetCriminalIcon(name, record.Status, uid);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
RemComp<CriminalRecordComponent>(uid);
|
|
}
|
|
}
|