From 4618b948074d2dd43a2597f58dd42e79a4eab5f7 Mon Sep 17 00:00:00 2001 From: Timfa Date: Fri, 7 Mar 2025 18:09:03 +0100 Subject: [PATCH] CharacterRequirements on ExtendDescriptions (#1862) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description 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 - [x] Add a bunch of CharacterRequirements to new and existing ExtendDescriptions for contraband or other neat info ---

Media

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)

--- # 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. ## 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. --------- Signed-off-by: VMSolidus Co-authored-by: VMSolidus (cherry picked from commit 0640f1f54619a95a4360a79b870654b2c4a1e433) --- .../JobRequirementsManager.cs | 6 + .../CharacterRequirements.Antagonist.cs | 51 ++++ .../Systems/CharacterRequirements.Job.cs | 16 +- .../Systems/CharacterRequirements.Logic.cs | 10 +- .../CharacterRequirements.Mindshield.cs | 40 ++++ .../Systems/CharacterRequirements.Misc.cs | 6 +- .../Systems/CharacterRequirements.Profile.cs | 31 ++- .../CharacterRequirements.Whitelist.cs | 4 +- .../Systems/CharacterRequirements.cs | 4 +- .../Systems/CharacterRequirementsSystem.cs | 39 +++- .../ISharedPlaytimeManager.cs | 8 +- .../Components/ExtendDescriptionComponent.cs | 7 + .../Systems/ExtendDescriptionSystem.cs | 12 +- .../customization/character-requirements.ftl | 12 +- .../items/syndicate/emag.ftl | 5 + .../en-US/extenddescriptions/mindshield.ftl | 28 +++ .../Entities/Objects/Tools/emag.yml | 55 ++++- .../Entities/Objects/Weapons/security.yml | 18 ++ .../Structures/Wallmounts/Signs/posters.yml | 218 +++++++++++++++++- 19 files changed, 540 insertions(+), 30 deletions(-) create mode 100644 Content.Shared/Customization/Systems/CharacterRequirements.Antagonist.cs create mode 100644 Content.Shared/Customization/Systems/CharacterRequirements.Mindshield.cs create mode 100644 Resources/Locale/en-US/extenddescriptions/items/syndicate/emag.ftl create mode 100644 Resources/Locale/en-US/extenddescriptions/mindshield.ftl diff --git a/Content.Client/Players/PlayTimeTracking/JobRequirementsManager.cs b/Content.Client/Players/PlayTimeTracking/JobRequirementsManager.cs index 9929267aee..dda6f436b2 100644 --- a/Content.Client/Players/PlayTimeTracking/JobRequirementsManager.cs +++ b/Content.Client/Players/PlayTimeTracking/JobRequirementsManager.cs @@ -41,6 +41,12 @@ public sealed partial class JobRequirementsManager : ISharedPlaytimeManager _client.RunLevelChanged += ClientOnRunLevelChanged; } + public bool TryGetTrackerTimes(ICommonSession id, [NotNullWhen(true)] out Dictionary? 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 0000000000..ccc5876dbb --- /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; + +/// +/// Requires the player to be a specific antagonist +/// +[UsedImplicitly] +[Serializable, NetSerializable] +public sealed partial class CharacterAntagonistRequirement : CharacterRequirement +{ + [DataField(required: true)] + public List> Antagonists; + + public override bool IsValid(JobPrototype job, + HumanoidCharacterProfile profile, + Dictionary 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)) + { + 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 c57d870a37..d9a04ed38a 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 sealed partial class CharacterJobRequirement : CharacterRequirement IPrototypeManager prototypeManager, IConfigurationManager configManager, out string? reason, - int depth = 0) + int depth = 0, + MindComponent? mind = null) { var jobs = new List(); var depts = prototypeManager.EnumeratePrototypes(); @@ -83,7 +85,8 @@ public sealed partial class CharacterDepartmentRequirement : CharacterRequiremen IPrototypeManager prototypeManager, IConfigurationManager configManager, out string? reason, - int depth = 0) + int depth = 0, + MindComponent? mind = null) { var departments = new List(); @@ -130,7 +133,8 @@ public sealed partial class CharacterDepartmentTimeRequirement : CharacterRequir 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 sealed partial class CharacterOverallTimeRequirement : CharacterRequireme 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 sealed partial class CharacterPlaytimeRequirement : CharacterRequirement 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 4f6f96bc65..dd60b05a20 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 sealed partial class CharacterLogicAndRequirement : CharacterRequirement IPrototypeManager prototypeManager, IConfigurationManager configManager, out string? reason, - int depth = 0) + int depth = 0, + MindComponent? mind = null) { var succeeded = entityManager.EntitySysManager.GetEntitySystem() .CheckRequirementsValid(Requirements, job, profile, playTimes, whitelisted, prototype, entityManager, @@ -72,7 +74,8 @@ public sealed partial class CharacterLogicOrRequirement : CharacterRequirement IPrototypeManager prototypeManager, IConfigurationManager configManager, out string? reason, - int depth = 0) + int depth = 0, + MindComponent? mind = null) { var succeeded = false; var reasons = new List(); @@ -127,7 +130,8 @@ public sealed partial class CharacterLogicXorRequirement : CharacterRequirement IPrototypeManager prototypeManager, IConfigurationManager configManager, out string? reason, - int depth = 0) + int depth = 0, + MindComponent? mind = null) { var reasons = new List(); 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 0000000000..e2c2b17213 --- /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; + +/// +/// Requires the player to have a mindshield +/// +[UsedImplicitly] +[Serializable, NetSerializable] +public sealed partial class CharacterMindshieldRequirement : CharacterRequirement +{ + public override bool IsValid(JobPrototype job, + HumanoidCharacterProfile profile, + Dictionary 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(mind.CurrentEntity) != Inverted; + } +} diff --git a/Content.Shared/Customization/Systems/CharacterRequirements.Misc.cs b/Content.Shared/Customization/Systems/CharacterRequirements.Misc.cs index f13cb94f39..e143200214 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 @@ using Robust.Shared.Serialization; namespace Content.Shared.Customization.Systems; /// -/// Requires the server to have a specific CVar value. +/// Requires the server to have a specific CVar value. /// [UsedImplicitly, Serializable, NetSerializable,] public sealed partial class CVarRequirement : CharacterRequirement @@ -30,7 +31,8 @@ public sealed partial class CVarRequirement : CharacterRequirement 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 bf6dff7a04..d52d955a58 100644 --- a/Content.Shared/Customization/Systems/CharacterRequirements.Profile.cs +++ b/Content.Shared/Customization/Systems/CharacterRequirements.Profile.cs @@ -2,6 +2,7 @@ using System.Linq; 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 sealed partial class CharacterAgeRequirement : CharacterRequirement IPrototypeManager prototypeManager, IConfigurationManager configManager, out string? reason, - int depth = 0 + int depth = 0, + MindComponent? mind = null ) { var localeString = ""; @@ -76,7 +78,8 @@ public sealed partial class CharacterGenderRequirement : CharacterRequirement IPrototypeManager prototypeManager, IConfigurationManager configManager, out string? reason, - int depth = 0 + int depth = 0, + MindComponent? mind = null ) { reason = Loc.GetString( @@ -106,7 +109,8 @@ public sealed partial class CharacterSexRequirement : CharacterRequirement IPrototypeManager prototypeManager, IConfigurationManager configManager, out string? reason, - int depth = 0 + int depth = 0, + MindComponent? mind = null ) { reason = Loc.GetString( @@ -136,7 +140,8 @@ public sealed partial class CharacterSpeciesRequirement : CharacterRequirement 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 sealed partial class CharacterHeightRequirement : CharacterRequirement 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 sealed partial class CharacterWidthRequirement : CharacterRequirement 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 sealed partial class CharacterWeightRequirement : CharacterRequirement 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 sealed partial class CharacterTraitRequirement : CharacterRequirement 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 sealed partial class CharacterLoadoutRequirement : CharacterRequirement 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 sealed partial class CharacterItemGroupRequirement : CharacterRequirement 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 59f5933732..5e5e4fc6dc 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 sealed partial class CharacterWhitelistRequirement : CharacterRequirement 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 c0acb5ddb8..f10d15e1a0 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 partial class CharacterRequirement 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 533975f740..cbf8a25ae6 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 playTimes, bool whitelisted, IPrototype prototype, @@ -30,10 +41,34 @@ public sealed class CharacterRequirementsSystem : EntitySystem : !requirement.Inverted; } + /// + /// Checks if a character entity meets the specified requirements. + /// + /// The list of requirements to validate. + /// The entity ID of the character to check. + /// The prototype associated with the requirements. + /// Output list of reasons why requirements weren't met. + /// Current recursion depth for nested requirements. + /// Whether the character is whitelisted. + /// True if all requirements are met, false otherwise. + public bool CheckRequirementsValid(List requirements, EntityUid characterUid, IPrototype prototype, out List reasons, int depth = 0, bool whitelisted = false) + { + reasons = new List(); + + 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 requirements, JobPrototype job, HumanoidCharacterProfile profile, Dictionary playTimes, bool whitelisted, IPrototype prototype, IEntityManager entityManager, IPrototypeManager prototypeManager, IConfigurationManager configManager, - out List reasons, int depth = 0) + out List reasons, int depth = 0, MindComponent? mind = null) { reasons = new List(); var valid = true; @@ -44,7 +79,7 @@ public sealed class CharacterRequirementsSystem : EntitySystem // 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 03fd040d0c..23b7f66e09 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? time); +} diff --git a/Content.Shared/Traits/Assorted/Components/ExtendDescriptionComponent.cs b/Content.Shared/Traits/Assorted/Components/ExtendDescriptionComponent.cs index dca8e10d4b..444cf65732 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? 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 0883da1eb8..364376dc9d 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 @@ public sealed class ExtendDescriptionSystem : EntitySystem 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 ddddbc7440..a92a36c51b 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 0000000000..95ca13cd42 --- /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 0000000000..b7cdee3620 --- /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 73753329ca..3be8873e0f 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 740af3a866..e0aba41a4b 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/security.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/security.yml @@ -149,6 +149,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 283599e617..5c77b566f3 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