CharacterRequirements on ExtendDescriptions (#1862)

<!--
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 `🆑` to change the name that appears
in the changelog (ex: `🆑 Death`)
Leaving it blank will default to your GitHub display name
This includes all available types for the changelog
-->

🆑
- 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 -->

---------

Signed-off-by: VMSolidus <evilexecutive@gmail.com>
Co-authored-by: VMSolidus <evilexecutive@gmail.com>
(cherry picked from commit 0640f1f54619a95a4360a79b870654b2c4a1e433)
This commit is contained in:
Timfa
2025-03-07 18:09:03 +01:00
committed by Spatison
parent 1984e05d44
commit 4618b94807
19 changed files with 540 additions and 30 deletions

View File

@@ -41,6 +41,12 @@ public sealed partial class JobRequirementsManager : ISharedPlaytimeManager
_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)

View File

@@ -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;
}
}

View File

@@ -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<string>();
var depts = prototypeManager.EnumeratePrototypes<DepartmentPrototype>();
@@ -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<string>();
@@ -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))

View File

@@ -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<CharacterRequirementsSystem>()
.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<string>();
@@ -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<string>();
var succeeded = false;

View File

@@ -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;
}
}

View File

@@ -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;
/// <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 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))

View File

@@ -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);

View File

@@ -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"))

View File

@@ -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
);
}

View File

@@ -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 sealed class CharacterRequirementsSystem : EntitySystem
: !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 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;

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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]");
}
}
}

View File

@@ -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]{""}

View File

@@ -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.

View File

@@ -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...

View File

@@ -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

View File

@@ -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

View File

@@ -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