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
189 changes: 189 additions & 0 deletions Content.Server/Chat/TelepathicChatSystem.Psychognomy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
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.Server.Bible.Components;
using Content.Server.Research.Oracle;
using Robust.Shared.GameObjects.Components.Localization;
using Robust.Shared.Enums;
using Content.Server.Nyanotrasen.Research.SophicScribe;
using Content.Server.Nyanotrasen.Cloning;
using Content.Server.Psionics.Glimmer;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Components;
using Content.Shared.Nutrition.Components;
using Content.Server.Vampiric;
using Content.Shared.Revenant.Components;
using Content.Shared.Abilities.Psionics;
using Content.Server.Abilities.Psionics;

namespace Content.Server.Chat;
public sealed partial class TelepathicChatSystem
{
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);
SubscribeLocalEvent<RevenantComponent, GetPsychognomicDescriptorEvent>(DescribeRevenant);
// SubscribeLocalEvent<GlimmerWispComponent, GetPsychognomicDescriptorEvent>(DescribeGlimmerWisp);
SubscribeLocalEvent<FamiliarComponent, GetPsychognomicDescriptorEvent>(DescribeFamiliar);
// SubscribeLocalEvent<GolemComponent, GetPsychognomicDescriptorEvent>(DescribeGolem);
SubscribeLocalEvent<OracleComponent, GetPsychognomicDescriptorEvent>(DescribeOracle);
SubscribeLocalEvent<SophicScribeComponent, GetPsychognomicDescriptorEvent>(DescribeSophia);
Elijahrane marked this conversation as resolved.
Show resolved Hide resolved
}

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 (!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"));
}

private void DescribeRevenant(EntityUid uid, RevenantComponent component, GetPsychognomicDescriptorEvent ev)
{
ev.Descriptors.Add(Loc.GetString("p-descriptor-vampiric"));
}

private void DescribeFamiliar(EntityUid uid, FamiliarComponent component, GetPsychognomicDescriptorEvent ev)
{
ev.Descriptors.Add(Loc.GetString("p-descriptor-bound"));
ev.Descriptors.Add(Loc.GetString("p-descriptor-cyclic"));
}

private void DescribeOracle(EntityUid uid, OracleComponent component, GetPsychognomicDescriptorEvent ev)
{
ev.Descriptors.Add(Loc.GetString("p-descriptor-old"));
ev.Descriptors.Add(Loc.GetString("p-descriptor-demiurgic"));
ev.Descriptors.Add(Loc.GetString("p-descriptor-mysterious"));
ev.Descriptors.Add(Loc.GetString("p-descriptor-emanative"));
}

private void DescribeSophia(EntityUid uid, SophicScribeComponent component, GetPsychognomicDescriptorEvent ev)
{
ev.Descriptors.Add(Loc.GetString("p-descriptor-old"));
ev.Descriptors.Add(Loc.GetString("p-descriptor-demiurgic"));
ev.Descriptors.Add(Loc.GetString("p-descriptor-mysterious"));
}
}
public sealed class GetPsychognomicDescriptorEvent : EntityEventArgs
{
public List<String> Descriptors = new List<String>();
}
49 changes: 39 additions & 10 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 @@ -22,20 +23,31 @@ namespace Content.Server.Chat
/// 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 @@ -64,7 +76,7 @@ private bool IsEligibleForTelepathy(EntityUid entity)
return HasComp<PsionicComponent>(entity)
&& !HasComp<PsionicsDisabledComponent>(entity)
&& !HasComp<PsionicInsulationComponent>(entity)
&& (!TryComp<MobStateComponent>(entity, out var mobstate) || mobstate.CurrentState == MobState.Alive);
&& (!TryComp<MobStateComponent>(entity, out var mobstate) || mobstate.CurrentState != MobState.Dead);
}

public void SendTelepathicChat(EntityUid source, string message, bool hideChat)
Expand All @@ -85,18 +97,35 @@ 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 ev = new GetPsychognomicDescriptorEvent();
RaiseLocalEvent(source, ev);

ev.Descriptors.Add("p-descriptor-ignorant");
string psychogMessageWrap;
string descriptor;

descriptor = _random.Pick(ev.Descriptors);

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 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 { }

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,4 +74,10 @@ 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.

mindbreaking-feedback = The light of life vanishes from {CAPITALIZE($entity)}'s eyes, leaving behind a husk pretending at sapience
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
4 changes: 4 additions & 0 deletions Resources/Migrations/eeMigration.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# 2024-06-08
CrateFunPlushie: CrateFunToyBox
CrateFunLizardPlushieBulk: CrateFunToyBox

# 2024-08-27
Oracle: OracleSpawner
SophicScribe: SophicScribeSpawner
25 changes: 25 additions & 0 deletions Resources/Prototypes/Entities/Markers/Spawners/statues.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
- type: entity
name: Oracle Spawner
id: OracleSpawner
parent: MarkerBase
components:
- type: Sprite
sprite: Markers/jobs.rsi
layers:
- state: green
- type: ConditionalSpawner
prototypes:
- Oracle

- type: entity
name: Sophia Spawner
id: SophicScribeSpawner
parent: MarkerBase
components:
- type: Sprite
sprite: Markers/jobs.rsi
layers:
- state: green
- type: ConditionalSpawner
prototypes:
- SophicScribe
7 changes: 7 additions & 0 deletions Resources/Prototypes/Entities/Structures/Specific/oracle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,14 @@
state: oracle-0
- type: Speech
speechSounds: Tenor
- type: Actions
- type: Psionic
removable: false
- type: InnatePsionicPowers
powersToAdd:
- XenoglossyPower
- PsychognomyPower
- NoosphericZapPower
- type: SolutionContainerManager
solutions:
fountain:
Expand Down
Loading
Loading