Skip to content

Commit

Permalink
Psychognomy (#808)
Browse files Browse the repository at this point in the history
<!--
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
Psychognomy is a passive power that gives you a descriptor on the origin
of a telepathic message. There's enough of them that are ambiguous
enough. I also changed Oracle and Sophia a little bit, giving them
innate psionic powers.

:cl: Rane
- add: Added Psychognomy.
- tweak: Reverted Sophia name change.
- tweak: Minor tweaks to psionic chat eligibility and formatting.
- add: Some new utility has been added to player-controlled Oracle or
Sophia. NPC functionality later :^)

---------

Signed-off-by: VMSolidus <[email protected]>
Co-authored-by: VMSolidus <[email protected]>
  • Loading branch information
Elijahrane and VMSolidus authored Sep 3, 2024
1 parent ed8a00a commit b2fc8b3
Show file tree
Hide file tree
Showing 17 changed files with 327 additions and 21 deletions.
171 changes: 171 additions & 0 deletions Content.Server/Chat/TelepathicChatSystem.Psychognomy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
using Content.Shared.Humanoid;
using Content.Shared.Damage;
using Content.Shared.Mobs.Components;
using Content.Shared.Physics;
using Content.Shared.Speech;
using Content.Shared.Speech.Muting;
using Content.Shared.CombatMode.Pacification;
using Content.Shared.CombatMode;
using Content.Shared.Nutrition.Components;
using Content.Server.Vampiric;
using Content.Shared.Abilities.Psionics;
using Content.Server.Abilities.Psionics;
using Content.Server.Cloning.Components;
using Content.Server.Psionics.Glimmer;
using Robust.Shared.GameObjects.Components.Localization;
using Robust.Shared.Enums;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Components;
using Robust.Shared.Random;

namespace Content.Server.Chat;
public sealed partial class TelepathicChatSystem
{
public string SourceToDescriptor(EntityUid source)
{
var ev = new GetPsychognomicDescriptorEvent();
RaiseLocalEvent(source, ev);

ev.Descriptors.Add(Loc.GetString("p-descriptor-ignorant"));

return _random.Pick(ev.Descriptors);
}
private void InitializePsychognomy()
{
SubscribeLocalEvent<HumanoidAppearanceComponent, GetPsychognomicDescriptorEvent>(DescribeHumanoid);
SubscribeLocalEvent<GrammarComponent, GetPsychognomicDescriptorEvent>(DescribeGrammar);
SubscribeLocalEvent<DamageableComponent, GetPsychognomicDescriptorEvent>(DescribeDamage);
SubscribeLocalEvent<MobStateComponent, GetPsychognomicDescriptorEvent>(DescribeMobState);
SubscribeLocalEvent<HungerComponent, GetPsychognomicDescriptorEvent>(DescribeHunger);
SubscribeLocalEvent<PhysicsComponent, GetPsychognomicDescriptorEvent>(DescribePhysics);
SubscribeLocalEvent<FixturesComponent, GetPsychognomicDescriptorEvent>(DescribeFixtures);
SubscribeLocalEvent<MetempsychosisKarmaComponent, GetPsychognomicDescriptorEvent>(DescribeKarma);
SubscribeLocalEvent<GlimmerSourceComponent, GetPsychognomicDescriptorEvent>(DescribeGlimmerSource);
SubscribeLocalEvent<PsionicComponent, GetPsychognomicDescriptorEvent>(DescribePsion);
SubscribeLocalEvent<InnatePsionicPowersComponent, GetPsychognomicDescriptorEvent>(DescribeInnatePsionics);
SubscribeLocalEvent<BloodSuckerComponent, GetPsychognomicDescriptorEvent>(DescribeBloodsucker);
}

private void DescribeHumanoid(EntityUid uid, HumanoidAppearanceComponent component, GetPsychognomicDescriptorEvent ev)
{
if (component.Sex != Sex.Unsexed)
ev.Descriptors.Add(component.Sex == Sex.Male ? Loc.GetString("p-descriptor-male") : Loc.GetString("p-descriptor-female"));

// Deliberately obfuscating a bit; psionic signatures use different standards
ev.Descriptors.Add(component.Age >= 100 ? Loc.GetString("p-descriptor-old") : Loc.GetString("p-descriptor-young"));
}

private void DescribeGrammar(EntityUid uid, GrammarComponent component, GetPsychognomicDescriptorEvent ev)
{
if (component.Gender == Gender.Male || component.Gender == Gender.Female)
ev.Descriptors.Add(component.Gender == Gender.Male ? Loc.GetString("p-descriptor-masculine") : Loc.GetString("p-descriptor-feminine"));
}

private void DescribeDamage(EntityUid uid, DamageableComponent component, GetPsychognomicDescriptorEvent ev)
{
if (component.DamageContainerID == "CorporealSpirit")
{
ev.Descriptors.Add(Loc.GetString("p-descriptor-liminal"));
if (!HasComp<HumanoidAppearanceComponent>(uid))
ev.Descriptors.Add(Loc.GetString("p-descriptor-old"));
return;
}

ev.Descriptors.Add(Loc.GetString("p-descriptor-hylic"));
}

private void DescribeMobState(EntityUid uid, MobStateComponent component, GetPsychognomicDescriptorEvent ev)
{
if (component.CurrentState != Shared.Mobs.MobState.Critical)
return;

ev.Descriptors.Add(Loc.GetString("p-descriptor-liminal"));
}

private void DescribeHunger(EntityUid uid, HungerComponent component, GetPsychognomicDescriptorEvent ev)
{
if (component.CurrentThreshold > HungerThreshold.Peckish)
return;

ev.Descriptors.Add(Loc.GetString("p-descriptor-hungry"));
}

private void DescribeFixtures(EntityUid uid, FixturesComponent component, GetPsychognomicDescriptorEvent ev)
{
foreach (var fixture in component.Fixtures.Values)
if (fixture.CollisionMask == (int) CollisionGroup.GhostImpassable)
{
ev.Descriptors.Add(Loc.GetString("p-descriptor-pneumatic"));
return;
}
}

private void DescribePhysics(EntityUid uid, PhysicsComponent component, GetPsychognomicDescriptorEvent ev)
{
if (component.FixturesMass < 45)
ev.Descriptors.Add(Loc.GetString("p-descriptor-light"));
else if (component.FixturesMass > 70)
ev.Descriptors.Add(Loc.GetString("p-descriptor-heavy"));
}

private void DescribeKarma(EntityUid uid, MetempsychosisKarmaComponent component, GetPsychognomicDescriptorEvent ev)
{
if (component.Score == 0)
return;

ev.Descriptors.Add(Loc.GetString("p-descriptor-cyclic"));
}

private void DescribeGlimmerSource(EntityUid uid, GlimmerSourceComponent component, GetPsychognomicDescriptorEvent ev)
{
if (component.AddToGlimmer)
ev.Descriptors.Add(Loc.GetString("p-descriptor-emanative"));
else
{
ev.Descriptors.Add(Loc.GetString("p-descriptor-vampiric"));
ev.Descriptors.Add(Loc.GetString("p-descriptor-hungry"));
}
}

// This one's also a bit of a catch-all for "lacks component"
private void DescribePsion(EntityUid uid, PsionicComponent component, GetPsychognomicDescriptorEvent ev)
{
if (component.PsychognomicDescriptors != null)
{
foreach (var descriptor in component.PsychognomicDescriptors)
{
ev.Descriptors.Add(Loc.GetString(descriptor));
}
}

if (!HasComp<SpeechComponent>(uid) || HasComp<MutedComponent>(uid))
ev.Descriptors.Add(Loc.GetString("p-descriptor-dumb"));

if (!HasComp<CombatModeComponent>(uid) || HasComp<PacifiedComponent>(uid))
ev.Descriptors.Add(Loc.GetString("p-descriptor-passive"));

foreach (var power in component.ActivePowers)
{
// TODO: Mime counts too and isn't added back to psions yet
if (power.ID != "PyrokinesisPower" && power.ID != "NoosphericZapPower")
continue;

ev.Descriptors.Add(Loc.GetString("p-descriptor-kinetic"));
return;
}
}

private void DescribeInnatePsionics(EntityUid uid, InnatePsionicPowersComponent component, GetPsychognomicDescriptorEvent ev)
{
ev.Descriptors.Add(Loc.GetString("p-descriptor-gnostic"));
}

private void DescribeBloodsucker(EntityUid uid, BloodSuckerComponent component, GetPsychognomicDescriptorEvent ev)
{
ev.Descriptors.Add(Loc.GetString("p-descriptor-vampiric"));
}
}
public sealed class GetPsychognomicDescriptorEvent : EntityEventArgs
{
public List<String> Descriptors = new List<String>();
}
45 changes: 34 additions & 11 deletions Content.Server/Chat/TelepathicChatSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Content.Server.Chat.Managers;
using Content.Server.Chat.Systems;
using Content.Shared.Abilities.Psionics;
using Content.Shared.Psionics.Passives;
using Content.Shared.Bed.Sleep;
using Content.Shared.Chat;
using Content.Shared.Database;
Expand All @@ -19,22 +20,33 @@
namespace Content.Server.Chat;

/// <summary>
/// Extensions for Telepathic chat stuff
/// Extensions for Telepathic chat stuff
/// </summary>
public sealed class TelepathicChatSystem : EntitySystem
public sealed partial class TelepathicChatSystem : EntitySystem
{
[Dependency] private readonly IAdminManager _adminManager = default!;
[Dependency] private readonly IChatManager _chatManager = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
[Dependency] private readonly GlimmerSystem _glimmerSystem = default!;
[Dependency] private readonly ChatSystem _chatSystem = default!;
private IEnumerable<INetChannel> GetPsionicChatClients()

public override void Initialize()
{
base.Initialize();
InitializePsychognomy();
}

private (IEnumerable<INetChannel> normal, IEnumerable<INetChannel> psychog) GetPsionicChatClients()
{
return Filter.Empty()
var psions = Filter.Empty()
.AddWhereAttachedEntity(IsEligibleForTelepathy)
.Recipients
.Select(p => p.ConnectedClient);
.Recipients;

var normalSessions = psions.Where(p => !HasComp<PsychognomistComponent>(p.AttachedEntity)).Select(p => p.Channel);
var psychogSessions = psions.Where(p => HasComp<PsychognomistComponent>(p.AttachedEntity)).Select(p => p.Channel);

return (normalSessions, psychogSessions);
}

private IEnumerable<INetChannel> GetAdminClients()
Expand Down Expand Up @@ -87,18 +99,29 @@ public void SendTelepathicChat(EntityUid source, string message, bool hideChat)

_adminLogger.Add(LogType.Chat, LogImpact.Low, $"Telepathic chat from {ToPrettyString(source):Player}: {message}");

_chatManager.ChatMessageToMany(ChatChannel.Telepathic, message, messageWrap, source, hideChat, true, clients.ToList(), Color.PaleVioletRed);
_chatManager.ChatMessageToMany(ChatChannel.Telepathic, message, messageWrap, source, hideChat, true, clients.normal.ToList(), Color.PaleVioletRed);

_chatManager.ChatMessageToMany(ChatChannel.Telepathic, message, adminMessageWrap, source, hideChat, true, admins, Color.PaleVioletRed);

if (clients.psychog.Count() > 0)
{
var descriptor = SourceToDescriptor(source);
string psychogMessageWrap;

psychogMessageWrap = Loc.GetString("chat-manager-send-telepathic-chat-wrap-message-psychognomy",
("source", descriptor.ToUpper()), ("message", message));

_chatManager.ChatMessageToMany(ChatChannel.Telepathic, message, psychogMessageWrap, source, hideChat, true, clients.psychog.ToList(), Color.PaleVioletRed);
}

if (_random.Prob(0.1f))
_glimmerSystem.Glimmer++;

if (_random.Prob(Math.Min(0.33f + ((float) _glimmerSystem.Glimmer / 1500), 1)))
if (_random.Prob(Math.Min(0.33f + (float) _glimmerSystem.Glimmer / 1500, 1)))
{
float obfuscation = (0.25f + (float) _glimmerSystem.Glimmer / 2000);
float obfuscation = 0.25f + (float) _glimmerSystem.Glimmer / 2000;
var obfuscated = ObfuscateMessageReadability(message, obfuscation);
_chatManager.ChatMessageToMany(ChatChannel.Telepathic, obfuscated, messageWrap, source, hideChat, false, GetDreamers(clients), Color.PaleVioletRed);
_chatManager.ChatMessageToMany(ChatChannel.Telepathic, obfuscated, messageWrap, source, hideChat, false, GetDreamers(clients.normal.Concat(clients.psychog)), Color.PaleVioletRed);
}

foreach (var repeater in EntityQuery<TelepathicRepeaterComponent>())
Expand All @@ -124,4 +147,4 @@ private string ObfuscateMessageReadability(string message, float chance)

return modifiedMessage.ToString();
}
}
}
30 changes: 22 additions & 8 deletions Content.Server/Research/Oracle/OracleSystem.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Content.Server.Botany;
using Content.Server.Chat;
using Content.Server.Chat.Managers;
using Content.Server.Chat.Systems;
using Content.Server.Chemistry.Containers.EntitySystems;
Expand All @@ -13,6 +14,7 @@
using Content.Shared.Interaction;
using Content.Shared.Mobs.Components;
using Content.Shared.Psionics.Glimmer;
using Content.Shared.Psionics.Passives;
using Content.Shared.Random.Helpers;
using Content.Shared.Research.Components;
using Content.Shared.Research.Prototypes;
Expand All @@ -29,6 +31,7 @@ namespace Content.Server.Research.Oracle;
public sealed class OracleSystem : EntitySystem
{
[Dependency] private readonly ChatSystem _chat = default!;
[Dependency] private readonly TelepathicChatSystem _tChat = default!;
[Dependency] private readonly IChatManager _chatMan = default!;
[Dependency] private readonly GlimmerSystem _glimmer = default!;
[Dependency] private readonly IPrototypeManager _protoMan = default!;
Expand Down Expand Up @@ -78,11 +81,11 @@ private void OnInteractHand(Entity<OracleComponent> oracle, ref InteractHandEven
return;

SendTelepathicInfo(oracle, actor.PlayerSession.Channel,
Loc.GetString("oracle-current-item", ("item", oracle.Comp.DesiredPrototype.Name)));
Loc.GetString("oracle-current-item", ("item", oracle.Comp.DesiredPrototype.Name)), HasComp<PsychognomistComponent>(args.User));

if (oracle.Comp.LastDesiredPrototype != null)
SendTelepathicInfo(oracle, actor.PlayerSession.Channel,
Loc.GetString("oracle-previous-item", ("item", oracle.Comp.LastDesiredPrototype.Name)));
Loc.GetString("oracle-previous-item", ("item", oracle.Comp.LastDesiredPrototype.Name)), HasComp<PsychognomistComponent>(args.User));
}

private void OnInteractUsing(Entity<OracleComponent> oracle, ref InteractUsingEvent args)
Expand Down Expand Up @@ -123,14 +126,25 @@ private void OnInteractUsing(Entity<OracleComponent> oracle, ref InteractUsingEv
NextItem(oracle);
}

private void SendTelepathicInfo(Entity<OracleComponent> oracle, INetChannel client, string message)
private void SendTelepathicInfo(Entity<OracleComponent> oracle, INetChannel client, string message, bool psychognomist = false)
{
var messageWrap = Loc.GetString("chat-manager-send-telepathic-chat-wrap-message",
("telepathicChannelName", Loc.GetString("chat-manager-telepathic-channel-name")),
("message", message));
if (!psychognomist)
{
var messageWrap = Loc.GetString("chat-manager-send-telepathic-chat-wrap-message",
("telepathicChannelName", Loc.GetString("chat-manager-telepathic-channel-name")),
("message", message));

_chatMan.ChatMessageToOne(ChatChannel.Telepathic,
message, messageWrap, oracle, false, client, Color.PaleVioletRed);
_chatMan.ChatMessageToOne(ChatChannel.Telepathic,
message, messageWrap, oracle, false, client, Color.PaleVioletRed);
}
else
{
var descriptor = _tChat.SourceToDescriptor(oracle);
var psychogMessageWrap = Loc.GetString("chat-manager-send-telepathic-chat-wrap-message-psychognomy",
("source", descriptor.ToUpper()), ("message", message));

_chatMan.ChatMessageToOne(ChatChannel.Telepathic, message, psychogMessageWrap, oracle, false, client, Color.PaleVioletRed);
}
}

private bool IsCorrectItem(EntityPrototype given, EntityPrototype target)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
namespace Content.Shared.Psionics.Passives;

[RegisterComponent]
public sealed partial class PsychognomistComponent : Component { }

7 changes: 7 additions & 0 deletions Content.Shared/Psionics/PsionicComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -140,5 +140,12 @@ private set
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
public float CurrentDampening;

/// <summary>
/// List of descriptors this entity will bring up for psychognomy. Used to remove
/// unneccesary subs for unique psionic entities like e.g. Oracle.
/// </summary>
[DataField]
public List<String>? PsychognomicDescriptors = null;
}
}
1 change: 1 addition & 0 deletions Resources/Locale/en-US/psionics/psionic-chat.ftl
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
chat-manager-send-telepathic-chat-wrap-message = {$telepathicChannelName}: {$message}
chat-manager-send-telepathic-chat-wrap-message-psychognomy = {$source}: {$message}
chat-manager-send-telepathic-chat-wrap-message-admin = {$source} (Ψ): {$message}
chat-manager-telepathic-channel-name = TELEPATHIC
hud-chatbox-select-channel-Telepathic = Telepathic
Expand Down
6 changes: 6 additions & 0 deletions Resources/Locale/en-US/psionics/psionic-powers.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,12 @@ xenoglossy-power-initialization-feedback =
psionic-language-power-metapsionic-feedback = The noösphere flows freely through {CAPITALIZE($entity)}, who seems to digest it and pass it back out undisturbed.
# Psychognomy
psychognomy-power-description = You have some vague sense of the form of the source of telepathic messages.
psychognomy-power-initialization-feedback =
I have pierced the veil, and I know I'm not alone. More concerning, the piercing I made seems to be still indefinitely permeable.
When energy passes through the perforations in the noösphere, I get a faint glimpse of the material origin.
# Telepathy
telepathy-power-description = You are capable of both sending and receiving telepathic messages.
telepathy-power-initialization-feedback =
Expand Down
23 changes: 23 additions & 0 deletions Resources/Locale/en-US/psionics/psychognomic-descriptors.ftl
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
p-descriptor-mysterious = mysterious
p-descriptor-male = male
p-descriptor-female = female
p-descriptor-young = young
p-descriptor-old = old
p-descriptor-masculine = masculine
p-descriptor-feminine = feminine
p-descriptor-hylic = hylic
p-descriptor-pneumatic = pneumatic
p-descriptor-liminal = liminal
p-descriptor-bound = bound
p-descriptor-ignorant = ignorant
p-descriptor-demiurgic = demiurgic
p-descriptor-emanative = emanative
p-descriptor-light = light
p-descriptor-heavy = heavy
p-descriptor-cyclic = cyclic
p-descriptor-hungry = hungry
p-descriptor-vampiric = vampiric
p-descriptor-kinetic = kinetic
p-descriptor-gnostic = gnostic
p-descriptor-dumb = dumb
p-descriptor-passive = passive
Loading

0 comments on commit b2fc8b3

Please sign in to comment.