]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Criminal record hud icons (#25192)
authorArendian <137322659+Arendian@users.noreply.github.com>
Mon, 11 Mar 2024 03:12:52 +0000 (04:12 +0100)
committerGitHub <noreply@github.com>
Mon, 11 Mar 2024 03:12:52 +0000 (14:12 +1100)
* Security hud shows icon based on criminal record status

* Criminal status now linked to name instead of identity

* parole loc

* Test fix

* review changes

* Check station records instead of storing names on criminal record consoles.

* cleanup

* more cleanup

* review changes

* change outdated comments

* rename

* review changes

* remove event subscription

* replaced event with trycomp

* default value

19 files changed:
Content.Client/CriminalRecords/CriminalRecordsConsoleBoundUserInterface.cs
Content.Client/CriminalRecords/CriminalRecordsConsoleWindow.xaml.cs
Content.Client/Overlays/ShowSecurityIconsSystem.cs
Content.Server/Administration/Systems/AdminSystem.cs
Content.Server/CriminalRecords/Systems/CriminalRecordsConsoleSystem.cs
Content.Server/IdentityManagement/IdentitySystem.cs
Content.Shared/CriminalRecords/Systems/SharedCriminalRecordsConsoleSystem.cs
Content.Shared/IdentityManagement/Components/IdentityComponent.cs
Content.Shared/IdentityManagement/SharedIdentitySystem.cs
Content.Shared/Security/Components/CriminalRecordComponent.cs [new file with mode: 0644]
Content.Shared/Security/SecurityStatus.cs
Resources/Locale/en-US/criminal-records/criminal-records.ftl
Resources/Prototypes/StatusEffects/security.yml [new file with mode: 0644]
Resources/Textures/Interface/Misc/security_icons.rsi/hud_discharged.png [new file with mode: 0644]
Resources/Textures/Interface/Misc/security_icons.rsi/hud_incarcerated.png [new file with mode: 0644]
Resources/Textures/Interface/Misc/security_icons.rsi/hud_paroled.png [new file with mode: 0644]
Resources/Textures/Interface/Misc/security_icons.rsi/hud_suspected.png [new file with mode: 0644]
Resources/Textures/Interface/Misc/security_icons.rsi/hud_wanted.png [new file with mode: 0644]
Resources/Textures/Interface/Misc/security_icons.rsi/meta.json [new file with mode: 0644]

index 88b9c90c2ff13bab0fd7984742010ab195257c26..9047624f49bece03379b843535e2d572374b731e 100644 (file)
@@ -37,8 +37,8 @@ public sealed class CriminalRecordsConsoleBoundUserInterface : BoundUserInterfac
             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;
index 61b5c3833158cc48b1cba8abb35ceb9dc374adfc..b259e08e723cc7948948916bd4dba3ae153072e8 100644 (file)
@@ -219,16 +219,16 @@ public sealed partial class CriminalRecordsConsoleWindow : FancyWindow
 
     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)
         {
@@ -237,7 +237,7 @@ public sealed partial class CriminalRecordsConsoleWindow : FancyWindow
         }
 
         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");
@@ -251,7 +251,7 @@ public sealed partial class CriminalRecordsConsoleWindow : FancyWindow
             if (reason.Length < 1 || reason.Length > _maxLength)
                 return;
 
-            OnDialogConfirmed?.Invoke(SecurityStatus.Wanted, reason);
+            OnDialogConfirmed?.Invoke(status, reason);
         };
 
         _reasonDialog.OnClose += () => { _reasonDialog = null; };
index 77c14c5ef0f7c7481e1cc74bbe55291bafacfb6c..7a4abd05e0057cc3e3887b3a8898dd863f9c8841 100644 (file)
@@ -3,6 +3,7 @@ using Content.Shared.Access.Systems;
 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;
@@ -74,7 +75,11 @@ public sealed class ShowSecurityIconsSystem : EquipmentHudSystem<ShowSecurityIco
                 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;
     }
index 9d41e918198883771db826430bb400d744dcd145..c3c024174ab3a297f3c0843bf7aa842529c044f6 100644 (file)
@@ -134,7 +134,7 @@ namespace Content.Server.Administration.Systems
             return value ?? null;
         }
 
-        private void OnIdentityChanged(IdentityChangedEvent ev)
+        private void OnIdentityChanged(ref IdentityChangedEvent ev)
         {
             if (!TryComp<ActorComponent>(ev.CharacterEntity, out var actor))
                 return;
index 84f8ffc2e50669bcd0516d58c72487b3bb3b6769..4290726cc4061f51cd77a745a02ac3e2a1a48fe8 100644 (file)
@@ -12,6 +12,8 @@ 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;
 
@@ -71,7 +73,8 @@ public sealed class CriminalRecordsConsoleSystem : SharedCriminalRecordsConsoleS
     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))
@@ -105,7 +108,7 @@ public sealed class CriminalRecordsConsoleSystem : SharedCriminalRecordsConsoleS
 
         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;
@@ -117,20 +120,32 @@ public sealed class CriminalRecordsConsoleSystem : SharedCriminalRecordsConsoleS
         // 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)
@@ -177,7 +192,7 @@ public sealed class CriminalRecordsConsoleSystem : SharedCriminalRecordsConsoleS
         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);
@@ -198,7 +213,7 @@ public sealed class CriminalRecordsConsoleSystem : SharedCriminalRecordsConsoleS
     {
         key = null;
         mob = null;
-        if (session.AttachedEntity is not {} user)
+        if (session.AttachedEntity is not { } user)
             return false;
 
         if (!_access.IsAllowed(user, ent))
@@ -207,11 +222,11 @@ public sealed class CriminalRecordsConsoleSystem : SharedCriminalRecordsConsoleS
             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);
@@ -229,4 +244,29 @@ public sealed class CriminalRecordsConsoleSystem : SharedCriminalRecordsConsoleS
 
         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);
+    }
 }
index dbd34d748438fa9bab43417851f17a3f38b2e5f7..ec8412ed1ab26ee34e8ad4e418006b4ce6bda718 100644 (file)
@@ -1,5 +1,6 @@
 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;
@@ -25,6 +26,7 @@ public class IdentitySystem : SharedIdentitySystem
     [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();
 
@@ -107,7 +109,9 @@ public class IdentitySystem : SharedIdentitySystem
         _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)
@@ -118,6 +122,16 @@ public class IdentitySystem : SharedIdentitySystem
         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.
@@ -159,15 +173,3 @@ public class IdentitySystem : SharedIdentitySystem
 
     #endregion
 }
-
-public sealed class IdentityChangedEvent : EntityEventArgs
-{
-    public EntityUid CharacterEntity;
-    public EntityUid IdentityEntity;
-
-    public IdentityChangedEvent(EntityUid characterEntity, EntityUid identityEntity)
-    {
-        CharacterEntity = characterEntity;
-        IdentityEntity = identityEntity;
-    }
-}
index f9fc67c896472709857bb1af999def6368bfb002..6c51cb77acc4bfa06e4b0fa4027bbad41830e4b3 100644 (file)
@@ -1,8 +1,52 @@
+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);
+    }
 }
index 7bbdeffe907c025e6ce3c7d7435fccd0c290f193..5e4c4531c17c48529d3ec2b1f03ff6f5af5427ec 100644 (file)
@@ -1,5 +1,4 @@
-using Content.Shared.Humanoid.Prototypes;
-using Robust.Shared.Containers;
+using Robust.Shared.Containers;
 using Robust.Shared.Enums;
 
 namespace Content.Shared.IdentityManagement.Components;
index a02e00f0b196cab7a2dc96e93677c65650d7fbe7..ef1c50f63ce183c3ccf5dbc61ab41cc386bca372 100644 (file)
@@ -40,3 +40,8 @@ public abstract class SharedIdentitySystem : EntitySystem
         ent.Comp.Enabled = !args.IsToggled;
     }
 }
+/// <summary>
+///     Gets called whenever an entity changes their identity.
+/// </summary>
+[ByRefEvent]
+public record struct IdentityChangedEvent(EntityUid CharacterEntity, EntityUid IdentityEntity);
diff --git a/Content.Shared/Security/Components/CriminalRecordComponent.cs b/Content.Shared/Security/Components/CriminalRecordComponent.cs
new file mode 100644 (file)
index 0000000..25fab3f
--- /dev/null
@@ -0,0 +1,15 @@
+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";
+}
index 95250a864598dbcc1c7cf48dcf3c1c136d023b25..c7fe3766f1f6d61a5544746a87a50d4847d307ea 100644 (file)
@@ -4,12 +4,18 @@
 /// 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
 }
index 8e8124edcbd6ed9585c159d51bd65bc062a61ae3..cd73883f06f2d50e168d96dfae7a4941eef600e2 100644 (file)
@@ -10,8 +10,12 @@ criminal-records-console-status = Status
 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}
 
@@ -28,9 +32,13 @@ criminal-records-permission-denied = Permission denied
 ## 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
diff --git a/Resources/Prototypes/StatusEffects/security.yml b/Resources/Prototypes/StatusEffects/security.yml
new file mode 100644 (file)
index 0000000..aeb2c1e
--- /dev/null
@@ -0,0 +1,40 @@
+- 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
diff --git a/Resources/Textures/Interface/Misc/security_icons.rsi/hud_discharged.png b/Resources/Textures/Interface/Misc/security_icons.rsi/hud_discharged.png
new file mode 100644 (file)
index 0000000..2e01234
Binary files /dev/null and b/Resources/Textures/Interface/Misc/security_icons.rsi/hud_discharged.png differ
diff --git a/Resources/Textures/Interface/Misc/security_icons.rsi/hud_incarcerated.png b/Resources/Textures/Interface/Misc/security_icons.rsi/hud_incarcerated.png
new file mode 100644 (file)
index 0000000..1dc03f7
Binary files /dev/null and b/Resources/Textures/Interface/Misc/security_icons.rsi/hud_incarcerated.png differ
diff --git a/Resources/Textures/Interface/Misc/security_icons.rsi/hud_paroled.png b/Resources/Textures/Interface/Misc/security_icons.rsi/hud_paroled.png
new file mode 100644 (file)
index 0000000..042fb14
Binary files /dev/null and b/Resources/Textures/Interface/Misc/security_icons.rsi/hud_paroled.png differ
diff --git a/Resources/Textures/Interface/Misc/security_icons.rsi/hud_suspected.png b/Resources/Textures/Interface/Misc/security_icons.rsi/hud_suspected.png
new file mode 100644 (file)
index 0000000..cfb3474
Binary files /dev/null and b/Resources/Textures/Interface/Misc/security_icons.rsi/hud_suspected.png differ
diff --git a/Resources/Textures/Interface/Misc/security_icons.rsi/hud_wanted.png b/Resources/Textures/Interface/Misc/security_icons.rsi/hud_wanted.png
new file mode 100644 (file)
index 0000000..2df379d
Binary files /dev/null and b/Resources/Textures/Interface/Misc/security_icons.rsi/hud_wanted.png differ
diff --git a/Resources/Textures/Interface/Misc/security_icons.rsi/meta.json b/Resources/Textures/Interface/Misc/security_icons.rsi/meta.json
new file mode 100644 (file)
index 0000000..042eda2
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "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"
+    }
+  ]
+}