Files
wwdpublic/Content.Server/IdentityManagement/IdentitySystem.cs
SimpleStation14 375b1c0739 Mirror: Criminal record hud icons (#144)
## 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>
2024-05-04 19:38:47 -04:00

176 lines
6.6 KiB
C#

using Content.Server.Access.Systems;
using Content.Server.Administration.Logs;
using Content.Server.CriminalRecords.Systems;
using Content.Server.Humanoid;
using Content.Shared.Clothing;
using Content.Shared.Database;
using Content.Shared.Hands;
using Content.Shared.Humanoid;
using Content.Shared.IdentityManagement;
using Content.Shared.IdentityManagement.Components;
using Content.Shared.Inventory;
using Content.Shared.Inventory.Events;
using Robust.Shared.Containers;
using Robust.Shared.Enums;
using Robust.Shared.GameObjects.Components.Localization;
namespace Content.Server.IdentityManagement;
/// <summary>
/// Responsible for updating the identity of an entity on init or clothing equip/unequip.
/// </summary>
public class IdentitySystem : SharedIdentitySystem
{
[Dependency] private readonly IdCardSystem _idCard = default!;
[Dependency] private readonly IAdminLogManager _adminLog = default!;
[Dependency] private readonly MetaDataSystem _metaData = default!;
[Dependency] private readonly SharedContainerSystem _container = default!;
[Dependency] private readonly HumanoidAppearanceSystem _humanoid = default!;
[Dependency] private readonly CriminalRecordsConsoleSystem _criminalRecordsConsole = default!;
private HashSet<EntityUid> _queuedIdentityUpdates = new();
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<IdentityComponent, DidEquipEvent>((uid, _, _) => QueueIdentityUpdate(uid));
SubscribeLocalEvent<IdentityComponent, DidEquipHandEvent>((uid, _, _) => QueueIdentityUpdate(uid));
SubscribeLocalEvent<IdentityComponent, DidUnequipEvent>((uid, _, _) => QueueIdentityUpdate(uid));
SubscribeLocalEvent<IdentityComponent, DidUnequipHandEvent>((uid, _, _) => QueueIdentityUpdate(uid));
SubscribeLocalEvent<IdentityComponent, WearerMaskToggledEvent>((uid, _, _) => QueueIdentityUpdate(uid));
SubscribeLocalEvent<IdentityComponent, MapInitEvent>(OnMapInit);
}
public override void Update(float frameTime)
{
base.Update(frameTime);
foreach (var ent in _queuedIdentityUpdates)
{
if (!TryComp<IdentityComponent>(ent, out var identity))
continue;
UpdateIdentityInfo(ent, identity);
}
_queuedIdentityUpdates.Clear();
}
// This is where the magic happens
private void OnMapInit(EntityUid uid, IdentityComponent component, MapInitEvent args)
{
var ident = Spawn(null, Transform(uid).Coordinates);
QueueIdentityUpdate(uid);
_container.Insert(ident, component.IdentityEntitySlot);
}
/// <summary>
/// Queues an identity update to the start of the next tick.
/// </summary>
public void QueueIdentityUpdate(EntityUid uid)
{
_queuedIdentityUpdates.Add(uid);
}
#region Private API
/// <summary>
/// Updates the metadata name for the id(entity) from the current state of the character.
/// </summary>
private void UpdateIdentityInfo(EntityUid uid, IdentityComponent identity)
{
if (identity.IdentityEntitySlot.ContainedEntity is not { } ident)
return;
var representation = GetIdentityRepresentation(uid);
var name = GetIdentityName(uid, representation);
// Clone the old entity's grammar to the identity entity, for loc purposes.
if (TryComp<GrammarComponent>(uid, out var grammar))
{
var identityGrammar = EnsureComp<GrammarComponent>(ident);
identityGrammar.Attributes.Clear();
foreach (var (k, v) in grammar.Attributes)
{
identityGrammar.Attributes.Add(k, v);
}
// If presumed name is null and we're using that, we set proper noun to be false ("the old woman")
if (name != representation.TrueName && representation.PresumedName == null)
identityGrammar.ProperNoun = false;
}
if (name == Name(ident))
return;
_metaData.SetEntityName(ident, name);
_adminLog.Add(LogType.Identity, LogImpact.Medium, $"{ToPrettyString(uid)} changed identity to {name}");
var identityChangedEvent = new IdentityChangedEvent(uid, ident);
RaiseLocalEvent(uid, ref identityChangedEvent);
SetIdentityCriminalIcon(uid);
}
private string GetIdentityName(EntityUid target, IdentityRepresentation representation)
{
var ev = new SeeIdentityAttemptEvent();
RaiseLocalEvent(target, ev);
return representation.ToStringKnown(!ev.Cancelled);
}
/// <summary>
/// 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.
/// </summary>
private void SetIdentityCriminalIcon(EntityUid uid)
{
_criminalRecordsConsole.CheckNewIdentity(uid);
}
/// <summary>
/// Gets an 'identity representation' of an entity, with their true name being the entity name
/// and their 'presumed name' and 'presumed job' being the name/job on their ID card, if they have one.
/// </summary>
private IdentityRepresentation GetIdentityRepresentation(EntityUid target,
InventoryComponent? inventory=null,
HumanoidAppearanceComponent? appearance=null)
{
int age = 18;
Gender gender = Gender.Epicene;
string species = SharedHumanoidAppearanceSystem.DefaultSpecies;
// Always use their actual age and gender, since that can't really be changed by an ID.
if (Resolve(target, ref appearance, false))
{
gender = appearance.Gender;
age = appearance.Age;
species = appearance.Species;
}
var ageString = _humanoid.GetAgeRepresentation(species, age);
var trueName = Name(target);
if (!Resolve(target, ref inventory, false))
return new(trueName, gender, ageString, string.Empty);
string? presumedJob = null;
string? presumedName = null;
// Get their name and job from their ID for their presumed name.
if (_idCard.TryFindIdCard(target, out var id))
{
presumedName = string.IsNullOrWhiteSpace(id.Comp.FullName) ? null : id.Comp.FullName;
presumedJob = id.Comp.JobTitle?.ToLowerInvariant();
}
// If it didn't find a job, that's fine.
return new(trueName, gender, ageString, presumedName, presumedJob);
}
#endregion
}