SendMessage(new SetStationRecordFilter(type, filterValue));
_window.OnStatusSelected += status =>
SendMessage(new CriminalRecordChangeStatus(status, null));
- _window.OnDialogConfirmed += (_, reason) =>
- SendMessage(new CriminalRecordChangeStatus(SecurityStatus.Wanted, reason));
+ _window.OnDialogConfirmed += (status, reason) =>
+ SendMessage(new CriminalRecordChangeStatus(status, reason));
_window.OnHistoryUpdated += UpdateHistory;
_window.OnHistoryClosed += () => _historyWindow?.Close();
_window.OnClose += Close;
private void SetStatus(SecurityStatus status)
{
- if (status == SecurityStatus.Wanted)
+ if (status == SecurityStatus.Wanted || status == SecurityStatus.Suspected)
{
- GetWantedReason();
+ GetReason(status);
return;
}
OnStatusSelected?.Invoke(status);
}
- private void GetWantedReason()
+ private void GetReason(SecurityStatus status)
{
if (_reasonDialog != null)
{
}
var field = "reason";
- var title = Loc.GetString("criminal-records-status-wanted");
+ var title = Loc.GetString("criminal-records-status-" + status.ToString().ToLower());
var placeholders = _proto.Index<DatasetPrototype>(ReasonPlaceholders);
var placeholder = Loc.GetString("criminal-records-console-reason-placeholder", ("placeholder", _random.Pick(placeholders.Values))); // just funny it doesn't actually get used
var prompt = Loc.GetString("criminal-records-console-reason");
if (reason.Length < 1 || reason.Length > _maxLength)
return;
- OnDialogConfirmed?.Invoke(SecurityStatus.Wanted, reason);
+ OnDialogConfirmed?.Invoke(status, reason);
};
_reasonDialog.OnClose += () => { _reasonDialog = null; };
using Content.Shared.Mindshield.Components;
using Content.Shared.Overlays;
using Content.Shared.PDA;
+using Content.Shared.Security.Components;
using Content.Shared.StatusIcon;
using Content.Shared.StatusIcon.Components;
using Robust.Shared.Prototypes;
result.Add(icon);
}
- // Add arrest icons here, WYCI.
+ if (TryComp<CriminalRecordComponent>(uid, out var record))
+ {
+ if(_prototypeMan.TryIndex<StatusIconPrototype>(record.StatusIcon.Id, out var criminalIcon))
+ result.Add(criminalIcon);
+ }
return result;
}
return value ?? null;
}
- private void OnIdentityChanged(IdentityChangedEvent ev)
+ private void OnIdentityChanged(ref IdentityChangedEvent ev)
{
if (!TryComp<ActorComponent>(ev.CharacterEntity, out var actor))
return;
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;
private void OnChangeStatus(Entity<CriminalRecordsConsoleComponent> ent, ref CriminalRecordChangeStatus msg)
{
// prevent malf client violating wanted/reason nullability
- if ((msg.Status == SecurityStatus.Wanted) != (msg.Reason != null))
+ 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))
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)
+ if (_idCard.TryFindIdCard(mob.Value, out var id) && id.Comp.FullName is { } fullName)
officer = fullName;
(string, object)[] args;
// figure out which radio message to send depending on transition
var statusString = (oldStatus, msg.Status) switch
{
- // going from wanted or detained on the spot
+ // 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.Detained, SecurityStatus.None) => "released",
- // going from wanted to none, must have been a mistake
- (_, SecurityStatus.None) => "not-wanted",
- // going from none or detained, AOS or prisonbreak / lazy secoff never set them to released and they reoffended
+ (_, 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);
+ _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)
var listing = _stationRecords.BuildListing((owningStation.Value, stationRecords), console.Filter);
var state = new CriminalRecordsConsoleState(listing, console.Filter);
- if (console.ActiveKey is {} id)
+ if (console.ActiveKey is { } id)
{
// get records to display when a crewmember is selected
var key = new StationRecordKey(id, owningStation.Value);
{
key = null;
mob = null;
- if (session.AttachedEntity is not {} user)
+ if (session.AttachedEntity is not { } user)
return false;
if (!_access.IsAllowed(user, ent))
return false;
}
- if (ent.Comp.ActiveKey is not {} id)
+ 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)
+ if (_station.GetOwningStation(ent) is not { } station)
return false;
key = new StationRecordKey(id, station);
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);
+ }
}
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;
[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();
_metaData.SetEntityName(ident, name);
_adminLog.Add(LogType.Identity, LogImpact.Medium, $"{ToPrettyString(uid)} changed identity to {name}");
- RaiseLocalEvent(new IdentityChangedEvent(uid, ident));
+ var identityChangedEvent = new IdentityChangedEvent(uid, ident);
+ RaiseLocalEvent(uid, ref identityChangedEvent);
+ SetIdentityCriminalIcon(uid);
}
private string GetIdentityName(EntityUid target, IdentityRepresentation representation)
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.
#endregion
}
-
-public sealed class IdentityChangedEvent : EntityEventArgs
-{
- public EntityUid CharacterEntity;
- public EntityUid IdentityEntity;
-
- public IdentityChangedEvent(EntityUid characterEntity, EntityUid identityEntity)
- {
- CharacterEntity = characterEntity;
- IdentityEntity = identityEntity;
- }
-}
+using Content.Shared.IdentityManagement;
+using Content.Shared.IdentityManagement.Components;
+using Content.Shared.Security;
+using Content.Shared.Security.Components;
+
namespace Content.Shared.CriminalRecords.Systems;
-/// <summary>
-/// Nothing is predicted just exists for access.
-/// </summary>
public abstract class SharedCriminalRecordsConsoleSystem : EntitySystem
{
+ /// <summary>
+ /// Any entity that has a 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.
+ /// </summary>
+ public void UpdateCriminalIdentity(string name, SecurityStatus status)
+ {
+ var query = EntityQueryEnumerator<IdentityComponent>();
+
+ while (query.MoveNext(out var uid, out var identity))
+ {
+ if (!Identity.Name(uid, EntityManager).Equals(name))
+ continue;
+
+ if (status == SecurityStatus.None)
+ RemComp<CriminalRecordComponent>(uid);
+ else
+ SetCriminalIcon(name, status, uid);
+ }
+ }
+
+ /// <summary>
+ /// Decides the icon that should be displayed on the entity based on the security status
+ /// </summary>
+ public void SetCriminalIcon(string name, SecurityStatus status, EntityUid characterUid)
+ {
+ EnsureComp<CriminalRecordComponent>(characterUid, out var record);
+
+ var previousIcon = record.StatusIcon;
+
+ record.StatusIcon = status switch
+ {
+ SecurityStatus.Paroled => "SecurityIconParoled",
+ SecurityStatus.Wanted => "SecurityIconWanted",
+ SecurityStatus.Detained => "SecurityIconIncarcerated",
+ SecurityStatus.Discharged => "SecurityIconDischarged",
+ SecurityStatus.Suspected => "SecurityIconSuspected",
+ _ => record.StatusIcon
+ };
+
+ if(previousIcon != record.StatusIcon)
+ Dirty(characterUid, record);
+ }
}
-using Content.Shared.Humanoid.Prototypes;
-using Robust.Shared.Containers;
+using Robust.Shared.Containers;
using Robust.Shared.Enums;
namespace Content.Shared.IdentityManagement.Components;
ent.Comp.Enabled = !args.IsToggled;
}
}
+/// <summary>
+/// Gets called whenever an entity changes their identity.
+/// </summary>
+[ByRefEvent]
+public record struct IdentityChangedEvent(EntityUid CharacterEntity, EntityUid IdentityEntity);
--- /dev/null
+using Content.Shared.StatusIcon;
+using Robust.Shared.GameStates;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.Security.Components;
+
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+public sealed partial class CriminalRecordComponent : Component
+{
+ /// <summary>
+ /// The icon that should be displayed based on the criminal status of the entity.
+ /// </summary>
+ [DataField, AutoNetworkedField]
+ public ProtoId<StatusIconPrototype> StatusIcon = "SecurityIconWanted";
+}
/// Status used in Criminal Records.
///
/// None - the default value
+/// Suspected - the person is suspected of doing something illegal
/// Wanted - the person is being wanted by security
/// Detained - the person is detained by security
+/// Paroled - the person is on parole
+/// Discharged - the person has been released from prison
/// </summary>
public enum SecurityStatus : byte
{
None,
+ Suspected,
Wanted,
- Detained
+ Detained,
+ Paroled,
+ Discharged
}
criminal-records-status-none = None
criminal-records-status-wanted = Wanted
criminal-records-status-detained = Detained
+criminal-records-status-suspected = Suspect
+criminal-records-status-discharged = Discharged
+criminal-records-status-paroled = Paroled
criminal-records-console-wanted-reason = [color=gray]Wanted Reason[/color]
+criminal-records-console-suspected-reason = [color=gray]Suspected Reason[/color]
criminal-records-console-reason = Reason
criminal-records-console-reason-placeholder = For example: {$placeholder}
## Security channel notifications
criminal-records-console-wanted = {$name} is wanted by {$officer} for: {$reason}.
+criminal-records-console-suspected = {$officer} marked {$name} as suspicious because of: {$reason}
+criminal-records-console-not-suspected = {$name} is no longer a suspect.
criminal-records-console-detained = {$name} has been detained by {$officer}.
criminal-records-console-released = {$name} has been released by {$officer}.
criminal-records-console-not-wanted = {$name} is no longer wanted.
+criminal-records-console-paroled = {$name} has been released on parole by {$officer}.
+criminal-records-console-not-parole = {$name} is no longer on parole.
criminal-records-console-unknown-officer = <unknown officer>
## Filters
--- /dev/null
+- type: statusIcon
+ id: SecurityIcon
+ abstract: true
+ priority: 1
+ locationPreference: Left
+
+- type: statusIcon
+ parent: SecurityIcon
+ id: SecurityIconDischarged
+ icon:
+ sprite: Interface/Misc/security_icons.rsi
+ state: hud_discharged
+
+- type: statusIcon
+ parent: SecurityIcon
+ id: SecurityIconIncarcerated
+ icon:
+ sprite: Interface/Misc/security_icons.rsi
+ state: hud_incarcerated
+
+- type: statusIcon
+ parent: SecurityIcon
+ id: SecurityIconParoled
+ icon:
+ sprite: Interface/Misc/security_icons.rsi
+ state: hud_paroled
+
+- type: statusIcon
+ parent: SecurityIcon
+ id: SecurityIconSuspected
+ icon:
+ sprite: Interface/Misc/security_icons.rsi
+ state: hud_suspected
+
+- type: statusIcon
+ parent: SecurityIcon
+ id: SecurityIconWanted
+ icon:
+ sprite: Interface/Misc/security_icons.rsi
+ state: hud_wanted
--- /dev/null
+{
+ "version": 1,
+ "license": "CC-BY-SA-3.0",
+ "copyright": "Taken from https://github.com/tgstation/tgstation/blob/7f654b6e8e59021607a9e888dfeb79920401c372/icons/mob/huds/hud.dmi",
+ "size": {
+ "x": 8,
+ "y": 8
+ },
+ "states": [
+ {
+ "name": "hud_discharged"
+ },
+ {
+ "name": "hud_incarcerated"
+ },
+ {
+ "name": "hud_paroled"
+ },
+ {
+ "name": "hud_suspected"
+ },
+ {
+ "name": "hud_wanted"
+ }
+ ]
+}