Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Psychognomy #808

Merged
merged 16 commits into from
Sep 3, 2024
Merged
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
Loading