diff --git a/Content.Server/Abilities/Psionics/PsionicAbilitiesSystem.cs b/Content.Server/Abilities/Psionics/PsionicAbilitiesSystem.cs index 005078242b0..1ac5db70151 100644 --- a/Content.Server/Abilities/Psionics/PsionicAbilitiesSystem.cs +++ b/Content.Server/Abilities/Psionics/PsionicAbilitiesSystem.cs @@ -10,6 +10,8 @@ using Robust.Shared.Serialization.Manager; using Content.Shared.Psionics; using System.Linq; +using Robust.Server.Player; +using Content.Server.Chat.Managers; namespace Content.Server.Abilities.Psionics { @@ -24,6 +26,8 @@ public sealed class PsionicAbilitiesSystem : EntitySystem [Dependency] private readonly SharedActionsSystem _actions = default!; [Dependency] private readonly SharedPopupSystem _popups = default!; [Dependency] private readonly ISerializationManager _serialization = default!; + [Dependency] private readonly IPlayerManager _playerManager = default!; + [Dependency] private readonly IChatManager _chatManager = default!; private ProtoId _pool = "RandomPsionicPowerPool"; private const string GenericInitializationMessage = "generic-power-initialization-feedback"; @@ -38,9 +42,6 @@ public override void Initialize() /// /// Special use-case for a InnatePsionicPowers, which allows an entity to start with any number of Psionic Powers. /// - /// - /// - /// private void InnatePowerStartup(EntityUid uid, InnatePsionicPowersComponent comp, ComponentStartup args) { // Any entity with InnatePowers should also be psionic, but in case they aren't already... @@ -64,7 +65,6 @@ private void OnPsionicShutdown(EntityUid uid, PsionicComponent component, Compon /// The most shorthand route to creating a Psion. If an entity is not already psionic, it becomes one. This also adds a random new PsionicPower. /// To create a "Latent Psychic"(Psion with no powers) just add or ensure the PsionicComponent normally. /// - /// public void AddPsionics(EntityUid uid) { if (Deleted(uid)) @@ -77,7 +77,6 @@ public void AddPsionics(EntityUid uid) /// Pretty straightforward, adds a random psionic power to a given Entity. If that Entity is not already Psychic, it will be made one. /// If an entity already has all possible powers, this will not add any new ones. /// - /// public void AddRandomPsionicPower(EntityUid uid) { // We need to EnsureComp here to make sure that we aren't iterating over a component that: @@ -95,7 +94,7 @@ public void AddRandomPsionicPower(EntityUid uid) _prototypeManager.TryIndex(s, out var p) && psionic.ActivePowers.Contains(p)); - if (newPool is null) + if (newPool.Count == 0) return; var newProto = _random.Pick(newPool); @@ -110,11 +109,7 @@ public void AddRandomPsionicPower(EntityUid uid) /// /// Initializes a new Psionic Power on a given entity, assuming the entity does not already have said power initialized. /// - /// - /// - /// - /// - public void InitializePsionicPower(EntityUid uid, PsionicPowerPrototype proto, PsionicComponent psionic, bool playPopup = true) + public void InitializePsionicPower(EntityUid uid, PsionicPowerPrototype proto, PsionicComponent psionic, bool playFeedback = true) { if (!_prototypeManager.HasIndex(proto.ID) || psionic.ActivePowers.Contains(proto)) @@ -126,31 +121,23 @@ public void InitializePsionicPower(EntityUid uid, PsionicPowerPrototype proto, P AddPsionicPowerComponents(uid, proto); AddPsionicStatSources(proto, psionic); RefreshPsionicModifiers(uid, psionic); - - if (playPopup) - _popups.PopupEntity(Loc.GetString(GenericInitializationMessage), uid, uid, PopupType.MediumCaution); - // TODO: Replace this with chat message: _popups.PopupEntity(proto.InitializationFeedback, uid, uid, PopupType.MediumCaution); + SendFeedbackMessage(uid, proto, playFeedback); + //SendFeedbackAudio(uid, proto, playPopup); // TODO: This one is coming next! } /// /// Initializes a new Psionic Power on a given entity, assuming the entity does not already have said power initialized. /// - /// - /// - /// - /// - public void InitializePsionicPower(EntityUid uid, PsionicPowerPrototype proto, bool playPopup = true) + public void InitializePsionicPower(EntityUid uid, PsionicPowerPrototype proto, bool playFeedback = true) { EnsureComp(uid, out var psionic); - InitializePsionicPower(uid, proto, psionic, playPopup); + InitializePsionicPower(uid, proto, psionic, playFeedback); } /// /// Updates a Psion's casting stats, call this anytime a system adds a new source of Amp or Damp. /// - /// - /// public void RefreshPsionicModifiers(EntityUid uid, PsionicComponent comp) { var ampModifier = 0f; @@ -173,7 +160,6 @@ public void RefreshPsionicModifiers(EntityUid uid, PsionicComponent comp) /// Updates a Psion's casting stats, call this anytime a system adds a new source of Amp or Damp. /// Variant function for systems that didn't already have the PsionicComponent. /// - /// public void RefreshPsionicModifiers(EntityUid uid) { if (!TryComp(uid, out var comp)) @@ -186,7 +172,6 @@ public void RefreshPsionicModifiers(EntityUid uid) /// A more advanced form of removing powers. Mindbreaking not only removes all psionic powers, /// it also disables the possibility of obtaining new ones. /// - /// public void MindBreak(EntityUid uid) { RemoveAllPsionicPowers(uid, true); @@ -196,8 +181,6 @@ public void MindBreak(EntityUid uid) /// Remove all Psionic powers, with accompanying actions, components, and casting stat sources, from a given Psion. /// Optionally, the Psion can also be rendered permanently non-Psionic. /// - /// - /// public void RemoveAllPsionicPowers(EntityUid uid, bool mindbreak = false) { if (!TryComp(uid, out var psionic) @@ -239,9 +222,6 @@ public void RemoveAllPsionicPowers(EntityUid uid, bool mindbreak = false) /// /// Add all actions associated with a specific Psionic Power /// - /// - /// - /// private void AddPsionicActions(EntityUid uid, PsionicPowerPrototype proto, PsionicComponent psionic) { foreach (var id in proto.Actions) @@ -258,8 +238,6 @@ private void AddPsionicActions(EntityUid uid, PsionicPowerPrototype proto, Psion /// /// Add all components associated with a specific Psionic power. /// - /// - /// private void AddPsionicPowerComponents(EntityUid uid, PsionicPowerPrototype proto) { if (proto.Components is null) @@ -279,8 +257,6 @@ private void AddPsionicPowerComponents(EntityUid uid, PsionicPowerPrototype prot /// /// Update the Amplification and Dampening sources of a Psion to include a new Power. /// - /// - /// private void AddPsionicStatSources(PsionicPowerPrototype proto, PsionicComponent psionic) { if (proto.AmplificationModifier != 0) @@ -290,11 +266,40 @@ private void AddPsionicStatSources(PsionicPowerPrototype proto, PsionicComponent psionic.DampeningSources.Add(proto.Name, proto.DampeningModifier); } + /// + /// Displays a message to alert the player when they have obtained a new psionic power. These generally will not play for Innate powers. + /// Chat messages of this nature should be written in the first-person. + /// Popup feedback should be no more than a sentence, while the full Initialization Feedback can be as much as a paragraph of text. + /// + private void SendFeedbackMessage(EntityUid uid, PsionicPowerPrototype proto, bool playFeedback = true) + { + if (!playFeedback + || !_playerManager.TryGetSessionByEntity(uid, out var session) + || session is null) + return; + + if (proto.InitializationPopup is null) + _popups.PopupEntity(Loc.GetString(GenericInitializationMessage), uid, uid, PopupType.MediumCaution); + else _popups.PopupEntity(Loc.GetString(proto.InitializationPopup), uid, uid, PopupType.MediumCaution); + + if (proto.InitializationFeedback is null) + return; + + if (!Loc.TryGetString(proto.InitializationFeedback, out var feedback)) + return; + var feedbackMessage = $"[font size={proto.InitializationFeedbackFontSize}][color={proto.InitializationFeedbackColor}]{feedback}[/color][/font]"; + _chatManager.ChatMessageToOne( + proto.InitializationFeedbackChannel, + feedbackMessage, + feedbackMessage, + EntityUid.Invalid, + false, + session.Channel); + } + /// /// Remove all Psychic Actions listed in an entity's Psionic Component. Unfortunately, removing actions associated with a specific Power Prototype is not supported. /// - /// - /// private void RemovePsionicActions(EntityUid uid, PsionicComponent psionic) { if (psionic.Actions is null) @@ -307,8 +312,6 @@ private void RemovePsionicActions(EntityUid uid, PsionicComponent psionic) /// /// Remove all Components associated with a specific Psionic Power. /// - /// - /// private void RemovePsionicPowerComponents(EntityUid uid, PsionicPowerPrototype proto) { if (proto.Components is null) @@ -327,8 +330,6 @@ private void RemovePsionicPowerComponents(EntityUid uid, PsionicPowerPrototype p /// /// Remove all stat sources associated with a specific Psionic Power. /// - /// - /// private void RemovePsionicStatSources(EntityUid uid, PsionicPowerPrototype proto, PsionicComponent psionic) { if (proto.AmplificationModifier != 0) diff --git a/Content.Shared/Psionics/PsionicPowerPrototype.cs b/Content.Shared/Psionics/PsionicPowerPrototype.cs index 621a3ceb114..3d389f6cdbe 100644 --- a/Content.Shared/Psionics/PsionicPowerPrototype.cs +++ b/Content.Shared/Psionics/PsionicPowerPrototype.cs @@ -1,3 +1,4 @@ +using Content.Shared.Chat; using Robust.Shared.Prototypes; namespace Content.Shared.Psionics; @@ -36,10 +37,37 @@ public sealed partial class PsionicPowerPrototype : IPrototype public ComponentRegistry Components = new(); /// - /// What message will play as a popup when the power is initialized. + /// What message will be sent to the player as a Popup. + /// If left blank, it will default to the Const "generic-power-initialization-feedback" /// - [DataField(required: true)] - public string InitializationFeedback = "psionic-power-initialization-default"; + [DataField] + public string? InitializationPopup; + + /// + /// What message will be sent to the chat window when the power is initialized. Leave it blank to send no message. + /// Initialization messages won't play for powers that are Innate, only powers obtained during the round. + /// These should generally also be written in the first person, and can be far lengthier than popups. + /// + [DataField] + public string? InitializationFeedback; + + /// + /// What color will the initialization feedback display in the chat window with. + /// + [DataField] + public string InitializationFeedbackColor = "#8A00C2"; + + /// + /// What font size will the initialization message use in chat. + /// + [DataField] + public int InitializationFeedbackFontSize = 12; + + /// + /// Which chat channel will the initialization message use. + /// + [DataField] + public ChatChannel InitializationFeedbackChannel = ChatChannel.Emotes; /// /// What message will this power generate when scanned by a Metempsionic Focused Pulse. diff --git a/Resources/Prototypes/Psionics/psionics.yml b/Resources/Prototypes/Psionics/psionics.yml index d4ae73669f9..dc43e6128e3 100644 --- a/Resources/Prototypes/Psionics/psionics.yml +++ b/Resources/Prototypes/Psionics/psionics.yml @@ -18,7 +18,7 @@ - ActionMassSleep components: - type: MassSleepPower - initializationFeedback: mass-sleep-power-initialization-feedback + # initializationFeedback: mass-sleep-power-initialization-feedback # I apologize, I don't feel like writing a paragraph of feedback for a power that's getting replaced with a new one. metapsionicFeedback: mass-sleep-power-metapsionic-feedback amplificationModifier: 0.5 dampeningModifier: 0.5