Repository: impstation/imp-station-14
Pull request #2462: Obvious Examine
Author: login: jtfg-dev
Created: 2025-05-12T02:27:28Z | Merged: 2025-05-26T23:29:22Z

Makes it so certain items are obvious to casual examination. 

![image](https://github.com/user-attachments/assets/7e25a65a-d8b7-4169-93c7-4b0a29241b5b)

WHY? In-world these things should be intuitive (and we have a limited sprite space), so now the game is respecting that somewhat. This is adding a system in anticipation of adding a novice mark pin that will mark players as self-described unskilled. 

WHAT DOES THIS AFFECT? Currently, the system applies to the pins, cloaks, scarves, medals, and lawyer badges in the game, but it can be applied to any clothes.

WHAT DID YOU ADD??? Adds two new components/systems:
- `WearerGetsExamineText`, to be applied to Clothing that should be apparent on Examine to someone else.
- `ExtraExamineText`, which applies examine text to the attached entity. Applied by `WearerGetsExamineText` to the entity wearing it, and can be implemented elsewhere.

My ECS know-how is a bit scuffed so pointers would be appreciated.

Also accounts for contraband status (some of the important ones):
![image](https://github.com/user-attachments/assets/507f3bdf-952c-4a49-8bc9-bc59ebbe8f57)

WHERE ELSE CAN THIS BE USED? Uh... hardsuits. It'd also be fun to add to some of the jokier "cool" in-round items like the `the coat of space asshole`. That'll wait for until after the novice mark is added.

**Changelog**
non-player facing CL: restores a missing string for the dragon rules

:cl:
- add: Your pride is now clear and obvious to all! Wearing a pin, cloak, medal or scarf now appears in your examine text. 
- tweak: Pins in your pockets can also be seen upon examination! Hooray! No more need to dedicate your neck to your lapel.
- tweak: This is my badge, and it proves that I am a lawyer.


Diff:
diff --git a/Content.Shared/_Impstation/Clothing/WearerGetsExamineTextComponent.cs b/Content.Shared/_Impstation/Clothing/WearerGetsExamineTextComponent.cs
new file mode 100644
index 0000000000000..5328c87d6faff
--- /dev/null
+++ b/Content.Shared/_Impstation/Clothing/WearerGetsExamineTextComponent.cs
@@ -0,0 +1,66 @@
+using Robust.Shared.GameStates;
+
+namespace Content.Shared._Impstation.Clothing;
+
+/// <summary>
+/// Adds examine text to the entity that wears item, for making things obvious.
+/// </summary>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+[Access(typeof(WearerGetsExamineTextSystem))]
+public sealed partial class WearerGetsExamineTextComponent : Component
+{
+    /// <summary>
+    /// The LocId that specifies what category of object this is.
+    /// i.e. "pin" or "scarf"
+    /// Should be redefined on a per-category basis, naturally.
+    /// </summary>
+    [DataField("thing")]
+    public LocId Category = "obvious-thing-default";
+
+    /// <summary>
+    /// The LocId that specifies what member of the category this is.
+    /// i.e. "lesbian pride"
+    /// Can be used to define text colors that are copied to all things
+    /// which share this specifier (i.e. the other items of the same pride).
+    /// (And summarily, makes accessibility-based changes for these colors a cinch.)
+    /// Should be defined by each thing that has this component.
+    /// </summary>
+    [DataField("thingType")]
+    public LocId Specifier = "obvious-type-default";
+
+    /// <summary>
+    /// The LocId that will be added to any wearing entity's examination.
+    /// Typically only needs redefining on a per-category basis,
+    /// but items that should have totally-unique obvious text can simply specify them here.
+    /// </summary>
+    [DataField("examineText", required: true)]
+    public LocId ExamineOnWearer = "obvious-desc-default";
+
+    /// <summary>
+    /// Reference to the entity wearing this clothing.
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public EntityUid? Wearer;
+    /// <summary>
+    /// The string that is attached to this item's ExamineOnWearer.
+    /// Typically doesn't need to be redefined.
+    /// </summary>
+    [DataField]
+    public LocId PrefixExamineOnWearer = "obvious-prefix-wearing";
+
+    /// <summary>
+    /// If true, an entity with this item in any slot (i.e. in pockets) will gain the examine text,
+    /// instead of when just equipped as clothing.
+    /// Should be used sparingly only when truly appropriate; this is effectively a half-measure for lack of a special pin slot.
+    /// </summary>
+    [DataField]
+    public bool PocketEvident;
+
+    /// <summary>
+    /// If true, the entity's description will inform examiners what others will see on the wearer (before they equip it).
+    /// If the item is contraband, the item will also warn that displaying it may cause undue attention.
+    /// Keep this false for good-natured jokes (i.e. the pride cloaks having funny, non-pride names)
+    /// </summary>
+    [DataField]
+    public bool WarnExamine = true;
+}
diff --git a/Content.Shared/_Impstation/Clothing/WearerGetsExamineTextSystem.cs b/Content.Shared/_Impstation/Clothing/WearerGetsExamineTextSystem.cs
new file mode 100644
index 0000000000000..bb38ee554071c
--- /dev/null
+++ b/Content.Shared/_Impstation/Clothing/WearerGetsExamineTextSystem.cs
@@ -0,0 +1,111 @@
+using Content.Shared.Clothing.Components;
+using Content.Shared.Examine;
+using Content.Shared.IdentityManagement;
+using Content.Shared.Inventory.Events;
+using Content.Shared.Contraband;
+using Content.Shared._Impstation.Examine;
+using System.Text;
+
+namespace Content.Shared._Impstation.Clothing;
+
+/// <summary>
+/// Adds examine text to the entity that wears item, for making things obvious.
+/// </summary>
+public sealed class WearerGetsExamineTextSystem : EntitySystem
+{
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<WearerGetsExamineTextComponent, GotEquippedEvent>(OnEquipped);
+        SubscribeLocalEvent<WearerGetsExamineTextComponent, GotUnequippedEvent>(OnUnequipped);
+        SubscribeLocalEvent<WearerGetsExamineTextComponent, ExaminedEvent>(OnExamine);
+    }
+
+    private void OnEquipped(Entity<WearerGetsExamineTextComponent> entity, ref GotEquippedEvent args)
+    {
+        if (!TryComp(entity, out ClothingComponent? clothing))
+            return;
+        var isCorrectSlot = (clothing.Slots & args.SlotFlags) != Inventory.SlotFlags.NONE;
+        if (!entity.Comp.PocketEvident) //if it can't be evident in our pockets
+        {
+            // Make sure the clothing item was equipped to the right slot, and not just held in a hand.
+            if (!isCorrectSlot)
+                return;
+        }
+
+        entity.Comp.Wearer = args.Equipee;
+        Dirty(entity);
+
+        //GIVE THEM INSPECT TEXT
+        var obviousExamine = EnsureComp<ExtraExamineTextComponent>(args.Equipee);
+        obviousExamine.Lines.TryAdd(entity.Owner,  //using try so that we don't cause an error if we move something from slot to slot
+            ConstructExamineText(entity, !isCorrectSlot, args.Equipee));
+    }
+
+
+    private string ConstructExamineText(Entity<WearerGetsExamineTextComponent> entity, bool prefixFallback, EntityUid affecting)
+    {
+        //parameters (these are the same between both constructions)
+        var user = Identity.Entity(affecting, EntityManager);
+        var nomen = Identity.Name(affecting, EntityManager);
+        var thing = Loc.GetString(entity.Comp.Category);
+        var type = Loc.GetString(entity.Comp.Specifier);
+        var stringSpec = entity.Comp.Specifier.ToString();
+        var shortType = stringSpec.Substring(stringSpec.LastIndexOf('-'));  // necessary for working with colored text...
+
+        var prefix = Loc.GetString(prefixFallback ? "obvious-prefix-default" : entity.Comp.PrefixExamineOnWearer, // uses a different prefix if worn / displayed
+                ("user", user),
+                ("name", nomen),
+                ("thing", thing),
+                ("type", type));
+        var suffix = Loc.GetString(entity.Comp.ExamineOnWearer,
+                ("user", user),
+                ("name", nomen),
+                ("thing", thing),
+                ("type", type),
+                ("short-type", shortType));
+        return prefix + " " + suffix;
+    }
+
+    private void OnUnequipped(Entity<WearerGetsExamineTextComponent> entity, ref GotUnequippedEvent args)
+    {
+        if (entity.Comp.Wearer is not { } wearer)
+            return;
+
+        if (TryComp(wearer, out ExtraExamineTextComponent? obviousExamine))
+        {
+            obviousExamine.Lines.Remove(entity.Owner);
+        }
+
+        entity.Comp.Wearer = null;
+        Dirty(entity);
+    }
+
+    private void OnExamine(Entity<WearerGetsExamineTextComponent> entity, ref ExaminedEvent args)
+    {
+        var currentlyWorn = entity.Comp.Wearer != null;
+        var outString = new StringBuilder(Loc.GetString(currentlyWorn ? "obvious-on-item-currently" : "obvious-on-item",
+            ("used", Loc.GetString(entity.Comp.PocketEvident ? "obvious-reveal-pockets" : "obvious-reveal-default")),
+            ("thing", entity.Comp.Category),
+            ("me", Identity.Entity(entity, EntityManager))));
+
+        if (entity.Comp.WarnExamine)
+        {
+            if (!currentlyWorn && TryComp(entity, out ContrabandComponent? contra)) // if the item's contra and we're not wearing it yet
+            {
+                var contraLocId = "obvious-on-item-contra-" + contra.Severity; // apply additional text if the item is contraband to note that displaying it might be really bad
+                if (Loc.HasString(contraLocId)) // saves us the trouble of making a switch block for this
+                    outString.Append(" " + Loc.GetString(contraLocId));
+            }
+            var affecting = currentlyWorn ? entity.Comp.Wearer.GetValueOrDefault() : args.Examiner;
+            var testOut = ConstructExamineText(entity, false, affecting);
+
+            outString.Append("\n" + Loc.GetString("obvious-on-item-for-others",
+                ("will", currentlyWorn ? "can" : "will"), // i love hardcoding strings it's my favorite thing ever
+                ("output", testOut)));
+        }
+
+        args.PushMarkup(outString.ToString());
+    }
+}
diff --git a/Content.Shared/_Impstation/DetailedExamine/DetailedInspectComponent.cs b/Content.Shared/_Impstation/Examine/DetailedInspectComponent.cs
similarity index 94%
rename from Content.Shared/_Impstation/DetailedExamine/DetailedInspectComponent.cs
rename to Content.Shared/_Impstation/Examine/DetailedInspectComponent.cs
index 9686a0b8b29bc..3c1d7e2bb982d 100644
--- a/Content.Shared/_Impstation/DetailedExamine/DetailedInspectComponent.cs
+++ b/Content.Shared/_Impstation/Examine/DetailedInspectComponent.cs
@@ -1,6 +1,6 @@
 using Robust.Shared.GameStates;
 
-namespace Content.Shared._Impstation.DetailedInspect;
+namespace Content.Shared._Impstation.Examine;
 
 [RegisterComponent, NetworkedComponent]
 public sealed partial class DetailedInspectComponent : Component
@@ -21,7 +21,7 @@ public sealed partial class DetailedInspectComponent : Component
     public bool LineBreak = true;
 
     /// <summary>
-    /// Whether or not the entries in ExamineText are preceded by ticks. 
+    /// Whether or not the entries in ExamineText are preceded by ticks.
     /// </summary>
     [DataField]
     public bool TickEntries = false;
diff --git a/Content.Shared/_Impstation/DetailedExamine/DetailedInspectSystem.cs b/Content.Shared/_Impstation/Examine/DetailedInspectSystem.cs
similarity index 94%
rename from Content.Shared/_Impstation/DetailedExamine/DetailedInspectSystem.cs
rename to Content.Shared/_Impstation/Examine/DetailedInspectSystem.cs
index c46b864460c85..a02c33d02557b 100644
--- a/Content.Shared/_Impstation/DetailedExamine/DetailedInspectSystem.cs
+++ b/Content.Shared/_Impstation/Examine/DetailedInspectSystem.cs
@@ -1,10 +1,9 @@
-using Content.Shared.Destructible;
 using Content.Shared.Examine;
 using Content.Shared.Verbs;
 using Robust.Shared.Network;
 using Robust.Shared.Utility;
 
-namespace Content.Shared._Impstation.DetailedInspect;
+namespace Content.Shared._Impstation.Examine;
 
 public sealed partial class DetailedInspectSystem : EntitySystem
 {
diff --git a/Content.Shared/_Impstation/Examine/ExtraExamineTextComponent.cs b/Content.Shared/_Impstation/Examine/ExtraExamineTextComponent.cs
new file mode 100644
index 0000000000000..2df9f89b53624
--- /dev/null
+++ b/Content.Shared/_Impstation/Examine/ExtraExamineTextComponent.cs
@@ -0,0 +1,24 @@
+using Robust.Shared.GameStates;
+using Content.Shared._Impstation.Clothing;
+
+namespace Content.Shared._Impstation.Examine;
+
+/// <summary>
+/// Adds examine text to the entity, intentionally "obvious details".
+/// Like, that's it. It's basic -- all it does is add the lines to the attached entity.
+/// This is particularly used for assigning players unique examine text.
+/// </summary>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+[Access(typeof(ExtraExamineTextSystem), typeof(WearerGetsExamineTextSystem))]
+public sealed partial class ExtraExamineTextComponent : Component
+{
+    /// <summary>
+    /// The LocIds that will be added to the attached entity's examination.
+    ///
+    /// The key is the source of the line, and the value is the text
+    /// (built by the component that creates this one).
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public Dictionary<EntityUid, LocId> Lines { get; set; } = new();
+
+}
diff --git a/Content.Shared/_Impstation/Examine/ExtraExamineTextSystem.cs b/Content.Shared/_Impstation/Examine/ExtraExamineTextSystem.cs
new file mode 100644
index 0000000000000..3cc94f3c0f03a
--- /dev/null
+++ b/Content.Shared/_Impstation/Examine/ExtraExamineTextSystem.cs
@@ -0,0 +1,33 @@
+using Content.Shared.Examine;
+using Content.Shared.IdentityManagement;
+
+namespace Content.Shared._Impstation.Examine;
+
+/// <summary>
+/// Adds examine text to the entity, intentionally "obvious details".
+/// Like, that's it. It's basic -- all it does is add the line to the attached entity.
+/// This is particularly used for assigning players unique examine text.
+/// </summary>
+public sealed class ExtraExamineTextSystem : EntitySystem
+{
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<ExtraExamineTextComponent, ExaminedEvent>(OnExamine);
+    }
+
+    private void OnExamine(Entity<ExtraExamineTextComponent> entity, ref ExaminedEvent args)
+    {
+        if (entity.Comp.Lines.Count == 0)
+        {
+            RemCompDeferred<ExtraExamineTextComponent>(entity); // no more need for me!
+            return;
+        }
+
+        foreach (var l in entity.Comp.Lines)
+        {
+            args.PushMarkup(l.Value);
+        }
+    }
+}
diff --git a/Resources/Locale/en-US/_Impstation/obvious.ftl b/Resources/Locale/en-US/_Impstation/obvious.ftl
new file mode 100644
index 0000000000000..3e0d11fa4e387
--- /dev/null
+++ b/Resources/Locale/en-US/_Impstation/obvious.ftl
@@ -0,0 +1,64 @@
+obvious-on-item = When {$used}, { SUBJECT($me) } will be [color=white]obvious[/color] to casual examination.
+obvious-on-item-currently = { CAPITALIZE(SUBJECT($me)) } { CONJUGATE-BE($me) } [color=white]obvious[/color] to casual examination.
+obvious-on-item-for-others = [italic][color=#777777]Others {$will} see:[/color] "{$output}"[/italic]
+
+obvious-reveal-default = worn
+obvious-reveal-pockets = worn or pocketed
+
+obvious-on-item-contra-Syndicate = Might make you seem [color=#ff0000]evil[/color].
+obvious-on-item-contra-Magical = Might make you seem [color=blue]mystical[/color].
+obvious-on-item-contra-Major = Don't be surprised if [color=#cb0000]some people[/color] aren't a fan.
+
+obvious-prefix-default = { CAPITALIZE(SUBJECT($user)) } { CONJUGATE-HAVE($user) }
+obvious-prefix-wearing = { CAPITALIZE(SUBJECT($user)) } { CONJUGATE-BE($user) } wearing
+
+## general descriptions
+obvious-desc-default = { INDEFINITE($short-type) } { $type } { $thing }.
+obvious-desc-colors = { INDEFINITE($thing) } { $thing } in { $type } colors.
+obvious-desc-proves = { INDEFINITE($thing) } [color=white]{ $thing }[/color] that proves { SUBJECT($user) } { CONJUGATE-BE($user) } { INDEFINITE($short-type) } { $type }.
+
+## item categories
+obvious-thing-default = [italic]something-or-another[/italic]
+obvious-thing-cloak = cloak
+obvious-thing-pin = pin
+obvious-thing-scarf = scarf
+obvious-thing-badge = badge
+obvious-thing-armband = armband
+obvious-thing-medal = medal
+
+## item types
+obvious-type-default = [italic]nothing[/italic]
+
+# prides
+obvious-type-pride = [color=#d479d4]LGBTQ[/color] [color=white]pride[/color]
+obvious-type-pride-aro = [color=#3DA542]aromantic[/color] [color=white]pride[/color]
+obvious-type-pride-ace = [color=#efefef]asexual[/color] [color=#800080]pride[/color]
+obvious-type-pride-aroace = [color=#efc337]aroace[/color] [color=#45bcee]pride[/color]
+obvious-type-pride-lesbian = [color=#EF7627]lesbian[/color] [color=#B55690]pride[/color]
+obvious-type-pride-bi = [color=#D60270]bisexual[/color] [color=#0038A8]pride[/color]
+obvious-type-pride-pan = [color=#FF218C]pansexual[/color] [color=#21B1FF]pride[/color]
+obvious-type-pride-gay = [color=#078D70]gay[/color] [color=#5049CC]pride[/color]
+obvious-type-pride-omni = [color=#FE9ACE]omnisexual[/color] [color=8EA6FF]pride[/color]
+obvious-type-pride-bear = [color=#FDDC62]bear[/color] [color=#613704]pride[/color]
+obvious-type-pride-intersex = [color=#FFD800]intersex[/color] [color=#7902AA]pride[/color]
+obvious-type-pride-nonbinary = [color=#FCF434]nonbinary[/color] [color=#9C59D1]pride[/color]
+obvious-type-pride-trans = [color=#5BCEFA]transgender[/color] [color=#F5A9B8]pride[/color]
+obvious-type-pride-gf = [color=#FF76A4]genderfluid[/color] [color=#2F3CBE]pride[/color]
+obvious-type-pride-aut = [color=#FFD700]autism[/color] [color=white]pride[/color]
+obvious-type-pride-straightally = [color=#7c7c7c]straight[/color] [color=#d479d4]LGBTQ ally[/color]
+obvious-type-pride-gq = [color=#B57EDC]genderqueer[/color] [color=#4A8123]pride[/color]
+
+# lawyers
+obvious-type-law = [color=white]certified[/color] [color=#FFD700]lawyer[/color]
+obvious-type-law-defense = [color=white]certified[/color] [color=#00C0C0]defense attorney[/color]
+obvious-type-law-prosecution = [color=white]certified[/color] [color=#FF2222]prosecuting attorney[/color]
+
+## SPECIFIC DESCRIPTIONS (skips the above description-builders)
+obvious-x-scarf-lesbian-long = some [color=#EF7627]long[/color] [color=white]ba[/color][color=#B55690]con[/color].
+obvious-x-medal-nothing = a [color=#FFD700]gleaming medal[/color]!
+# medals should be in the description building functions but there's too many of them and this feature is already over-scope
+
+# imp only items
+obvious-x-pin-straight = a [color=#7c7c7c]straight[/color] [color=white]pride[/color] pin... [italic]Ew.[/italic]
+obvious-x-cloak-straight = a cloak in the [color=#7c7c7c]straight[/color] [color=white]pride[/color] colors... [italic]Ew.[/italic]
+
diff --git a/Resources/Locale/en-US/ghost/roles/ghost-role-component.ftl b/Resources/Locale/en-US/ghost/roles/ghost-role-component.ftl
index 6a4b08aa4c855..11e352137421e 100644
--- a/Resources/Locale/en-US/ghost/roles/ghost-role-component.ftl
+++ b/Resources/Locale/en-US/ghost/roles/ghost-role-component.ftl
@@ -135,6 +135,7 @@ ghost-role-information-ifrit-description = Listen to your owner. Don't tank dama
 
 ghost-role-information-space-dragon-name = Space Dragon
 ghost-role-information-space-dragon-description = Call in 3 carp rifts and take over this quadrant! You have only 5 minutes in between each rift before you will disappear.
+ghost-role-information-space-dragon-rules = You are a [color=red][bold]Team Antagonist[/bold][/color] with all your summoned carp.
                                             You don't remember any of your previous life, and you don't remember anything you learned as a ghost.
 ghost-role-information-space-dragon-summoned-carp-rules = You are a [color=red][bold]Team Antagonist[/bold][/color] with your dragon and its allies.
                                                           You don't remember any of your previous life, and you don't remember anything you learned as a ghost.
diff --git a/Resources/Prototypes/Entities/Clothing/Neck/cloaks.yml b/Resources/Prototypes/Entities/Clothing/Neck/cloaks.yml
index 8e2b78859c741..1309f690ce957 100644
--- a/Resources/Prototypes/Entities/Clothing/Neck/cloaks.yml
+++ b/Resources/Prototypes/Entities/Clothing/Neck/cloaks.yml
@@ -136,13 +136,15 @@
     sprite: Clothing/Neck/Cloaks/miner.rsi
 
 - type: entity
-  parent: ClothingNeckBase
+  parent: [ ClothingNeckBase, BaseObviousCloak ] # imp
   id: ClothingNeckCloakTrans
   name: vampire cloak
   description: Worn by high ranking vampires of the transylvanian society of vampires.
   components:
   - type: Sprite
     sprite: Clothing/Neck/Cloaks/trans.rsi
+  - type: WearerGetsExamineText # imp
+    thingType: obvious-type-pride-trans
 
 - type: entity
   parent: ClothingNeckBase
@@ -209,92 +211,112 @@
     proto: alien
 
 - type: entity
-  parent: ClothingNeckBase
+  parent: [ ClothingNeckBase, BaseObviousCloak ] # imp
   id: ClothingNeckCloakAce
   name: pilot's cloak
   description: Cloak awarded to Nanotrasen's finest space aces.
   components:
   - type: Sprite
     sprite: Clothing/Neck/Cloaks/ace.rsi
+  - type: WearerGetsExamineText # imp
+    thingType: obvious-type-pride-ace
 
 - type: entity
-  parent: ClothingNeckBase
+  parent: [ ClothingNeckBase, BaseObviousCloak ] # imp
   id: ClothingNeckCloakAro
   name: werewolf cloak
   description: This cloak lets others know you're a lone wolf.
   components:
   - type: Sprite
     sprite: Clothing/Neck/Cloaks/aro.rsi
+  - type: WearerGetsExamineText # imp
+    thingType: obvious-type-pride-aro
 
 - type: entity
-  parent: ClothingNeckBase
+  parent: [ ClothingNeckBase, BaseObviousCloak ] # imp
   id: ClothingNeckCloakAroace
   name: aeropilot's cloak # thank you happyman442 this was the best name idea ever
   description: Cloak awarded to Nanotrasen's finest pilots on planets with inhabitable atmospheres.
   components:
   - type: Sprite
     sprite: Clothing/Neck/Cloaks/aroace.rsi
+  - type: WearerGetsExamineText # imp
+    thingType: obvious-type-pride-aroace
 
 - type: entity
-  parent: ClothingNeckBase
+  parent: [ ClothingNeckBase, BaseObviousCloak ] # imp
   id: ClothingNeckCloakBi
   name: poison cloak
   description: The purple color is a clear indicator you are poisonous.
   components:
   - type: Sprite
     sprite: Clothing/Neck/Cloaks/bi.rsi
+  - type: WearerGetsExamineText # imp
+    thingType: obvious-type-pride-bi
 
 - type: entity
-  parent: ClothingNeckBase
+  parent: [ ClothingNeckBase, BaseObviousCloak ] # imp
   id: ClothingNeckCloakIntersex
   name: cyclops cloak
   description: The circle on this cloak represents a cyclops' eye.
   components:
   - type: Sprite
     sprite: Clothing/Neck/Cloaks/intersex.rsi
+  - type: WearerGetsExamineText # imp
+    thingType: obvious-type-pride-intersex
 
 - type: entity
-  parent: ClothingNeckBase
+  parent: [ ClothingNeckBase, BaseObviousCloak ] # imp
   id: ClothingNeckCloakLesbian
   name: poet cloak
   description: This cloak belonged to an ancient poet, you forgot which one.
   components:
   - type: Sprite
     sprite: Clothing/Neck/Cloaks/les.rsi
+  - type: WearerGetsExamineText # imp
+    thingType: obvious-type-pride-lesbian
 
 - type: entity
-  parent: ClothingNeckBase
+  parent: [ ClothingNeckBase, BaseObviousCloak ] # imp
   id: ClothingNeckCloakGay
   name: multi-level marketing cloak
   description: This cloak is highly sought after in the Nanotrasen Marketing Offices.
   components:
   - type: Sprite
     sprite: Clothing/Neck/Cloaks/gay.rsi
+  - type: WearerGetsExamineText # imp
+    thingType: obvious-type-pride-gay
 
 - type: entity
-  parent: ClothingNeckBase
+  parent: [ ClothingNeckBase, BaseObviousCloak ] # imp
   id: ClothingNeckCloakEnby
   name: treasure hunter cloak
   description: This cloak belonged to a greedy treasure hunter.
   components:
   - type: Sprite
     sprite: Clothing/Neck/Cloaks/enby.rsi
+  - type: WearerGetsExamineText # imp
+    thingType: obvious-type-pride-nonbinary
 
 - type: entity
-  parent: ClothingNeckBase
+  parent: [ ClothingNeckBase, BaseObviousCloak ] # imp
   id: ClothingNeckCloakPan
   name: chef's cloak
   description: Meant to be worn alongside a frying pan.
   components:
   - type: Sprite
     sprite: Clothing/Neck/Cloaks/pan.rsi
+  - type: WearerGetsExamineText # imp
+    thingType: obvious-type-pride-pan
 
 - type: entity
-  parent: ClothingNeckBase
+  parent: [ ClothingNeckBase, BaseObviousCloak ] # imp
   id: ClothingNeckCloakBear
   name: druid cloak
   description: This cloak has a wild-looking shape on it.
   components:
   - type: Sprite
     sprite: Clothing/Neck/Cloaks/bear.rsi
+  - type: WearerGetsExamineText # imp
+    thingType: obvious-type-pride-bear
 
diff --git a/Resources/Prototypes/Entities/Clothing/Neck/medals.yml b/Resources/Prototypes/Entities/Clothing/Neck/medals.yml
index 75e89420e7d24..aea9f8a4d9d9f 100644
--- a/Resources/Prototypes/Entities/Clothing/Neck/medals.yml
+++ b/Resources/Prototypes/Entities/Clothing/Neck/medals.yml
@@ -19,6 +19,11 @@
   - type: Tag
     tags:
       - Medal
+  - type: WearerGetsExamineText # imp
+    thing: obvious-thing-medal
+    examineText: obvious-x-medal-nothing
+    pocketEvident: true
+
 
 - type: entity
   parent: ClothingNeckBronzeheart
diff --git a/Resources/Prototypes/Entities/Clothing/Neck/misc.yml b/Resources/Prototypes/Entities/Clothing/Neck/misc.yml
index b68ad6295caaf..687d8c2780237 100644
--- a/Resources/Prototypes/Entities/Clothing/Neck/misc.yml
+++ b/Resources/Prototypes/Entities/Clothing/Neck/misc.yml
@@ -81,7 +81,7 @@
     sprite: Clothing/Neck/Misc/bling.rsi
 
 - type: entity
-  parent: ClothingNeckBase
+  parent: [ ClothingNeckBase, BaseObviousBadge ] # imp
   id: ClothingNeckLawyerbadge
   name: lawyer badge
   description: A badge to show that the owner is a 'legitimate' lawyer who passed the NT bar exam required to practice law.
@@ -92,6 +92,8 @@
     sprite: _Impstation/Clothing/Neck/Misc/lawyerbadge.rsi #imp
   - type: TypingIndicatorClothing
     proto: lawyer
+  - type: WearerGetsExamineText # imp
+    thingType: obvious-type-law
 
 - type: entity
   parent: ClothingNeckBase
diff --git a/Resources/Prototypes/Entities/Clothing/Neck/pins.yml b/Resources/Prototypes/Entities/Clothing/Neck/pins.yml
index e517d41f30c0f..db087fe89abc6 100644
--- a/Resources/Prototypes/Entities/Clothing/Neck/pins.yml
+++ b/Resources/Prototypes/Entities/Clothing/Neck/pins.yml
@@ -16,6 +16,10 @@
       types:
         Blunt: 1
     staminaCost: 1
+  - type: WearerGetsExamineText # imp
+    thing: obvious-thing-pin
+    examineText: obvious-desc-default
+    pocketEvident: true
 
 - type: entity
   parent: ClothingNeckPinBase
@@ -27,6 +31,8 @@
     state: lgbt
   - type: Clothing
     equippedPrefix: lgbt
+  - type: WearerGetsExamineText # imp
+    thingType: obvious-type-pride
 
 - type: entity
   parent: ClothingNeckPinBase
@@ -38,6 +44,8 @@
     state: ally
   - type: Clothing
     equippedPrefix: ally
+  - type: WearerGetsExamineText # imp
+    thingType: obvious-type-pride-straightally
 
 - type: entity
   parent: ClothingNeckPinBase
@@ -49,6 +57,8 @@
     state: aro
   - type: Clothing
     equippedPrefix: aro
+  - type: WearerGetsExamineText # imp
+    thingType: obvious-type-pride-aro
 
 - type: entity
   parent: ClothingNeckPinBase
@@ -60,6 +70,8 @@
     state: aroace
   - type: Clothing
     equippedPrefix: aroace
+  - type: WearerGetsExamineText # imp
+    thingType: obvious-type-pride-aroace
 
 - type: entity
   parent: ClothingNeckPinBase
@@ -71,6 +83,8 @@
     state: asex
   - type: Clothing
     equippedPrefix: asex
+  - type: WearerGetsExamineText # imp
+    thingType: obvious-type-pride-ace
 
 - type: entity
   parent: ClothingNeckPinBase
@@ -82,6 +96,8 @@
     state: bi
   - type: Clothing
     equippedPrefix: bi
+  - type: WearerGetsExamineText # imp
+    thingType: obvious-type-pride-bi
 
 - type: entity
   parent: ClothingNeckPinBase
@@ -93,6 +109,8 @@
     state: gay
   - type: Clothing
     equippedPrefix: gay
+  - type: WearerGetsExamineText # imp
+    thingType: obvious-type-pride-gay
 
 - type: entity
   parent: ClothingNeckPinBase
@@ -104,6 +122,8 @@
     state: inter
   - type: Clothing
     equippedPrefix: inter
+  - type: WearerGetsExamineText # imp
+    thingType: obvious-type-pride-intersex
 
 - type: entity
   parent: ClothingNeckPinBase
@@ -115,6 +135,8 @@
     state: les
   - type: Clothing
     equippedPrefix: les
+  - type: WearerGetsExamineText # imp
+    thingType: obvious-type-pride-lesbian
 
 - type: entity
   parent: ClothingNeckPinBase
@@ -126,6 +148,8 @@
     state: non
   - type: Clothing
     equippedPrefix: non
+  - type: WearerGetsExamineText # imp
+    thingType: obvious-type-pride-nonbinary
 
 - type: entity
   parent: ClothingNeckPinBase
@@ -137,6 +161,8 @@
     state: pan
   - type: Clothing
     equippedPrefix: pan
+  - type: WearerGetsExamineText # imp
+    thingType: obvious-type-pride-pan
 
 - type: entity
   parent: ClothingNeckPinBase
@@ -148,6 +174,8 @@
     state: omni
   - type: Clothing
     equippedPrefix: omni
+  - type: WearerGetsExamineText # imp
+    thingType: obvious-type-pride-omni
 
 - type: entity
   parent: ClothingNeckPinBase
@@ -159,6 +187,8 @@
     state: gender
   - type: Clothing
     equippedPrefix: gender
+  - type: WearerGetsExamineText # imp
+    thingType: obvious-type-pride-gq
 
 - type: entity
   parent: ClothingNeckPinBase
@@ -170,6 +200,8 @@
     state: trans
   - type: Clothing
     equippedPrefix: trans
+  - type: WearerGetsExamineText # imp
+    thingType: obvious-type-pride-trans
 
 - type: entity
   parent: ClothingNeckPinBase
@@ -181,6 +213,8 @@
     state: autism
   - type: Clothing
     equippedPrefix: autism
+  - type: WearerGetsExamineText # imp
+    thingType: obvious-type-pride-aut
 
 - type: entity
   parent: ClothingNeckPinBase
@@ -192,3 +226,5 @@
     state: goldautism
   - type: Clothing
     equippedPrefix: goldautism
+  - type: WearerGetsExamineText # imp
+    thingType: obvious-type-pride-aut
diff --git a/Resources/Prototypes/Entities/Clothing/Neck/scarfs.yml b/Resources/Prototypes/Entities/Clothing/Neck/scarfs.yml
index 43fdb406d20b9..548a8fc7ce45f 100644
--- a/Resources/Prototypes/Entities/Clothing/Neck/scarfs.yml
+++ b/Resources/Prototypes/Entities/Clothing/Neck/scarfs.yml
@@ -163,7 +163,7 @@
 
 # Pride Scarves
 - type: entity
-  parent: ClothingScarfBase
+  parent: [ ClothingScarfBase, BaseObviousScarf ] # imp
   id: ClothingNeckScarfStripedAce
   name: striped asexual scarf
   description: A stylish striped asexual scarf. The perfect winter accessory for those with a keen fashion sense, and those who just can't handle a cold breeze on their necks.
@@ -172,9 +172,11 @@
     sprite: Clothing/Neck/Scarfs/PrideScarfs/ace.rsi
   - type: Clothing
     sprite: Clothing/Neck/Scarfs/PrideScarfs/ace.rsi
+  - type: WearerGetsExamineText # imp
+    thingType: obvious-type-pride-ace
 
 - type: entity
-  parent: ClothingScarfBase
+  parent: [ ClothingScarfBase, BaseObviousScarf ] # imp
   id: ClothingNeckScarfStripedAro
   name: striped aromantic scarf
   description: A stylish striped aromantic scarf. The perfect winter accessory for those with a keen fashion sense, and those who just can't handle a cold breeze on their necks.
@@ -183,9 +185,11 @@
     sprite: Clothing/Neck/Scarfs/PrideScarfs/aro.rsi
   - type: Clothing
     sprite: Clothing/Neck/Scarfs/PrideScarfs/aro.rsi
+  - type: WearerGetsExamineText # imp
+    thingType: obvious-type-pride-aro
 
 - type: entity
-  parent: ClothingScarfBase
+  parent: [ ClothingScarfBase, BaseObviousScarf ] # imp
   id: ClothingNeckScarfStripedAroace
   name: striped aroace scarf
   description: A stylish striped aroace scarf. The perfect winter accessory for those with a keen fashion sense, and those who just can't handle a cold breeze on their necks.
@@ -194,9 +198,11 @@
     sprite: Clothing/Neck/Scarfs/PrideScarfs/aroace.rsi
   - type: Clothing
     sprite: Clothing/Neck/Scarfs/PrideScarfs/aroace.rsi
+  - type: WearerGetsExamineText # imp
+    thingType: obvious-type-pride-aroace
 
 - type: entity
-  parent: ClothingScarfBase
+  parent: [ ClothingScarfBase, BaseObviousScarf ] # imp
   id: ClothingNeckScarfStripedBiSexual
   name: striped bisexual scarf
   description: A stylish striped bisexual scarf. The perfect winter accessory for those with a keen fashion sense, and those who just can't handle a cold breeze on their necks.
@@ -205,9 +211,11 @@
     sprite: Clothing/Neck/Scarfs/PrideScarfs/bi.rsi
   - type: Clothing
     sprite: Clothing/Neck/Scarfs/PrideScarfs/bi.rsi
+  - type: WearerGetsExamineText # imp
+    thingType: obvious-type-pride-bi
 
 - type: entity
-  parent: ClothingScarfBase
+  parent: [ ClothingScarfBase, BaseObviousScarf ] # imp
   id: ClothingNeckScarfStripedGay
   name: striped gay scarf
   description: A stylish striped gay scarf. The perfect winter accessory for those with a keen fashion sense, and those who just can't handle a cold breeze on their necks.
@@ -216,9 +224,11 @@
     sprite: Clothing/Neck/Scarfs/PrideScarfs/gay.rsi
   - type: Clothing
     sprite: Clothing/Neck/Scarfs/PrideScarfs/gay.rsi
+  - type: WearerGetsExamineText # imp
+    thingType: obvious-type-pride-gay
 
 - type: entity
-  parent: ClothingScarfBase
+  parent: [ ClothingScarfBase, BaseObviousScarf ] # imp
   id: ClothingNeckScarfStripedInter
   name: striped intersex scarf
   description: A stylish striped intersex scarf. The perfect winter accessory for those with a keen fashion sense, and those who just can't handle a cold breeze on their necks.
@@ -227,9 +237,11 @@
     sprite: Clothing/Neck/Scarfs/PrideScarfs/inter.rsi
   - type: Clothing
     sprite: Clothing/Neck/Scarfs/PrideScarfs/inter.rsi
+  - type: WearerGetsExamineText # imp
+    thingType: obvious-type-pride-intersex
 
 - type: entity
-  parent: ClothingScarfBase
+  parent: [ ClothingScarfBase, BaseObviousScarf ] # imp
   id: ClothingNeckScarfStripedLesbian
   name: striped lesbian scarf
   description: A stylish striped lesbian scarf. The perfect winter accessory for those with a keen fashion sense, and those who just can't handle a cold breeze on their necks.
@@ -238,9 +250,11 @@
     sprite: Clothing/Neck/Scarfs/PrideScarfs/lesbian.rsi
   - type: Clothing
     sprite: Clothing/Neck/Scarfs/PrideScarfs/lesbian.rsi
+  - type: WearerGetsExamineText # imp
+    thingType: obvious-type-pride-lesbian
 
 - type: entity
-  parent: ClothingScarfBase
+  parent: [ ClothingScarfBase, BaseObviousScarf ] # imp
   id: ClothingNeckScarfStripedPan
   name: striped pan scarf
   description: A stylish striped pan scarf. The perfect winter accessory for those with a keen fashion sense, and those who just can't handle a cold breeze on their necks.
@@ -249,9 +263,11 @@
     sprite: Clothing/Neck/Scarfs/PrideScarfs/pan.rsi
   - type: Clothing
     sprite: Clothing/Neck/Scarfs/PrideScarfs/pan.rsi
+  - type: WearerGetsExamineText # imp
+    thingType: obvious-type-pride-pan
 
 - type: entity
-  parent: ClothingScarfBase
+  parent: [ ClothingScarfBase, BaseObviousScarf ] # imp
   id: ClothingNeckScarfStripedNonBinary
   name: striped non-binary scarf
   description: A stylish striped non-binary scarf. The perfect winter accessory for those with a keen fashion sense, and those who just can't handle a cold breeze on their necks.
@@ -260,9 +276,11 @@
     sprite: Clothing/Neck/Scarfs/PrideScarfs/non.rsi
   - type: Clothing
     sprite: Clothing/Neck/Scarfs/PrideScarfs/non.rsi
+  - type: WearerGetsExamineText # imp
+    thingType: obvious-type-pride-nonbinary
 
 - type: entity
-  parent: ClothingScarfBase
+  parent: [ ClothingScarfBase, BaseObviousScarf ] # imp
   id: ClothingNeckScarfStripedRainbow
   name: rainbow scarf
   description: A stylish rainbow scarf. The perfect winter accessory for those with a keen fashion sense, and those who just can't handle a cold breeze on their necks.
@@ -271,9 +289,11 @@
     sprite: Clothing/Neck/Scarfs/PrideScarfs/rainbow.rsi
   - type: Clothing
     sprite: Clothing/Neck/Scarfs/PrideScarfs/rainbow.rsi
+  - type: WearerGetsExamineText # imp
+    thingType: obvious-type-pride
 
 - type: entity
-  parent: ClothingScarfBase
+  parent: [ ClothingScarfBase, BaseObviousScarf ] # imp
   id: ClothingNeckScarfStripedTrans
   name: striped trans scarf
   description: A stylish striped trans scarf. The perfect winter accessory for those with a keen fashion sense, and those who just can't handle a cold breeze on their necks.
@@ -282,9 +302,11 @@
     sprite: Clothing/Neck/Scarfs/PrideScarfs/trans.rsi
   - type: Clothing
     sprite: Clothing/Neck/Scarfs/PrideScarfs/trans.rsi
+  - type: WearerGetsExamineText # imp
+    thingType: obvious-type-pride-trans
 
 - type: entity
-  parent: ClothingScarfBase
+  parent: [ ClothingScarfBase, BaseObviousScarf ] # imp
   id: ClothingNeckScarfStripedLesbianLong
   name: long bacon
   description: Long bacon! Perfect for sharing with your girlfriend!
@@ -293,6 +315,8 @@
     sprite: Clothing/Neck/Scarfs/PrideScarfs/lesbian-long.rsi
   - type: Clothing
     sprite: Clothing/Neck/Scarfs/PrideScarfs/lesbian-long.rsi
+  - type: WearerGetsExamineText # imp
+    examineText: obvious-x-scarf-lesbian-long
 
 - type: entity
   parent: ClothingScarfBase
diff --git a/Resources/Prototypes/_DV/Entities/Clothing/Neck/misc.yml b/Resources/Prototypes/_DV/Entities/Clothing/Neck/misc.yml
index 330689e010f2d..3bf79180bf96e 100644
--- a/Resources/Prototypes/_DV/Entities/Clothing/Neck/misc.yml
+++ b/Resources/Prototypes/_DV/Entities/Clothing/Neck/misc.yml
@@ -1,7 +1,7 @@
 ﻿# prosecutor
 
 - type: entity
-  parent: ClothingNeckBase
+  parent: [ ClothingNeckBase, BaseObviousBadge ] # imp
   id: ClothingNeckProsecutorbadge
   name: prosecutor badge
   description: A badge to show that the owner is a 'legitimate' prosecutor who passed the NT bar exam required to practice law.
@@ -12,6 +12,8 @@
     sprite: _Impstation/Clothing/Neck/Misc/prosecutorbadge.rsi #imp
   - type: TypingIndicatorClothing
     proto: lawyer
+  - type: WearerGetsExamineText # imp
+    thingType: obvious-type-law-prosecution
 
 # ponchos
 
diff --git a/Resources/Prototypes/_Impstation/Entities/Clothing/Neck/cloaks.yml b/Resources/Prototypes/_Impstation/Entities/Clothing/Neck/cloaks.yml
index 4c6bed22f34e3..65d8acf1b82ae 100644
--- a/Resources/Prototypes/_Impstation/Entities/Clothing/Neck/cloaks.yml
+++ b/Resources/Prototypes/_Impstation/Entities/Clothing/Neck/cloaks.yml
@@ -1,11 +1,22 @@
 - type: entity
-  parent: [ ClothingNeckBase, BaseSyndicateContraband ]
+  id: BaseObviousCloak
+  abstract: true
+  components:
+  - type: WearerGetsExamineText
+    examineText: obvious-desc-colors
+    thing: obvious-thing-cloak
+    warnExamine: false # for funny jokes!
+
+- type: entity
+  parent: [ ClothingNeckBase, BaseSyndicateContraband, BaseObviousCloak ]
   id: ClothingNeckCloakStraight
   name: hamburglar allegiance cloak
   description: A cloak signifying allegiance with the greatest hamburger thief of all time. Wear with caution.
   components:
   - type: Sprite
     sprite: _Impstation/Clothing/Neck/Cloaks/straight.rsi
+  - type: WearerGetsExamineText
+    examineText: obvious-x-cloak-straight
 
 - type: entity
   parent: [ClothingNeckCloakStraight, BaseToggleClothing]
@@ -123,9 +134,9 @@
   - type: Sprite
     sprite: _Impstation/Clothing/Neck/Cloaks/juggernautcloak.rsi
 
-    
+
 - type: entity
-  parent: [ClothingNeckBase, BaseSyndicateContraband]
+  parent: [ClothingNeckBase, BaseSyndicateContraband, BaseObviousCloak]
   id: ClothingNeckCloakFugitiveCloak
   name: monochrome cloak
   description: It's a federal crime, buster.
@@ -137,6 +148,10 @@
   - type: HideLayerClothing
     slots:
     - Tail
+  - type: WearerGetsExamineText # imp
+    examineText: obvious-x-cloak-straight
+    warnExamine: true # the fugitive knows they're straight as fuck
+
 - type: entity
   parent: [ClothingNeckBase, BaseCommandContraband]
   id: ClothingNeckCloakCaptainAdmiral
@@ -146,4 +161,4 @@
   - type: Sprite
     sprite: _Impstation/Clothing/Neck/Cloaks/capcloakadmiral.rsi
   - type: StealTarget
-    stealGroup: HeadCloak
\ No newline at end of file
+    stealGroup: HeadCloak
diff --git a/Resources/Prototypes/_Impstation/Entities/Clothing/Neck/misc.yml b/Resources/Prototypes/_Impstation/Entities/Clothing/Neck/misc.yml
index e34dd757217b7..796eef3a8849d 100644
--- a/Resources/Prototypes/_Impstation/Entities/Clothing/Neck/misc.yml
+++ b/Resources/Prototypes/_Impstation/Entities/Clothing/Neck/misc.yml
@@ -1,5 +1,13 @@
 - type: entity
-  parent: ClothingNeckBase
+  id: BaseObviousBadge
+  abstract: true
+  components:
+  - type: WearerGetsExamineText
+    examineText: obvious-desc-proves
+    thing: obvious-thing-badge
+
+- type: entity
+  parent: [ ClothingNeckBase, BaseObviousBadge ]
   id: ClothingNeckDefenseAttorneyBadge
   name: defense attorney badge
   description: A badge to show that the owner is a 'legitimate' defense attorney who passed the NT bar exam required to practice law.
@@ -10,6 +18,8 @@
     sprite: _Impstation/Clothing/Neck/Misc/defenseattorneybadge.rsi
   - type: TypingIndicatorClothing
     proto: lawyerblue
+  - type: WearerGetsExamineText
+    thingType: obvious-type-law-defense
 
 # Senior lawyer armbands
 - type: entity
@@ -22,6 +32,8 @@
     sprite: _Impstation/Clothing/Neck/Misc/lawyerarmband.rsi
   - type: Clothing
     sprite: _Impstation/Clothing/Neck/Misc/lawyerarmband.rsi
+  - type: WearerGetsExamineText
+    thing: obvious-thing-armband
 
 - type: entity
   parent: ClothingNeckProsecutorbadge
@@ -33,6 +45,8 @@
     sprite: _Impstation/Clothing/Neck/Misc/prosecutorarmband.rsi
   - type: Clothing
     sprite: _Impstation/Clothing/Neck/Misc/prosecutorarmband.rsi
+  - type: WearerGetsExamineText
+    thing: obvious-thing-armband
 
 - type: entity
   parent: ClothingNeckDefenseAttorneyBadge
@@ -46,6 +60,8 @@
     sprite: _Impstation/Clothing/Neck/Misc/defenseattorneyarmband.rsi
   - type: TypingIndicatorClothing
     proto: lawyerblue
+  - type: WearerGetsExamineText
+    thing: obvious-thing-armband
 
 # your angel or your devil
 - type: entity
@@ -69,4 +85,3 @@
     sprite: _Impstation/Clothing/Neck/Misc/devilwings.rsi
   - type: Clothing
     sprite: _Impstation/Clothing/Neck/Misc/devilwings.rsi
-  
diff --git a/Resources/Prototypes/_Impstation/Entities/Clothing/Neck/pins.yml b/Resources/Prototypes/_Impstation/Entities/Clothing/Neck/pins.yml
index 9e6837651849b..42458d06f1429 100644
--- a/Resources/Prototypes/_Impstation/Entities/Clothing/Neck/pins.yml
+++ b/Resources/Prototypes/_Impstation/Entities/Clothing/Neck/pins.yml
@@ -10,3 +10,5 @@
   - type: Clothing
     sprite: _Impstation/Clothing/Neck/Misc/pins.rsi
     equippedPrefix: straight
+  - type: WearerGetsExamineText
+    examineText: obvious-x-pin-straight
diff --git a/Resources/Prototypes/_Impstation/Entities/Clothing/Neck/scarfs.yml b/Resources/Prototypes/_Impstation/Entities/Clothing/Neck/scarfs.yml
index 870e52e27c594..b232411905774 100644
--- a/Resources/Prototypes/_Impstation/Entities/Clothing/Neck/scarfs.yml
+++ b/Resources/Prototypes/_Impstation/Entities/Clothing/Neck/scarfs.yml
@@ -1,3 +1,11 @@
+- type: entity
+  id: BaseObviousScarf
+  abstract: true
+  components:
+  - type: WearerGetsExamineText
+    examineText: obvious-desc-colors
+    thing: obvious-thing-scarf
+
 - type: entity
   parent: ClothingScarfBase
   id: ClothingNeckScarfStripedLimeGreen


Reviews:
user: login: Darkmajia
state: DISMISSED
author_association: NONE
submitted_at: 2025-05-22T14:56:14Z
user: login: jtfg-dev
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-23T18:33:01Z
user: login: jtfg-dev
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-23T18:37:29Z
user: login: jtfg-dev
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-23T18:40:53Z
user: login: jtfg-dev
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-23T19:00:24Z
user: login: widgetbeck
body: loogs good to me
state: APPROVED
author_association: NONE
submitted_at: 2025-05-26T23:12:12Z

Review comments:
diff_hunk: @@ -0,0 +1,54 @@
+using Robust.Shared.GameStates;
+using Content.Shared.Clothing.EntitySystems;
path: Content.Shared/_Impstation/Clothing/Obvious/WearerGetsExamineTextComponent.cs
user: login: Darkmajia
body: unused
created_at: 2025-05-22T14:16:00Z
author_association: NONE
diff_hunk: @@ -0,0 +1,54 @@
+using Robust.Shared.GameStates;
+using Content.Shared.Clothing.EntitySystems;
+
+namespace Content.Shared._Impstation.Obvious;
path: Content.Shared/_Impstation/Clothing/Obvious/WearerGetsExamineTextComponent.cs
user: login: Darkmajia
body: the folder structure for this would make `_Impstation.Clothing.Obvious` preferred, though obvious is a bit un-descriptive. maybe just `Clothing/ExamineText` as a folder structure? or WearerExamineText? or.. i dunno. fuc
created_at: 2025-05-22T14:20:39Z
author_association: NONE
diff_hunk: @@ -0,0 +1,113 @@
+using Content.Shared.Clothing.Components;
+using Content.Shared.Examine;
+using Content.Shared.IdentityManagement;
+using Content.Shared.Inventory.Events;
+using Content.Shared.Contraband;
+using System.Net;
+using Content.Shared.Destructible;
path: Content.Shared/_Impstation/Clothing/Obvious/WearerGetsExamineTextSystem.cs
user: login: Darkmajia
body: unused
created_at: 2025-05-22T14:23:04Z
author_association: NONE
diff_hunk: @@ -0,0 +1,113 @@
+using Content.Shared.Clothing.Components;
+using Content.Shared.Examine;
+using Content.Shared.IdentityManagement;
+using Content.Shared.Inventory.Events;
+using Content.Shared.Contraband;
+using System.Net;
path: Content.Shared/_Impstation/Clothing/Obvious/WearerGetsExamineTextSystem.cs
user: login: Darkmajia
body: unused
created_at: 2025-05-22T14:23:09Z
author_association: NONE
diff_hunk: @@ -0,0 +1,113 @@
+using Content.Shared.Clothing.Components;
+using Content.Shared.Examine;
+using Content.Shared.IdentityManagement;
+using Content.Shared.Inventory.Events;
+using Content.Shared.Contraband;
+using System.Net;
+using Content.Shared.Destructible;
+
+namespace Content.Shared._Impstation.Obvious;
+
+/// <summary>
+/// Adds examine text to the entity that wears item, for making things obvious.
+/// </summary>
+public sealed class WearerGetsExamineTextSystem : EntitySystem
+{
+    [Dependency] private readonly IEntityManager _entManager = default!;
path: Content.Shared/_Impstation/Clothing/Obvious/WearerGetsExamineTextSystem.cs
user: login: Darkmajia
body: unused
created_at: 2025-05-22T14:23:17Z
author_association: NONE
diff_hunk: @@ -0,0 +1,113 @@
+using Content.Shared.Clothing.Components;
+using Content.Shared.Examine;
+using Content.Shared.IdentityManagement;
+using Content.Shared.Inventory.Events;
+using Content.Shared.Contraband;
+using System.Net;
+using Content.Shared.Destructible;
+
+namespace Content.Shared._Impstation.Obvious;
+
+/// <summary>
+/// Adds examine text to the entity that wears item, for making things obvious.
+/// </summary>
+public sealed class WearerGetsExamineTextSystem : EntitySystem
+{
+    [Dependency] private readonly IEntityManager _entManager = default!;
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<WearerGetsExamineTextComponent, GotEquippedEvent>(OnEquipped);
+        SubscribeLocalEvent<WearerGetsExamineTextComponent, GotUnequippedEvent>(OnUnequipped);
+        SubscribeLocalEvent<WearerGetsExamineTextComponent, ExaminedEvent>(OnExamine);
+    }
+
+    private void OnEquipped(Entity<WearerGetsExamineTextComponent> entity, ref GotEquippedEvent args)
+    {
+        if (!TryComp(entity, out ClothingComponent? clothing))
+            return;
+        var isCorrectSlot = (clothing.Slots & args.SlotFlags) != Inventory.SlotFlags.NONE;
+        if (!entity.Comp.PocketEvident) //if it can't be evident in our pockets
+        {
+            // Make sure the clothing item was equipped to the right slot, and not just held in a hand.
+            if (!isCorrectSlot)
+                return;
+        }
+
+        entity.Comp.Wearer = args.Equipee;
+        Dirty(entity);
+
+        //GIVE THEM INSPECT TEXT
+        var obviousExamine = EnsureComp<ExtraExamineTextComponent>(args.Equipee);
+        obviousExamine.Lines.Remove(entity.Comp.ExamineOnWearer); // if prefix needs to be updated
+        obviousExamine.Lines.TryAdd(entity.Comp.ExamineOnWearer,  //using try so that we don't cause an error if we move something from slot to slot
+            isCorrectSlot ? entity.Comp.PrefixExamineOnWearer : "obvious-prefix-have"); // uses a different prefix if worn / displayed
+    }
+
+    private void OnUnequipped(Entity<WearerGetsExamineTextComponent> entity, ref GotUnequippedEvent args)
+    {
+        //REMOVE THE FUCKIN INSPECT TEXT
+        if (entity.Comp.Wearer == null)
+            return;
+
+        var wearer = entity.Comp.Wearer.Value;
path: Content.Shared/_Impstation/Clothing/Obvious/WearerGetsExamineTextSystem.cs
user: login: Darkmajia
body: prefer pattern matching over `.Value` - line 51 can be:
```csharp
if (entity.Comp.Wearer is not { } wearer)
```
to early return if `Wearer` is null, and assign it to `wearer` in one line, without relying on `.Value`
created_at: 2025-05-22T14:34:23Z
author_association: NONE
diff_hunk: @@ -0,0 +1,113 @@
+using Content.Shared.Clothing.Components;
+using Content.Shared.Examine;
+using Content.Shared.IdentityManagement;
+using Content.Shared.Inventory.Events;
+using Content.Shared.Contraband;
+using System.Net;
+using Content.Shared.Destructible;
+
+namespace Content.Shared._Impstation.Obvious;
+
+/// <summary>
+/// Adds examine text to the entity that wears item, for making things obvious.
+/// </summary>
+public sealed class WearerGetsExamineTextSystem : EntitySystem
+{
+    [Dependency] private readonly IEntityManager _entManager = default!;
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<WearerGetsExamineTextComponent, GotEquippedEvent>(OnEquipped);
+        SubscribeLocalEvent<WearerGetsExamineTextComponent, GotUnequippedEvent>(OnUnequipped);
+        SubscribeLocalEvent<WearerGetsExamineTextComponent, ExaminedEvent>(OnExamine);
+    }
+
+    private void OnEquipped(Entity<WearerGetsExamineTextComponent> entity, ref GotEquippedEvent args)
+    {
+        if (!TryComp(entity, out ClothingComponent? clothing))
+            return;
+        var isCorrectSlot = (clothing.Slots & args.SlotFlags) != Inventory.SlotFlags.NONE;
+        if (!entity.Comp.PocketEvident) //if it can't be evident in our pockets
+        {
+            // Make sure the clothing item was equipped to the right slot, and not just held in a hand.
+            if (!isCorrectSlot)
+                return;
+        }
+
+        entity.Comp.Wearer = args.Equipee;
+        Dirty(entity);
+
+        //GIVE THEM INSPECT TEXT
+        var obviousExamine = EnsureComp<ExtraExamineTextComponent>(args.Equipee);
+        obviousExamine.Lines.Remove(entity.Comp.ExamineOnWearer); // if prefix needs to be updated
+        obviousExamine.Lines.TryAdd(entity.Comp.ExamineOnWearer,  //using try so that we don't cause an error if we move something from slot to slot
+            isCorrectSlot ? entity.Comp.PrefixExamineOnWearer : "obvious-prefix-have"); // uses a different prefix if worn / displayed
+    }
+
+    private void OnUnequipped(Entity<WearerGetsExamineTextComponent> entity, ref GotUnequippedEvent args)
+    {
+        //REMOVE THE FUCKIN INSPECT TEXT
+        if (entity.Comp.Wearer == null)
+            return;
+
+        var wearer = entity.Comp.Wearer.Value;
+        if (TryComp(wearer, out ExtraExamineTextComponent? obviousExamine))
+        {
+            obviousExamine.Lines.Remove(entity.Comp.ExamineOnWearer);
+        }
+
+        entity.Comp.Wearer = null;
+        Dirty(entity);
+    }
+
+    private void OnExamine(Entity<WearerGetsExamineTextComponent> entity, ref ExaminedEvent args)
+    {
+        var currentlyWorn = entity.Comp.Wearer != null;
+        var contraString = "";
+        var outString = "";
+        outString = Loc.GetString(currentlyWorn ? "obvious-on-item-currently" : "obvious-on-item",
path: Content.Shared/_Impstation/Clothing/Obvious/WearerGetsExamineTextSystem.cs
user: login: Darkmajia
body: could just assign this in the line above, no?
created_at: 2025-05-22T14:39:28Z
author_association: NONE
diff_hunk: @@ -0,0 +1,113 @@
+using Content.Shared.Clothing.Components;
+using Content.Shared.Examine;
+using Content.Shared.IdentityManagement;
+using Content.Shared.Inventory.Events;
+using Content.Shared.Contraband;
+using System.Net;
+using Content.Shared.Destructible;
+
+namespace Content.Shared._Impstation.Obvious;
+
+/// <summary>
+/// Adds examine text to the entity that wears item, for making things obvious.
+/// </summary>
+public sealed class WearerGetsExamineTextSystem : EntitySystem
+{
+    [Dependency] private readonly IEntityManager _entManager = default!;
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<WearerGetsExamineTextComponent, GotEquippedEvent>(OnEquipped);
+        SubscribeLocalEvent<WearerGetsExamineTextComponent, GotUnequippedEvent>(OnUnequipped);
+        SubscribeLocalEvent<WearerGetsExamineTextComponent, ExaminedEvent>(OnExamine);
+    }
+
+    private void OnEquipped(Entity<WearerGetsExamineTextComponent> entity, ref GotEquippedEvent args)
+    {
+        if (!TryComp(entity, out ClothingComponent? clothing))
+            return;
+        var isCorrectSlot = (clothing.Slots & args.SlotFlags) != Inventory.SlotFlags.NONE;
+        if (!entity.Comp.PocketEvident) //if it can't be evident in our pockets
+        {
+            // Make sure the clothing item was equipped to the right slot, and not just held in a hand.
+            if (!isCorrectSlot)
+                return;
+        }
+
+        entity.Comp.Wearer = args.Equipee;
+        Dirty(entity);
+
+        //GIVE THEM INSPECT TEXT
+        var obviousExamine = EnsureComp<ExtraExamineTextComponent>(args.Equipee);
+        obviousExamine.Lines.Remove(entity.Comp.ExamineOnWearer); // if prefix needs to be updated
+        obviousExamine.Lines.TryAdd(entity.Comp.ExamineOnWearer,  //using try so that we don't cause an error if we move something from slot to slot
+            isCorrectSlot ? entity.Comp.PrefixExamineOnWearer : "obvious-prefix-have"); // uses a different prefix if worn / displayed
+    }
+
+    private void OnUnequipped(Entity<WearerGetsExamineTextComponent> entity, ref GotUnequippedEvent args)
+    {
+        //REMOVE THE FUCKIN INSPECT TEXT
+        if (entity.Comp.Wearer == null)
+            return;
+
+        var wearer = entity.Comp.Wearer.Value;
+        if (TryComp(wearer, out ExtraExamineTextComponent? obviousExamine))
+        {
+            obviousExamine.Lines.Remove(entity.Comp.ExamineOnWearer);
+        }
+
+        entity.Comp.Wearer = null;
+        Dirty(entity);
+    }
+
+    private void OnExamine(Entity<WearerGetsExamineTextComponent> entity, ref ExaminedEvent args)
+    {
+        var currentlyWorn = entity.Comp.Wearer != null;
+        var contraString = "";
+        var outString = "";
+        outString = Loc.GetString(currentlyWorn ? "obvious-on-item-currently" : "obvious-on-item",
+            ("used", Loc.GetString(entity.Comp.PocketEvident ? "obvious-type-pockets" : "obvious-type-default")),
+            ("thing", entity.Comp.Thing),
+            ("me", Identity.Entity(entity, EntityManager)));
+        if (entity.Comp.WarnExamine && !currentlyWorn && TryComp(entity, out ContrabandComponent? contra))
+        {
+            switch (contra.Severity) // apply additional text if the item is contraband to note that displaying it might be really bad
path: Content.Shared/_Impstation/Clothing/Obvious/WearerGetsExamineTextSystem.cs
user: login: Darkmajia
body: might be worth just appending the contraband severity to the end of "obvious-on-item-contra-" so this doesn't have to be manually expanded in the case of additional contraband severities
created_at: 2025-05-22T14:42:08Z
author_association: NONE
diff_hunk: @@ -0,0 +1,113 @@
+using Content.Shared.Clothing.Components;
+using Content.Shared.Examine;
+using Content.Shared.IdentityManagement;
+using Content.Shared.Inventory.Events;
+using Content.Shared.Contraband;
+using System.Net;
+using Content.Shared.Destructible;
+
+namespace Content.Shared._Impstation.Obvious;
+
+/// <summary>
+/// Adds examine text to the entity that wears item, for making things obvious.
+/// </summary>
+public sealed class WearerGetsExamineTextSystem : EntitySystem
+{
+    [Dependency] private readonly IEntityManager _entManager = default!;
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<WearerGetsExamineTextComponent, GotEquippedEvent>(OnEquipped);
+        SubscribeLocalEvent<WearerGetsExamineTextComponent, GotUnequippedEvent>(OnUnequipped);
+        SubscribeLocalEvent<WearerGetsExamineTextComponent, ExaminedEvent>(OnExamine);
+    }
+
+    private void OnEquipped(Entity<WearerGetsExamineTextComponent> entity, ref GotEquippedEvent args)
+    {
+        if (!TryComp(entity, out ClothingComponent? clothing))
+            return;
+        var isCorrectSlot = (clothing.Slots & args.SlotFlags) != Inventory.SlotFlags.NONE;
+        if (!entity.Comp.PocketEvident) //if it can't be evident in our pockets
+        {
+            // Make sure the clothing item was equipped to the right slot, and not just held in a hand.
+            if (!isCorrectSlot)
+                return;
+        }
+
+        entity.Comp.Wearer = args.Equipee;
+        Dirty(entity);
+
+        //GIVE THEM INSPECT TEXT
+        var obviousExamine = EnsureComp<ExtraExamineTextComponent>(args.Equipee);
+        obviousExamine.Lines.Remove(entity.Comp.ExamineOnWearer); // if prefix needs to be updated
+        obviousExamine.Lines.TryAdd(entity.Comp.ExamineOnWearer,  //using try so that we don't cause an error if we move something from slot to slot
+            isCorrectSlot ? entity.Comp.PrefixExamineOnWearer : "obvious-prefix-have"); // uses a different prefix if worn / displayed
+    }
+
+    private void OnUnequipped(Entity<WearerGetsExamineTextComponent> entity, ref GotUnequippedEvent args)
+    {
+        //REMOVE THE FUCKIN INSPECT TEXT
+        if (entity.Comp.Wearer == null)
+            return;
+
+        var wearer = entity.Comp.Wearer.Value;
+        if (TryComp(wearer, out ExtraExamineTextComponent? obviousExamine))
+        {
+            obviousExamine.Lines.Remove(entity.Comp.ExamineOnWearer);
+        }
+
+        entity.Comp.Wearer = null;
+        Dirty(entity);
+    }
+
+    private void OnExamine(Entity<WearerGetsExamineTextComponent> entity, ref ExaminedEvent args)
+    {
+        var currentlyWorn = entity.Comp.Wearer != null;
+        var contraString = "";
+        var outString = "";
+        outString = Loc.GetString(currentlyWorn ? "obvious-on-item-currently" : "obvious-on-item",
+            ("used", Loc.GetString(entity.Comp.PocketEvident ? "obvious-type-pockets" : "obvious-type-default")),
+            ("thing", entity.Comp.Thing),
+            ("me", Identity.Entity(entity, EntityManager)));
+        if (entity.Comp.WarnExamine && !currentlyWorn && TryComp(entity, out ContrabandComponent? contra))
+        {
+            switch (contra.Severity) // apply additional text if the item is contraband to note that displaying it might be really bad
+            {
+                case "Syndicate":
+                    contraString = Loc.GetString("obvious-on-item-contra-syndicate");
+                    break;
+                case "Magical":
+                    contraString = Loc.GetString("obvious-on-item-contra-magical");
+                    break;
+                case "Major":
+                    contraString = Loc.GetString("obvious-on-item-contra-major");
+                    break;
+                default:
+                    break;
+            }
+            outString += " " + contraString;
+        }
+
+        if (entity.Comp.WarnExamine)
+        {
+            var affecting = currentlyWorn ? entity.Comp.Wearer.GetValueOrDefault() : args.Examiner;
+
+            var prefix = Loc.GetString(entity.Comp.PocketEvident ? "obvious-prefix-have" : entity.Comp.PrefixExamineOnWearer, // this is hacky but it's less noticeable than having no check here
+                ("user", Identity.Entity(affecting, EntityManager)),
+                ("name", Identity.Name(affecting, EntityManager)));
+            var examine = Loc.GetString(entity.Comp.ExamineOnWearer,
+                ("user", Identity.Entity(affecting, EntityManager)),
+                ("name", Identity.Name(affecting, EntityManager)));
+
+            var warnString = Loc.GetString("obvious-on-item-for-others",
+                ("will", currentlyWorn ? "can" : "will"),
+                ("output", prefix + " " + examine));
+
+            outString += "\n" + warnString;
path: Content.Shared/_Impstation/Clothing/Obvious/WearerGetsExamineTextSystem.cs
user: login: Darkmajia
body: this works fine, for any future prs like this it might be worth looking into `StringBuilder`. i won't ask you to redo all this just to do that, though
created_at: 2025-05-22T14:44:28Z
author_association: NONE
diff_hunk: @@ -0,0 +1,113 @@
+using Content.Shared.Clothing.Components;
+using Content.Shared.Examine;
+using Content.Shared.IdentityManagement;
+using Content.Shared.Inventory.Events;
+using Content.Shared.Contraband;
+using System.Net;
+using Content.Shared.Destructible;
+
+namespace Content.Shared._Impstation.Obvious;
+
+/// <summary>
+/// Adds examine text to the entity that wears item, for making things obvious.
+/// </summary>
+public sealed class WearerGetsExamineTextSystem : EntitySystem
+{
+    [Dependency] private readonly IEntityManager _entManager = default!;
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<WearerGetsExamineTextComponent, GotEquippedEvent>(OnEquipped);
+        SubscribeLocalEvent<WearerGetsExamineTextComponent, GotUnequippedEvent>(OnUnequipped);
+        SubscribeLocalEvent<WearerGetsExamineTextComponent, ExaminedEvent>(OnExamine);
+    }
+
+    private void OnEquipped(Entity<WearerGetsExamineTextComponent> entity, ref GotEquippedEvent args)
+    {
+        if (!TryComp(entity, out ClothingComponent? clothing))
+            return;
+        var isCorrectSlot = (clothing.Slots & args.SlotFlags) != Inventory.SlotFlags.NONE;
+        if (!entity.Comp.PocketEvident) //if it can't be evident in our pockets
+        {
+            // Make sure the clothing item was equipped to the right slot, and not just held in a hand.
+            if (!isCorrectSlot)
+                return;
+        }
+
+        entity.Comp.Wearer = args.Equipee;
+        Dirty(entity);
+
+        //GIVE THEM INSPECT TEXT
+        var obviousExamine = EnsureComp<ExtraExamineTextComponent>(args.Equipee);
+        obviousExamine.Lines.Remove(entity.Comp.ExamineOnWearer); // if prefix needs to be updated
+        obviousExamine.Lines.TryAdd(entity.Comp.ExamineOnWearer,  //using try so that we don't cause an error if we move something from slot to slot
+            isCorrectSlot ? entity.Comp.PrefixExamineOnWearer : "obvious-prefix-have"); // uses a different prefix if worn / displayed
+    }
+
+    private void OnUnequipped(Entity<WearerGetsExamineTextComponent> entity, ref GotUnequippedEvent args)
+    {
+        //REMOVE THE FUCKIN INSPECT TEXT
+        if (entity.Comp.Wearer == null)
+            return;
+
+        var wearer = entity.Comp.Wearer.Value;
+        if (TryComp(wearer, out ExtraExamineTextComponent? obviousExamine))
+        {
+            obviousExamine.Lines.Remove(entity.Comp.ExamineOnWearer);
+        }
+
+        entity.Comp.Wearer = null;
+        Dirty(entity);
+    }
+
+    private void OnExamine(Entity<WearerGetsExamineTextComponent> entity, ref ExaminedEvent args)
+    {
+        var currentlyWorn = entity.Comp.Wearer != null;
+        var contraString = "";
+        var outString = "";
+        outString = Loc.GetString(currentlyWorn ? "obvious-on-item-currently" : "obvious-on-item",
+            ("used", Loc.GetString(entity.Comp.PocketEvident ? "obvious-type-pockets" : "obvious-type-default")),
+            ("thing", entity.Comp.Thing),
+            ("me", Identity.Entity(entity, EntityManager)));
+        if (entity.Comp.WarnExamine && !currentlyWorn && TryComp(entity, out ContrabandComponent? contra))
+        {
+            switch (contra.Severity) // apply additional text if the item is contraband to note that displaying it might be really bad
+            {
+                case "Syndicate":
+                    contraString = Loc.GetString("obvious-on-item-contra-syndicate");
+                    break;
+                case "Magical":
+                    contraString = Loc.GetString("obvious-on-item-contra-magical");
+                    break;
+                case "Major":
+                    contraString = Loc.GetString("obvious-on-item-contra-major");
+                    break;
+                default:
+                    break;
+            }
+            outString += " " + contraString;
+        }
+
+        if (entity.Comp.WarnExamine)
+        {
+            var affecting = currentlyWorn ? entity.Comp.Wearer.GetValueOrDefault() : args.Examiner;
+
+            var prefix = Loc.GetString(entity.Comp.PocketEvident ? "obvious-prefix-have" : entity.Comp.PrefixExamineOnWearer, // this is hacky but it's less noticeable than having no check here
+                ("user", Identity.Entity(affecting, EntityManager)),
+                ("name", Identity.Name(affecting, EntityManager)));
+            var examine = Loc.GetString(entity.Comp.ExamineOnWearer,
+                ("user", Identity.Entity(affecting, EntityManager)),
+                ("name", Identity.Name(affecting, EntityManager)));
+
+            var warnString = Loc.GetString("obvious-on-item-for-others",
+                ("will", currentlyWorn ? "can" : "will"),
+                ("output", prefix + " " + examine));
+
+            outString += "\n" + warnString;
+        }
+
+        args.PushMarkup(outString);
+
path: Content.Shared/_Impstation/Clothing/Obvious/WearerGetsExamineTextSystem.cs
user: login: Darkmajia
body: ouuuhhh my extra newline
created_at: 2025-05-22T14:45:44Z
author_association: NONE
diff_hunk: @@ -0,0 +1,25 @@
+using Robust.Shared.GameStates;
+using Content.Shared.Clothing.EntitySystems;
path: Content.Shared/_Impstation/ObviousExamine/ExtraExamineTextComponent.cs
user: login: Darkmajia
body: unused
created_at: 2025-05-22T14:46:03Z
author_association: NONE
diff_hunk: @@ -0,0 +1,25 @@
+using Robust.Shared.GameStates;
+using Content.Shared.Clothing.EntitySystems;
+
+namespace Content.Shared._Impstation.Obvious;
+
+/// <summary>
+/// Adds examine text to the entity, intentionally "obvious details".
+/// Like, that's it. It's basic -- all it does is add the lines to the attached entity.
+/// This is particularly used for assigning players unique examine text.
+/// </summary>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+[Access(typeof(ExtraExamineTextSystem), typeof(WearerGetsExamineTextSystem))]
+public sealed partial class ExtraExamineTextComponent : Component
+{
+    /// <summary>
+    /// The LocIds that will be added to the attached entity's examination.
+    ///
+    /// The key is the examine text that will be added, and the value is optional prefix text.
+    /// We have to put the specific examine text in front so that keys are unique and so we can remove text when necessary.
+    /// </summary>
+    [DataField, AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)]
path: Content.Shared/_Impstation/ObviousExamine/ExtraExamineTextComponent.cs
user: login: Darkmajia
body: `VVAccess.ReadWrite` is the default for `ViewVariables`, so defining that here is redundant
created_at: 2025-05-22T14:46:50Z
author_association: NONE
diff_hunk: @@ -0,0 +1,25 @@
+using Robust.Shared.GameStates;
+using Content.Shared.Clothing.EntitySystems;
+
+namespace Content.Shared._Impstation.Obvious;
+
+/// <summary>
+/// Adds examine text to the entity, intentionally "obvious details".
+/// Like, that's it. It's basic -- all it does is add the lines to the attached entity.
+/// This is particularly used for assigning players unique examine text.
+/// </summary>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+[Access(typeof(ExtraExamineTextSystem), typeof(WearerGetsExamineTextSystem))]
+public sealed partial class ExtraExamineTextComponent : Component
+{
+    /// <summary>
+    /// The LocIds that will be added to the attached entity's examination.
+    ///
+    /// The key is the examine text that will be added, and the value is optional prefix text.
+    /// We have to put the specific examine text in front so that keys are unique and so we can remove text when necessary.
+    /// </summary>
+    [DataField, AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)]
+
path: Content.Shared/_Impstation/ObviousExamine/ExtraExamineTextComponent.cs
user: login: Darkmajia
body: unnecessary newline here
created_at: 2025-05-22T14:47:06Z
author_association: NONE
diff_hunk: @@ -0,0 +1,37 @@
+using Content.Shared.Examine;
+using Content.Shared.IdentityManagement;
+using Content.Shared._Impstation.Obvious;
+
+namespace Content.Shared._Impstation.Obvious;
+
+/// <summary>
+/// Adds examine text to the entity, intentionally "obvious details".
+/// Like, that's it. It's basic -- all it does is add the line to the attached entity.
+/// This is particularly used for assigning players unique examine text.
+/// </summary>
+public sealed class ExtraExamineTextSystem : EntitySystem
+{
+
path: Content.Shared/_Impstation/ObviousExamine/ExtraExamineTextSystem.cs
user: login: Darkmajia
body: newline!!
created_at: 2025-05-22T14:47:21Z
author_association: NONE
diff_hunk: @@ -0,0 +1,37 @@
+using Content.Shared.Examine;
+using Content.Shared.IdentityManagement;
+using Content.Shared._Impstation.Obvious;
+
+namespace Content.Shared._Impstation.Obvious;
+
+/// <summary>
+/// Adds examine text to the entity, intentionally "obvious details".
+/// Like, that's it. It's basic -- all it does is add the line to the attached entity.
+/// This is particularly used for assigning players unique examine text.
+/// </summary>
+public sealed class ExtraExamineTextSystem : EntitySystem
+{
+
+    [Dependency] private readonly IEntityManager _entManager = default!;
path: Content.Shared/_Impstation/ObviousExamine/ExtraExamineTextSystem.cs
user: login: Darkmajia
body: unused
created_at: 2025-05-22T14:48:59Z
author_association: NONE
diff_hunk: @@ -0,0 +1,37 @@
+using Content.Shared.Examine;
+using Content.Shared.IdentityManagement;
+using Content.Shared._Impstation.Obvious;
+
+namespace Content.Shared._Impstation.Obvious;
+
+/// <summary>
+/// Adds examine text to the entity, intentionally "obvious details".
+/// Like, that's it. It's basic -- all it does is add the line to the attached entity.
+/// This is particularly used for assigning players unique examine text.
+/// </summary>
+public sealed class ExtraExamineTextSystem : EntitySystem
+{
+
+    [Dependency] private readonly IEntityManager _entManager = default!;
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<ExtraExamineTextComponent, ExaminedEvent>(OnExamine);
+    }
+
+    private void OnExamine(Entity<ExtraExamineTextComponent> entity, ref ExaminedEvent args)
+    {
+        if (entity.Comp.Lines.Count != 0)
path: Content.Shared/_Impstation/ObviousExamine/ExtraExamineTextSystem.cs
user: login: Darkmajia
body: rider is yelling at me to invert this (i.e. early return if == 0) to reduce nesting
created_at: 2025-05-22T14:49:54Z
author_association: NONE
diff_hunk: @@ -0,0 +1,37 @@
+using Content.Shared.Examine;
+using Content.Shared.IdentityManagement;
+using Content.Shared._Impstation.Obvious;
+
+namespace Content.Shared._Impstation.Obvious;
+
+/// <summary>
+/// Adds examine text to the entity, intentionally "obvious details".
+/// Like, that's it. It's basic -- all it does is add the line to the attached entity.
+/// This is particularly used for assigning players unique examine text.
+/// </summary>
+public sealed class ExtraExamineTextSystem : EntitySystem
+{
+
+    [Dependency] private readonly IEntityManager _entManager = default!;
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<ExtraExamineTextComponent, ExaminedEvent>(OnExamine);
+    }
+
+    private void OnExamine(Entity<ExtraExamineTextComponent> entity, ref ExaminedEvent args)
+    {
+        if (entity.Comp.Lines.Count != 0)
+        {
+            var prefix = "";
path: Content.Shared/_Impstation/ObviousExamine/ExtraExamineTextSystem.cs
user: login: Darkmajia
body: `prefix` could be assigned inside the foreach loop, rather than being defined outside and reset at the end of each loop
created_at: 2025-05-22T14:51:07Z
author_association: NONE
diff_hunk: @@ -0,0 +1,113 @@
+using Content.Shared.Clothing.Components;
+using Content.Shared.Examine;
+using Content.Shared.IdentityManagement;
+using Content.Shared.Inventory.Events;
+using Content.Shared.Contraband;
+using System.Net;
+using Content.Shared.Destructible;
+
+namespace Content.Shared._Impstation.Obvious;
+
+/// <summary>
+/// Adds examine text to the entity that wears item, for making things obvious.
+/// </summary>
+public sealed class WearerGetsExamineTextSystem : EntitySystem
+{
+    [Dependency] private readonly IEntityManager _entManager = default!;
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<WearerGetsExamineTextComponent, GotEquippedEvent>(OnEquipped);
+        SubscribeLocalEvent<WearerGetsExamineTextComponent, GotUnequippedEvent>(OnUnequipped);
+        SubscribeLocalEvent<WearerGetsExamineTextComponent, ExaminedEvent>(OnExamine);
+    }
+
+    private void OnEquipped(Entity<WearerGetsExamineTextComponent> entity, ref GotEquippedEvent args)
+    {
+        if (!TryComp(entity, out ClothingComponent? clothing))
+            return;
+        var isCorrectSlot = (clothing.Slots & args.SlotFlags) != Inventory.SlotFlags.NONE;
+        if (!entity.Comp.PocketEvident) //if it can't be evident in our pockets
+        {
+            // Make sure the clothing item was equipped to the right slot, and not just held in a hand.
+            if (!isCorrectSlot)
+                return;
+        }
+
+        entity.Comp.Wearer = args.Equipee;
+        Dirty(entity);
+
+        //GIVE THEM INSPECT TEXT
+        var obviousExamine = EnsureComp<ExtraExamineTextComponent>(args.Equipee);
+        obviousExamine.Lines.Remove(entity.Comp.ExamineOnWearer); // if prefix needs to be updated
+        obviousExamine.Lines.TryAdd(entity.Comp.ExamineOnWearer,  //using try so that we don't cause an error if we move something from slot to slot
+            isCorrectSlot ? entity.Comp.PrefixExamineOnWearer : "obvious-prefix-have"); // uses a different prefix if worn / displayed
+    }
+
+    private void OnUnequipped(Entity<WearerGetsExamineTextComponent> entity, ref GotUnequippedEvent args)
+    {
+        //REMOVE THE FUCKIN INSPECT TEXT
+        if (entity.Comp.Wearer == null)
+            return;
+
+        var wearer = entity.Comp.Wearer.Value;
+        if (TryComp(wearer, out ExtraExamineTextComponent? obviousExamine))
+        {
+            obviousExamine.Lines.Remove(entity.Comp.ExamineOnWearer);
path: Content.Shared/_Impstation/Clothing/Obvious/WearerGetsExamineTextSystem.cs
user: login: Darkmajia
body: `ExtraExamineTextComponent` should be removed if no lines remain
created_at: 2025-05-22T14:54:50Z
author_association: NONE
diff_hunk: @@ -0,0 +1,25 @@
+using Robust.Shared.GameStates;
+using Content.Shared.Clothing.EntitySystems;
+
+namespace Content.Shared._Impstation.Obvious;
+
+/// <summary>
+/// Adds examine text to the entity, intentionally "obvious details".
+/// Like, that's it. It's basic -- all it does is add the lines to the attached entity.
+/// This is particularly used for assigning players unique examine text.
+/// </summary>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+[Access(typeof(ExtraExamineTextSystem), typeof(WearerGetsExamineTextSystem))]
+public sealed partial class ExtraExamineTextComponent : Component
path: Content.Shared/_Impstation/ObviousExamine/ExtraExamineTextComponent.cs
user: login: Darkmajia
body: `ExtraExamineText` is a bit vague - this is a clothing component, so maybe ClothingExamineText or something?
created_at: 2025-05-22T14:55:53Z
author_association: NONE
diff_hunk: @@ -0,0 +1,25 @@
+using Robust.Shared.GameStates;
+using Content.Shared.Clothing.EntitySystems;
+
+namespace Content.Shared._Impstation.Obvious;
+
+/// <summary>
+/// Adds examine text to the entity, intentionally "obvious details".
+/// Like, that's it. It's basic -- all it does is add the lines to the attached entity.
+/// This is particularly used for assigning players unique examine text.
+/// </summary>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+[Access(typeof(ExtraExamineTextSystem), typeof(WearerGetsExamineTextSystem))]
+public sealed partial class ExtraExamineTextComponent : Component
path: Content.Shared/_Impstation/ObviousExamine/ExtraExamineTextComponent.cs
user: login: jtfg-dev
body: this specifically is NOT a component that should be applied to Clothing. it applies extra examine text to the attached entity, so i've named it accordingly. discussion happened [here](https://discord.com/channels/1327800873660842097/1328088460434739393/1372951686959730738)
created_at: 2025-05-23T18:33:01Z
author_association: NONE
diff_hunk: @@ -0,0 +1,113 @@
+using Content.Shared.Clothing.Components;
+using Content.Shared.Examine;
+using Content.Shared.IdentityManagement;
+using Content.Shared.Inventory.Events;
+using Content.Shared.Contraband;
+using System.Net;
+using Content.Shared.Destructible;
+
+namespace Content.Shared._Impstation.Obvious;
+
+/// <summary>
+/// Adds examine text to the entity that wears item, for making things obvious.
+/// </summary>
+public sealed class WearerGetsExamineTextSystem : EntitySystem
+{
+    [Dependency] private readonly IEntityManager _entManager = default!;
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<WearerGetsExamineTextComponent, GotEquippedEvent>(OnEquipped);
+        SubscribeLocalEvent<WearerGetsExamineTextComponent, GotUnequippedEvent>(OnUnequipped);
+        SubscribeLocalEvent<WearerGetsExamineTextComponent, ExaminedEvent>(OnExamine);
+    }
+
+    private void OnEquipped(Entity<WearerGetsExamineTextComponent> entity, ref GotEquippedEvent args)
+    {
+        if (!TryComp(entity, out ClothingComponent? clothing))
+            return;
+        var isCorrectSlot = (clothing.Slots & args.SlotFlags) != Inventory.SlotFlags.NONE;
+        if (!entity.Comp.PocketEvident) //if it can't be evident in our pockets
+        {
+            // Make sure the clothing item was equipped to the right slot, and not just held in a hand.
+            if (!isCorrectSlot)
+                return;
+        }
+
+        entity.Comp.Wearer = args.Equipee;
+        Dirty(entity);
+
+        //GIVE THEM INSPECT TEXT
+        var obviousExamine = EnsureComp<ExtraExamineTextComponent>(args.Equipee);
+        obviousExamine.Lines.Remove(entity.Comp.ExamineOnWearer); // if prefix needs to be updated
+        obviousExamine.Lines.TryAdd(entity.Comp.ExamineOnWearer,  //using try so that we don't cause an error if we move something from slot to slot
+            isCorrectSlot ? entity.Comp.PrefixExamineOnWearer : "obvious-prefix-have"); // uses a different prefix if worn / displayed
+    }
+
+    private void OnUnequipped(Entity<WearerGetsExamineTextComponent> entity, ref GotUnequippedEvent args)
+    {
+        //REMOVE THE FUCKIN INSPECT TEXT
+        if (entity.Comp.Wearer == null)
+            return;
+
+        var wearer = entity.Comp.Wearer.Value;
+        if (TryComp(wearer, out ExtraExamineTextComponent? obviousExamine))
+        {
+            obviousExamine.Lines.Remove(entity.Comp.ExamineOnWearer);
+        }
+
+        entity.Comp.Wearer = null;
+        Dirty(entity);
+    }
+
+    private void OnExamine(Entity<WearerGetsExamineTextComponent> entity, ref ExaminedEvent args)
+    {
+        var currentlyWorn = entity.Comp.Wearer != null;
+        var contraString = "";
+        var outString = "";
+        outString = Loc.GetString(currentlyWorn ? "obvious-on-item-currently" : "obvious-on-item",
path: Content.Shared/_Impstation/Clothing/Obvious/WearerGetsExamineTextSystem.cs
user: login: jtfg-dev
body: oops
created_at: 2025-05-23T18:37:29Z
author_association: NONE
diff_hunk: @@ -0,0 +1,54 @@
+using Robust.Shared.GameStates;
+using Content.Shared.Clothing.EntitySystems;
+
+namespace Content.Shared._Impstation.Obvious;
path: Content.Shared/_Impstation/Clothing/Obvious/WearerGetsExamineTextComponent.cs
user: login: jtfg-dev
body: i'll move it out of a subfolder/specific namespace and just into `_Impstation.Clothing`
created_at: 2025-05-23T18:40:53Z
author_association: NONE
diff_hunk: @@ -0,0 +1,113 @@
+using Content.Shared.Clothing.Components;
+using Content.Shared.Examine;
+using Content.Shared.IdentityManagement;
+using Content.Shared.Inventory.Events;
+using Content.Shared.Contraband;
+using System.Net;
+using Content.Shared.Destructible;
+
+namespace Content.Shared._Impstation.Obvious;
+
+/// <summary>
+/// Adds examine text to the entity that wears item, for making things obvious.
+/// </summary>
+public sealed class WearerGetsExamineTextSystem : EntitySystem
+{
+    [Dependency] private readonly IEntityManager _entManager = default!;
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<WearerGetsExamineTextComponent, GotEquippedEvent>(OnEquipped);
+        SubscribeLocalEvent<WearerGetsExamineTextComponent, GotUnequippedEvent>(OnUnequipped);
+        SubscribeLocalEvent<WearerGetsExamineTextComponent, ExaminedEvent>(OnExamine);
+    }
+
+    private void OnEquipped(Entity<WearerGetsExamineTextComponent> entity, ref GotEquippedEvent args)
+    {
+        if (!TryComp(entity, out ClothingComponent? clothing))
+            return;
+        var isCorrectSlot = (clothing.Slots & args.SlotFlags) != Inventory.SlotFlags.NONE;
+        if (!entity.Comp.PocketEvident) //if it can't be evident in our pockets
+        {
+            // Make sure the clothing item was equipped to the right slot, and not just held in a hand.
+            if (!isCorrectSlot)
+                return;
+        }
+
+        entity.Comp.Wearer = args.Equipee;
+        Dirty(entity);
+
+        //GIVE THEM INSPECT TEXT
+        var obviousExamine = EnsureComp<ExtraExamineTextComponent>(args.Equipee);
+        obviousExamine.Lines.Remove(entity.Comp.ExamineOnWearer); // if prefix needs to be updated
+        obviousExamine.Lines.TryAdd(entity.Comp.ExamineOnWearer,  //using try so that we don't cause an error if we move something from slot to slot
+            isCorrectSlot ? entity.Comp.PrefixExamineOnWearer : "obvious-prefix-have"); // uses a different prefix if worn / displayed
+    }
+
+    private void OnUnequipped(Entity<WearerGetsExamineTextComponent> entity, ref GotUnequippedEvent args)
+    {
+        //REMOVE THE FUCKIN INSPECT TEXT
+        if (entity.Comp.Wearer == null)
+            return;
+
+        var wearer = entity.Comp.Wearer.Value;
+        if (TryComp(wearer, out ExtraExamineTextComponent? obviousExamine))
+        {
+            obviousExamine.Lines.Remove(entity.Comp.ExamineOnWearer);
path: Content.Shared/_Impstation/Clothing/Obvious/WearerGetsExamineTextSystem.cs
user: login: jtfg-dev
body: now handled in ExtraExamineTextSystem 😄 
created_at: 2025-05-23T19:00:24Z
author_association: NONE

PR comments:
user: login: jtfg-dev
created_at: 2025-05-14T16:47:59Z
body: i'm going to be out until sunday, but someone else could easily finish this PR; the only thing that needs to be done is adding flag colors to the pride pins in `obvious.ftl`.
author_association: NONE
user: login: jtfg-dev
created_at: 2025-05-21T18:03:40Z
body: ![image](https://github.com/user-attachments/assets/5a51bc41-6cdd-4fec-a771-c98e7fd28f74)
feature creep! woo
author_association: NONE
user: login: jtfg-dev
created_at: 2025-05-21T18:55:50Z
body: Pretty happy with this as-is. Ready for review. Some of the parenting / redundant components could be better, but I didn't want to reparent an upstream item if I could help it, just to avoid writing one string multiple times.
author_association: NONE
user: login: Darkmajia
created_at: 2025-05-22T14:59:14Z
body: haven't looked over the yaml or locales, very cute feature tho. while i like the flag colours on the examine text, it might be a little distracting when the rest of the text in-game is pretty clearly.. blocked out, if that makes sense? i dunno, i dont mind too much either way
author_association: NONE
user: login: jtfg-dev
created_at: 2025-05-22T15:03:53Z
body: thank you for the reviews! i will go over them shortly

and yeah those're mostly just to catch eyes. it's half preparation since the novice mark should do the same thing
author_association: NONE
user: login: widgetbeck
created_at: 2025-05-23T18:57:58Z
body: personally I'd vouch against the flag colors. if you wanna do colors for the key words, I'd recommend (just for legibility, i'm dyslexic) picking a single color from the relevant flag and making the whole word that color
author_association: NONE
user: login: jtfg-dev
created_at: 2025-05-23T18:59:52Z
body: > personally I'd vouch against the flag colors. if you wanna do colors for the key words, I'd recommend (just for legibility, i'm dyslexic) picking a single color from the relevant flag and making the whole word that color

copy -- i did try to make the colors as readable as possible but honestly fine with massively reducing it

author_association: NONE
user: login: jtfg-dev
created_at: 2025-05-23T22:54:10Z
body: really long changelog but 
- moved ExtraExamineText in with DetailedInspect to form a new "`_Impstation.Examine`" namespace and moved to a subfolder accordingly
- moved WearerGetsExamineText to `_Impstation.Clothing`
- addressed review
- made some abstract entities to minimize copied text
- addressed concerns about the colors (every item now gets per-word coloring rather than multiple colors per word, with the singular exception of long bacon, since. it's cute)
- made grammar more flexible
- rewrote how the examine text is generated so that it's more responsive and less code is copied
- used string builder for the complicated part (thanks Dark)
- refactored basically the entire system to make future data entry with this component easier. now various words about the obvious item are inheritable, so filling in the missing pride items (as one example) will be easy as hell
author_association: NONE