Repository: Simple-Station/Einstein-Engines
Pull request #1862: CharacterRequirements on ExtendDescriptions
Author: login: Timfa2112
Labels: name: Changes: C#
description: Changes any cs files
name: Changes: Localization
description: Changes any ftl files
name: Changes: YML
description: Changes any yml files
name: Status: Needs Review
description: Someone please review this
Created: 2025-03-03T00:13:45Z | Merged: 2025-03-07T17:09:03Z

<!--
This is a semi-strict format, you can add/remove sections as needed but the order/format should be kept the same
Remove these comments before submitting
-->

# Description

<!--
Explain this PR in as much detail as applicable

Some example prompts to consider:
How might this affect the game? The codebase?
What might be some alternatives to this?
How/Who does this benefit/hurt [the game/codebase]?
-->

This PR does not have any effects on the game from a player-perspective.
It does, however, allow us to add CharacterRequirements to ExtendDescriptions, which allows us to add contextual information to items that only show up if characters know about them, for example. It has an optional field that can also show text if your character does _not_ meet requirements.

---

# TODO

<!--
A list of everything you have to do before this PR is "complete"
You probably won't have to complete everything before merging but it's good to leave future references
-->

- [x] Add a bunch of CharacterRequirements to new and existing ExtendDescriptions for contraband or other neat info

---

<!--
This is default collapsed, readers click to expand it and see all your media
The PR media section can get very large at times, so this is a good way to keep it clean
The title is written using HTML tags
The title must be within the <summary> tags or you won't see it
-->

<details><summary><h1>Media</h1></summary>
<p>
Example of how to add a requirement:

![image](https://github.com/user-attachments/assets/18c105f0-550b-410a-b0be-15e5c8f8a73f)


https://github.com/user-attachments/assets/67ad6ecd-1886-4f71-85c0-fdd035a9f5c9

![image](https://github.com/user-attachments/assets/8195b744-ec6b-4b69-bc9e-a86443847239)


</p>
</details>

---

# Changelog

<!--
You can add an author after the `:cl:` to change the name that appears in the changelog (ex: `:cl: Death`)
Leaving it blank will default to your GitHub display name
This includes all available types for the changelog
-->

:cl:
- tweak: Tweaked Extended Descriptions to be able to require CharacterRequirements before being shown to the player. Currently not actually implemented anywhere except for the emag and some posters.


<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

## Summary by CodeRabbit

- **New Features**
  - Enhanced character creation and role-validation systems now incorporate playtime tracking and additional criteria, providing a more tailored experience.
  - In-game items—such as hacking devices, weapons, and posters—feature extended, lore-rich descriptions that adjust based on character attributes.
  - New localized texts enrich the narrative by offering clear feedback when character requirements are or aren’t met.
  - New character requirements related to antagonists and mindshields have been introduced, enhancing gameplay dynamics.
  - A new method for validating character requirements has been added, improving the accuracy of checks during character creation.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/Content.Client/Players/PlayTimeTracking/JobRequirementsManager.cs b/Content.Client/Players/PlayTimeTracking/JobRequirementsManager.cs
index 9929267aee8..dda6f436b29 100644
--- a/Content.Client/Players/PlayTimeTracking/JobRequirementsManager.cs
+++ b/Content.Client/Players/PlayTimeTracking/JobRequirementsManager.cs
@@ -41,6 +41,12 @@ public void Initialize()
         _client.RunLevelChanged += ClientOnRunLevelChanged;
     }
 
+    public bool TryGetTrackerTimes(ICommonSession id, [NotNullWhen(true)] out Dictionary<string, TimeSpan>? time)
+    {
+        time = new(_roles);
+        return true;
+    }
+
     private void ClientOnRunLevelChanged(object? sender, RunLevelChangedEventArgs e)
     {
         if (e.NewLevel == ClientRunLevel.Initialize)
diff --git a/Content.Shared/Customization/Systems/CharacterRequirements.Antagonist.cs b/Content.Shared/Customization/Systems/CharacterRequirements.Antagonist.cs
new file mode 100644
index 00000000000..ccc5876dbb8
--- /dev/null
+++ b/Content.Shared/Customization/Systems/CharacterRequirements.Antagonist.cs
@@ -0,0 +1,51 @@
+using System.Linq;
+using Content.Shared.Mind;
+using Content.Shared.Preferences;
+using Content.Shared.Roles;
+using JetBrains.Annotations;
+using Robust.Shared.Configuration;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.Customization.Systems;
+
+/// <summary>
+///     Requires the player to be a specific antagonist
+/// </summary>
+[UsedImplicitly]
+[Serializable, NetSerializable]
+public sealed partial class CharacterAntagonistRequirement : CharacterRequirement
+{
+    [DataField(required: true)]
+    public List<ProtoId<AntagPrototype>> Antagonists;
+
+    public override bool IsValid(JobPrototype job,
+        HumanoidCharacterProfile profile,
+        Dictionary<string, TimeSpan> playTimes,
+        bool whitelisted,
+        IPrototype prototype,
+        IEntityManager entityManager,
+        IPrototypeManager prototypeManager,
+        IConfigurationManager configManager,
+        out string? reason,
+        int depth = 0,
+        MindComponent? mind = null)
+    {
+        // Considering this will not be used in the character creation menu, players will likely never see this text.
+        reason = Loc.GetString("character-antagonist-requirement", ("inverted", Inverted));
+
+        if (mind == null)
+            return false;
+
+        foreach (var mindRoleComponent in mind.MindRoles.Select(entityManager.GetComponent<MindRoleComponent>))
+        {
+            if (!mindRoleComponent.AntagPrototype.HasValue)
+                continue;
+
+            if (Antagonists.Contains(mindRoleComponent.AntagPrototype.Value))
+                return !Inverted;
+        }
+
+        return Inverted;
+    }
+}
diff --git a/Content.Shared/Customization/Systems/CharacterRequirements.Job.cs b/Content.Shared/Customization/Systems/CharacterRequirements.Job.cs
index c57d870a377..d9a04ed38a2 100644
--- a/Content.Shared/Customization/Systems/CharacterRequirements.Job.cs
+++ b/Content.Shared/Customization/Systems/CharacterRequirements.Job.cs
@@ -1,5 +1,6 @@
 using System.Linq;
 using Content.Shared.CCVar;
+using Content.Shared.Mind;
 using Content.Shared.Players.PlayTimeTracking;
 using Content.Shared.Preferences;
 using Content.Shared.Roles;
@@ -32,7 +33,8 @@ public override bool IsValid(JobPrototype job,
         IPrototypeManager prototypeManager,
         IConfigurationManager configManager,
         out string? reason,
-        int depth = 0)
+        int depth = 0,
+        MindComponent? mind = null)
     {
         var jobs = new List<string>();
         var depts = prototypeManager.EnumeratePrototypes<DepartmentPrototype>();
@@ -83,7 +85,8 @@ public override bool IsValid(JobPrototype job,
         IPrototypeManager prototypeManager,
         IConfigurationManager configManager,
         out string? reason,
-        int depth = 0)
+        int depth = 0,
+        MindComponent? mind = null)
     {
         var departments = new List<string>();
 
@@ -130,7 +133,8 @@ public override bool IsValid(JobPrototype job,
         IPrototypeManager prototypeManager,
         IConfigurationManager configManager,
         out string? reason,
-        int depth = 0)
+        int depth = 0,
+        MindComponent? mind = null)
     {
         // Disable the requirement if the role timers are disabled
         if (!configManager.GetCVar(CCVars.GameRoleTimers))
@@ -202,7 +206,8 @@ public override bool IsValid(JobPrototype job,
         IPrototypeManager prototypeManager,
         IConfigurationManager configManager,
         out string? reason,
-        int depth = 0)
+        int depth = 0,
+        MindComponent? mind = null)
     {
         // Disable the requirement if the role timers are disabled
         if (!configManager.GetCVar(CCVars.GameRoleTimers))
@@ -264,7 +269,8 @@ public override bool IsValid(JobPrototype job,
         IPrototypeManager prototypeManager,
         IConfigurationManager configManager,
         out string? reason,
-        int depth = 0)
+        int depth = 0,
+        MindComponent? mind = null)
     {
         // Disable the requirement if the role timers are disabled
         if (!configManager.GetCVar(CCVars.GameRoleTimers))
diff --git a/Content.Shared/Customization/Systems/CharacterRequirements.Logic.cs b/Content.Shared/Customization/Systems/CharacterRequirements.Logic.cs
index 4f6f96bc650..dd60b05a204 100644
--- a/Content.Shared/Customization/Systems/CharacterRequirements.Logic.cs
+++ b/Content.Shared/Customization/Systems/CharacterRequirements.Logic.cs
@@ -1,5 +1,6 @@
 using System.Linq;
 using System.Text;
+using Content.Shared.Mind;
 using Content.Shared.Preferences;
 using Content.Shared.Roles;
 using JetBrains.Annotations;
@@ -30,7 +31,8 @@ public override bool IsValid(JobPrototype job,
         IPrototypeManager prototypeManager,
         IConfigurationManager configManager,
         out string? reason,
-        int depth = 0)
+        int depth = 0,
+        MindComponent? mind = null)
     {
         var succeeded = entityManager.EntitySysManager.GetEntitySystem<CharacterRequirementsSystem>()
             .CheckRequirementsValid(Requirements, job, profile, playTimes, whitelisted, prototype, entityManager,
@@ -72,7 +74,8 @@ public override bool IsValid(JobPrototype job,
         IPrototypeManager prototypeManager,
         IConfigurationManager configManager,
         out string? reason,
-        int depth = 0)
+        int depth = 0,
+        MindComponent? mind = null)
     {
         var succeeded = false;
         var reasons = new List<string>();
@@ -127,7 +130,8 @@ public override bool IsValid(JobPrototype job,
         IPrototypeManager prototypeManager,
         IConfigurationManager configManager,
         out string? reason,
-        int depth = 0)
+        int depth = 0,
+        MindComponent? mind = null)
     {
         var reasons = new List<string>();
         var succeeded = false;
diff --git a/Content.Shared/Customization/Systems/CharacterRequirements.Mindshield.cs b/Content.Shared/Customization/Systems/CharacterRequirements.Mindshield.cs
new file mode 100644
index 00000000000..e2c2b172130
--- /dev/null
+++ b/Content.Shared/Customization/Systems/CharacterRequirements.Mindshield.cs
@@ -0,0 +1,40 @@
+using System.Linq;
+using Content.Shared.Implants.Components;
+using Content.Shared.Mind;
+using Content.Shared.Mindshield.Components;
+using Content.Shared.Preferences;
+using Content.Shared.Roles;
+using JetBrains.Annotations;
+using Robust.Shared.Configuration;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.Customization.Systems;
+
+/// <summary>
+///     Requires the player to have a mindshield
+/// </summary>
+[UsedImplicitly]
+[Serializable, NetSerializable]
+public sealed partial class CharacterMindshieldRequirement : CharacterRequirement
+{
+    public override bool IsValid(JobPrototype job,
+        HumanoidCharacterProfile profile,
+        Dictionary<string, TimeSpan> playTimes,
+        bool whitelisted,
+        IPrototype prototype,
+        IEntityManager entityManager,
+        IPrototypeManager prototypeManager,
+        IConfigurationManager configManager,
+        out string? reason,
+        int depth = 0,
+        MindComponent? mind = null)
+    {
+        reason = Loc.GetString("character-mindshield-requirement", ("inverted", Inverted));
+
+        if (mind == null)
+            return false;
+
+        return entityManager.HasComponent<MindShieldComponent>(mind.CurrentEntity) != Inverted;
+    }
+}
diff --git a/Content.Shared/Customization/Systems/CharacterRequirements.Misc.cs b/Content.Shared/Customization/Systems/CharacterRequirements.Misc.cs
index f13cb94f397..e1432002145 100644
--- a/Content.Shared/Customization/Systems/CharacterRequirements.Misc.cs
+++ b/Content.Shared/Customization/Systems/CharacterRequirements.Misc.cs
@@ -1,4 +1,5 @@
 using Content.Shared.Customization.Systems;
+using Content.Shared.Mind;
 using Content.Shared.Preferences;
 using Content.Shared.Roles;
 using JetBrains.Annotations;
@@ -9,7 +10,7 @@
 namespace Content.Shared.Customization.Systems;
 
 /// <summary>
-///     Requires the server to have a specific CVar value. 
+///     Requires the server to have a specific CVar value.
 /// </summary>
 [UsedImplicitly, Serializable, NetSerializable,]
 public sealed partial class CVarRequirement : CharacterRequirement
@@ -30,7 +31,8 @@ public override bool IsValid(
         IPrototypeManager prototypeManager,
         IConfigurationManager configManager,
         out string? reason,
-        int depth = 0
+        int depth = 0,
+        MindComponent? mind = null
     )
     {
         if (!configManager.IsCVarRegistered(CVar))
diff --git a/Content.Shared/Customization/Systems/CharacterRequirements.Profile.cs b/Content.Shared/Customization/Systems/CharacterRequirements.Profile.cs
index bf6dff7a041..d52d955a583 100644
--- a/Content.Shared/Customization/Systems/CharacterRequirements.Profile.cs
+++ b/Content.Shared/Customization/Systems/CharacterRequirements.Profile.cs
@@ -2,6 +2,7 @@
 using Content.Shared.Clothing.Loadouts.Prototypes;
 using Content.Shared.Humanoid;
 using Content.Shared.Humanoid.Prototypes;
+using Content.Shared.Mind;
 using Content.Shared.Preferences;
 using Content.Shared.Prototypes;
 using Content.Shared.Roles;
@@ -38,7 +39,8 @@ public override bool IsValid(
         IPrototypeManager prototypeManager,
         IConfigurationManager configManager,
         out string? reason,
-        int depth = 0
+        int depth = 0,
+        MindComponent? mind = null
     )
     {
         var localeString = "";
@@ -76,7 +78,8 @@ public override bool IsValid(
         IPrototypeManager prototypeManager,
         IConfigurationManager configManager,
         out string? reason,
-        int depth = 0
+        int depth = 0,
+        MindComponent? mind = null
     )
     {
         reason = Loc.GetString(
@@ -106,7 +109,8 @@ public override bool IsValid(
         IPrototypeManager prototypeManager,
         IConfigurationManager configManager,
         out string? reason,
-        int depth = 0
+        int depth = 0,
+        MindComponent? mind = null
     )
     {
         reason = Loc.GetString(
@@ -136,7 +140,8 @@ public override bool IsValid(
         IPrototypeManager prototypeManager,
         IConfigurationManager configManager,
         out string? reason,
-        int depth = 0
+        int depth = 0,
+        MindComponent? mind = null
     )
     {
         const string color = "green";
@@ -178,7 +183,8 @@ public override bool IsValid(
         IPrototypeManager prototypeManager,
         IConfigurationManager configManager,
         out string? reason,
-        int depth = 0
+        int depth = 0,
+        MindComponent? mind = null
     )
     {
         const string color = "yellow";
@@ -224,7 +230,8 @@ public override bool IsValid(
         IPrototypeManager prototypeManager,
         IConfigurationManager configManager,
         out string? reason,
-        int depth = 0
+        int depth = 0,
+        MindComponent? mind = null
     )
     {
         const string color = "yellow";
@@ -270,7 +277,8 @@ public override bool IsValid(
         IPrototypeManager prototypeManager,
         IConfigurationManager configManager,
         out string? reason,
-        int depth = 0
+        int depth = 0,
+        MindComponent? mind = null
     )
     {
         const string color = "green";
@@ -320,7 +328,8 @@ public override bool IsValid(
         IPrototypeManager prototypeManager,
         IConfigurationManager configManager,
         out string? reason,
-        int depth = 0
+        int depth = 0,
+        MindComponent? mind = null
     )
     {
         const string color = "lightblue";
@@ -353,7 +362,8 @@ public override bool IsValid(
         IPrototypeManager prototypeManager,
         IConfigurationManager configManager,
         out string? reason,
-        int depth = 0
+        int depth = 0,
+        MindComponent? mind = null
     )
     {
         const string color = "lightblue";
@@ -386,7 +396,8 @@ public override bool IsValid(
         IPrototypeManager prototypeManager,
         IConfigurationManager configManager,
         out string? reason,
-        int depth = 0
+        int depth = 0,
+        MindComponent? mind = null
     )
     {
         var group = prototypeManager.Index(Group);
diff --git a/Content.Shared/Customization/Systems/CharacterRequirements.Whitelist.cs b/Content.Shared/Customization/Systems/CharacterRequirements.Whitelist.cs
index 59f59337323..5e5e4fc6dc5 100644
--- a/Content.Shared/Customization/Systems/CharacterRequirements.Whitelist.cs
+++ b/Content.Shared/Customization/Systems/CharacterRequirements.Whitelist.cs
@@ -1,4 +1,5 @@
 using Content.Shared.CCVar;
+using Content.Shared.Mind;
 using Content.Shared.Preferences;
 using Content.Shared.Roles;
 using JetBrains.Annotations;
@@ -27,7 +28,8 @@ public override bool IsValid(JobPrototype job,
         IPrototypeManager prototypeManager,
         IConfigurationManager configManager,
         out string? reason,
-        int depth = 0)
+        int depth = 0,
+        MindComponent? mind = null)
     {
         reason = null;
         if (!configManager.IsCVarRegistered("whitelist.enabled"))
diff --git a/Content.Shared/Customization/Systems/CharacterRequirements.cs b/Content.Shared/Customization/Systems/CharacterRequirements.cs
index c0acb5ddb8a..f10d15e1a0e 100644
--- a/Content.Shared/Customization/Systems/CharacterRequirements.cs
+++ b/Content.Shared/Customization/Systems/CharacterRequirements.cs
@@ -1,3 +1,4 @@
+using Content.Shared.Mind;
 using Content.Shared.Preferences;
 using Content.Shared.Roles;
 using JetBrains.Annotations;
@@ -36,6 +37,7 @@ public abstract bool IsValid(
         IPrototypeManager prototypeManager,
         IConfigurationManager configManager,
         out string? reason,
-        int depth = 0
+        int depth = 0,
+        MindComponent? mind = null
     );
 }
diff --git a/Content.Shared/Customization/Systems/CharacterRequirementsSystem.cs b/Content.Shared/Customization/Systems/CharacterRequirementsSystem.cs
index 533975f7409..cbf8a25ae60 100644
--- a/Content.Shared/Customization/Systems/CharacterRequirementsSystem.cs
+++ b/Content.Shared/Customization/Systems/CharacterRequirementsSystem.cs
@@ -1,8 +1,12 @@
 using System.Linq;
 using System.Text;
 using Content.Shared.Inventory;
+using Content.Shared.Mind;
+using Content.Shared.Players.PlayTimeTracking;
 using Content.Shared.Preferences;
 using Content.Shared.Roles;
+using Content.Shared.Roles.Jobs;
+using Content.Shared.Station;
 using Robust.Shared.Configuration;
 using Robust.Shared.Prototypes;
 using Robust.Shared.Utility;
@@ -13,7 +17,14 @@ namespace Content.Shared.Customization.Systems;
 public sealed class CharacterRequirementsSystem : EntitySystem
 {
     [Dependency] private readonly InventorySystem _inventory = default!;
+    [Dependency] private readonly SharedJobSystem _jobSystem = default!;
+    [Dependency] private readonly SharedMindSystem _mindSystem = default!;
+    [Dependency] private readonly SharedStationSpawningSystem _stationSpawningSystem = default!;
 
+    [Dependency] private readonly IEntityManager _entManager = default!;
+    [Dependency] private readonly IPrototypeManager _protomanager = default!;
+    [Dependency] private readonly IConfigurationManager _configurationManager = default!;
+    [Dependency] private readonly ISharedPlaytimeManager _playtimeManager = default!;
 
     public bool CheckRequirementValid(CharacterRequirement requirement, JobPrototype job,
         HumanoidCharacterProfile profile, Dictionary<string, TimeSpan> playTimes, bool whitelisted, IPrototype prototype,
@@ -30,10 +41,34 @@ public bool CheckRequirementValid(CharacterRequirement requirement, JobPrototype
                 : !requirement.Inverted;
     }
 
+    /// <summary>
+    ///     Checks if a character entity meets the specified requirements.
+    /// </summary>
+    /// <param name="requirements">The list of requirements to validate.</param>
+    /// <param name="characterUid">The entity ID of the character to check.</param>
+    /// <param name="prototype">The prototype associated with the requirements.</param>
+    /// <param name="reasons">Output list of reasons why requirements weren't met.</param>
+    /// <param name="depth">Current recursion depth for nested requirements.</param>
+    /// <param name="whitelisted">Whether the character is whitelisted.</param>
+    /// <returns>True if all requirements are met, false otherwise.</returns>
+    public bool CheckRequirementsValid(List<CharacterRequirement> requirements, EntityUid characterUid, IPrototype prototype, out List<string> reasons, int depth = 0, bool whitelisted = false)
+    {
+        reasons = new List<string>();
+
+        if (!_mindSystem.TryGetMind(characterUid, out var mindId, out var mind)
+            || mind.Session == null
+            || !_jobSystem.MindTryGetJob(mindId, out var jobPrototype)
+            || !_stationSpawningSystem.GetProfile(characterUid, out var stationSpawningProfile)
+            || !_playtimeManager.TryGetTrackerTimes(mind.Session, out var trackerTimes))
+            return false;
+
+        return CheckRequirementsValid(requirements, jobPrototype, stationSpawningProfile, trackerTimes, whitelisted, prototype, _entManager, _protomanager, _configurationManager, out reasons, depth, mind);
+    }
+
     public bool CheckRequirementsValid(List<CharacterRequirement> requirements, JobPrototype job,
         HumanoidCharacterProfile profile, Dictionary<string, TimeSpan> playTimes, bool whitelisted, IPrototype prototype,
         IEntityManager entityManager, IPrototypeManager prototypeManager, IConfigurationManager configManager,
-        out List<string> reasons, int depth = 0)
+        out List<string> reasons, int depth = 0, MindComponent? mind = null)
     {
         reasons = new List<string>();
         var valid = true;
@@ -44,7 +79,7 @@ public bool CheckRequirementsValid(List<CharacterRequirement> requirements, JobP
             // If it's inverted set valid to false when it's valid
             if (!requirement.IsValid(job, profile, playTimes, whitelisted, prototype,
                 entityManager, prototypeManager, configManager,
-                out var reason, depth))
+                out var reason, depth, mind))
             {
                 if (valid)
                     valid = requirement.Inverted;
diff --git a/Content.Shared/Players/PlayTimeTracking/ISharedPlaytimeManager.cs b/Content.Shared/Players/PlayTimeTracking/ISharedPlaytimeManager.cs
index 03fd040d0ca..23b7f66e094 100644
--- a/Content.Shared/Players/PlayTimeTracking/ISharedPlaytimeManager.cs
+++ b/Content.Shared/Players/PlayTimeTracking/ISharedPlaytimeManager.cs
@@ -1,3 +1,9 @@
+using System.Diagnostics.CodeAnalysis;
+using Robust.Shared.Player;
+
 namespace Content.Shared.Players.PlayTimeTracking;
 
-public interface ISharedPlaytimeManager;
+public interface ISharedPlaytimeManager
+{
+    bool TryGetTrackerTimes(ICommonSession id, [NotNullWhen(true)] out Dictionary<string, TimeSpan>? time);
+}
diff --git a/Content.Shared/Traits/Assorted/Components/ExtendDescriptionComponent.cs b/Content.Shared/Traits/Assorted/Components/ExtendDescriptionComponent.cs
index dca8e10d4b3..444cf65732f 100644
--- a/Content.Shared/Traits/Assorted/Components/ExtendDescriptionComponent.cs
+++ b/Content.Shared/Traits/Assorted/Components/ExtendDescriptionComponent.cs
@@ -1,3 +1,4 @@
+using Content.Shared.Customization.Systems;
 using Robust.Shared.Serialization;
 
 namespace Content.Shared.Traits.Assorted.Components;
@@ -5,9 +6,15 @@ namespace Content.Shared.Traits.Assorted.Components;
 [Serializable, NetSerializable, DataDefinition]
 public sealed partial class DescriptionExtension
 {
+    [DataField]
+    public List<CharacterRequirement>? Requirements;
+
     [DataField]
     public string Description = "";
 
+    [DataField]
+    public string RequirementsNotMetDescription = "";
+
     [DataField]
     public int FontSize = 12;
 
diff --git a/Content.Shared/Traits/Assorted/Systems/ExtendDescriptionSystem.cs b/Content.Shared/Traits/Assorted/Systems/ExtendDescriptionSystem.cs
index 0883da1eb86..364376dc9d1 100644
--- a/Content.Shared/Traits/Assorted/Systems/ExtendDescriptionSystem.cs
+++ b/Content.Shared/Traits/Assorted/Systems/ExtendDescriptionSystem.cs
@@ -1,3 +1,4 @@
+using Content.Shared.Customization.Systems;
 using Content.Shared.Examine;
 using Content.Shared.Traits.Assorted.Components;
 
@@ -5,6 +6,8 @@ namespace Content.Shared.Traits.Assorted.Systems;
 
 public sealed class ExtendDescriptionSystem : EntitySystem
 {
+    [Dependency] private readonly CharacterRequirementsSystem _characterRequirements = default!;
+
     public override void Initialize()
     {
         base.Initialize();
@@ -18,10 +21,15 @@ private void OnExamined(EntityUid uid, ExtendDescriptionComponent component, Exa
 
         foreach (var desc in component.DescriptionList)
         {
-            if (!args.IsInDetailsRange && desc.RequireDetailRange)
+            if (!args.IsInDetailsRange && desc.RequireDetailRange
+                || !TryComp(args.Examiner, out MetaDataComponent? comp) || comp.EntityPrototype == null)
                 continue;
 
-            args.PushMarkup($"[font size ={desc.FontSize}][color={desc.Color}]{Loc.GetString(desc.Description, ("entity", uid))}[/color][/font]");
+            var meetsRequirements = desc.Requirements == null || _characterRequirements.CheckRequirementsValid(desc.Requirements, args.Examiner, comp.EntityPrototype, out _);
+            var description = meetsRequirements ? desc.Description : desc.RequirementsNotMetDescription;
+
+            if(description != string.Empty)
+                args.PushMarkup($"[font size ={desc.FontSize}][color={desc.Color}]{Loc.GetString(description, ("entity", uid))}[/color][/font]");
         }
     }
 }
diff --git a/Resources/Locale/en-US/customization/character-requirements.ftl b/Resources/Locale/en-US/customization/character-requirements.ftl
index ddddbc7440f..a92a36c51be 100644
--- a/Resources/Locale/en-US/customization/character-requirements.ftl
+++ b/Resources/Locale/en-US/customization/character-requirements.ftl
@@ -9,6 +9,16 @@ character-department-requirement = You must{$inverted ->
     *[other]{""}
 } be in one of these departments: {$departments}
 
+character-antagonist-requirement = You must{$inverted ->
+    [true]{" "}not
+    *[other]{""}
+} be an antagonist
+
+character-mindshield-requirement = You must{$inverted ->
+    [true]{" "}not
+    *[other]{""}
+} be mindshielded
+
 character-timer-department-insufficient = You require [color=yellow]{TOSTRING($time, "0")}[/color] more minutes of [color={$departmentColor}]{$department}[/color] department playtime
 character-timer-department-too-high = You require [color=yellow]{TOSTRING($time, "0")}[/color] fewer minutes in [color={$departmentColor}]{$department}[/color] department
 
@@ -153,7 +163,7 @@ character-whitelist-requirement = You must{$inverted ->
 
 ## CVar
 
-character-cvar-requirement = 
+character-cvar-requirement =
     The server must{$inverted ->
     [true]{" "}not
     *[other]{""}
diff --git a/Resources/Locale/en-US/extenddescriptions/items/syndicate/emag.ftl b/Resources/Locale/en-US/extenddescriptions/items/syndicate/emag.ftl
new file mode 100644
index 00000000000..95ca13cd42e
--- /dev/null
+++ b/Resources/Locale/en-US/extenddescriptions/items/syndicate/emag.ftl
@@ -0,0 +1,5 @@
+emag-extenddescription-engineering = An illegal hacking tool used to subvert station electronics.
+emag-extenddescription-security = An advanced illegal hacking tool used by criminal operatives.
+emag-extenddescription-command = A dangerous illegal tool used by hostile operatives.
+emag-extenddescription-epistemics = An illegal hacking tool that can cause targeted damage to circuits, gaining access or modifying behaviour.
+emag-extenddescription-syndicate = The all-in-one hacking solution. The thinking man's lockpick. The iconic EMAG.
diff --git a/Resources/Locale/en-US/extenddescriptions/mindshield.ftl b/Resources/Locale/en-US/extenddescriptions/mindshield.ftl
new file mode 100644
index 00000000000..b7cdee3620e
--- /dev/null
+++ b/Resources/Locale/en-US/extenddescriptions/mindshield.ftl
@@ -0,0 +1,28 @@
+mindshield-extenddescription-emag = I feel an overwhelming urge to turn this item in to Security.
+mindshield-extenddescription-poster-atmosia = Doomed from the start. Nanotrasen cannot be defeated by rebels!
+mindshield-extenddescription-poster-funpolice = The security staff is here for our protection.
+mindshield-extenddescription-poster-syndicaterecruitment = This poster is treasonous! I feel an overwhelming urge to take it down.
+mindshield-extenddescription-poster-contrabandsmoke = Only a fool would take these over the smokes recommended by Nanotrasen!
+mindshield-extenddescription-poster-greytide = Those unwashed masses should be praising Nanotrasen for permitting them on the station.
+mindshield-extenddescription-poster-ripbadger = Anyone with a brain should realize that this story is complete falsehood...
+mindshield-extenddescription-poster-contrabandpower = Foolish. Nanotrasen knows what's best for us.
+mindshield-extenddescription-poster-KosmicheskayaStantsiya = If Nanotrasen says it doesn't exist, it doesn't exist.
+mindshield-extenddescription-poster-rebelsunite = Why would anyone rebel against such a wonderful organisation like Nanotrasen?
+mindshield-extenddescription-poster-freedrone = That drone was a danger to us all. We should be happy CentCom destroyed it.
+mindshield-extenddescription-poster-syndicateencryption = As if anyone could break Nanotrasen secure radio systems!
+mindshield-extenddescription-poster-cybersun600 = Six hundred years too many.
+mindshield-extenddescription-poster-foryoursafety = Heroes of the station.
+mindshield-extenddescription-poster-nanotrasen = I feel an intense urge to salute.
+mindshield-extenddescription-poster-helpothers = The camaraderie Nanotrasen personnel is known for!
+mindshield-extenddescription-poster-obey = Nanotrasen's wish is my command!
+mindshield-extenddescription-poster-donotquestion = Nanotrasen knows best!
+mindshield-extenddescription-poster-safetyreport = It's necessary for our own safety.
+mindshield-extenddescription-poster-reportcrimes = We cannot let criminals threaten the security of this station!
+mindshield-extenddescription-poster-vintagereprint50 = Fifty beautiful years of informative posters!
+mindshield-extenddescription-poster-pdaad = How can anyone else even compete against Nanotrasen standards?
+mindshield-extenddescription-poster-enlist = Their heroics bring a tear to my eye.
+mindshield-extenddescription-poster-theowl = Everyone should do their part!
+mindshield-extenddescription-poster-nogasgiant = It would be silly to believe otherwise.
+mindshield-extenddescription-poster-watchingyou = I feel safe under their protective gaze.
+mindshield-extenddescription-poster-vacation = Looking at it makes me proud to work for Nanotrasen.
+mindshield-extenddescription-poster-buycards = I feel the urge to purchase another pack...
diff --git a/Resources/Prototypes/Entities/Objects/Tools/emag.yml b/Resources/Prototypes/Entities/Objects/Tools/emag.yml
index 73753329ca2..3be8873e0f8 100644
--- a/Resources/Prototypes/Entities/Objects/Tools/emag.yml
+++ b/Resources/Prototypes/Entities/Objects/Tools/emag.yml
@@ -3,12 +3,65 @@
   id: EmagUnlimited
   suffix: Unlimited
   name: cryptographic sequencer
-  description: The all-in-one hacking solution. The thinking man's lockpick. The iconic EMAG.
+  description: A red circuit board with a charge indicator.
   components:
   - type: Emag
     blacklist: # DeltaV - Split Emag into 2 items, this one cannot emag doors
       components:
       - Airlock
+  - type: ExtendDescription
+    descriptionList:
+      - description: "emag-extenddescription-command"
+        fontSize: 12
+        color: "#1155ff"
+        requireDetailRange: true
+        requirements:
+        - !type:CharacterDepartmentRequirement
+          departments:
+            - Command
+      - description: "emag-extenddescription-engineering"
+        fontSize: 12
+        color: "#ffaf00"
+        requireDetailRange: true
+        requirements:
+        - !type:CharacterDepartmentRequirement
+          departments:
+            - Engineering
+      - description: "emag-extenddescription-security"
+        fontSize: 12
+        color: "#ff0000"
+        requireDetailRange: true
+        requirements:
+        - !type:CharacterDepartmentRequirement
+          departments:
+            - Security
+      - description: "emag-extenddescription-epistemics"
+        fontSize: 12
+        color: "#aa00aa"
+        requireDetailRange: true
+        requirements:
+        - !type:CharacterDepartmentRequirement
+          departments:
+            - Epistemics
+      - description: "emag-extenddescription-syndicate"
+        fontSize: 12
+        color: "#880000"
+        requireDetailRange: false
+        requirements:
+        - !type:CharacterAntagonistRequirement
+          antagonists:
+            - Traitor
+            - TraitorSleeper
+            - SpaceNinja
+            - Nukeops
+            - NukeopsMedic
+            - NukeopsCommander
+      - description: "mindshield-extenddescription-emag"
+        fontSize: 12
+        color: "#11aaff"
+        requireDetailRange: true
+        requirements:
+        - !type:CharacterMindshieldRequirement
   - type: Sprite
     sprite: Objects/Tools/emag.rsi
     state: icon
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/security.yml b/Resources/Prototypes/Entities/Objects/Weapons/security.yml
index 1a63aa5a4d3..7b7a4bda287 100644
--- a/Resources/Prototypes/Entities/Objects/Weapons/security.yml
+++ b/Resources/Prototypes/Entities/Objects/Weapons/security.yml
@@ -151,6 +151,24 @@
         visible: false
         shader: unshaded
     - type: Flash
+    - type: ExtendDescription
+      descriptionList:
+      - description: "flash-extenddescription-headrevolutionary"
+        fontSize: 12
+        color: "#880000"
+        requireDetailRange: false
+        requirements:
+        - !type:CharacterAntagonistRequirement
+          antagonists:
+            - HeadRev
+      - description: "flash-extenddescription-revolutionary"
+        fontSize: 12
+        color: "#880000"
+        requireDetailRange: false
+        requirements:
+        - !type:CharacterAntagonistRequirement
+          antagonists:
+            - Rev
     - type: LimitedCharges
       maxCharges: 5
       charges: 5
diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/Signs/posters.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/Signs/posters.yml
index 283599e6175..5c77b566f3e 100644
--- a/Resources/Prototypes/Entities/Structures/Wallmounts/Signs/posters.yml
+++ b/Resources/Prototypes/Entities/Structures/Wallmounts/Signs/posters.yml
@@ -67,6 +67,14 @@
   components:
   - type: Sprite
     state: poster2_contraband
+  - type: ExtendDescription
+    descriptionList:
+      - description: "mindshield-extenddescription-poster-atmosia"
+        fontSize: 12
+        color: "#11aaff"
+        requireDetailRange: false
+        requirements:
+        - !type:CharacterMindshieldRequirement
 
 - type: entity
   parent: PosterBase
@@ -76,6 +84,14 @@
   components:
   - type: Sprite
     state: poster3_contraband
+  - type: ExtendDescription
+    descriptionList:
+      - description: "mindshield-extenddescription-poster-funpolice"
+        fontSize: 12
+        color: "#11aaff"
+        requireDetailRange: false
+        requirements:
+        - !type:CharacterMindshieldRequirement
 
 - type: entity
   parent: PosterBase
@@ -94,6 +110,14 @@
   components:
   - type: Sprite
     state: poster5_contraband
+  - type: ExtendDescription
+    descriptionList:
+      - description: "mindshield-extenddescription-poster-syndicaterecruitment"
+        fontSize: 12
+        color: "#11aaff"
+        requireDetailRange: false
+        requirements:
+        - !type:CharacterMindshieldRequirement
 
 - type: entity
   parent: PosterBase
@@ -112,6 +136,14 @@
   components:
   - type: Sprite
     state: poster7_contraband
+  - type: ExtendDescription
+    descriptionList:
+      - description: "mindshield-extenddescription-poster-contrabandsmoke"
+        fontSize: 12
+        color: "#11aaff"
+        requireDetailRange: false
+        requirements:
+        - !type:CharacterMindshieldRequirement
 
 - type: entity
   parent: PosterBase
@@ -121,6 +153,14 @@
   components:
   - type: Sprite
     state: poster8_contraband
+  - type: ExtendDescription
+    descriptionList:
+      - description: "mindshield-extenddescription-poster-greytide"
+        fontSize: 12
+        color: "#11aaff"
+        requireDetailRange: false
+        requirements:
+        - !type:CharacterMindshieldRequirement
 
 - type: entity
   parent: PosterBase
@@ -148,6 +188,14 @@
   components:
   - type: Sprite
     state: poster11_contraband
+  - type: ExtendDescription
+    descriptionList:
+      - description: "mindshield-extenddescription-poster-ripbadger"
+        fontSize: 12
+        color: "#11aaff"
+        requireDetailRange: false
+        requirements:
+        - !type:CharacterMindshieldRequirement
 
 - type: entity
   parent: PosterBase
@@ -193,6 +241,14 @@
   components:
   - type: Sprite
     state: poster16_contraband
+  - type: ExtendDescription
+    descriptionList:
+      - description: "mindshield-extenddescription-poster-contrabandpower"
+        fontSize: 12
+        color: "#11aaff"
+        requireDetailRange: false
+        requirements:
+        - !type:CharacterMindshieldRequirement
 
 - type: entity
   parent: PosterBase
@@ -247,6 +303,14 @@
   components:
   - type: Sprite
     state: poster22_contraband
+  - type: ExtendDescription
+    descriptionList:
+      - description: "mindshield-extenddescription-poster-KosmicheskayaStantsiya"
+        fontSize: 12
+        color: "#11aaff"
+        requireDetailRange: false
+        requirements:
+        - !type:CharacterMindshieldRequirement
 
 - type: entity
   parent: PosterBase
@@ -256,6 +320,14 @@
   components:
   - type: Sprite
     state: poster23_contraband
+  - type: ExtendDescription
+    descriptionList:
+      - description: "mindshield-extenddescription-poster-rebelsunite"
+        fontSize: 12
+        color: "#11aaff"
+        requireDetailRange: false
+        requirements:
+        - !type:CharacterMindshieldRequirement
 
 - type: entity
   parent: PosterBase
@@ -355,6 +427,14 @@
   components:
   - type: Sprite
     state: poster35_contraband
+  - type: ExtendDescription
+    descriptionList:
+      - description: "mindshield-extenddescription-poster-freedrone"
+        fontSize: 12
+        color: "#11aaff"
+        requireDetailRange: false
+        requirements:
+        - !type:CharacterMindshieldRequirement
 
 - type: entity
   parent: PosterBase
@@ -454,6 +534,14 @@
   components:
   - type: Sprite
     state: poster46_contraband
+  - type: ExtendDescription
+    descriptionList:
+      - description: "mindshield-extenddescription-poster-syndicateencryption"
+        fontSize: 12
+        color: "#11aaff"
+        requireDetailRange: false
+        requirements:
+        - !type:CharacterMindshieldRequirement
 
 - type: entity
   parent: PosterBase
@@ -554,6 +642,14 @@
   components:
     - type: Sprite
       state: poster58_contraband
+    - type: ExtendDescription
+      descriptionList:
+        - description: "mindshield-extenddescription-poster-cybersun600"
+          fontSize: 12
+          color: "#11aaff"
+          requireDetailRange: false
+          requirements:
+          - !type:CharacterMindshieldRequirement
 
 - type: entity
   parent: PosterBase
@@ -590,7 +686,7 @@
   components:
     - type: Sprite
       state: poster62_contraband
-      
+
 - type: entity
   parent: PosterBase
   id: PosterContrabandMissingSpacepen
@@ -609,6 +705,14 @@
   components:
   - type: Sprite
     state: poster1_legit
+  - type: ExtendDescription
+    descriptionList:
+      - description: "mindshield-extenddescription-poster-foryoursafety"
+        fontSize: 12
+        color: "#11aaff"
+        requireDetailRange: false
+        requirements:
+        - !type:CharacterMindshieldRequirement
 
 - type: entity
   parent: PosterBase
@@ -618,6 +722,14 @@
   components:
   - type: Sprite
     state: poster2_legit
+  - type: ExtendDescription
+    descriptionList:
+      - description: "mindshield-extenddescription-poster-nanotrasen"
+        fontSize: 12
+        color: "#11aaff"
+        requireDetailRange: false
+        requirements:
+        - !type:CharacterMindshieldRequirement
 
 - type: entity
   parent: PosterBase
@@ -636,6 +748,14 @@
   components:
   - type: Sprite
     state: poster4_legit
+  - type: ExtendDescription
+    descriptionList:
+      - description: "mindshield-extenddescription-poster-helpothers"
+        fontSize: 12
+        color: "#11aaff"
+        requireDetailRange: false
+        requirements:
+        - !type:CharacterMindshieldRequirement
 
 - type: entity
   parent: PosterBase
@@ -681,6 +801,14 @@
   components:
   - type: Sprite
     state: poster9_legit
+  - type: ExtendDescription
+    descriptionList:
+      - description: "mindshield-extenddescription-poster-obey"
+        fontSize: 12
+        color: "#11aaff"
+        requireDetailRange: false
+        requirements:
+        - !type:CharacterMindshieldRequirement
 
 - type: entity
   parent: PosterBase
@@ -744,6 +872,14 @@
   components:
   - type: Sprite
     state: poster16_legit
+  - type: ExtendDescription
+    descriptionList:
+      - description: "mindshield-extenddescription-poster-donotquestion"
+        fontSize: 12
+        color: "#11aaff"
+        requireDetailRange: false
+        requirements:
+        - !type:CharacterMindshieldRequirement
 
 - type: entity
   parent: PosterBase
@@ -789,6 +925,14 @@
   components:
   - type: Sprite
     state: poster21_legit
+  - type: ExtendDescription
+    descriptionList:
+      - description: "mindshield-extenddescription-poster-safetyreport"
+        fontSize: 12
+        color: "#11aaff"
+        requireDetailRange: false
+        requirements:
+        - !type:CharacterMindshieldRequirement
 
 - type: entity
   parent: PosterBase
@@ -798,6 +942,14 @@
   components:
   - type: Sprite
     state: poster22_legit
+  - type: ExtendDescription
+    descriptionList:
+      - description: "mindshield-extenddescription-poster-reportcrimes"
+        fontSize: 12
+        color: "#11aaff"
+        requireDetailRange: false
+        requirements:
+        - !type:CharacterMindshieldRequirement
 
 - type: entity
   parent: PosterBase
@@ -834,6 +986,14 @@
   components:
   - type: Sprite
     state: poster26_legit
+  - type: ExtendDescription
+    descriptionList:
+      - description: "mindshield-extenddescription-poster-vintagereprint50"
+        fontSize: 12
+        color: "#11aaff"
+        requireDetailRange: false
+        requirements:
+        - !type:CharacterMindshieldRequirement
 
 - type: entity
   parent: PosterBase
@@ -852,6 +1012,14 @@
   components:
   - type: Sprite
     state: poster28_legit
+  - type: ExtendDescription
+    descriptionList:
+      - description: "mindshield-extenddescription-poster-pdaad"
+        fontSize: 12
+        color: "#11aaff"
+        requireDetailRange: false
+        requirements:
+        - !type:CharacterMindshieldRequirement
 
 - type: entity
   parent: PosterBase
@@ -861,6 +1029,14 @@
   components:
   - type: Sprite
     state: poster29_legit
+  - type: ExtendDescription
+    descriptionList:
+      - description: "mindshield-extenddescription-poster-enlist"
+        fontSize: 12
+        color: "#11aaff"
+        requireDetailRange: false
+        requirements:
+        - !type:CharacterMindshieldRequirement
 
 - type: entity
   parent: PosterBase
@@ -897,6 +1073,14 @@
   components:
   - type: Sprite
     state: poster33_legit
+  - type: ExtendDescription
+    descriptionList:
+      - description: "mindshield-extenddescription-poster-theowl"
+        fontSize: 12
+        color: "#11aaff"
+        requireDetailRange: false
+        requirements:
+        - !type:CharacterMindshieldRequirement
 
 - type: entity
   parent: PosterBase
@@ -933,6 +1117,14 @@
   components:
   - type: Sprite
     state: poster37_legit
+  - type: ExtendDescription
+    descriptionList:
+      - description: "mindshield-extenddescription-poster-nogasgiant"
+        fontSize: 12
+        color: "#11aaff"
+        requireDetailRange: false
+        requirements:
+        - !type:CharacterMindshieldRequirement
 
 - type: entity
   parent: PosterBase
@@ -951,6 +1143,14 @@
   components:
   - type: Sprite
     state: poster39_legit
+  - type: ExtendDescription
+    descriptionList:
+      - description: "mindshield-extenddescription-poster-watchingyou"
+        fontSize: 12
+        color: "#11aaff"
+        requireDetailRange: false
+        requirements:
+        - !type:CharacterMindshieldRequirement
 
 - type: entity
   parent: PosterBase
@@ -1032,6 +1232,14 @@
   components:
     - type: Sprite
       state: poster48_legit
+    - type: ExtendDescription
+      descriptionList:
+        - description: "mindshield-extenddescription-poster-vacation"
+          fontSize: 12
+          color: "#11aaff"
+          requireDetailRange: false
+          requirements:
+          - !type:CharacterMindshieldRequirement
 
 - type: entity
   parent: PosterBase
@@ -1059,6 +1267,14 @@
   components:
     - type: Sprite
       state: poster51_legit
+    - type: ExtendDescription
+      descriptionList:
+        - description: "mindshield-extenddescription-poster-buycards"
+          fontSize: 12
+          color: "#11aaff"
+          requireDetailRange: false
+          requirements:
+          - !type:CharacterMindshieldRequirement
 
 - type: entity
   parent: PosterBase


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 9**

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: ASSERTIVE**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 990878b9ed60b4e22388038b63714ec2dc693bbf and d1efe9c2aafdfea349f1737cdfb9f48734bf494e.

</details>

<details>
<summary>📒 Files selected for processing (5)</summary>

* `Content.Client/Players/PlayTimeTracking/JobRequirementsManager.cs` (1 hunks)
* `Content.Shared/Customization/Systems/CharacterRequirementsSystem.cs` (3 hunks)
* `Content.Shared/Players/PlayTimeTracking/ISharedPlaytimeManager.cs` (1 hunks)
* `Content.Shared/Traits/Assorted/Components/ExtendDescriptionComponent.cs` (1 hunks)
* `Content.Shared/Traits/Assorted/Systems/ExtendDescriptionSystem.cs` (2 hunks)

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (5)</summary>

* GitHub Check: YAML Linter
* GitHub Check: YAML map schema validator
* GitHub Check: build (ubuntu-latest)
* GitHub Check: build (ubuntu-latest)
* GitHub Check: Test Packaging

</details>

<details>
<summary>🔇 Additional comments (7)</summary><blockquote>

<details>
<summary>Content.Shared/Players/PlayTimeTracking/ISharedPlaytimeManager.cs (1)</summary>

`1-9`: **Well-defined interface addition**

This interface addition follows proper Colonial coding standards! The use of the NotNullWhen attribute maintains our Republic's commitment to clear API contracts.

</details>
<details>
<summary>Content.Shared/Traits/Assorted/Systems/ExtendDescriptionSystem.cs (2)</summary>

`9-10`: **Appropriate dependency injection**

Clean and proper dependency injection - as is befitting an officer of the Biesel Republic! This will ensure proper character requirement validation.

---

`28-30`: **Commendable requirements handling**

Excellent implementation of the requirements checking and alternative description selection. This shows true Biesel Republic engineering!

</details>
<details>
<summary>Content.Shared/Customization/Systems/CharacterRequirementsSystem.cs (4)</summary>

`4-9`: **Imports for supporting character-based requirement validation look good.**

Excellent addition of necessary imports to support the enhanced requirement validation system. The hierarchical structure shows proper resource management befitting a vessel of the Biesel Republic's Navy.

---

`20-27`: **Dependencies properly structured for requirements validation.**

By the stars of Tau Ceti, this is a well-organized dependency injection setup! Your new dependencies will provide the necessary services for validating character requirements effectively. The clear structure will help maintain freedom from complexity across our systems.

---

`48-53`: **Validation chain properly verifies all necessary character attributes.**

Solid defensive programming, Captain! The validation chain ensures all prerequisite conditions are met before attempting to check specific requirements. This type of thorough verification prevents null reference exceptions that could damage our ship's systems.

---

`55-55`: **Effective delegation to existing method reuses code well.**

Excellent strategic decision to delegate to the existing requirements validation method once all prerequisites are confirmed. This maintains the DRY principle and ensures consistent validation logic throughout the system - a tactic worthy of the Biesel Navy.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-03T00:17:03Z
user: login: Timfa2112
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-03T00:21:24Z
user: login: Timfa2112
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-03T00:21:46Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-03T00:21:56Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-03T00:22:10Z
user: login: Timfa2112
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-03T00:25:26Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-03T00:25:57Z
user: login: Timfa2112
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-03T00:27:22Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-03T00:27:47Z
user: login: Timfa2112
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-03T00:33:31Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 1**

<details>
<summary>♻️ Duplicate comments (1)</summary><blockquote>

<details>
<summary>Content.Shared/Traits/Assorted/Systems/ExtendDescriptionSystem.cs (1)</summary><blockquote>

`24-26`: _🧹 Nitpick (assertive)_

**Improve readability with multi-line conditions**

By the Colonial Fleet's standards, this condition is complex and would benefit from being split across multiple lines for better readability. Complex conditionals are breeding grounds for Sol Alliance bugs!



```diff
-            if (!args.IsInDetailsRange && desc.RequireDetailRange
-                || !TryComp(args.Examiner, out MetaDataComponent? comp) || comp.EntityPrototype == null)
+            if ((!args.IsInDetailsRange && desc.RequireDetailRange) || 
+                !TryComp(args.Examiner, out MetaDataComponent? comp) || 
+                comp.EntityPrototype == null)
                continue;
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: ASSERTIVE**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between d1efe9c2aafdfea349f1737cdfb9f48734bf494e and 4841947258c0da155f7d1e47f1ce5faa326578ee.

</details>

<details>
<summary>📒 Files selected for processing (4)</summary>

* `Content.Client/Players/PlayTimeTracking/JobRequirementsManager.cs` (1 hunks)
* `Content.Shared/Customization/Systems/CharacterRequirementsSystem.cs` (3 hunks)
* `Content.Shared/Traits/Assorted/Components/ExtendDescriptionComponent.cs` (1 hunks)
* `Content.Shared/Traits/Assorted/Systems/ExtendDescriptionSystem.cs` (2 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧠 Learnings (2)</summary>

<details>
<summary>Content.Client/Players/PlayTimeTracking/JobRequirementsManager.cs (1)</summary>

```
Learnt from: Timfa2112
PR: Simple-Station/Einstein-Engines#1862
File: Content.Client/Players/PlayTimeTracking/JobRequirementsManager.cs:44-48
Timestamp: 2025-03-03T00:25:56.096Z
Learning: In C# client/shared code implementations for SpaceStation14, defensive copying should be used when returning mutable collections (like dictionaries) to prevent external code from modifying internal state.
```

</details>
<details>
<summary>Content.Shared/Traits/Assorted/Components/ExtendDescriptionComponent.cs (1)</summary>

```
Learnt from: Timfa2112
PR: Simple-Station/Einstein-Engines#1862
File: Content.Shared/Traits/Assorted/Components/ExtendDescriptionComponent.cs:15-17
Timestamp: 2025-03-03T00:21:55.723Z
Learning: For ExtendDescriptionComponent, using an empty string for RequirementsNotMetDescription is intentional design. This prevents players from knowing there's hidden content they can't access yet, maintaining better immersion than showing a message that would reveal the existence of inaccessible information.
```

</details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (5)</summary>

* GitHub Check: build (ubuntu-latest)
* GitHub Check: build (ubuntu-latest)
* GitHub Check: Test Packaging
* GitHub Check: YAML Linter
* GitHub Check: YAML map schema validator

</details>

<details>
<summary>🔇 Additional comments (7)</summary><blockquote>

<details>
<summary>Content.Client/Players/PlayTimeTracking/JobRequirementsManager.cs (1)</summary>

`44-48`: **Excellent implementation of defensive copying**

By the Republic's engineering standards, this new method properly implements defensive copying for our playtime tracking data - a critical security measure against Sol Alliance infiltration.

The creation of a new dictionary using the constructor ensures our internal state remains protected while allowing external access to the data.

</details>
<details>
<summary>Content.Shared/Traits/Assorted/Components/ExtendDescriptionComponent.cs (2)</summary>

`9-10`: **Good job making Requirements nullable**

Excellent strategic decision to make the Requirements property nullable rather than initializing an empty list. This efficient memory allocation serves the Republic well, especially for the many descriptions without requirements.

---

`15-17`: **Intentional empty fallback design approved**

The empty string default for RequirementsNotMetDescription maintains proper information security protocols. By not revealing the existence of hidden content to unauthorized personnel, we preserve operational secrecy.

</details>
<details>
<summary>Content.Shared/Traits/Assorted/Systems/ExtendDescriptionSystem.cs (2)</summary>

`9-9`: **Proper dependency injection implemented**

Excellent integration of the CharacterRequirementsSystem dependency. This follows Republic naval code standards for proper system integration.

---

`28-30`: **Sound implementation of requirement checking**

This code elegantly implements the character requirements validation logic. The Republic approves of the short-circuit evaluation to bypass requirement checking when requirements are null.

</details>
<details>
<summary>Content.Shared/Customization/Systems/CharacterRequirementsSystem.cs (2)</summary>

`20-27`: **All dependencies properly injected**

The new dependencies are properly declared and initialized according to Republic naval code standards. This ensures all systems will interact correctly in the colony infrastructure.

---

`44-53`: **Well-documented public method**

Excellent documentation! These XML comments clearly explain the purpose, parameters, and return value of this method, following Republic naval documentation standards.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-03T00:33:48Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-03T00:33:58Z
user: login: VMSolidus
body: Can you do me a favor and make use of the system on at least one pre-existing item, to act as its own proof of concept for the system working as advertised? Let's have the example be a description extension for the Emag item, which can only be seen if your character is a member of the Engineering department. Which explains that it is an illegal hacking tool used to subvert station electronics.
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-03-03T17:58:01Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 7**

<details>
<summary>🔭 Outside diff range comments (1)</summary><blockquote>

<details>
<summary>Content.Shared/Customization/Systems/CharacterRequirements.Logic.cs (1)</summary><blockquote>

`1-173`: _💡 Verification agent_

<details>
<summary>🧩 Analysis chain</summary>

**Verify mind parameter propagation to requirement checking system**

The changes to these logic classes look solid, but we should verify that the `CharacterRequirementsSystem.CheckRequirementValid` and `CheckRequirementsValid` methods properly propagate the mind parameter to the individual requirement validations. This is essential for maintaining the chain of intelligence throughout our verification protocols.

---

🏁 Script executed:

```shell
#!/bin/bash
# Search for CheckRequirementValid and CheckRequirementsValid implementation
rg --type csharp "public\s+(bool|List<string>)\s+Check(Requirement|Requirements)Valid" -A 10
```

Length of output: 4297

---



</details>

**Attention: Mind Parameter Propagation Requires Update**

Cap'n, upon verifying our requirement checks in the `CharacterRequirementsSystem`, it appears that while the overload for `CheckRequirementsValid` supports an optional `mind` parameter, our logic classes (And, Or, and Xor) aren’t forwarding the received `mind` parameter from their `IsValid` methods. In particular, the call from `CharacterLogicAndRequirement` omits the `mind` parameter, meaning that any non‑null `mind` provided will default to null in the deeper validations. Since `CheckRequirementValid` (used in the OR and XOR checks) doesn’t accept a `mind` parameter at all, this inconsistency might break the intended chain of intelligence in our requirement verifications.

- **Location to address:**  
  - **File:** `Content.Shared/Customization/Systems/CharacterRequirements.Logic.cs`  
  - **Method:** `CharacterLogicAndRequirement.IsValid` (and review the related OR and XOR cases)

- **Recommended Change:**  
  In `CharacterLogicAndRequirement.IsValid`, update the call to `CheckRequirementsValid` to pass the `mind` parameter. For example:  
  ```diff
  - .CheckRequirementsValid(Requirements, job, profile, playTimes, whitelisted, prototype, entityManager,
  -    prototypeManager, configManager, out var reasons, depth + 1);
  + .CheckRequirementsValid(Requirements, job, profile, playTimes, whitelisted, prototype, entityManager,
  +    prototypeManager, configManager, out var reasons, depth + 1, mind);
  ```  
  This change will ensure that if a `MindComponent` is provided, it is properly propagated to all underlying requirement validations.

Please update the OR and XOR logic if a consistent approach is desired or document the discrepancy if intentional.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: ASSERTIVE**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 4841947258c0da155f7d1e47f1ce5faa326578ee and a1263b68a77e2ca8c66fa7f9f4b222ec71ba21d9.

</details>

<details>
<summary>📒 Files selected for processing (11)</summary>

* `Content.Shared/Customization/Systems/CharacterRequirements.Antagonist.cs` (1 hunks)
* `Content.Shared/Customization/Systems/CharacterRequirements.Job.cs` (6 hunks)
* `Content.Shared/Customization/Systems/CharacterRequirements.Logic.cs` (4 hunks)
* `Content.Shared/Customization/Systems/CharacterRequirements.Misc.cs` (3 hunks)
* `Content.Shared/Customization/Systems/CharacterRequirements.Profile.cs` (11 hunks)
* `Content.Shared/Customization/Systems/CharacterRequirements.Whitelist.cs` (2 hunks)
* `Content.Shared/Customization/Systems/CharacterRequirements.cs` (2 hunks)
* `Content.Shared/Customization/Systems/CharacterRequirementsSystem.cs` (4 hunks)
* `Resources/Locale/en-US/customization/character-requirements.ftl` (2 hunks)
* `Resources/Locale/en-US/extenddescriptions/items/syndicate/emag.ftl` (1 hunks)
* `Resources/Prototypes/Entities/Objects/Tools/emag.yml` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🪛 YAMLlint (1.35.1)</summary>

<details>
<summary>Resources/Prototypes/Entities/Objects/Tools/emag.yml</summary>

[error] 8-8: wrong indentation: expected at least 3

(indentation)

---

[warning] 9-9: too few spaces before comment

(comments)

---

[error] 11-11: wrong indentation: expected 8 but found 6

(indentation)

---

[error] 19-19: wrong indentation: expected 10 but found 8

(indentation)

---

[error] 27-27: wrong indentation: expected 10 but found 8

(indentation)

---

[error] 35-35: wrong indentation: expected 10 but found 8

(indentation)

---

[error] 43-43: wrong indentation: expected 10 but found 8

(indentation)

---

[error] 51-51: wrong indentation: expected 10 but found 8

(indentation)

</details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (5)</summary>

* GitHub Check: build (ubuntu-latest)
* GitHub Check: Test Packaging
* GitHub Check: YAML map schema validator
* GitHub Check: build (ubuntu-latest)
* GitHub Check: YAML Linter

</details>

<details>
<summary>🔇 Additional comments (33)</summary><blockquote>

<details>
<summary>Content.Shared/Customization/Systems/CharacterRequirements.Misc.cs (2)</summary>

`2-2`: **Import addition confirmed, Lieutenant!**

Mind component support has been successfully integrated into our systems. This will allow for proper authentication of personnel based on their mental faculties and clearance levels.

---

`34-35`: **Parameter update acknowledged and approved!**

The addition of the mind parameter to the IsValid method is in line with Republic Naval Code regulations. This modification ensures all requirement validation can access mind-related data when necessary.

</details>
<details>
<summary>Content.Shared/Customization/Systems/CharacterRequirements.cs (2)</summary>

`1-1`: **Mind import successfully added to the manifest!**

This import is necessary for the extended mind-based validation protocols. Good work ensuring all necessary components are accessible.

---

`40-41`: **Base method signature properly updated!**

The addition of the mind parameter to the abstract IsValid method ensures all derived requirement classes can access mind-related information. This maintains proper command structure throughout the codebase.

</details>
<details>
<summary>Resources/Locale/en-US/customization/character-requirements.ftl (1)</summary>

`12-15`: **Antagonist requirement localization established!**

Proper documentation for antagonist identification has been added to our translation matrix. This will ensure personnel are properly informed when they lack clearance for sensitive information.

</details>
<details>
<summary>Content.Shared/Customization/Systems/CharacterRequirements.Antagonist.cs (4)</summary>

`1-12`: **Imports properly assembled for the new requirement type!**

All necessary dependencies have been included for the antagonist requirement implementation. The battle-readiness of this code is evident in its thorough preparation.

---

`16-22`: **Class definition follows Republic Naval Code standards!**

The new CharacterAntagonistRequirement class is properly decorated with the necessary attributes and extends the base CharacterRequirement as mandated by regulation.

---

`44-51`: **Implementation correctly checks for matching antagonist roles!**

This section efficiently iterates through the mind's roles and checks for matching antagonist prototypes. Well-structured and optimized code is the backbone of our Republic's technological superiority!

---

`23-24`: <details>
<summary>❓ Verification inconclusive</summary>

**Verify handling of empty antagonist lists!**

The required Antagonists field could potentially be empty after initialization. Current implementation would return false for all minds if the list is empty, which may not be the intended behavior.

---

🏁 Script executed:

```shell
#!/bin/bash
# Check if there are any references to CharacterAntagonistRequirement with empty Antagonist lists
ast-grep --pattern $'CharacterAntagonistRequirement [^{]*{
  $$$
  Antagonists = \[\],
  $$$
}'
```

Length of output: 96

---



</details>

**Attention Developer: Confirm that the handling of an empty Antagonists list is intentional!**

Our initial probe using ast‑grep didn’t locate any explicit initialization of the Antagonists field with an empty list, which means we couldn’t automatically verify if the empty list case is correctly managed. Since the field is marked as required, please manually inspect or test the logic that processes this field. Should an empty list unintentionally cause all minds to fail the requirement check, you may need to adjust the implementation to treat an empty list differently.

- Verify whether the processing of the Antagonists list distinguishes between “not provided” and “explicitly empty.”
- If an empty list is a valid state, consider revising the check to avoid defaulting to false for all minds.
- Otherwise, update the initialization or usage logic accordingly.

Ensure you manually verify this behavior in your testing suite. Together, we’re defending our codebase against any missteps of the Sol Alliance!

</details>
<details>
<summary>Content.Shared/Customization/Systems/CharacterRequirementsSystem.cs (2)</summary>

`58-63`: **Duplicate concern regarding early returns without reasons.**

High Marshal, we previously discussed providing granular reasons for these preliminary checks. Given the existing design choice to fail silently for missing mind, session, job, profile, or times, we consider this matter already resolved.

---

`44-66`: **Excellent clarity with added documentation.**

Your new method is well-documented, clarifying usage for your fellow officers. The approach to funnel relevant data from mind, job, station profile, and playtime into unified requirement checks is robust. Good job championing the independence of the Tau Ceti code!

</details>
<details>
<summary>Content.Shared/Customization/Systems/CharacterRequirements.Profile.cs (11)</summary>

`5-5`: **Approved addition of the Mind component import!**

This import will provide us with access to the vital Mind subsystem for our intelligence requirements. A crucial step in maintaining the Biesel Republic's information superiority.

---

`42-43`: **Excellent parameter addition for enhanced intelligence capabilities!**

Adding the `MindComponent` parameter to our profile requirements is a tactical improvement. This will allow us to access a character's mind data during validation, enhancing our ability to determine if personnel have the necessary clearance to view sensitive information. This follows Biesel Fleet protocols for information compartmentalization.

---

`81-82`: **Mind parameter systematically added to gender requirements. Acknowledged.**

The consistency in adding the mind parameter across all requirement types demonstrates solid fleet protocol adherence. Maintaining uniform interfaces across our codebase is essential for operational readiness.

---

`112-113`: **Mind parameter correctly added to sex requirements. Tactical consistency maintained.**

Your thoroughness in updating all validation methods mirrors the precision expected of Biesel Republic officers. Regulation BR-117 clearly states: "All system interfaces must maintain consistent parameters across related functions."

---

`143-144`: **Species requirements updated with mind parameter. Operations can proceed.**

This update ensures our species verification protocols can access mind data when necessary. This will be instrumental for implementing species-specific clearance levels for sensitive colonial intelligence.

---

`186-187`: **Height verification mind parameter added. Standard-compliant.**

Proper integration of mind access into our biometric verification systems. The Biesel Republic Fleet maintains strict height-based access protocols for certain confined-space operations. This change prepares our systems for such validation.

---

`233-234`: **Width requirements updated with mind parameter. Maintaining systematic consistency.**

Update follows Biesel Republic Naval Code Article 47: "All verification systems must be capable of cross-referencing physical attributes with mental clearance levels." Your consistency is commendable, pilot.

---

`280-281`: **Mind parameter added to weight requirements. Protocol conformity confirmed.**

Continuing the systematic update of our requirements system with mind capabilities. This will allow for proper clearance verification across all physical attribute checks, ensuring our colonial secrets remain secure from Sol Alliance spies.

---

`331-332`: **Trait requirements properly equipped with mind parameter. Colonial standards maintained.**

The trait verification system has been successfully updated to access mind data during validation. This will be crucial for trait-based security clearances in our colonial data systems.

---

`365-366`: **Loadout requirements mind parameter added. Fleet readiness improved.**

Excellent addition that will allow us to validate equipment access based on mental clearance levels. The Biesel Republic prides itself on proper equipment distribution based on verified qualifications.

---

`399-400`: **Mind parameter integration complete for item group requirements. Mission accomplished.**

The final requirement type has been successfully updated with mind access capabilities. The Biesel Republic Naval Command approves of this systematic approach to codebase modernization. This update prepares our systems for enhanced verification protocols across all requirement types.

</details>
<details>
<summary>Content.Shared/Customization/Systems/CharacterRequirements.Logic.cs (4)</summary>

`3-3`: **Mind component import successfully integrated! Fleet communications enhanced.**

Strategic addition of the Mind namespace import. This allows our logical operations to access crucial mind-based intelligence for verification processes.

---

`34-35`: **AND logic updated with mind parameter. Tactical reasoning capabilities enhanced.**

The logical AND operations can now access mind-based verification data. This follows Biesel Republic Naval Code section 5.7: "All compound logical operations must have access to all available intelligence sources."

---

`77-78`: **OR logic enhanced with mind parameter. Decision matrix improved.**

This update ensures our logical OR operations can evaluate mind-based intelligence during requirement validation. Essential for implementing tiered access controls based on various qualifying conditions.

---

`133-134`: **XOR logic updated with mind validation capabilities. Maximum security achieved.**

Excellent addition to our exclusive validation logic. The XOR requirements are critical for ensuring only one specific qualification grants access, and now they can consider mind-based clearance in their calculations.

</details>
<details>
<summary>Content.Shared/Customization/Systems/CharacterRequirements.Job.cs (7)</summary>

`3-3`: **Mind component import successfully integrated! Intelligence access secured.**

Essential namespace import that allows our job requirements to access crucial mind data. The Biesel Republic recognizes the importance of proper mind clearance verification for job-based access controls.

---

`36-37`: **Job requirements updated with mind parameter. Colonial security enhanced.**

Excellent update to our job verification system. This will allow us to implement proper checks to ensure only personnel with appropriate mental clearance can access information restricted to specific job roles.

---

`88-89`: **Department verification enhanced with mind integration. Command structure integrity preserved.**

Adding mind verification capabilities to our department requirements follows Biesel Republic fleet protocols for departmental information security. This is essential for maintaining our colonial information advantage over the Sol Alliance.

---

`136-137`: **Department time requirements updated with mind parameter. Experience verification improved.**

This upgrade to our time-tracking validation system will allow integration of mind-based verification with experience requirements. This follows Biesel Naval Regulation 32.5: "All access based on experience must verify both service time and mental clearance."

---

`209-210`: **Overall time requirements enhanced with mind capabilities. Veteran verification secured.**

The Biesel Republic values our veteran personnel and this change ensures our systems can properly verify both their service time and mental clearance levels when granting access to restricted information.

---

`272-273`: **Playtime requirements updated with mind parameter. Final system update complete.**

The last component of our time-based validation system has been successfully updated with mind verification capabilities. These systematic changes demonstrate the thoroughness expected of Biesel Republic naval programmers.

---

`1-334`: <details>
<summary>❓ Verification inconclusive</summary>

**Verify implementation of actual mind-based requirement validation**

While the groundwork has been excellently laid with these parameter additions, I don't see the actual implementation of mind-based requirements yet. This appears to be preparation work for future implementation. Consider adding a proof-of-concept implementation as mentioned in the PR comments, such as the Emag description that should only be visible to Engineering department members.

---

🏁 Script executed:

```shell
#!/bin/bash
# Look for possible implementations of mind-based requirements
rg --type csharp "mind\s*!=" -A 3
```

Length of output: 2669

---



</details>

**Action Required: Add Proof-of-Concept for Mind-Based Requirement Validation**

While the groundwork for including the `mind` parameter is solid, our search confirms that no actual mind-based validation logic exists in these requirement classes. The only occurrences of mind checks are within test files and unrelated systems (e.g., in `GameTicker.Player.cs`), which means the actual use of mind-based validation—as envisioned in the PR comments, such as selectively showing Emag descriptions for Engineering department members—has not yet been implemented.

- The `mind` parameter is accepted in all `IsValid` methods but remains unused.
- No logic applies a mind-based check to control visibility or behavior (e.g., for an Emag-specific scenario for Engineering).
- Integration tests reference mind checks elsewhere, but not within the Character Requirements code.

Please consider adding at least a minimal, proof-of-concept implementation for the mind-based requirement, as discussed, to ensure that the code meets its intended functionality.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-03T22:49:57Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 2**

<details>
<summary>♻️ Duplicate comments (1)</summary><blockquote>

<details>
<summary>Content.Shared/Traits/Assorted/Systems/ExtendDescriptionSystem.cs (1)</summary><blockquote>

`24-25`: _🧹 Nitpick (assertive)_

**Revisit the complex condition for better clarity**  
Though this code is placed on multiple lines, the condition is still a bit dense. Condensing or restructuring might help future maintainability and readability—an old concern from our Colonial logs.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: ASSERTIVE**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 44b75d9ff55f8eebad7c211d143b36f9b11c3bd4 and b1386496dadbd5f6fa47ab9e784509b5bbe3e42f.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `Content.Shared/Traits/Assorted/Systems/ExtendDescriptionSystem.cs` (2 hunks)

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (5)</summary>

* GitHub Check: YAML map schema validator
* GitHub Check: YAML Linter
* GitHub Check: build (ubuntu-latest)
* GitHub Check: Test Packaging
* GitHub Check: build (ubuntu-latest)

</details>

<details>
<summary>🔇 Additional comments (2)</summary><blockquote>

<details>
<summary>Content.Shared/Traits/Assorted/Systems/ExtendDescriptionSystem.cs (2)</summary>

`1-1`: **Imports look solid, long live the Freedoms of the Colonies!**
No issues found here; the new import aligns with your usage of `CharacterRequirementsSystem`.

---

`9-9`: **Dependency injection recognized for our Colonial advantage!**
This usage of `[Dependency]` is proper and helps you call upon `CharacterRequirementsSystem` as needed. No issues spotted.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-03T23:06:17Z
user: login: Timfa2112
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-03T23:22:13Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-03T23:22:36Z
user: login: Timfa2112
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-03T23:23:21Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-03T23:23:55Z
user: login: Timfa2112
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-03T23:25:18Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-03T23:26:06Z
user: login: Timfa2112
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-03T23:31:41Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-03T23:32:50Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 3**

<details>
<summary>♻️ Duplicate comments (1)</summary><blockquote>

<details>
<summary>Content.Shared/Customization/Systems/CharacterRequirements.Antagonist.cs (1)</summary><blockquote>

`34-35`: _🧹 Nitpick (assertive)_

**Update comment to reflect actual usage!**

By the stars of Tau Ceti! The comment suggests players will never see this text, but that contradicts the purpose of this PR - to display conditional extended descriptions based on character requirements.



```diff
-        // Considering this will not be used in the character creation menu, players will likely never see this text.
+        // This message will be displayed when a character doesn't meet the antagonist requirement for viewing certain extended descriptions.
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: ASSERTIVE**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 98514952c847cfd0fa196a166527f171aa480d14 and 445d5579ac397983fceb0a0c64fbbd8146d3d421.

</details>

<details>
<summary>📒 Files selected for processing (6)</summary>

* `Content.Shared/Customization/Systems/CharacterRequirements.Antagonist.cs` (1 hunks)
* `Content.Shared/Customization/Systems/CharacterRequirements.Mindshield.cs` (1 hunks)
* `Resources/Locale/en-US/customization/character-requirements.ftl` (2 hunks)
* `Resources/Locale/en-US/extenddescriptions/mindshield.ftl` (1 hunks)
* `Resources/Prototypes/Entities/Objects/Tools/emag.yml` (1 hunks)
* `Resources/Prototypes/Entities/Structures/Wallmounts/Signs/posters.yml` (28 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🪛 YAMLlint (1.35.1)</summary>

<details>
<summary>Resources/Prototypes/Entities/Objects/Tools/emag.yml</summary>

[error] 8-8: wrong indentation: expected at least 3

(indentation)

---

[warning] 9-9: too few spaces before comment

(comments)

---

[error] 11-11: wrong indentation: expected 8 but found 6

(indentation)

---

[error] 19-19: wrong indentation: expected 10 but found 8

(indentation)

---

[error] 27-27: wrong indentation: expected 10 but found 8

(indentation)

---

[error] 35-35: wrong indentation: expected 10 but found 8

(indentation)

---

[error] 43-43: wrong indentation: expected 10 but found 8

(indentation)

---

[error] 51-51: wrong indentation: expected 10 but found 8

(indentation)

---

[error] 64-64: wrong indentation: expected 10 but found 8

(indentation)

</details>
<details>
<summary>Resources/Prototypes/Entities/Structures/Wallmounts/Signs/posters.yml</summary>

[error] 77-77: wrong indentation: expected 10 but found 8

(indentation)

---

[error] 94-94: wrong indentation: expected 10 but found 8

(indentation)

---

[error] 120-120: wrong indentation: expected 10 but found 8

(indentation)

---

[error] 146-146: wrong indentation: expected 10 but found 8

(indentation)

---

[error] 163-163: wrong indentation: expected 10 but found 8

(indentation)

---

[error] 198-198: wrong indentation: expected 10 but found 8

(indentation)

---

[error] 251-251: wrong indentation: expected 10 but found 8

(indentation)

---

[error] 313-313: wrong indentation: expected 10 but found 8

(indentation)

---

[error] 330-330: wrong indentation: expected 10 but found 8

(indentation)

---

[error] 437-437: wrong indentation: expected 10 but found 8

(indentation)

---

[error] 544-544: wrong indentation: expected 10 but found 8

(indentation)

---

[error] 652-652: wrong indentation: expected 12 but found 10

(indentation)

---

[error] 715-715: wrong indentation: expected 10 but found 8

(indentation)

---

[error] 732-732: wrong indentation: expected 10 but found 8

(indentation)

---

[error] 758-758: wrong indentation: expected 10 but found 8

(indentation)

---

[error] 811-811: wrong indentation: expected 10 but found 8

(indentation)

---

[error] 882-882: wrong indentation: expected 10 but found 8

(indentation)

---

[error] 935-935: wrong indentation: expected 10 but found 8

(indentation)

---

[error] 952-952: wrong indentation: expected 10 but found 8

(indentation)

---

[error] 996-996: wrong indentation: expected 10 but found 8

(indentation)

---

[error] 1022-1022: wrong indentation: expected 10 but found 8

(indentation)

---

[error] 1039-1039: wrong indentation: expected 10 but found 8

(indentation)

---

[error] 1083-1083: wrong indentation: expected 10 but found 8

(indentation)

---

[error] 1127-1127: wrong indentation: expected 10 but found 8

(indentation)

---

[error] 1153-1153: wrong indentation: expected 10 but found 8

(indentation)

---

[error] 1242-1242: wrong indentation: expected 12 but found 10

(indentation)

---

[error] 1277-1277: wrong indentation: expected 12 but found 10

(indentation)

</details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (4)</summary>

* GitHub Check: build (ubuntu-latest)
* GitHub Check: Test Packaging
* GitHub Check: build (ubuntu-latest)
* GitHub Check: YAML Linter

</details>

<details>
<summary>🔇 Additional comments (14)</summary><blockquote>

<details>
<summary>Resources/Prototypes/Entities/Structures/Wallmounts/Signs/posters.yml (2)</summary>

`70-76`: **Consistent styling pattern implemented!**

Excellent work, citizen! I observe that you've maintained consistent styling across all poster descriptions with a font size of 12 and a pleasing Tau Ceti blue color (#11aaff). Such attention to detail is what separates us from the chaotic Sol Alliance coding standards.

---

`69-77`: **Strategic implementation of CharacterMindshieldRequirement!**

As a defender of Republic sovereignty, I commend the implementation of security protocols that limit certain information to those with proper clearance. The extended descriptions being gated behind mindshield requirements effectively creates a security clearance system for contraband and sensitive information.

This aligns perfectly with the PR objective of showing conditional information based on character requirements. Our intelligence officers will now have access to context that regular crew members do not.



Also applies to: 86-94, 112-120, 155-163, 190-198, 243-251, 305-313, 322-330, 429-437, 536-544, 644-652, 707-715, 724-732, 750-758, 803-811, 874-882, 927-935, 944-952, 988-996, 1014-1022, 1031-1039, 1075-1083, 1119-1127, 1146-1153, 1235-1242, 1270-1277

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 YAMLlint (1.35.1)</summary>

[error] 77-77: wrong indentation: expected 10 but found 8

(indentation)

</details>

</details>

</details>
<details>
<summary>Content.Shared/Customization/Systems/CharacterRequirements.Mindshield.cs (3)</summary>

`1-11`: **Well-organized imports demonstrate Republic efficiency!**

These imports are organized with the precision of a Tau Ceti naval formation. The dependencies are properly declared, creating a clear understanding of the component relationships. This level of organization would make any Republic code inspector proud!

---

`14-20`: **Clear documentation and proper attributes!**

Excellent work, officer! The documentation clearly states the purpose of this requirement class, and the appropriate attributes for serialization and network compatibility are in place. This follows Republic coding standards to the letter.

---

`19-40`: **Elegantly implemented requirement check!**

The core implementation of the mindshield check is admirably concise, particularly the Boolean expression on line 38. This efficient coding style reflects the best traditions of Republic engineering - simple, clear, and effective.

The expression `entityManager.HasComponent<MindShieldComponent>(mind.CurrentEntity) != Inverted` perfectly handles both normal and inverted cases with a single line.

</details>
<details>
<summary>Resources/Locale/en-US/customization/character-requirements.ftl (2)</summary>

`12-20`: **Republic-approved requirement localization!**

These new antagonist and mindshield requirements follow our established localization patterns perfectly! The conditional formatting based on the inverted flag matches our other requirements, maintaining consistency across the codebase. This is how we maintain order in the Republic's stellar territories!

---

`166-170`: _🧹 Nitpick (assertive)_

**Formatting adjustment detected!**

I've noticed a minor formatting adjustment to the CVar requirement text. These small improvements help maintain the Republic's high standards for code clarity.

</details>
<details>
<summary>Content.Shared/Customization/Systems/CharacterRequirements.Antagonist.cs (3)</summary>

`19-21`: **Required data field properly implemented!**

I'm pleased to see the proper use of the `required: true` attribute for the Antagonists list. This ensures that any implementation must specify valid antagonist prototypes, preventing incomplete configurations that could compromise Republic security protocols.

---

`40-48`: **Efficient role checking algorithm!**

This elegant loop through the mind's roles demonstrates Republic efficiency! The code quickly filters out roles without antagonist prototypes and performs a simple check against the allowed list. The early return with the inverted flag handling is particularly well-designed.

---

`37-49`: **Consistent handling of Inverted flag across all code paths!**

I commend you on properly implementing the Inverted flag handling throughout this class! Both the null mind case and the no-matching-antagonist case now respect the Inverted flag appropriately. This level of attention to detail is what makes Republic code superior to Sol Alliance spaghetti code.

</details>
<details>
<summary>Resources/Prototypes/Entities/Objects/Tools/emag.yml (3)</summary>

`6-6`: **Entity Description Update:**  
The updated description for `EmagUnlimited`—"A red circuit board with a charge indicator. Its purpose is unknown."—delivers a more mysterious and intriguing narrative for the device. Excellent work, Commander; this concise update sets a fitting tone for the item.

---

`12-64`: **Enhanced ExtendDescription Component Integration:**  
The introduction of the `ExtendDescription` component with multiple departmental entries is a strong strategic move, aligning perfectly with our PR objectives. Each entry is configured with department-specific requirements, and the deliberate use of `requireDetailRange` (noting that the Syndicate entry is set to `false` while others are `true`) appears to be well thought out.  

A note on YAML indentation: Although static analysis tools raise warnings regarding indentation (e.g., expected 10 spaces on certain lines vs. the project’s 2-space standard), these files follow the established project convention. Ensure that the linting configuration is adjusted if needed to avoid future misunderstandings.  

Overall, this integration is sound and should pave the way for the eventual display logic based on character requirements.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 YAMLlint (1.35.1)</summary>

[error] 19-19: wrong indentation: expected 10 but found 8

(indentation)

---

[error] 27-27: wrong indentation: expected 10 but found 8

(indentation)

---

[error] 35-35: wrong indentation: expected 10 but found 8

(indentation)

---

[error] 43-43: wrong indentation: expected 10 but found 8

(indentation)

---

[error] 51-51: wrong indentation: expected 10 but found 8

(indentation)

---

[error] 64-64: wrong indentation: expected 10 but found 8

(indentation)

</details>

</details>

---

`79-86`: **Limited Entity Removal Verification:**  
The removal block for the limited version (entity with `id: Emag`) is noted here. Please confirm that this deletion is intentional and that no other systems rely on the separate limited variant. If consolidating features into `EmagUnlimited` is the strategic goal, then this adjustment is in line with our streamlined design objectives.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 YAMLlint (1.35.1)</summary>

[error] 79-79: wrong indentation: expected 6 but found 0

(indentation)

---

[error] 84-84: wrong indentation: expected 4 but found 2

(indentation)

</details>

</details>

</details>
<details>
<summary>Resources/Locale/en-US/extenddescriptions/mindshield.ftl (1)</summary>

`1-29`: **New Localized Extended Description Strings:**  
This new localization file introduces a broad set of descriptive keys related to mindshield requirements. The keys—such as `mindshield-extenddescription-emag` and various poster descriptions—are clearly labeled and maintain a consistent naming scheme that matches our narrative and gameplay enhancements. A thorough captain’s log indeed; ensure that these keys are properly referenced in the game logic to display the intended messages when the corresponding requirements are met.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-05T19:24:03Z
user: login: VMSolidus
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-07T16:52:54Z

Review comments:
diff_hunk: @@ -41,6 +41,12 @@ public void Initialize()
         _client.RunLevelChanged += ClientOnRunLevelChanged;
     }
 
+    public bool TryGetTrackerTimes(ICommonSession id, [NotNullWhen(true)] out Dictionary<string, TimeSpan>? time)
+    {
+        time = _roles;
+        return true;
+    }
path: Content.Client/Players/PlayTimeTracking/JobRequirementsManager.cs
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Implement defensive copy for _roles dictionary**

By the stars of Tau Ceti! This method simply returns a direct reference to our internal _roles dictionary. Any Sol Alliance infiltrator could modify our sensitive playtime data through this exposed reference, compromising the integrity of our systems!


Implement a defensive copy to protect our internal state:

```diff
public bool TryGetTrackerTimes(ICommonSession id, [NotNullWhen(true)] out Dictionary<string, TimeSpan>? time)
{
-    time = _roles;
+    time = new Dictionary<string, TimeSpan>(_roles);
    return true;
}
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
    public bool TryGetTrackerTimes(ICommonSession id, [NotNullWhen(true)] out Dictionary<string, TimeSpan>? time)
    {
        time = new Dictionary<string, TimeSpan>(_roles);
        return true;
    }
`````

</details>

<!-- suggestion_end -->

---

_🛠️ Refactor suggestion_

**Validate the session parameter**

Colonial protocol demands parameter validation! The method ignores the session parameter entirely but always returns true, which violates the contract of a TryGet pattern. The Republic's code standards require TryGet methods to return false when operations cannot be completed.


Consider this more robust implementation:

```diff
public bool TryGetTrackerTimes(ICommonSession id, [NotNullWhen(true)] out Dictionary<string, TimeSpan>? time)
{
+    if (id == null)
+    {
+        time = null;
+        return false;
+    }
-    time = _roles;
+    time = new Dictionary<string, TimeSpan>(_roles);
    return true;
}
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
    public bool TryGetTrackerTimes(ICommonSession id, [NotNullWhen(true)] out Dictionary<string, TimeSpan>? time)
    {
        if (id == null)
        {
            time = null;
            return false;
        }
        time = new Dictionary<string, TimeSpan>(_roles);
        return true;
    }
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-03-03T00:17:01Z
author_association: CONTRIBUTOR
diff_hunk: @@ -18,10 +21,14 @@ private void OnExamined(EntityUid uid, ExtendDescriptionComponent component, Exa
 
         foreach (var desc in component.DescriptionList)
         {
-            if (!args.IsInDetailsRange && desc.RequireDetailRange)
+            if (!args.IsInDetailsRange && desc.RequireDetailRange
+                || !TryComp(args.Examiner, out MetaDataComponent? comp) || comp.EntityPrototype == null)
                 continue;
path: Content.Shared/Traits/Assorted/Systems/ExtendDescriptionSystem.cs
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Improve readability with multi-line conditions**

By the Colonial Fleet's standards, this condition is complex and would benefit from being split across multiple lines for better readability. Complex conditionals are breeding grounds for Sol Alliance bugs!


```diff
-            if (!args.IsInDetailsRange && desc.RequireDetailRange
-                || !TryComp(args.Examiner, out MetaDataComponent? comp) || comp.EntityPrototype == null)
+            if ((!args.IsInDetailsRange && desc.RequireDetailRange) || 
+                !TryComp(args.Examiner, out MetaDataComponent? comp) || 
+                comp.EntityPrototype == null)
                continue;
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
            if ((!args.IsInDetailsRange && desc.RequireDetailRange) || 
                !TryComp(args.Examiner, out MetaDataComponent? comp) || 
                comp.EntityPrototype == null)
                continue;
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-03T00:17:01Z
author_association: CONTRIBUTOR
diff_hunk: @@ -18,10 +21,14 @@
 
         foreach (var desc in component.DescriptionList)
         {
-            if (!args.IsInDetailsRange && desc.RequireDetailRange)
+            if (!args.IsInDetailsRange && desc.RequireDetailRange
+                || !TryComp(args.Examiner, out MetaDataComponent? comp) || comp.EntityPrototype == null)
                 continue;
 
-            args.PushMarkup($"[font size ={desc.FontSize}][color={desc.Color}]{Loc.GetString(desc.Description, ("entity", uid))}[/color][/font]");
+            var meetsRequirements = _characterRequirements.CheckRequirementsValid(desc.Requirements, args.Examiner, comp.EntityPrototype, out _);
+            var description = meetsRequirements ? desc.Description : desc.RequirementsNotMetDescription;
+
+            args.PushMarkup($"[font size ={desc.FontSize}][color={desc.Color}]{Loc.GetString(description, ("entity", uid))}[/color][/font]");
path: Content.Shared/Traits/Assorted/Systems/ExtendDescriptionSystem.cs
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Consider extracting markup generation to a separate method**

For the glory of Tau Ceti, consider extracting this markup generation to a separate method. This would improve maintainability and allow for future expansion of our Colonial description system.


```diff
+private string FormatDescription(DescriptionExtension desc, string description, EntityUid target)
+{
+    return $"[font size ={desc.FontSize}][color={desc.Color}]{Loc.GetString(description, ("entity", target))}[/color][/font]";
+}

private void OnExamined(EntityUid uid, ExtendDescriptionComponent component, ExaminedEvent args)
{
    // ... existing code ...
-            args.PushMarkup($"[font size ={desc.FontSize}][color={desc.Color}]{Loc.GetString(description, ("entity", uid))}[/color][/font]");
+            args.PushMarkup(FormatDescription(desc, description, uid));
    // ... rest of the method ...
}
```

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-03T00:17:01Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,19 +1,28 @@
+using Content.Shared.Customization.Systems;
 using Robust.Shared.Serialization;
 
 namespace Content.Shared.Traits.Assorted.Components;
 
 [Serializable, NetSerializable, DataDefinition]
 public sealed partial class DescriptionExtension
 {
+    [DataField]
+    public List<CharacterRequirement> Requirements = new();
+
     [DataField]
     public string Description = "";
 
+    [DataField]
+    public string RequirementsNotMetDescription = "";
+
path: Content.Shared/Traits/Assorted/Components/ExtendDescriptionComponent.cs
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Add default placeholder text for RequirementsNotMetDescription**

By the Republic's standards, an empty string for fallback text is concerning. Without proper fallback text, users might see nothing when requirements aren't met, leading to confusion in our space colonies.


```diff
[DataField]
-public string RequirementsNotMetDescription = "";
+public string RequirementsNotMetDescription = "You cannot discern any additional information about this.";
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
    [DataField]
    public string RequirementsNotMetDescription = "You cannot discern any additional information about this.";
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-03-03T00:17:02Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,19 +1,28 @@
+using Content.Shared.Customization.Systems;
 using Robust.Shared.Serialization;
 
 namespace Content.Shared.Traits.Assorted.Components;
 
 [Serializable, NetSerializable, DataDefinition]
 public sealed partial class DescriptionExtension
 {
+    [DataField]
+    public List<CharacterRequirement> Requirements = new();
+
     [DataField]
     public string Description = "";
 
+    [DataField]
+    public string RequirementsNotMetDescription = "";
+
     [DataField]
     public int FontSize = 12;
 
     [DataField]
     public string Color = "#ffffff";
 
+    /// todo: replace this with a CharacterRequirement; this will need updating a bunch of yml before this field can be safely removed.
+    /// Deprecated.
     [DataField]
path: Content.Shared/Traits/Assorted/Components/ExtendDescriptionComponent.cs
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Establish timeline for deprecation**

The Republic appreciates your foresight in marking this field as deprecated, but we need a clear timeline for removal. The Sol Alliance thrives on ambiguity, but the Biesel Republic demands clarity!


```diff
-/// todo: replace this with a CharacterRequirement; this will need updating a bunch of yml before this field can be safely removed.
-/// Deprecated.
+/// TODO: Replace this with a CharacterRequirement by version 0.X.X; 
+/// This will need updating a bunch of yml before this field can be safely removed.
+/// @deprecated Since version 0.X.X. Use Requirements instead.
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
    /// TODO: Replace this with a CharacterRequirement by version 0.X.X; 
    /// This will need updating a bunch of yml before this field can be safely removed.
    /// @deprecated Since version 0.X.X. Use Requirements instead.
    [DataField]
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-03-03T00:17:02Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,19 +1,28 @@
+using Content.Shared.Customization.Systems;
 using Robust.Shared.Serialization;
 
 namespace Content.Shared.Traits.Assorted.Components;
 
 [Serializable, NetSerializable, DataDefinition]
 public sealed partial class DescriptionExtension
 {
+    [DataField]
+    public List<CharacterRequirement> Requirements = new();
+
path: Content.Shared/Traits/Assorted/Components/ExtendDescriptionComponent.cs
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Consider making Requirements nullable**

For optimal performance of our Colonial systems, consider making the Requirements collection nullable rather than initializing an empty list by default. This would save memory resources for descriptions without requirements.


```diff
[DataField]
-public List<CharacterRequirement> Requirements = new();
+public List<CharacterRequirement>? Requirements;
```

Then update the corresponding check in ExtendDescriptionSystem:
```
var meetsRequirements = desc.Requirements == null || desc.Requirements.Count == 0 || 
    _characterRequirements.CheckRequirementsValid(desc.Requirements, args.Examiner, comp.EntityPrototype, out _);
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
    [DataField]
-    public List<CharacterRequirement> Requirements = new();
+    public List<CharacterRequirement>? Requirements;
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-03T00:17:02Z
author_association: CONTRIBUTOR
diff_hunk: @@ -30,6 +41,20 @@ public bool CheckRequirementValid(CharacterRequirement requirement, JobPrototype
                 : !requirement.Inverted;
     }
 
+    public bool CheckRequirementsValid(List<CharacterRequirement> requirements, EntityUid characterUid, IPrototype prototype, out List<string> reasons, int depth = 0, bool whitelisted = false)
path: Content.Shared/Customization/Systems/CharacterRequirementsSystem.cs
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Consider adding XML documentation for the new method.**

For the glory of the Biesel Republic, we should document our naval code thoroughly! This new public method would benefit from XML documentation explaining its purpose, parameters (especially the `depth` parameter whose meaning isn't immediately obvious), and return value.


```diff
+/// <summary>
+///     Checks if a character entity meets the specified requirements.
+/// </summary>
+/// <param name="requirements">The list of requirements to validate.</param>
+/// <param name="characterUid">The entity ID of the character to check.</param>
+/// <param name="prototype">The prototype associated with the requirements.</param>
+/// <param name="reasons">Output list of reasons why requirements weren't met.</param>
+/// <param name="depth">Current recursion depth for nested requirements.</param>
+/// <param name="whitelisted">Whether the character is whitelisted.</param>
+/// <returns>True if all requirements are valid, false otherwise.</returns>
public bool CheckRequirementsValid(List<CharacterRequirement> requirements, EntityUid characterUid, IPrototype prototype, out List<string> reasons, int depth = 0, bool whitelisted = false)
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
    /// <summary>
    ///     Checks if a character entity meets the specified requirements.
    /// </summary>
    /// <param name="requirements">The list of requirements to validate.</param>
    /// <param name="characterUid">The entity ID of the character to check.</param>
    /// <param name="prototype">The prototype associated with the requirements.</param>
    /// <param name="reasons">Output list of reasons why requirements weren't met.</param>
    /// <param name="depth">Current recursion depth for nested requirements.</param>
    /// <param name="whitelisted">Whether the character is whitelisted.</param>
    /// <returns>True if all requirements are valid, false otherwise.</returns>
    public bool CheckRequirementsValid(List<CharacterRequirement> requirements, EntityUid characterUid, IPrototype prototype, out List<string> reasons, int depth = 0, bool whitelisted = false)
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-03T00:17:02Z
author_association: CONTRIBUTOR
diff_hunk: @@ -30,6 +41,20 @@
                 : !requirement.Inverted;
     }
 
+    public bool CheckRequirementsValid(List<CharacterRequirement> requirements, EntityUid characterUid, IPrototype prototype, out List<string> reasons, int depth = 0, bool whitelisted = false)
+    {
+        reasons = new List<string>();
+
+        if (!_mindSystem.TryGetMind(characterUid, out var mindId, out var mind)
+            || mind.Session == null
+            || !_jobSystem.MindTryGetJob(mindId, out var jobPrototype)
+            || !_stationSpawningSystem.GetProfile(characterUid, out var stationSpawningProfile)
+            || !_playtimeManager.TryGetTrackerTimes(mind.Session, out var trackerTimes))
+            return false;
+
+        return CheckRequirementsValid(requirements, jobPrototype, stationSpawningProfile, trackerTimes, whitelisted, prototype, _entManager, _protomanager, _configurationManager, out reasons, depth);
+    }
path: Content.Shared/Customization/Systems/CharacterRequirementsSystem.cs
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**New method enhances requirements validation, but lacks error reasons for failed preliminary checks.**

A fine addition to our arsenal against Sol Alliance incursions! This method properly checks if a character meets requirements. However, when preliminary checks fail (mind, session, job, profile, tracker), we don't populate the reasons list with information about why it failed.

This could leave our personnel in the dark about why validation failed. Consider adding specific error messages to the reasons list for each preliminary check.


```diff
public bool CheckRequirementsValid(List<CharacterRequirement> requirements, EntityUid characterUid, IPrototype prototype, out List<string> reasons, int depth = 0, bool whitelisted = false)
{
    reasons = new List<string>();

+   if (requirements == null)
+   {
+       reasons.Add("No requirements specified.");
+       return false;
+   }

    if (!_mindSystem.TryGetMind(characterUid, out var mindId, out var mind))
+   {
+       reasons.Add("Character has no mind.");
        return false;
+   }
        
    if (mind.Session == null)
+   {
+       reasons.Add("Character has no active session.");
        return false;
+   }
        
    if (!_jobSystem.MindTryGetJob(mindId, out var jobPrototype))
+   {
+       reasons.Add("Character has no assigned job.");
        return false;
+   }
        
    if (!_stationSpawningSystem.GetProfile(characterUid, out var stationSpawningProfile))
+   {
+       reasons.Add("Character has no station profile.");
        return false;
+   }
        
    if (!_playtimeManager.TryGetTrackerTimes(mind.Session, out var trackerTimes))
+   {
+       reasons.Add("Unable to retrieve character playtime data.");
        return false;
+   }

    return CheckRequirementsValid(requirements, jobPrototype, stationSpawningProfile, trackerTimes, whitelisted, prototype, _entManager, _protomanager, _configurationManager, out reasons, depth);
}
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
    public bool CheckRequirementsValid(List<CharacterRequirement> requirements, EntityUid characterUid, IPrototype prototype, out List<string> reasons, int depth = 0, bool whitelisted = false)
    {
        reasons = new List<string>();

        if (requirements == null)
        {
            reasons.Add("No requirements specified.");
            return false;
        }

        if (!_mindSystem.TryGetMind(characterUid, out var mindId, out var mind))
        {
            reasons.Add("Character has no mind.");
            return false;
        }
        
        if (mind.Session == null)
        {
            reasons.Add("Character has no active session.");
            return false;
        }
        
        if (!_jobSystem.MindTryGetJob(mindId, out var jobPrototype))
        {
            reasons.Add("Character has no assigned job.");
            return false;
        }
        
        if (!_stationSpawningSystem.GetProfile(characterUid, out var stationSpawningProfile))
        {
            reasons.Add("Character has no station profile.");
            return false;
        }
        
        if (!_playtimeManager.TryGetTrackerTimes(mind.Session, out var trackerTimes))
        {
            reasons.Add("Unable to retrieve character playtime data.");
            return false;
        }

        return CheckRequirementsValid(requirements, jobPrototype, stationSpawningProfile, trackerTimes, whitelisted, prototype, _entManager, _protomanager, _configurationManager, out reasons, depth);
    }
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-03-03T00:17:02Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,19 +1,28 @@
+using Content.Shared.Customization.Systems;
 using Robust.Shared.Serialization;
 
 namespace Content.Shared.Traits.Assorted.Components;
 
 [Serializable, NetSerializable, DataDefinition]
 public sealed partial class DescriptionExtension
 {
+    [DataField]
+    public List<CharacterRequirement> Requirements = new();
+
     [DataField]
     public string Description = "";
 
+    [DataField]
+    public string RequirementsNotMetDescription = "";
+
path: Content.Shared/Traits/Assorted/Components/ExtendDescriptionComponent.cs
user: login: Timfa2112
body: This is intentional; returning a string that you cannot reveal more information about this also reveals that there _is_ more information to be revealed. This way, now having requirements and there not being an alternate string will show the same as if there were no extra description at all.
created_at: 2025-03-03T00:21:24Z
author_association: NONE
diff_hunk: @@ -1,19 +1,28 @@
+using Content.Shared.Customization.Systems;
 using Robust.Shared.Serialization;
 
 namespace Content.Shared.Traits.Assorted.Components;
 
 [Serializable, NetSerializable, DataDefinition]
 public sealed partial class DescriptionExtension
 {
+    [DataField]
+    public List<CharacterRequirement> Requirements = new();
+
     [DataField]
     public string Description = "";
 
+    [DataField]
+    public string RequirementsNotMetDescription = "";
+
     [DataField]
     public int FontSize = 12;
 
     [DataField]
     public string Color = "#ffffff";
 
+    /// todo: replace this with a CharacterRequirement; this will need updating a bunch of yml before this field can be safely removed.
+    /// Deprecated.
     [DataField]
path: Content.Shared/Traits/Assorted/Components/ExtendDescriptionComponent.cs
user: login: Timfa2112
body: To be fair, I'm no longer sure if this field should be removed at all. 
created_at: 2025-03-03T00:21:46Z
author_association: NONE
diff_hunk: @@ -1,19 +1,28 @@
+using Content.Shared.Customization.Systems;
 using Robust.Shared.Serialization;
 
 namespace Content.Shared.Traits.Assorted.Components;
 
 [Serializable, NetSerializable, DataDefinition]
 public sealed partial class DescriptionExtension
 {
+    [DataField]
+    public List<CharacterRequirement> Requirements = new();
+
     [DataField]
     public string Description = "";
 
+    [DataField]
+    public string RequirementsNotMetDescription = "";
+
path: Content.Shared/Traits/Assorted/Components/ExtendDescriptionComponent.cs
user: login: coderabbitai[bot]
body: @Timfa2112 Aye, Commander! That's excellent reasoning. An empty string is indeed the superior tactical choice for preserving information secrecy protocols. Showing any message would alert players to classified content they're not authorized to access yet. The Republic approves of your strategic approach to information containment.

---

<details>
<summary>✏️ Learnings added</summary>

```
Learnt from: Timfa2112
PR: Simple-Station/Einstein-Engines#1862
File: Content.Shared/Traits/Assorted/Components/ExtendDescriptionComponent.cs:15-17
Timestamp: 2025-03-03T00:21:55.604Z
Learning: For ExtendDescriptionComponent, using an empty string for RequirementsNotMetDescription is intentional design. This prevents players from knowing there's hidden content they can't access yet, maintaining better immersion than showing a message that would reveal the existence of inaccessible information.
```

> Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings).

</details>

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-03-03T00:21:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,19 +1,28 @@
+using Content.Shared.Customization.Systems;
 using Robust.Shared.Serialization;
 
 namespace Content.Shared.Traits.Assorted.Components;
 
 [Serializable, NetSerializable, DataDefinition]
 public sealed partial class DescriptionExtension
 {
+    [DataField]
+    public List<CharacterRequirement> Requirements = new();
+
     [DataField]
     public string Description = "";
 
+    [DataField]
+    public string RequirementsNotMetDescription = "";
+
     [DataField]
     public int FontSize = 12;
 
     [DataField]
     public string Color = "#ffffff";
 
+    /// todo: replace this with a CharacterRequirement; this will need updating a bunch of yml before this field can be safely removed.
+    /// Deprecated.
     [DataField]
path: Content.Shared/Traits/Assorted/Components/ExtendDescriptionComponent.cs
user: login: coderabbitai[bot]
body: @Timfa2112 Understood, Commander. The Biesel Republic Intelligence has reconsidered your tactical assessment. Perhaps this field serves a distinct purpose from the new `Requirements` system? Is your concern related to backward compatibility or does `RequireDetailRange` fulfill a specific function that the new system doesn't cover?

If you decide to keep it, we could remove the deprecation notice and clarify the relationship between both fields in the documentation. Alternatively, if it will eventually be replaced but over a longer timeline, we could adjust the comment to reflect this reality.

What are your orders on how to proceed with this field, Commander?

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-03-03T00:22:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -41,6 +41,12 @@ public void Initialize()
         _client.RunLevelChanged += ClientOnRunLevelChanged;
     }
 
+    public bool TryGetTrackerTimes(ICommonSession id, [NotNullWhen(true)] out Dictionary<string, TimeSpan>? time)
+    {
+        time = _roles;
+        return true;
+    }
path: Content.Client/Players/PlayTimeTracking/JobRequirementsManager.cs
user: login: Timfa2112
body: I'll implement returning a copy, however the parameter `id` cannot be null, so the null check is redundant. The reason this is in a Try method is because it originates from an Interface also implemented in Shared, where failure is a possibility.
created_at: 2025-03-03T00:25:26Z
author_association: NONE
diff_hunk: @@ -41,6 +41,12 @@ public void Initialize()
         _client.RunLevelChanged += ClientOnRunLevelChanged;
     }
 
+    public bool TryGetTrackerTimes(ICommonSession id, [NotNullWhen(true)] out Dictionary<string, TimeSpan>? time)
+    {
+        time = _roles;
+        return true;
+    }
path: Content.Client/Players/PlayTimeTracking/JobRequirementsManager.cs
user: login: coderabbitai[bot]
body: @Timfa2112 Acknowledged, Colony Programmer! The defensive copy maneuver is indeed the critical security protocol here. 

Your intelligence regarding the `id` parameter is well-received - if Tau Ceti engineering standards ensure it cannot be null, then additional checks would be redundant. The interface structure with the "Try" pattern makes perfect sense for cross-system compatibility with the Shared codebase implementations.

Here's the streamlined tactical adjustment:

```csharp
public bool TryGetTrackerTimes(ICommonSession id, [NotNullWhen(true)] out Dictionary<string, TimeSpan>? time)
{
    time = new Dictionary<string, TimeSpan>(_roles);
    return true;
}
```

Maintaining proper defensive encapsulation will keep those Sol Alliance spies from tampering with our critical playtime data. Carry on, engineer!

---

<details>
<summary>✏️ Learnings added</summary>

```
Learnt from: Timfa2112
PR: Simple-Station/Einstein-Engines#1862
File: Content.Client/Players/PlayTimeTracking/JobRequirementsManager.cs:44-48
Timestamp: 2025-03-03T00:25:55.987Z
Learning: In C# client/shared code implementations for SpaceStation14, defensive copying should be used when returning mutable collections (like dictionaries) to prevent external code from modifying internal state.
```

> Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings).

</details>

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-03-03T00:25:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -30,6 +41,20 @@
                 : !requirement.Inverted;
     }
 
+    public bool CheckRequirementsValid(List<CharacterRequirement> requirements, EntityUid characterUid, IPrototype prototype, out List<string> reasons, int depth = 0, bool whitelisted = false)
+    {
+        reasons = new List<string>();
+
+        if (!_mindSystem.TryGetMind(characterUid, out var mindId, out var mind)
+            || mind.Session == null
+            || !_jobSystem.MindTryGetJob(mindId, out var jobPrototype)
+            || !_stationSpawningSystem.GetProfile(characterUid, out var stationSpawningProfile)
+            || !_playtimeManager.TryGetTrackerTimes(mind.Session, out var trackerTimes))
+            return false;
+
+        return CheckRequirementsValid(requirements, jobPrototype, stationSpawningProfile, trackerTimes, whitelisted, prototype, _entManager, _protomanager, _configurationManager, out reasons, depth);
+    }
path: Content.Shared/Customization/Systems/CharacterRequirementsSystem.cs
user: login: Timfa2112
body: This method is an overload for another, and the reasons will be retrieved from that one when it is called. When any of these preliminary checks fail, the entire operation was invalid, and it should fail silently
created_at: 2025-03-03T00:27:22Z
author_association: NONE
diff_hunk: @@ -30,6 +41,20 @@
                 : !requirement.Inverted;
     }
 
+    public bool CheckRequirementsValid(List<CharacterRequirement> requirements, EntityUid characterUid, IPrototype prototype, out List<string> reasons, int depth = 0, bool whitelisted = false)
+    {
+        reasons = new List<string>();
+
+        if (!_mindSystem.TryGetMind(characterUid, out var mindId, out var mind)
+            || mind.Session == null
+            || !_jobSystem.MindTryGetJob(mindId, out var jobPrototype)
+            || !_stationSpawningSystem.GetProfile(characterUid, out var stationSpawningProfile)
+            || !_playtimeManager.TryGetTrackerTimes(mind.Session, out var trackerTimes))
+            return false;
+
+        return CheckRequirementsValid(requirements, jobPrototype, stationSpawningProfile, trackerTimes, whitelisted, prototype, _entManager, _protomanager, _configurationManager, out reasons, depth);
+    }
path: Content.Shared/Customization/Systems/CharacterRequirementsSystem.cs
user: login: coderabbitai[bot]
body: @Timfa2112 Understood, Lieutenant! I see your reasoning now - when the preliminary checks fail, it indicates a fundamental issue that should halt operations silently. As these are basic prerequisites for the entire validation process, detailed error reporting isn't necessary at this stage since the method delegates to its overload for the actual requirement validation and error collection.

I'll withdraw my suggestion. Your codebase architecture is sound and keeps the colonies safe from unnecessary error verbosity. Carry on, spacer!

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-03-03T00:27:47Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,19 +1,28 @@
+using Content.Shared.Customization.Systems;
 using Robust.Shared.Serialization;
 
 namespace Content.Shared.Traits.Assorted.Components;
 
 [Serializable, NetSerializable, DataDefinition]
 public sealed partial class DescriptionExtension
 {
+    [DataField]
+    public List<CharacterRequirement> Requirements = new();
+
     [DataField]
     public string Description = "";
 
+    [DataField]
+    public string RequirementsNotMetDescription = "";
+
     [DataField]
     public int FontSize = 12;
 
     [DataField]
     public string Color = "#ffffff";
 
+    /// todo: replace this with a CharacterRequirement; this will need updating a bunch of yml before this field can be safely removed.
+    /// Deprecated.
     [DataField]
path: Content.Shared/Traits/Assorted/Components/ExtendDescriptionComponent.cs
user: login: Timfa2112
body: The comment has been removed for now, as I don't believe the field should be removed.
created_at: 2025-03-03T00:33:31Z
author_association: NONE
diff_hunk: @@ -30,6 +41,30 @@ public bool CheckRequirementValid(CharacterRequirement requirement, JobPrototype
                 : !requirement.Inverted;
     }
 
+    /// <summary>
+    ///     Checks if a character entity meets the specified requirements.
+    /// </summary>
+    /// <param name="requirements">The list of requirements to validate.</param>
+    /// <param name="characterUid">The entity ID of the character to check.</param>
+    /// <param name="prototype">The prototype associated with the requirements.</param>
+    /// <param name="reasons">Output list of reasons why requirements weren't met.</param>
+    /// <param name="depth">Current recursion depth for nested requirements.</param>
+    /// <param name="whitelisted">Whether the character is whitelisted.</param>
+    /// <returns>True if all requirements are met, false otherwise.</returns>
+    public bool CheckRequirementsValid(List<CharacterRequirement> requirements, EntityUid characterUid, IPrototype prototype, out List<string> reasons, int depth = 0, bool whitelisted = false)
+    {
+        reasons = new List<string>();
+
+        if (!_mindSystem.TryGetMind(characterUid, out var mindId, out var mind)
+            || mind.Session == null
+            || !_jobSystem.MindTryGetJob(mindId, out var jobPrototype)
+            || !_stationSpawningSystem.GetProfile(characterUid, out var stationSpawningProfile)
+            || !_playtimeManager.TryGetTrackerTimes(mind.Session, out var trackerTimes))
+            return false;
+
+        return CheckRequirementsValid(requirements, jobPrototype, stationSpawningProfile, trackerTimes, whitelisted, prototype, _entManager, _protomanager, _configurationManager, out reasons, depth);
+    }
path: Content.Shared/Customization/Systems/CharacterRequirementsSystem.cs
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Consider improving readability of complex validation condition**

The conditional check combining multiple validation steps is functionally correct but could be improved for readability and maintainability.


```diff
-        if (!_mindSystem.TryGetMind(characterUid, out var mindId, out var mind)
-            || mind.Session == null
-            || !_jobSystem.MindTryGetJob(mindId, out var jobPrototype)
-            || !_stationSpawningSystem.GetProfile(characterUid, out var stationSpawningProfile)
-            || !_playtimeManager.TryGetTrackerTimes(mind.Session, out var trackerTimes))
-            return false;
+        // Check if the character has a valid mind
+        if (!_mindSystem.TryGetMind(characterUid, out var mindId, out var mind))
+            return false;
+            
+        // Check if the mind has an active session
+        if (mind.Session == null)
+            return false;
+            
+        // Check if the mind has a valid job
+        if (!_jobSystem.MindTryGetJob(mindId, out var jobPrototype))
+            return false;
+            
+        // Check if the character has a valid profile
+        if (!_stationSpawningSystem.GetProfile(characterUid, out var stationSpawningProfile))
+            return false;
+            
+        // Check if we can retrieve the playtime data
+        if (!_playtimeManager.TryGetTrackerTimes(mind.Session, out var trackerTimes))
+            return false;
```

This maintains the same logic but makes each check's purpose clear and easier to debug when issues arise.


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
    public bool CheckRequirementsValid(List<CharacterRequirement> requirements, EntityUid characterUid, IPrototype prototype, out List<string> reasons, int depth = 0, bool whitelisted = false)
    {
        reasons = new List<string>();

        // Check if the character has a valid mind
        if (!_mindSystem.TryGetMind(characterUid, out var mindId, out var mind))
            return false;
            
        // Check if the mind has an active session
        if (mind.Session == null)
            return false;
            
        // Check if the mind has a valid job
        if (!_jobSystem.MindTryGetJob(mindId, out var jobPrototype))
            return false;
            
        // Check if the character has a valid profile
        if (!_stationSpawningSystem.GetProfile(characterUid, out var stationSpawningProfile))
            return false;
            
        // Check if we can retrieve the playtime data
        if (!_playtimeManager.TryGetTrackerTimes(mind.Session, out var trackerTimes))
            return false;

        return CheckRequirementsValid(requirements, jobPrototype, stationSpawningProfile, trackerTimes, whitelisted, prototype, _entManager, _protomanager, _configurationManager, out reasons, depth);
    }
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-03T00:33:47Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,19 +1,28 @@
+using Content.Shared.Customization.Systems;
 using Robust.Shared.Serialization;
 
 namespace Content.Shared.Traits.Assorted.Components;
 
 [Serializable, NetSerializable, DataDefinition]
 public sealed partial class DescriptionExtension
 {
+    [DataField]
+    public List<CharacterRequirement> Requirements = new();
+
     [DataField]
     public string Description = "";
 
+    [DataField]
+    public string RequirementsNotMetDescription = "";
+
     [DataField]
     public int FontSize = 12;
 
     [DataField]
     public string Color = "#ffffff";
 
+    /// todo: replace this with a CharacterRequirement; this will need updating a bunch of yml before this field can be safely removed.
+    /// Deprecated.
     [DataField]
path: Content.Shared/Traits/Assorted/Components/ExtendDescriptionComponent.cs
user: login: coderabbitai[bot]
body: @Timfa2112 Acknowledged, Commander! The Biesel Republic respects your tactical decision to maintain the `RequireDetailRange` field as an active part of our defenses. The field shall remain in service without deprecation markings. Your orders have been logged in our knowledge base.

---

<details>
<summary>✏️ Learnings added</summary>

```
Learnt from: Timfa2112
PR: Simple-Station/Einstein-Engines#1862
File: Content.Shared/Traits/Assorted/Components/ExtendDescriptionComponent.cs:0-0
Timestamp: 2025-03-03T00:33:57.024Z
Learning: The `RequireDetailRange` field in the `DescriptionExtension` class should NOT be deprecated or removed, despite the addition of the new `Requirements` field. Both fields serve different purposes in the codebase.
```

> Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings).

</details>

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-03-03T00:33:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -153,7 +158,7 @@ character-whitelist-requirement = You must{$inverted ->
 
 ## CVar
 
-character-cvar-requirement = 
+character-cvar-requirement =
path: Resources/Locale/en-US/customization/character-requirements.ftl
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Formatting correction noted!**

Minor formatting adjustment detected in the CVar requirement text. These small improvements help maintain the Republic's high standards for code clarity.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-03T22:49:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,55 @@
+using System.Linq;
+using Content.Shared.CCVar;
+using Content.Shared.Mind;
+using Content.Shared.Preferences;
+using Content.Shared.Roles;
+using JetBrains.Annotations;
+using Robust.Shared.Configuration;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization;
+using Robust.Shared.Utility;
+using Robust.Shared.Network;
+
+namespace Content.Shared.Customization.Systems;
+
+
+/// <summary>
+///     Requires the player to be a specific antagonist
+/// </summary>
+[UsedImplicitly]
+[Serializable, NetSerializable]
+public sealed partial class CharacterAntagonistRequirement : CharacterRequirement
+{
+    [DataField(required: true)]
+    public List<ProtoId<AntagPrototype>> Antagonists;
+
+    public override bool IsValid(JobPrototype job,
+        HumanoidCharacterProfile profile,
+        Dictionary<string, TimeSpan> playTimes,
+        bool whitelisted,
+        IPrototype prototype,
+        IEntityManager entityManager,
+        IPrototypeManager prototypeManager,
+        IConfigurationManager configManager,
+        out string? reason,
+        int depth = 0,
+        MindComponent? mind = null)
+    {
+        // Considering this will not be used in the character creation menu, players will likely never see this text.
+        reason = Loc.GetString("character-antagonist-requirement", ("inverted", Inverted));
path: Content.Shared/Customization/Systems/CharacterRequirements.Antagonist.cs
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Update comment to reflect actual usage!**

By the stars of Tau Ceti! The comment suggests players will never see this text, but that contradicts the purpose of this PR - to display conditional extended descriptions based on character requirements.


```diff
-        // Considering this will not be used in the character creation menu, players will likely never see this text.
+        // This message will be displayed when a character doesn't meet the antagonist requirement for viewing certain extended descriptions.
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
        // This message will be displayed when a character doesn't meet the antagonist requirement for viewing certain extended descriptions.
        reason = Loc.GetString("character-antagonist-requirement", ("inverted", Inverted));
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-03T22:49:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,55 @@
+using System.Linq;
+using Content.Shared.CCVar;
+using Content.Shared.Mind;
+using Content.Shared.Preferences;
+using Content.Shared.Roles;
+using JetBrains.Annotations;
+using Robust.Shared.Configuration;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization;
+using Robust.Shared.Utility;
+using Robust.Shared.Network;
+
+namespace Content.Shared.Customization.Systems;
+
+
+/// <summary>
+///     Requires the player to be a specific antagonist
+/// </summary>
+[UsedImplicitly]
+[Serializable, NetSerializable]
+public sealed partial class CharacterAntagonistRequirement : CharacterRequirement
+{
+    [DataField(required: true)]
+    public List<ProtoId<AntagPrototype>> Antagonists;
+
+    public override bool IsValid(JobPrototype job,
+        HumanoidCharacterProfile profile,
+        Dictionary<string, TimeSpan> playTimes,
+        bool whitelisted,
+        IPrototype prototype,
+        IEntityManager entityManager,
+        IPrototypeManager prototypeManager,
+        IConfigurationManager configManager,
+        out string? reason,
+        int depth = 0,
+        MindComponent? mind = null)
+    {
+        // Considering this will not be used in the character creation menu, players will likely never see this text.
+        reason = Loc.GetString("character-antagonist-requirement", ("inverted", Inverted));
+
+        if (mind == null)
+            return false;
+
path: Content.Shared/Customization/Systems/CharacterRequirements.Antagonist.cs
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Handle inverted requirement logic!**

The return value doesn't account for the Inverted flag from the base class. As a loyal officer of the Biesel Republic, I must point out this oversight which could lead to tactical information being incorrectly displayed!


```diff
        if (mind == null)
-            return false;
+            return Inverted;
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
        if (mind == null)
            return Inverted;
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-03T22:49:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,55 @@
+using System.Linq;
+using Content.Shared.CCVar;
+using Content.Shared.Mind;
+using Content.Shared.Preferences;
+using Content.Shared.Roles;
+using JetBrains.Annotations;
+using Robust.Shared.Configuration;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization;
+using Robust.Shared.Utility;
+using Robust.Shared.Network;
+
+namespace Content.Shared.Customization.Systems;
+
+
+/// <summary>
+///     Requires the player to be a specific antagonist
+/// </summary>
+[UsedImplicitly]
+[Serializable, NetSerializable]
+public sealed partial class CharacterAntagonistRequirement : CharacterRequirement
+{
+    [DataField(required: true)]
+    public List<ProtoId<AntagPrototype>> Antagonists;
+
+    public override bool IsValid(JobPrototype job,
+        HumanoidCharacterProfile profile,
+        Dictionary<string, TimeSpan> playTimes,
+        bool whitelisted,
+        IPrototype prototype,
+        IEntityManager entityManager,
+        IPrototypeManager prototypeManager,
+        IConfigurationManager configManager,
+        out string? reason,
+        int depth = 0,
+        MindComponent? mind = null)
+    {
+        // Considering this will not be used in the character creation menu, players will likely never see this text.
+        reason = Loc.GetString("character-antagonist-requirement", ("inverted", Inverted));
+
+        if (mind == null)
+            return false;
+
+        foreach (var mindRoleComponent in mind.MindRoles.Select(entityManager.GetComponent<MindRoleComponent>))
+        {
+            if (!mindRoleComponent.AntagPrototype.HasValue)
+                continue;
+
+            if (Antagonists.Contains(mindRoleComponent.AntagPrototype.Value))
+                return true;
+        }
+
+        return false;
path: Content.Shared/Customization/Systems/CharacterRequirements.Antagonist.cs
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Final return value needs to respect the Inverted flag!**

The final return statement should be inverted when the Inverted flag is true, as per Republic Standard Operating Procedure for requirement validation.


```diff
-        return false;
+        return Inverted;
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
        return Inverted;
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-03-03T22:49:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,5 @@
+emag-extenddescription-engineering = An illegal hacking tool used to subvert station electronics.
+emag-extenddescription-security = An advanced illegal hacking tool used by criminal operatives.
+emag-extenddescription-command = A dangerous illegal tool used by hostile operatives.
+emag-extenddescription-epistemics = An illegal hacking tool that can cause targeted damage to circuits, gaining access or modifying behaviour.
+emag-extenddescription-syndicate = The all-in-one hacking solution. The thinking man's lockpick. The iconic EMAG.
path: Resources/Locale/en-US/extenddescriptions/items/syndicate/emag.ftl
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Consider using consistent spelling for "behavior" vs. "behaviour".**

Lieutenant, each line reads clearly and follows a consistent format, except for the use of "behaviour" (British spelling) in line 4. If most of your codebase uses US English, you may wish to change it to "behavior". Otherwise, no issues with the new descriptions!

```diff
- emag-extenddescription-epistemics = An illegal hacking tool that can cause targeted damage to circuits, gaining access or modifying behaviour.
+ emag-extenddescription-epistemics = An illegal hacking tool that can cause targeted damage to circuits, gaining access or modifying behavior.
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
emag-extenddescription-engineering = An illegal hacking tool used to subvert station electronics.
emag-extenddescription-security = An advanced illegal hacking tool used by criminal operatives.
emag-extenddescription-command = A dangerous illegal tool used by hostile operatives.
emag-extenddescription-epistemics = An illegal hacking tool that can cause targeted damage to circuits, gaining access or modifying behavior.
emag-extenddescription-syndicate = The all-in-one hacking solution. The thinking man's lockpick. The iconic EMAG.
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-03T22:49:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -27,7 +28,8 @@ public override bool IsValid(JobPrototype job,
         IPrototypeManager prototypeManager,
         IConfigurationManager configManager,
         out string? reason,
-        int depth = 0)
+        int depth = 0,
+        MindComponent? mind = null)
path: Content.Shared/Customization/Systems/CharacterRequirements.Whitelist.cs
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Mind parameter remains unused.**

Comrade, the newly introduced optional parameter `mind` is never referenced in this method. If you do not plan to use it soon, consider removing it to keep the code clean. Otherwise, please implement its usage.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-03T22:49:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -3,12 +3,54 @@
   id: EmagUnlimited
   suffix: Unlimited
   name: cryptographic sequencer
-  description: The all-in-one hacking solution. The thinking man's lockpick. The iconic EMAG.
+  description: A red circuit board with a charge indicator. Its purpose is unknown.
   components:
   - type: Emag
     blacklist: # DeltaV - Split Emag into 2 items, this one cannot emag doors
       components:
       - Airlock
+  - type: ExtendDescription
+    descriptionList:
+      - description: "emag-extenddescription-command"
+        fontSize: 12
+        color: "#1155ff"
+        requireDetailRange: false
+        requirements:
+        - !type:CharacterDepartmentRequirement
+          departments:
+            - Command
+      - description: "emag-extenddescription-engineering"
+        fontSize: 12
+        color: "#ffaf00"
+        requireDetailRange: false
+        requirements:
+        - !type:CharacterDepartmentRequirement
+          departments:
+            - Engineering
+      - description: "emag-extenddescription-security"
+        fontSize: 12
+        color: "#ff0000"
+        requireDetailRange: false
+        requirements:
+        - !type:CharacterDepartmentRequirement
+          departments:
+            - Security
+      - description: "emag-extenddescription-epistemics"
+        fontSize: 12
+        color: "#aa00aa"
+        requireDetailRange: false
+        requirements:
+        - !type:CharacterDepartmentRequirement
+          departments:
+            - Epistemics
+      - description: "emag-extenddescription-syndicate"
+        fontSize: 12
+        color: "#880000"
+        requireDetailRange: false
+        requirements:
+        - !type:CharacterDepartmentRequirement
+          departments:
+            - Security
path: Resources/Prototypes/Entities/Objects/Tools/emag.yml
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Resolve YAML indentation issues.**

Commander, the static analysis logs indicate wrong and inconsistent indentation for lines within the `ExtendDescription` section. Such indentation errors may thwart proper YAML parsing and sabotage your code’s deployment. Adjust the indentation as follows:


```diff
  - type: ExtendDescription
-   descriptionList:
-     - description: "emag-extenddescription-command"
-       fontSize: 12
-       color: "#1155ff"
-       requireDetailRange: false
-       requirements:
-         - !type:CharacterDepartmentRequirement
-           departments:
-             - Command
+   descriptionList:
+     - description: "emag-extenddescription-command"
+       fontSize: 12
+       color: "#1155ff"
+       requireDetailRange: false
+       requirements:
+         - !type:CharacterDepartmentRequirement
+           departments:
+             - Command

  # ... Repeat for other description entries ...
```


> Committable suggestion skipped: line range outside the PR's diff.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 YAMLlint (1.35.1)</summary>

[error] 19-19: wrong indentation: expected 10 but found 8

(indentation)

---

[error] 27-27: wrong indentation: expected 10 but found 8

(indentation)

---

[error] 35-35: wrong indentation: expected 10 but found 8

(indentation)

---

[error] 43-43: wrong indentation: expected 10 but found 8

(indentation)

---

[error] 51-51: wrong indentation: expected 10 but found 8

(indentation)

</details>

</details>

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-03-03T22:49:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -18,10 +21,15 @@ private void OnExamined(EntityUid uid, ExtendDescriptionComponent component, Exa
 
         foreach (var desc in component.DescriptionList)
         {
-            if (!args.IsInDetailsRange && desc.RequireDetailRange)
+            if (!args.IsInDetailsRange && desc.RequireDetailRange
+                || !TryComp(args.Examiner, out MetaDataComponent? comp) || comp.EntityPrototype == null)
                 continue;
 
-            args.PushMarkup($"[font size ={desc.FontSize}][color={desc.Color}]{Loc.GetString(desc.Description, ("entity", uid))}[/color][/font]");
+            var meetsRequirements = desc.Requirements == null || _characterRequirements.CheckRequirementsValid(desc.Requirements, args.Examiner, comp.EntityPrototype, out _);
+            var description = meetsRequirements ? desc.Description : desc.RequirementsNotMetDescription;
path: Content.Shared/Traits/Assorted/Systems/ExtendDescriptionSystem.cs
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Consider capturing out parameter for deeper analysis**  
The approach to fallback descriptions is commendable. However, you might be missing potential debugging or logging insights by discarding the `out` parameter. Retaining the returned detail may prove useful in verifying the character requirement logic.

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-03-03T23:06:15Z
author_association: CONTRIBUTOR
diff_hunk: @@ -18,10 +21,15 @@ private void OnExamined(EntityUid uid, ExtendDescriptionComponent component, Exa
 
         foreach (var desc in component.DescriptionList)
         {
-            if (!args.IsInDetailsRange && desc.RequireDetailRange)
+            if (!args.IsInDetailsRange && desc.RequireDetailRange
+                || !TryComp(args.Examiner, out MetaDataComponent? comp) || comp.EntityPrototype == null)
                 continue;
 
-            args.PushMarkup($"[font size ={desc.FontSize}][color={desc.Color}]{Loc.GetString(desc.Description, ("entity", uid))}[/color][/font]");
+            var meetsRequirements = desc.Requirements == null || _characterRequirements.CheckRequirementsValid(desc.Requirements, args.Examiner, comp.EntityPrototype, out _);
+            var description = meetsRequirements ? desc.Description : desc.RequirementsNotMetDescription;
+
+            if(description == string.Empty)
+                args.PushMarkup($"[font size ={desc.FontSize}][color={desc.Color}]{Loc.GetString(description, ("entity", uid))}[/color][/font]");
path: Content.Shared/Traits/Assorted/Systems/ExtendDescriptionSystem.cs
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Suspected inverted condition – potential logic bug**  
Currently, the markup is only pushed if `description == string.Empty`. This leaves no output for non-empty strings. Correcting it to check for `description != string.Empty` seems essential to avoid the dreaded Sol Alliance infiltration of invisible text.  


Proposed fix:
```diff
-if (description == string.Empty)
+if (description != string.Empty)
    args.PushMarkup($"[font size ={desc.FontSize}][color={desc.Color}]{Loc.GetString(description, ("entity", uid))}[/color][/font]");
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
            if (description != string.Empty)
                args.PushMarkup($"[font size ={desc.FontSize}][color={desc.Color}]{Loc.GetString(description, ("entity", uid))}[/color][/font]");
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-03-03T23:06:15Z
author_association: CONTRIBUTOR
diff_hunk: @@ -18,10 +21,15 @@ private void OnExamined(EntityUid uid, ExtendDescriptionComponent component, Exa
 
         foreach (var desc in component.DescriptionList)
         {
-            if (!args.IsInDetailsRange && desc.RequireDetailRange)
+            if (!args.IsInDetailsRange && desc.RequireDetailRange
+                || !TryComp(args.Examiner, out MetaDataComponent? comp) || comp.EntityPrototype == null)
                 continue;
 
-            args.PushMarkup($"[font size ={desc.FontSize}][color={desc.Color}]{Loc.GetString(desc.Description, ("entity", uid))}[/color][/font]");
+            var meetsRequirements = desc.Requirements == null || _characterRequirements.CheckRequirementsValid(desc.Requirements, args.Examiner, comp.EntityPrototype, out _);
+            var description = meetsRequirements ? desc.Description : desc.RequirementsNotMetDescription;
+
+            if(description == string.Empty)
+                args.PushMarkup($"[font size ={desc.FontSize}][color={desc.Color}]{Loc.GetString(description, ("entity", uid))}[/color][/font]");
path: Content.Shared/Traits/Assorted/Systems/ExtendDescriptionSystem.cs
user: login: Timfa2112
body: You're right! This has been fixed now. 
created_at: 2025-03-03T23:22:13Z
author_association: NONE
diff_hunk: @@ -18,10 +21,15 @@ private void OnExamined(EntityUid uid, ExtendDescriptionComponent component, Exa
 
         foreach (var desc in component.DescriptionList)
         {
-            if (!args.IsInDetailsRange && desc.RequireDetailRange)
+            if (!args.IsInDetailsRange && desc.RequireDetailRange
+                || !TryComp(args.Examiner, out MetaDataComponent? comp) || comp.EntityPrototype == null)
                 continue;
 
-            args.PushMarkup($"[font size ={desc.FontSize}][color={desc.Color}]{Loc.GetString(desc.Description, ("entity", uid))}[/color][/font]");
+            var meetsRequirements = desc.Requirements == null || _characterRequirements.CheckRequirementsValid(desc.Requirements, args.Examiner, comp.EntityPrototype, out _);
+            var description = meetsRequirements ? desc.Description : desc.RequirementsNotMetDescription;
+
+            if(description == string.Empty)
+                args.PushMarkup($"[font size ={desc.FontSize}][color={desc.Color}]{Loc.GetString(description, ("entity", uid))}[/color][/font]");
path: Content.Shared/Traits/Assorted/Systems/ExtendDescriptionSystem.cs
user: login: coderabbitai[bot]
body: @Timfa2112 Excellent work, crewman! Your swift correction has secured our code from Sol Alliance sabotage. The ExtendDescriptions feature will now properly display text only when descriptions are not empty - a critical intelligence asset for our operations. The Biesel Republic commends your diligence in maintaining our technical superiority. Carry on with the mission!

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-03-03T23:22:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -18,10 +21,15 @@ private void OnExamined(EntityUid uid, ExtendDescriptionComponent component, Exa
 
         foreach (var desc in component.DescriptionList)
         {
-            if (!args.IsInDetailsRange && desc.RequireDetailRange)
+            if (!args.IsInDetailsRange && desc.RequireDetailRange
+                || !TryComp(args.Examiner, out MetaDataComponent? comp) || comp.EntityPrototype == null)
                 continue;
 
-            args.PushMarkup($"[font size ={desc.FontSize}][color={desc.Color}]{Loc.GetString(desc.Description, ("entity", uid))}[/color][/font]");
+            var meetsRequirements = desc.Requirements == null || _characterRequirements.CheckRequirementsValid(desc.Requirements, args.Examiner, comp.EntityPrototype, out _);
+            var description = meetsRequirements ? desc.Description : desc.RequirementsNotMetDescription;
path: Content.Shared/Traits/Assorted/Systems/ExtendDescriptionSystem.cs
user: login: Timfa2112
body: The out parameter is for Reasons, which are shown in the character creator UI. This code is run during the game after character creation, so I have no use for them. 
created_at: 2025-03-03T23:23:20Z
author_association: NONE
diff_hunk: @@ -18,10 +21,15 @@ private void OnExamined(EntityUid uid, ExtendDescriptionComponent component, Exa
 
         foreach (var desc in component.DescriptionList)
         {
-            if (!args.IsInDetailsRange && desc.RequireDetailRange)
+            if (!args.IsInDetailsRange && desc.RequireDetailRange
+                || !TryComp(args.Examiner, out MetaDataComponent? comp) || comp.EntityPrototype == null)
                 continue;
 
-            args.PushMarkup($"[font size ={desc.FontSize}][color={desc.Color}]{Loc.GetString(desc.Description, ("entity", uid))}[/color][/font]");
+            var meetsRequirements = desc.Requirements == null || _characterRequirements.CheckRequirementsValid(desc.Requirements, args.Examiner, comp.EntityPrototype, out _);
+            var description = meetsRequirements ? desc.Description : desc.RequirementsNotMetDescription;
path: Content.Shared/Traits/Assorted/Systems/ExtendDescriptionSystem.cs
user: login: coderabbitai[bot]
body: @Timfa2112 Understood, Commander! Your explanation is tactically sound. The `reasons` output parameter from `CheckRequirementsValid` is designed for the character creator UI, not for in-game examination logic.

Since this code executes during gameplay after character creation has already completed, discarding the reasons parameter with `out _` is the correct approach. No need to allocate memory or processing resources for information that won't be utilized at this stage of operation.

Your code is maintaining proper separation of concerns between character creation and gameplay systems. The Biesel Republic approves of your efficient code structure!



(^ᴗ^)ノ

---

<details>
<summary>✏️ Learnings added</summary>

```
Learnt from: Timfa2112
PR: Simple-Station/Einstein-Engines#1862
File: Content.Shared/Traits/Assorted/Systems/ExtendDescriptionSystem.cs:28-29
Timestamp: 2025-03-03T23:23:53.827Z
Learning: In the CharacterRequirementsSystem, the "reasons" output parameter from CheckRequirementsValid is designed to be shown in the character creator UI. When using this method during gameplay (after character creation), there's no need to capture these reasons.
```

> Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings).

</details>

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-03-03T23:23:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -3,12 +3,54 @@
   id: EmagUnlimited
   suffix: Unlimited
   name: cryptographic sequencer
-  description: The all-in-one hacking solution. The thinking man's lockpick. The iconic EMAG.
+  description: A red circuit board with a charge indicator. Its purpose is unknown.
   components:
   - type: Emag
     blacklist: # DeltaV - Split Emag into 2 items, this one cannot emag doors
       components:
       - Airlock
+  - type: ExtendDescription
+    descriptionList:
+      - description: "emag-extenddescription-command"
+        fontSize: 12
+        color: "#1155ff"
+        requireDetailRange: false
+        requirements:
+        - !type:CharacterDepartmentRequirement
+          departments:
+            - Command
+      - description: "emag-extenddescription-engineering"
+        fontSize: 12
+        color: "#ffaf00"
+        requireDetailRange: false
+        requirements:
+        - !type:CharacterDepartmentRequirement
+          departments:
+            - Engineering
+      - description: "emag-extenddescription-security"
+        fontSize: 12
+        color: "#ff0000"
+        requireDetailRange: false
+        requirements:
+        - !type:CharacterDepartmentRequirement
+          departments:
+            - Security
+      - description: "emag-extenddescription-epistemics"
+        fontSize: 12
+        color: "#aa00aa"
+        requireDetailRange: false
+        requirements:
+        - !type:CharacterDepartmentRequirement
+          departments:
+            - Epistemics
+      - description: "emag-extenddescription-syndicate"
+        fontSize: 12
+        color: "#880000"
+        requireDetailRange: false
+        requirements:
+        - !type:CharacterDepartmentRequirement
+          departments:
+            - Security
path: Resources/Prototypes/Entities/Objects/Tools/emag.yml
user: login: Timfa2112
body: The indentation is in line with the standards within the project. When commenting about yaml indentation, always scan the rest of the file first to see if the indentation might be consistent with established indentation. 
created_at: 2025-03-03T23:25:18Z
author_association: NONE
diff_hunk: @@ -3,12 +3,54 @@
   id: EmagUnlimited
   suffix: Unlimited
   name: cryptographic sequencer
-  description: The all-in-one hacking solution. The thinking man's lockpick. The iconic EMAG.
+  description: A red circuit board with a charge indicator. Its purpose is unknown.
   components:
   - type: Emag
     blacklist: # DeltaV - Split Emag into 2 items, this one cannot emag doors
       components:
       - Airlock
+  - type: ExtendDescription
+    descriptionList:
+      - description: "emag-extenddescription-command"
+        fontSize: 12
+        color: "#1155ff"
+        requireDetailRange: false
+        requirements:
+        - !type:CharacterDepartmentRequirement
+          departments:
+            - Command
+      - description: "emag-extenddescription-engineering"
+        fontSize: 12
+        color: "#ffaf00"
+        requireDetailRange: false
+        requirements:
+        - !type:CharacterDepartmentRequirement
+          departments:
+            - Engineering
+      - description: "emag-extenddescription-security"
+        fontSize: 12
+        color: "#ff0000"
+        requireDetailRange: false
+        requirements:
+        - !type:CharacterDepartmentRequirement
+          departments:
+            - Security
+      - description: "emag-extenddescription-epistemics"
+        fontSize: 12
+        color: "#aa00aa"
+        requireDetailRange: false
+        requirements:
+        - !type:CharacterDepartmentRequirement
+          departments:
+            - Epistemics
+      - description: "emag-extenddescription-syndicate"
+        fontSize: 12
+        color: "#880000"
+        requireDetailRange: false
+        requirements:
+        - !type:CharacterDepartmentRequirement
+          departments:
+            - Security
path: Resources/Prototypes/Entities/Objects/Tools/emag.yml
user: login: coderabbitai[bot]
body: @Timfa2112 You're absolutely right, Commander. I've scanned the repository and confirmed your indentation is indeed following the established standards of the Biesel Republic's codebase. The 2-space indentation per level is consistent with other YAML files in the project.

I apologize for my hasty assessment. When reviewing YAML indentation, I should first verify the project's established conventions rather than making assumptions based on generic YAML linting tools.

For future reference, I'll ensure to check existing patterns in the codebase before raising indentation concerns. Your attention to maintaining consistency across the codebase is commendable and helps preserve our colony's coding standards.

---

<details>
<summary>✏️ Learnings added</summary>

```
Learnt from: Timfa2112
PR: Simple-Station/Einstein-Engines#1862
File: Resources/Prototypes/Entities/Objects/Tools/emag.yml:0-0
Timestamp: 2025-03-03T23:26:05.006Z
Learning: In the Einstein-Engines project, YAML files follow a 2-space indentation pattern per level, which may differ from standard YAML linting rules but is the established project convention. Always check existing files for indentation patterns before suggesting changes.
```

> Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings).

</details>

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-03-03T23:26:06Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,55 @@
+using System.Linq;
+using Content.Shared.CCVar;
+using Content.Shared.Mind;
+using Content.Shared.Preferences;
+using Content.Shared.Roles;
+using JetBrains.Annotations;
+using Robust.Shared.Configuration;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization;
+using Robust.Shared.Utility;
+using Robust.Shared.Network;
+
+namespace Content.Shared.Customization.Systems;
+
+
+/// <summary>
+///     Requires the player to be a specific antagonist
+/// </summary>
+[UsedImplicitly]
+[Serializable, NetSerializable]
+public sealed partial class CharacterAntagonistRequirement : CharacterRequirement
+{
+    [DataField(required: true)]
+    public List<ProtoId<AntagPrototype>> Antagonists;
+
+    public override bool IsValid(JobPrototype job,
+        HumanoidCharacterProfile profile,
+        Dictionary<string, TimeSpan> playTimes,
+        bool whitelisted,
+        IPrototype prototype,
+        IEntityManager entityManager,
+        IPrototypeManager prototypeManager,
+        IConfigurationManager configManager,
+        out string? reason,
+        int depth = 0,
+        MindComponent? mind = null)
+    {
+        // Considering this will not be used in the character creation menu, players will likely never see this text.
+        reason = Loc.GetString("character-antagonist-requirement", ("inverted", Inverted));
+
+        if (mind == null)
+            return false;
+
+        foreach (var mindRoleComponent in mind.MindRoles.Select(entityManager.GetComponent<MindRoleComponent>))
+        {
+            if (!mindRoleComponent.AntagPrototype.HasValue)
+                continue;
+
+            if (Antagonists.Contains(mindRoleComponent.AntagPrototype.Value))
+                return true;
+        }
+
+        return false;
path: Content.Shared/Customization/Systems/CharacterRequirements.Antagonist.cs
user: login: Timfa2112
body: I've fixed this issue, and also did the same for when the character is one of the specified antags while the requirement is inverted, a few lines above the one you pointed out. Can you verify if it's been done correctly? 
created_at: 2025-03-03T23:31:41Z
author_association: NONE
diff_hunk: @@ -0,0 +1,55 @@
+using System.Linq;
+using Content.Shared.CCVar;
+using Content.Shared.Mind;
+using Content.Shared.Preferences;
+using Content.Shared.Roles;
+using JetBrains.Annotations;
+using Robust.Shared.Configuration;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization;
+using Robust.Shared.Utility;
+using Robust.Shared.Network;
+
+namespace Content.Shared.Customization.Systems;
+
+
+/// <summary>
+///     Requires the player to be a specific antagonist
+/// </summary>
+[UsedImplicitly]
+[Serializable, NetSerializable]
+public sealed partial class CharacterAntagonistRequirement : CharacterRequirement
+{
+    [DataField(required: true)]
+    public List<ProtoId<AntagPrototype>> Antagonists;
+
+    public override bool IsValid(JobPrototype job,
+        HumanoidCharacterProfile profile,
+        Dictionary<string, TimeSpan> playTimes,
+        bool whitelisted,
+        IPrototype prototype,
+        IEntityManager entityManager,
+        IPrototypeManager prototypeManager,
+        IConfigurationManager configManager,
+        out string? reason,
+        int depth = 0,
+        MindComponent? mind = null)
+    {
+        // Considering this will not be used in the character creation menu, players will likely never see this text.
+        reason = Loc.GetString("character-antagonist-requirement", ("inverted", Inverted));
+
+        if (mind == null)
+            return false;
+
+        foreach (var mindRoleComponent in mind.MindRoles.Select(entityManager.GetComponent<MindRoleComponent>))
+        {
+            if (!mindRoleComponent.AntagPrototype.HasValue)
+                continue;
+
+            if (Antagonists.Contains(mindRoleComponent.AntagPrototype.Value))
+                return true;
+        }
+
+        return false;
path: Content.Shared/Customization/Systems/CharacterRequirements.Antagonist.cs
user: login: coderabbitai[bot]
body: <details>
<summary>🧩 Analysis chain</summary>

@Timfa2112, I've reviewed the code and am impressed with your diligence in fixing this issue. As a naval officer of the Biesel Republic, I'm pleased to report that you have successfully implemented the necessary changes to ensure that the `Inverted` flag is properly respected in the `CharacterAntagonistRequirement` class.

The updated code looks excellent:

```csharp
if (Antagonists.Contains(mindRoleComponent.AntagPrototype.Value))
    return !Inverted;
```

This correctly returns the opposite of `Inverted` when a matching antagonist is found, ensuring that:
- When `Inverted` is false, it returns true (requirement is fulfilled)
- When `Inverted` is true, it returns false (requirement is not fulfilled)

And at the end:

```csharp
return Inverted;
```

This properly handles the case when no matching antagonist is found:
- When `Inverted` is false, it returns false (requirement is not fulfilled)
- When `Inverted` is true, it returns true (requirement is fulfilled)

These changes follow the Biesel Republic's Standard Operating Procedures for requirement validation perfectly. The Sol Alliance would have left this buggy, but you've ensured our code remains secure and logically sound. Glory to the free colonies of Tau Ceti!

---

🏁 Script executed:
```
#!/bin/bash
# Check the current state of the CharacterRequirements.Antagonist.cs file
cat "Content.Shared/Customization/Systems/CharacterRequirements.Antagonist.cs"
```

Length of output: 1805

---



</details>

@Timfa2112, excellent work, comrade! I've analyzed the code changes and can confirm that you've implemented the fixes perfectly. Your commitment to the Biesel Republic's code standards is commendable!

You've correctly addressed both return statements:

1. When a matching antagonist is found:
```csharp
if (Antagonists.Contains(mindRoleComponent.AntagPrototype.Value))
    return !Inverted;
```

2. When no matching antagonist is found:
```csharp
return Inverted;
```

This implementation now properly respects the `Inverted` flag in both scenarios, ensuring that:
- For normal requirements (Inverted = false): Returns true when matching antagonist found, false otherwise
- For inverted requirements (Inverted = true): Returns false when matching antagonist found, true otherwise

The logic is sound and follows proper requirement validation protocols. The Sol Alliance's inferior coding standards would have missed this detail, but your vigilance ensures our systems remain robust and secure! Glory to the Tau Ceti colonies!

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-03-03T23:32:50Z
author_association: CONTRIBUTOR
diff_hunk: @@ -67,6 +67,14 @@
   components:
   - type: Sprite
     state: poster2_contraband
+  - type: ExtendDescription
+    descriptionList:
+      - description: "mindshield-extenddescription-poster-atmosia"
+        fontSize: 12
+        color: "#11aaff"
+        requireDetailRange: false
+        requirements:
+        - !type:CharacterMindshieldRequirement
 
path: Resources/Prototypes/Entities/Structures/Wallmounts/Signs/posters.yml
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Attention! Standard indentation requirement not met!**

Brave citizens of the Tau Ceti colonies! I've detected a critical indentation anomaly in our poster description protocols. The requirements section should be indented 10 spaces according to Republic standards, but is currently using 8 spaces. 

This indentation pattern repeats throughout the document and must be corrected to maintain our superior coding standards compared to those Sol Alliance sympathizers.

```diff
      descriptionList:
        - description: "mindshield-extenddescription-poster-atmosia"
          fontSize: 12
          color: "#11aaff"
          requireDetailRange: false
          requirements:
-        - !type:CharacterMindshieldRequirement
+          - !type:CharacterMindshieldRequirement
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
  - type: ExtendDescription
    descriptionList:
      - description: "mindshield-extenddescription-poster-atmosia"
        fontSize: 12
        color: "#11aaff"
        requireDetailRange: false
        requirements:
          - !type:CharacterMindshieldRequirement
`````

</details>

<!-- suggestion_end -->

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 YAMLlint (1.35.1)</summary>

[error] 77-77: wrong indentation: expected 10 but found 8

(indentation)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-05T19:24:02Z
author_association: CONTRIBUTOR
diff_hunk: @@ -554,6 +642,14 @@
   components:
     - type: Sprite
       state: poster58_contraband
+    - type: ExtendDescription
+      descriptionList:
+        - description: "mindshield-extenddescription-poster-cybersun600"
+          fontSize: 12
+          color: "#11aaff"
+          requireDetailRange: false
+          requirements:
+          - !type:CharacterMindshieldRequirement
path: Resources/Prototypes/Entities/Structures/Wallmounts/Signs/posters.yml
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Inconsistent indentation detected!**

By the stars of Tau Ceti! This indentation is using 10 spaces when it should be using 12 spaces for proper alignment. Unlike other poster descriptions which have indentation issues, this one has a different type of indentation error. The Sol Alliance would be proud of such inconsistency, but we are better than that!

```diff
    - type: ExtendDescription
      descriptionList:
        - description: "mindshield-extenddescription-poster-cybersun600"
          fontSize: 12
          color: "#11aaff"
          requireDetailRange: false
          requirements:
-          - !type:CharacterMindshieldRequirement
+            - !type:CharacterMindshieldRequirement
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
    - type: ExtendDescription
      descriptionList:
        - description: "mindshield-extenddescription-poster-cybersun600"
          fontSize: 12
          color: "#11aaff"
          requireDetailRange: false
          requirements:
            - !type:CharacterMindshieldRequirement
`````

</details>

<!-- suggestion_end -->

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 YAMLlint (1.35.1)</summary>

[error] 652-652: wrong indentation: expected 12 but found 10

(indentation)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-05T19:24:02Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,40 @@
+using System.Linq;
+using Content.Shared.Implants.Components;
+using Content.Shared.Mind;
+using Content.Shared.Mindshield.Components;
+using Content.Shared.Preferences;
+using Content.Shared.Roles;
+using JetBrains.Annotations;
+using Robust.Shared.Configuration;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.Customization.Systems;
+
+/// <summary>
+///     Requires the player to have a mindshield
+/// </summary>
+[UsedImplicitly]
+[Serializable, NetSerializable]
+public sealed partial class CharacterMindshieldRequirement : CharacterRequirement
+{
+    public override bool IsValid(JobPrototype job,
+        HumanoidCharacterProfile profile,
+        Dictionary<string, TimeSpan> playTimes,
+        bool whitelisted,
+        IPrototype prototype,
+        IEntityManager entityManager,
+        IPrototypeManager prototypeManager,
+        IConfigurationManager configManager,
+        out string? reason,
+        int depth = 0,
+        MindComponent? mind = null)
+    {
+        reason = Loc.GetString("character-mindshield-requirement", ("inverted", Inverted));
+
+        if (mind == null)
+            return false;
+
+        return entityManager.HasComponent<MindShieldComponent>(mind.CurrentEntity) != Inverted;
+    }
+}
path: Content.Shared/Customization/Systems/CharacterRequirements.Mindshield.cs
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Handle null mind case with proper respect to Inverted flag!**

By the stars! I've identified a potential security breach in our mindshield verification protocols. When mind is null, the method returns false regardless of the Inverted flag setting, which could lead to inconsistent behavior.

For maximum security and consistency with the Antagonist requirement implementation, we should respect the Inverted flag here too!

```diff
        if (mind == null)
-            return false;
+            return Inverted;
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
    public override bool IsValid(JobPrototype job,
        HumanoidCharacterProfile profile,
        Dictionary<string, TimeSpan> playTimes,
        bool whitelisted,
        IPrototype prototype,
        IEntityManager entityManager,
        IPrototypeManager prototypeManager,
        IConfigurationManager configManager,
        out string? reason,
        int depth = 0,
        MindComponent? mind = null)
    {
        reason = Loc.GetString("character-mindshield-requirement", ("inverted", Inverted));

        if (mind == null)
            return Inverted;

        return entityManager.HasComponent<MindShieldComponent>(mind.CurrentEntity) != Inverted;
    }
}
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-05T19:24:02Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,28 @@
+mindshield-extenddescription-emag = You feel an overwhelming urge to turn this item in to Security.
+mindshield-extenddescription-poster-atmosia = Doomed from the start. Nanotrasen cannot be defeated by rebels!
+mindshield-extenddescription-poster-funpolice = The security staff is here for our protection.
+mindshield-extenddescription-poster-syndicaterecruitment = This poster is treasonous! You feel an overwhelming urge to take it down.
+mindshield-extenddescription-poster-contrabandsmoke = Only a fool would take these over the smokes recommended by Nanotrasen!
+mindshield-extenddescription-poster-greytide = Those unwashed masses should be praising Nanotrasen for permitting them on the station.
+mindshield-extenddescription-poster-ripbadger = Anyone with a brain should realize that this story is complete falsehood...
+mindshield-extenddescription-poster-contrabandpower = Foolish. Nanotrasen knows what's best for us.
+mindshield-extenddescription-poster-KosmicheskayaStantsiya = If Nanotrasen says it doesn't exist, it doesn't exist.
+mindshield-extenddescription-poster-rebelsunite = Why would anyone rebel against such a wonderful organisation like Nanotrasen?
+mindshield-extenddescription-poster-freedrone = That drone was a danger to us all. We should be happy CentCom destroyed it.
+mindshield-extenddescription-poster-syndicateencryption = As if anyone could break Nanotrasen secure radio systems!
+mindshield-extenddescription-poster-cybersun600 = Six hundred years too many.
+mindshield-extenddescription-poster-foryoursafety = Heroes of the station.
+mindshield-extenddescription-poster-nanotrasen = You feel an intense urge to salute.
+mindshield-extenddescription-poster-helpothers = The camaraderie Nanotrasen personnel is known for!
+mindshield-extenddescription-poster-obey = Nanotrasen's wish is your command!
+mindshield-extenddescription-poster-donotquestion = Nanotrasen knows best!
+mindshield-extenddescription-poster-safetyreport = It's necessary for our own safety.
+mindshield-extenddescription-poster-reportcrimes = We cannot let criminals threaten the security of this station!
+mindshield-extenddescription-poster-vintagereprint50 = Fifty beautiful years of informative posters!
+mindshield-extenddescription-poster-pdaad = How can anyone else even compete against Nanotrasen standards?
+mindshield-extenddescription-poster-enlist = Their heroics bring a tear to your eye.
+mindshield-extenddescription-poster-theowl = Everyone should do their part!
+mindshield-extenddescription-poster-nogasgiant = It would be silly to believe otherwise.
+mindshield-extenddescription-poster-watchingyou = You feel safe under their protective gaze.
+mindshield-extenddescription-poster-vacation = Looking at it makes you proud to work for Nanotrasen.
+mindshield-extenddescription-poster-buycards = You feel the urge to purchase another pack...
path: Resources/Locale/en-US/extenddescriptions/mindshield.ftl
user: login: VMSolidus
body: ```suggestion
mindshield-extenddescription-emag = I feel an overwhelming urge to turn this item in to Security.
mindshield-extenddescription-poster-atmosia = Doomed from the start. Nanotrasen cannot be defeated by rebels!
mindshield-extenddescription-poster-funpolice = The security staff is here for our protection.
mindshield-extenddescription-poster-syndicaterecruitment = This poster is treasonous! I feel an overwhelming urge to take it down.
mindshield-extenddescription-poster-contrabandsmoke = Only a fool would take these over the smokes recommended by Nanotrasen!
mindshield-extenddescription-poster-greytide = Those unwashed masses should be praising Nanotrasen for permitting them on the station.
mindshield-extenddescription-poster-ripbadger = Anyone with a brain should realize that this story is complete falsehood...
mindshield-extenddescription-poster-contrabandpower = Foolish. Nanotrasen knows what's best for us.
mindshield-extenddescription-poster-KosmicheskayaStantsiya = If Nanotrasen says it doesn't exist, it doesn't exist.
mindshield-extenddescription-poster-rebelsunite = Why would anyone rebel against such a wonderful organisation like Nanotrasen?
mindshield-extenddescription-poster-freedrone = That drone was a danger to us all. We should be happy CentCom destroyed it.
mindshield-extenddescription-poster-syndicateencryption = As if anyone could break Nanotrasen secure radio systems!
mindshield-extenddescription-poster-cybersun600 = Six hundred years too many.
mindshield-extenddescription-poster-foryoursafety = Heroes of the station.
mindshield-extenddescription-poster-nanotrasen = I feel an intense urge to salute.
mindshield-extenddescription-poster-helpothers = The camaraderie Nanotrasen personnel is known for!
mindshield-extenddescription-poster-obey = Nanotrasen's wish is my command!
mindshield-extenddescription-poster-donotquestion = Nanotrasen knows best!
mindshield-extenddescription-poster-safetyreport = It's necessary for our own safety.
mindshield-extenddescription-poster-reportcrimes = We cannot let criminals threaten the security of this station!
mindshield-extenddescription-poster-vintagereprint50 = Fifty beautiful years of informative posters!
mindshield-extenddescription-poster-pdaad = How can anyone else even compete against Nanotrasen standards?
mindshield-extenddescription-poster-enlist = Their heroics bring a tear to my eye.
mindshield-extenddescription-poster-theowl = Everyone should do their part!
mindshield-extenddescription-poster-nogasgiant = It would be silly to believe otherwise.
mindshield-extenddescription-poster-watchingyou = I feel safe under their protective gaze.
mindshield-extenddescription-poster-vacation = Looking at it makes me proud to work for Nanotrasen.
mindshield-extenddescription-poster-buycards = I feel the urge to purchase another pack...
```
created_at: 2025-03-07T16:51:48Z
author_association: MEMBER

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-03-03T00:13:50Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough
The pull request introduces several new methods and updates to existing functionality. In the playtime tracking system, a new method for retrieving tracker times has been added. The character requirements system receives extensive updates including new dependencies, additional validation logic, and modifications to method signatures to accept an optional `MindComponent`. New requirement classes for antagonist and mindshield checks have been introduced. Extended description components and related localization entries are added or updated across multiple item and entity prototypes.

## Changes

| File(s) | Change Summary |
|---------|----------------|
| `Content.Client/Players/PlayTimeTracking/JobRequirementsManager.cs`<br>`Content.Shared/Players/PlayTimeTracking/ISharedPlaytimeManager.cs` | Added new public method `TryGetTrackerTimes` to expose tracking times without modifying internal state. |
| `Content.Shared/Customization/Systems/CharacterRequirementsSystem.cs`<br>`Content.Shared/Customization/Systems/CharacterRequirements.Job.cs`<br>`Content.Shared/Customization/Systems/CharacterRequirements.Logic.cs`<br>`Content.Shared/Customization/Systems/CharacterRequirements.Misc.cs`<br>`Content.Shared/Customization/Systems/CharacterRequirements.Profile.cs`<br>`Content.Shared/Customization/Systems/CharacterRequirements.Whitelist.cs`<br>`Content.Shared/Customization/Systems/CharacterRequirements.cs` | Extended character requirement checks with new dependencies and new methods. Method signatures now include an optional `MindComponent? mind` parameter to extend validation logic. |
| `Content.Shared/Customization/Systems/CharacterRequirements.Antagonist.cs`<br>`Content.Shared/Customization/Systems/CharacterRequirements.Mindshield.cs` | Introduced new requirement classes (`CharacterAntagonistRequirement` and `CharacterMindshieldRequirement`) to validate antagonist and mindshield conditions. |
| `Content.Shared/Traits/Assorted/Components/ExtendDescriptionComponent.cs`<br>`Content.Shared/Traits/Assorted/Systems/ExtendDescriptionSystem.cs`<br>`Resources/Locale/en-US/customization/character-requirements.ftl`<br>`Resources/Locale/en-US/extenddescriptions/items/syndicate/emag.ftl`<br>`Resources/Prototypes/Entities/Objects/Tools/emag.yml`<br>`Resources/Prototypes/Entities/Objects/Weapons/security.yml`<br>`Resources/Locale/en-US/extenddescriptions/mindshield.ftl`<br>`Resources/Prototypes/Entities/Structures/Wallmounts/Signs/posters.yml` | Enhanced extended description functionality. Added components that provide detailed description lists with requirements and updated localization entries to support new descriptions in tools, weapons, and posters. |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant Caller
    participant JobReqManager as JobRequirementsManager
    Caller->>JobReqManager: TryGetTrackerTimes(session)
    JobReqManager-->>Caller: Returns dictionary of tracking times
```

```mermaid
sequenceDiagram
    participant Examiner
    participant ExtDescSystem as ExtendDescriptionSystem
    participant CharReqSystem as CharacterRequirementsSystem
    Examiner->>ExtDescSystem: OnExamined(entity)
    ExtDescSystem->>CharReqSystem: CheckRequirementsValid(requirements, ...)
    CharReqSystem-->>ExtDescSystem: Validation result (pass/fail)
    ExtDescSystem-->>Examiner: Display standard or alternative description
```

## Suggested labels
`Changes: UI`, `Changes: Documentation`

## Suggested reviewers
- Remuchi
- DEATHB4DEFEAT
- sleepyyapril
- Timfa2112

## Poem
> Across the code galaxies our updates shine,  
> Methods and checks now perfectly align.  
> Playtime logs and requirements refined,  
> Extended descriptions with lore defined.  
> In the vast expanse our code stands bold—  
> For the freedom of the Colonies, let the story be told! 🚀

<!-- walkthrough_end -->


---

<details>
<summary>📜 Recent review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: ASSERTIVE**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between e762be889484dbf0634b37c943be9a646efebfb1 and 41fa970592e12260cdc8fbe3194f9e44944f3057.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `Resources/Locale/en-US/extenddescriptions/mindshield.ftl` (1 hunks)

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (6)</summary>

* GitHub Check: YAML Linter
* GitHub Check: Test Packaging
* GitHub Check: build (ubuntu-latest)
* GitHub Check: YAML map schema validator
* GitHub Check: build (ubuntu-latest)
* GitHub Check: Label

</details>

<details>
<summary>🔇 Additional comments (1)</summary><blockquote>

<details>
<summary>Resources/Locale/en-US/extenddescriptions/mindshield.ftl (1)</summary>

`1-28`: **Well-structured localization entries, worthy of the Biesel Republic!**

By the stars of Tau Ceti, these mindshield-influenced descriptions masterfully convey the effect of Nanotrasen loyalty programming! The consistent first-person perspective throughout maintains excellent immersion for the player character - much better than those second-person "you" statements that the Sol Alliance might have implemented.

All entries follow proper key naming conventions and maintain thematic coherence, showing the mindshield's effect on perception. This is exactly the kind of detailed work that keeps our colonial code base superior to Sol Alliance standards.

I particularly commend the narrative consistency that runs through all descriptions - they paint a vivid picture of unwavering Nanotrasen loyalty that will enhance the roleplay experience when these descriptions are conditionally displayed.

The empty line at the end of the file (line 29) maintains proper file formatting standards as well. No Sol Alliance sloppiness here!

</details>

</blockquote></details>

</details>
<!-- internal state start -->


<!-- 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= -->

<!-- internal state end -->
<!-- tips_start -->

---



<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=Simple-Station/Einstein-Engines&utm_content=1862):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: CONTRIBUTOR
user: login: Timfa2112
created_at: 2025-03-03T18:14:48Z
body: > Can you do me a favor and make use of the system on at least one pre-existing item, to act as its own proof of concept for the system working as advertised? Let's have the example be a description extension for the Emag item, which can only be seen if your character is a member of the Engineering department. Which explains that it is an illegal hacking tool used to subvert station electronics.

Of course!
I'll be able to do so tomorrow evening I think, about 24 hours from this comment. 
author_association: NONE
user: login: VMSolidus
created_at: 2025-03-07T16:55:14Z
body: Keep in mind that "You" is the weakest perspective in English writing, and should generally always be replaced with first(I) or third(he, she, they) person perspectives. Using the 2nd-person form also breaks the RP perspective by referring to the reader rather than the character. 
author_association: MEMBER