diff --git a/Content.Server/Abilities/Psionics/PsionicAbilitiesSystem.cs b/Content.Server/Abilities/Psionics/PsionicAbilitiesSystem.cs
index 32e51d3c101..536235b6d63 100644
--- a/Content.Server/Abilities/Psionics/PsionicAbilitiesSystem.cs
+++ b/Content.Server/Abilities/Psionics/PsionicAbilitiesSystem.cs
@@ -12,6 +12,7 @@
using System.Linq;
using Robust.Server.Player;
using Content.Server.Chat.Managers;
+using Content.Server.Psionics.Glimmer;
namespace Content.Server.Abilities.Psionics
{
@@ -122,6 +123,8 @@ public void InitializePsionicPower(EntityUid uid, PsionicPowerPrototype proto, P
AddPsionicStatSources(proto, psionic);
RefreshPsionicModifiers(uid, psionic);
SendFeedbackMessage(uid, proto, playFeedback);
+ UpdatePowerSlots(psionic);
+ //UpdatePsionicDanger(uid, psionic); // TODO: After Glimmer Refactor
//SendFeedbackAudio(uid, proto, playPopup); // TODO: This one is coming next!
}
@@ -297,6 +300,27 @@ private void SendFeedbackMessage(EntityUid uid, PsionicPowerPrototype proto, boo
session.Channel);
}
+ private void UpdatePowerSlots(PsionicComponent psionic)
+ {
+ var slotsUsed = 0;
+ foreach (var power in psionic.ActivePowers)
+ slotsUsed += power.PowerSlotCost;
+
+ psionic.PowerSlotsTaken = slotsUsed;
+ }
+
+ ///
+ /// Psions over a certain power threshold become a glimmer source. This cannot be fully implemented until after I rework Glimmer
+ ///
+ //private void UpdatePsionicDanger(EntityUid uid, PsionicComponent psionic)
+ //{
+ // if (psionic.PowerSlotsTaken <= psionic.PowerSlots)
+ // return;
+ //
+ // EnsureComp(uid, out var glimmerSource);
+ // glimmerSource.SecondsPerGlimmer = 10 / (psionic.PowerSlotsTaken - psionic.PowerSlots);
+ //}
+
///
/// Remove all Psychic Actions listed in an entity's Psionic Component. Unfortunately, removing actions associated with a specific Power Prototype is not supported.
///
diff --git a/Content.Server/Psionics/PsionicsSystem.cs b/Content.Server/Psionics/PsionicsSystem.cs
index 23cf6aeb80b..0d05000a3cd 100644
--- a/Content.Server/Psionics/PsionicsSystem.cs
+++ b/Content.Server/Psionics/PsionicsSystem.cs
@@ -11,139 +11,242 @@
using Robust.Shared.Audio.Systems;
using Robust.Shared.Configuration;
using Robust.Shared.Random;
+using Content.Shared.Popups;
+using Content.Shared.Chat;
+using Robust.Server.Player;
+using Content.Server.Chat.Managers;
+using Robust.Shared.Prototypes;
+using Content.Shared.Psionics;
-namespace Content.Server.Psionics
+namespace Content.Server.Psionics;
+
+public sealed class PsionicsSystem : EntitySystem
{
- public sealed class PsionicsSystem : EntitySystem
+ [Dependency] private readonly IRobustRandom _random = default!;
+ [Dependency] private readonly PsionicAbilitiesSystem _psionicAbilitiesSystem = default!;
+ [Dependency] private readonly StatusEffectsSystem _statusEffects = default!;
+ [Dependency] private readonly ElectrocutionSystem _electrocutionSystem = default!;
+ [Dependency] private readonly MindSwapPowerSystem _mindSwapPowerSystem = default!;
+ [Dependency] private readonly GlimmerSystem _glimmerSystem = default!;
+ [Dependency] private readonly NpcFactionSystem _npcFactonSystem = default!;
+ [Dependency] private readonly IConfigurationManager _cfg = default!;
+ [Dependency] private readonly SharedAudioSystem _audio = default!;
+ [Dependency] private readonly SharedPopupSystem _popups = default!;
+ [Dependency] private readonly IPlayerManager _playerManager = default!;
+ [Dependency] private readonly IChatManager _chatManager = default!;
+ [Dependency] private readonly IPrototypeManager _protoMan = default!;
+
+ private const string BaselineAmplification = "Baseline Amplification";
+ private const string BaselineDampening = "Baseline Dampening";
+
+ // Yes these are a mirror of what's normally default datafields on the PsionicPowerPrototype.
+ // We haven't generated a prototype yet, and I'm not going to duplicate them on the PsionicComponent.
+ private const string PsionicRollFailedMessage = "psionic-roll-failed";
+ private const string PsionicRollFailedColor = "#8A00C2";
+ private const int PsionicRollFailedFontSize = 12;
+ private const ChatChannel PsionicRollFailedChatChannel = ChatChannel.Emotes;
+
+ ///
+ /// Unfortunately, since spawning as a normal role and anything else is so different,
+ /// this is the only way to unify them, for now at least.
+ ///
+ Queue<(PsionicComponent component, EntityUid uid)> _rollers = new();
+ public override void Update(float frameTime)
{
- [Dependency] private readonly IRobustRandom _random = default!;
- [Dependency] private readonly PsionicAbilitiesSystem _psionicAbilitiesSystem = default!;
- [Dependency] private readonly StatusEffectsSystem _statusEffects = default!;
- [Dependency] private readonly ElectrocutionSystem _electrocutionSystem = default!;
- [Dependency] private readonly MindSwapPowerSystem _mindSwapPowerSystem = default!;
- [Dependency] private readonly GlimmerSystem _glimmerSystem = default!;
- [Dependency] private readonly NpcFactionSystem _npcFactonSystem = default!;
- [Dependency] private readonly IConfigurationManager _cfg = default!;
- [Dependency] private readonly SharedAudioSystem _audio = default!;
-
- private const string BaselineAmplification = "Baseline Amplification";
- private const string BaselineDampening = "Baseline Dampening";
-
- ///
- /// Unfortunately, since spawning as a normal role and anything else is so different,
- /// this is the only way to unify them, for now at least.
- ///
- Queue<(PsionicComponent component, EntityUid uid)> _rollers = new();
- public override void Update(float frameTime)
- {
- base.Update(frameTime);
- foreach (var roller in _rollers)
- RollPsionics(roller.uid, roller.component, false);
- _rollers.Clear();
- }
- public override void Initialize()
- {
- base.Initialize();
- SubscribeLocalEvent(OnStartup);
- SubscribeLocalEvent(OnMeleeHit);
- SubscribeLocalEvent(OnStamHit);
+ base.Update(frameTime);
+ foreach (var roller in _rollers)
+ RollPsionics(roller.uid, roller.component, false);
+ _rollers.Clear();
+ }
+ public override void Initialize()
+ {
+ base.Initialize();
+ SubscribeLocalEvent(OnStartup);
+ SubscribeLocalEvent(OnMeleeHit);
+ SubscribeLocalEvent(OnStamHit);
- SubscribeLocalEvent(OnInit);
- SubscribeLocalEvent(OnRemove);
- }
+ SubscribeLocalEvent(OnInit);
+ SubscribeLocalEvent(OnRemove);
+ }
- private void OnStartup(EntityUid uid, PsionicComponent component, MapInitEvent args)
- {
- _rollers.Enqueue((component, uid));
- }
+ private void OnStartup(EntityUid uid, PsionicComponent component, MapInitEvent args)
+ {
+ if (!component.Removable
+ || !component.CanReroll)
+ return;
- private void OnMeleeHit(EntityUid uid, AntiPsionicWeaponComponent component, MeleeHitEvent args)
- {
- foreach (var entity in args.HitEntities)
- {
- if (HasComp(entity))
- {
- _audio.PlayPvs("/Audio/Effects/lightburn.ogg", entity);
- args.ModifiersList.Add(component.Modifiers);
- if (_random.Prob(component.DisableChance))
- _statusEffects.TryAddStatusEffect(entity, component.DisableStatus, TimeSpan.FromSeconds(component.DisableDuration), true, component.DisableStatus);
- }
-
- if (TryComp(entity, out var swapped))
- {
- _mindSwapPowerSystem.Swap(entity, swapped.OriginalEntity, true);
- return;
- }
-
- if (component.Punish && !HasComp(entity) && _random.Prob(component.PunishChances))
- _electrocutionSystem.TryDoElectrocution(args.User, null, component.PunishSelfDamage, TimeSpan.FromSeconds(component.PunishStunDuration), false);
- }
- }
+ CheckPowerCost(uid, component);
+ _rollers.Enqueue((component, uid));
+ }
- private void OnInit(EntityUid uid, PsionicComponent component, ComponentStartup args)
- {
- component.AmplificationSources.Add(BaselineAmplification, _random.NextFloat(component.BaselineAmplification.Item1, component.BaselineAmplification.Item2));
- component.DampeningSources.Add(BaselineDampening, _random.NextFloat(component.BaselineDampening.Item1, component.BaselineDampening.Item2));
+ ///
+ /// On MapInit, PsionicComponent isn't going to contain any powers.
+ /// So before we send a Latent Psychic into the roundstart roll queue, we need to calculate their power cost in advance.
+ ///
+ private void CheckPowerCost(EntityUid uid, PsionicComponent component)
+ {
+ if (!TryComp(uid, out var innate))
+ return;
- if (!component.Removable
- || !TryComp(uid, out var factions)
- || _npcFactonSystem.ContainsFaction(uid, "GlimmerMonster", factions))
- return;
+ var powerCount = 0;
+ foreach (var powerId in innate.PowersToAdd)
+ if (_protoMan.TryIndex(powerId, out var power))
+ powerCount += power.PowerSlotCost;
- _npcFactonSystem.AddFaction(uid, "PsionicInterloper");
- }
+ component.NextPowerCost = 100 * MathF.Pow(2, powerCount);
+ }
- private void OnRemove(EntityUid uid, PsionicComponent component, ComponentRemove args)
+ private void OnMeleeHit(EntityUid uid, AntiPsionicWeaponComponent component, MeleeHitEvent args)
+ {
+ foreach (var entity in args.HitEntities)
+ CheckAntiPsionic(entity, component, args);
+ }
+
+ private void CheckAntiPsionic(EntityUid entity, AntiPsionicWeaponComponent component, MeleeHitEvent args)
+ {
+ if (HasComp(entity))
{
- if (!HasComp(uid))
+ _audio.PlayPvs("/Audio/Effects/lightburn.ogg", entity);
+ args.ModifiersList.Add(component.Modifiers);
+
+ if (!_random.Prob(component.DisableChance))
return;
- _npcFactonSystem.RemoveFaction(uid, "PsionicInterloper");
+ _statusEffects.TryAddStatusEffect(entity, component.DisableStatus, TimeSpan.FromSeconds(component.DisableDuration), true, component.DisableStatus);
}
- private void OnStamHit(EntityUid uid, AntiPsionicWeaponComponent component, TakeStaminaDamageEvent args)
- {
- if (HasComp(args.Target))
- args.FlatModifier += component.PsychicStaminaDamage;
- }
+ if (TryComp(entity, out var swapped))
+ _mindSwapPowerSystem.Swap(entity, swapped.OriginalEntity, true);
- public void RollPsionics(EntityUid uid, PsionicComponent component, bool applyGlimmer = true, float rollEventMultiplier = 1f)
- {
- if (!_cfg.GetCVar(CCVars.PsionicRollsEnabled)
- || !component.Removable)
- return;
+ if (!component.Punish
+ || HasComp(entity)
+ || !_random.Prob(component.PunishChances))
+ return;
+
+ _electrocutionSystem.TryDoElectrocution(args.User, null, component.PunishSelfDamage, TimeSpan.FromSeconds(component.PunishStunDuration), false);
+ }
- // Calculate the initial odds based on the innate potential
- var baselineChance = component.Chance
- * component.PowerRollMultiplier
- + component.PowerRollFlatBonus;
+ private void OnInit(EntityUid uid, PsionicComponent component, ComponentStartup args)
+ {
+ component.AmplificationSources.Add(BaselineAmplification, _random.NextFloat(component.BaselineAmplification.Item1, component.BaselineAmplification.Item2));
+ component.DampeningSources.Add(BaselineDampening, _random.NextFloat(component.BaselineDampening.Item1, component.BaselineDampening.Item2));
- // Increase the initial odds based on Glimmer.
- // TODO: Change this equation when I do my Glimmer Refactor
- baselineChance += applyGlimmer
- ? (float) _glimmerSystem.Glimmer / 1000 //Convert from Glimmer to %chance
- : 0;
+ if (!component.Removable
+ || !TryComp(uid, out var factions)
+ || _npcFactonSystem.ContainsFaction(uid, "GlimmerMonster", factions))
+ return;
- // Certain sources of power rolls provide their own multiplier.
- baselineChance *= rollEventMultiplier;
+ _npcFactonSystem.AddFaction(uid, "PsionicInterloper");
+ }
- // Ask if the Roller has any other effects to contribute, such as Traits.
- var ev = new OnRollPsionicsEvent(uid, baselineChance);
- RaiseLocalEvent(uid, ref ev);
+ private void OnRemove(EntityUid uid, PsionicComponent component, ComponentRemove args)
+ {
+ if (!HasComp(uid))
+ return;
- if (_random.Prob(Math.Clamp(ev.BaselineChance, 0, 1)))
- _psionicAbilitiesSystem.AddPsionics(uid);
- }
+ _npcFactonSystem.RemoveFaction(uid, "PsionicInterloper");
+ }
- public void RerollPsionics(EntityUid uid, PsionicComponent? psionic = null, float bonusMuliplier = 1f)
- {
- if (!Resolve(uid, ref psionic, false)
- || !psionic.Removable
- || psionic.CanReroll)
- return;
+ private void OnStamHit(EntityUid uid, AntiPsionicWeaponComponent component, TakeStaminaDamageEvent args)
+ {
+ if (!HasComp(args.Target))
+ return;
- RollPsionics(uid, psionic, true, bonusMuliplier);
- psionic.CanReroll = true;
- }
+ args.FlatModifier += component.PsychicStaminaDamage;
+ }
+
+ ///
+ /// Now we handle Potentia calculations, the more powers you have, the harder it is to obtain psionics, but the content of your roll carries over to the next roll.
+ /// Your first power costs 100(2^0 is always 1), your second power costs 200, your 3rd power costs 400, and so on. This also considers people with roundstart powers.
+ /// Such that a Mystagogue(who has 3 powers at roundstart) needs 800 Potentia to gain his 4th power.
+ ///
+ ///
+ /// This exponential cost is mainly done to prevent stations from becoming "Space Hogwarts",
+ /// which was a common complaint with Psionic Refactor opening up the opportunity for people to have multiple powers.
+ ///
+ private bool HandlePotentiaCalculations(EntityUid uid, PsionicComponent component, float psionicChance)
+ {
+ component.Potentia += _random.NextFloat(0 + psionicChance, 100 + psionicChance);
+
+ if (component.Potentia < component.NextPowerCost)
+ return false;
+
+ component.Potentia -= component.NextPowerCost;
+ _psionicAbilitiesSystem.AddPsionics(uid);
+ component.NextPowerCost = 100 * MathF.Pow(2, component.PowerSlotsTaken);
+ return true;
+ }
+
+ ///
+ /// Provide the player with feedback about their roll failure, so they don't just think nothing happened.
+ /// TODO: Add an audio cue to this and other areas of psionic player feedback.
+ ///
+ private void HandleRollFeedback(EntityUid uid)
+ {
+ if (!_playerManager.TryGetSessionByEntity(uid, out var session)
+ || !Loc.TryGetString(PsionicRollFailedMessage, out var rollFailedMessage))
+ return;
+
+ _popups.PopupEntity(rollFailedMessage, uid, uid, PopupType.MediumCaution);
+
+ // Popups only last a few seconds, and are easily ignored.
+ // So we also put a message in chat to make it harder to miss.
+ var feedbackMessage = $"[font size={PsionicRollFailedFontSize}][color={PsionicRollFailedColor}]{rollFailedMessage}[/color][/font]";
+ _chatManager.ChatMessageToOne(
+ PsionicRollFailedChatChannel,
+ feedbackMessage,
+ feedbackMessage,
+ EntityUid.Invalid,
+ false,
+ session.Channel);
+ }
+
+ ///
+ /// This function attempts to generate a psionic power by incrementing a Psion's Potentia stat by a random amount, then checking if it beats a certain threshold.
+ /// Please consider going through RerollPsionics or PsionicAbilitiesSystem.InitializePsionicPower instead of this function, particularly if you don't have a good reason to call this directly.
+ ///
+ public void RollPsionics(EntityUid uid, PsionicComponent component, bool applyGlimmer = true, float rollEventMultiplier = 1f)
+ {
+ if (!_cfg.GetCVar(CCVars.PsionicRollsEnabled)
+ || !component.Removable)
+ return;
+
+ // Calculate the initial odds based on the innate potential
+ var baselineChance = component.Chance
+ * component.PowerRollMultiplier
+ + component.PowerRollFlatBonus;
+
+ // Increase the initial odds based on Glimmer.
+ // TODO: Change this equation when I do my Glimmer Refactor
+ baselineChance += applyGlimmer
+ ? (float) _glimmerSystem.Glimmer / 1000 //Convert from Glimmer to %chance
+ : 0;
+
+ // Certain sources of power rolls provide their own multiplier.
+ baselineChance *= rollEventMultiplier;
+
+ // Ask if the Roller has any other effects to contribute, such as Traits.
+ var ev = new OnRollPsionicsEvent(uid, baselineChance);
+ RaiseLocalEvent(uid, ref ev);
+
+ if (HandlePotentiaCalculations(uid, component, ev.BaselineChance))
+ return;
+
+ HandleRollFeedback(uid);
+ }
+
+ ///
+ /// Each person has a single free reroll for their Psionics, which certain conditions can restore.
+ /// This function attempts to "Spend" a reroll, if one is available.
+ ///
+ public void RerollPsionics(EntityUid uid, PsionicComponent? psionic = null, float bonusMuliplier = 1f)
+ {
+ if (!Resolve(uid, ref psionic, false)
+ || !psionic.Removable
+ || !psionic.CanReroll)
+ return;
+
+ RollPsionics(uid, psionic, true, bonusMuliplier);
+ psionic.CanReroll = false;
}
}
diff --git a/Content.Server/StationEvents/Events/NoosphericZapRule.cs b/Content.Server/StationEvents/Events/NoosphericZapRule.cs
index 4be7b6e63fc..96c33612036 100644
--- a/Content.Server/StationEvents/Events/NoosphericZapRule.cs
+++ b/Content.Server/StationEvents/Events/NoosphericZapRule.cs
@@ -36,9 +36,9 @@ protected override void Started(EntityUid uid, NoosphericZapRuleComponent compon
_stunSystem.TryParalyze(psion, TimeSpan.FromSeconds(component.StunDuration), false);
_statusEffectsSystem.TryAddStatusEffect(psion, "Stutter", TimeSpan.FromSeconds(component.StutterDuration), false, "StutteringAccent");
- if (psionicComponent.CanReroll)
+ if (!psionicComponent.CanReroll)
{
- psionicComponent.CanReroll = false;
+ psionicComponent.CanReroll = true;
_popupSystem.PopupEntity(Loc.GetString("noospheric-zap-seize-potential-regained"), psion, psion, Shared.Popups.PopupType.LargeCaution);
}
else
diff --git a/Content.Shared/Psionics/PsionicComponent.cs b/Content.Shared/Psionics/PsionicComponent.cs
index 63529576152..85b7e380fea 100644
--- a/Content.Shared/Psionics/PsionicComponent.cs
+++ b/Content.Shared/Psionics/PsionicComponent.cs
@@ -8,11 +8,19 @@ namespace Content.Shared.Abilities.Psionics
public sealed partial class PsionicComponent : Component
{
///
- /// How close a Psion is to awakening a new power.
- /// TODO: Implement this in a separate PR.
+ /// How close a Psion is to generating a new power. When Potentia reaches the NextPowerCost, it is "Spent" in order to "Buy" a random new power.
+ /// TODO: Psi-Potentiometry should be able to read how much Potentia a person has.
+ ///
+ [DataField]
+ public float Potentia;
+
+ ///
+ /// Each time a Psion rolls for a new power, they roll a number between 0 and 100, adding any relevant modifiers. This number is then added to Potentia,
+ /// meaning that it carries over between rolls. When a character has an amount of potentia equal to at least 100 * 2^(total powers), the potentia is then spent, and a power is generated.
+ /// This variable stores the cost of the next power.
///
[DataField]
- public float Potentia = 0;
+ public float NextPowerCost = 100;
///
/// The baseline chance of obtaining a psionic power when rolling for one.
@@ -24,7 +32,7 @@ public sealed partial class PsionicComponent : Component
/// Whether or not a Psion has an available "Reroll" to spend on attempting to gain powers.
///
[DataField]
- public bool CanReroll;
+ public bool CanReroll = true;
///
/// The Base amount of time (in minutes) this Psion is given the stutter effect if they become mindbroken.
@@ -142,6 +150,18 @@ private set
public float CurrentDampening;
///
+ /// How many "Slots" an entity has for psionic powers. This is not a hard limit, and is instead used for calculating the cost to generate new powers.
+ /// Exceeding this limit causes an entity to become a Glimmer Source.
+ ///
+ [DataField]
+ public int PowerSlots = 1;
+
+ ///
+ /// How many "Slots" are currently occupied by psionic powers.
+ ///
+ [ViewVariables(VVAccess.ReadWrite)]
+ public int PowerSlotsTaken;
+
/// List of descriptors this entity will bring up for psychognomy. Used to remove
/// unneccesary subs for unique psionic entities like e.g. Oracle.
///
diff --git a/Content.Shared/Psionics/PsionicPowerPrototype.cs b/Content.Shared/Psionics/PsionicPowerPrototype.cs
index 3d389f6cdbe..c4a3326733a 100644
--- a/Content.Shared/Psionics/PsionicPowerPrototype.cs
+++ b/Content.Shared/Psionics/PsionicPowerPrototype.cs
@@ -86,4 +86,10 @@ public sealed partial class PsionicPowerPrototype : IPrototype
///
[DataField]
public float DampeningModifier = 0;
+
+ ///
+ /// How many "Power Slots" this power occupies.
+ ///
+ [DataField]
+ public int PowerSlotCost = 1;
}
\ No newline at end of file
diff --git a/Resources/Locale/en-US/psionics/psionic-powers.ftl b/Resources/Locale/en-US/psionics/psionic-powers.ftl
index ad6bbef02bd..c68bb2a4968 100644
--- a/Resources/Locale/en-US/psionics/psionic-powers.ftl
+++ b/Resources/Locale/en-US/psionics/psionic-powers.ftl
@@ -86,7 +86,9 @@ telepathy-power-initialization-feedback =
The voices I've heard all my life begin to clear, yet they do not leave me. Before, they were as incoherent whispers,
now my senses broaden, I come to a realization that they are part of a communal shared hallucination. Behind every voice is a glimmering sentience.
+# Psionic System Messages
mindbreaking-feedback = The light of life vanishes from {CAPITALIZE($entity)}'s eyes, leaving behind a husk pretending at sapience
examine-mindbroken-message =
Eyes unblinking, staring deep into the horizon. {CAPITALIZE($entity)} is a sack of meat pretending it has a soul.
- There is nothing behind its gaze, no evidence there can be found of the divine light of creation.
\ No newline at end of file
+ There is nothing behind its gaze, no evidence there can be found of the divine light of creation.
+psionic-roll-failed = For a moment, my consciousness expands, yet I feel that it is not enough.
\ No newline at end of file
diff --git a/Resources/Prototypes/Nyanotrasen/Roles/Jobs/Epistemics/forensicmantis.yml b/Resources/Prototypes/Nyanotrasen/Roles/Jobs/Epistemics/forensicmantis.yml
index 1f78015cd63..f7a31205706 100644
--- a/Resources/Prototypes/Nyanotrasen/Roles/Jobs/Epistemics/forensicmantis.yml
+++ b/Resources/Prototypes/Nyanotrasen/Roles/Jobs/Epistemics/forensicmantis.yml
@@ -32,6 +32,7 @@
- !type:AddComponentSpecial
components:
- type: Psionic
+ powerSlots: 2
- !type:AddComponentSpecial
components:
- type: InnatePsionicPowers
diff --git a/Resources/Prototypes/Psionics/psionics.yml b/Resources/Prototypes/Psionics/psionics.yml
index b8f798d4b63..461098eab21 100644
--- a/Resources/Prototypes/Psionics/psionics.yml
+++ b/Resources/Prototypes/Psionics/psionics.yml
@@ -119,6 +119,7 @@
- type: UniversalLanguageSpeaker
initializationFeedback: xenoglossy-power-initialization-feedback
metapsionicFeedback: psionic-language-power-feedback # Reuse for telepathy, clairaudience, etc
+ powerSlotCost: 0
- type: psionicPower
id: PsychognomyPower #i.e. reverse physiognomy
@@ -128,6 +129,7 @@
- type: Psychognomist
initializationFeedback: psychognomy-power-initialization-feedback
metapsionicFeedback: psionic-language-power-feedback
+ powerSlotCost: 0
- type: psionicPower
id: TelepathyPower
@@ -137,3 +139,4 @@
- type: Telepathy
initializationFeedback: telepathy-power-initialization-feedback
metapsionicFeedback: psionic-language-power-feedback # Reuse for telepathy, clairaudience, etc
+ powerSlotCost: 0
diff --git a/Resources/Prototypes/Roles/Jobs/Science/research_director.yml b/Resources/Prototypes/Roles/Jobs/Science/research_director.yml
index e2d06f3af96..46d91ee00ee 100644
--- a/Resources/Prototypes/Roles/Jobs/Science/research_director.yml
+++ b/Resources/Prototypes/Roles/Jobs/Science/research_director.yml
@@ -38,6 +38,7 @@
components:
- type: BibleUser # Nyano - Lets them heal with bibles
- type: Psionic # Nyano - They start with telepathic chat
+ powerSlots: 3
- !type:AddImplantSpecial
implants: [ MindShieldImplant ]
- !type:AddComponentSpecial