diff --git a/Content.Client/Nyanotrasen/CartridgeLoader/Cartridges/GlimmerMonitorUiFragment.xaml.cs b/Content.Client/Nyanotrasen/CartridgeLoader/Cartridges/GlimmerMonitorUiFragment.xaml.cs
index 149c590d91..43d9202aa4 100644
--- a/Content.Client/Nyanotrasen/CartridgeLoader/Cartridges/GlimmerMonitorUiFragment.xaml.cs
+++ b/Content.Client/Nyanotrasen/CartridgeLoader/Cartridges/GlimmerMonitorUiFragment.xaml.cs
@@ -1,10 +1,10 @@
using System.Linq;
using System.Numerics;
+using Content.Client.Nyanotrasen.UserInterface;
using Robust.Client.AutoGenerated;
using Robust.Client.ResourceManagement;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
-using Content.Client.Nyanotrasen.UserInterface.CustomControls;
namespace Content.Client.Nyanotrasen.CartridgeLoader.Cartridges;
diff --git a/Content.Client/Nyanotrasen/UserInterface/GlimmerGraph.cs b/Content.Client/Nyanotrasen/UserInterface/GlimmerGraph.cs
index b121186b14..c4a9109dcd 100644
--- a/Content.Client/Nyanotrasen/UserInterface/GlimmerGraph.cs
+++ b/Content.Client/Nyanotrasen/UserInterface/GlimmerGraph.cs
@@ -1,10 +1,10 @@
using System.Numerics;
-using Robust.Client.UserInterface;
+using Content.Client.Resources;
using Robust.Client.Graphics;
using Robust.Client.ResourceManagement;
-using Content.Client.Resources;
+using Robust.Client.UserInterface;
-namespace Content.Client.Nyanotrasen.UserInterface.CustomControls;
+namespace Content.Client.Nyanotrasen.UserInterface;
public sealed class GlimmerGraph : Control
{
diff --git a/Content.Server/Nyanotrasen/Research/SophicScribe/SophicScribeComponent.cs b/Content.Server/Nyanotrasen/Research/SophicScribe/SophicScribeComponent.cs
new file mode 100644
index 0000000000..4866432d17
--- /dev/null
+++ b/Content.Server/Nyanotrasen/Research/SophicScribe/SophicScribeComponent.cs
@@ -0,0 +1,22 @@
+namespace Content.Server.Nyanotrasen.Research.SophicScribe;
+
+[RegisterComponent]
+public sealed partial class SophicScribeComponent : Component
+{
+ [DataField("accumulator")]
+ public float Accumulator;
+
+ [DataField("announceInterval")]
+ public TimeSpan AnnounceInterval = TimeSpan.FromMinutes(2);
+
+ [DataField("nextAnnounce")]
+ public TimeSpan NextAnnounceTime;
+
+ ///
+ /// Antispam.
+ ///
+ public TimeSpan StateTime = default!;
+
+ [DataField("stateCD")]
+ public TimeSpan StateCD = TimeSpan.FromSeconds(5);
+}
diff --git a/Content.Server/Nyanotrasen/Research/SophicScribe/SophicScribeSystem.cs b/Content.Server/Nyanotrasen/Research/SophicScribe/SophicScribeSystem.cs
new file mode 100644
index 0000000000..bc3c22cc35
--- /dev/null
+++ b/Content.Server/Nyanotrasen/Research/SophicScribe/SophicScribeSystem.cs
@@ -0,0 +1,86 @@
+using Content.Server.Abilities.Psionics;
+using Content.Server.Chat.Systems;
+using Content.Server.Radio.Components;
+using Content.Server.Radio.EntitySystems;
+using Content.Server.StationEvents.Events;
+using Content.Shared.Interaction;
+using Content.Shared.Psionics.Glimmer;
+using Content.Shared.Radio;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Timing;
+
+namespace Content.Server.Nyanotrasen.Research.SophicScribe;
+
+public sealed partial class SophicScribeSystem : EntitySystem
+{
+ [Dependency] private readonly ChatSystem _chat = default!;
+ [Dependency] private readonly GlimmerSystem _glimmerSystem = default!;
+ [Dependency] private readonly RadioSystem _radioSystem = default!;
+ [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
+ [Dependency] private readonly IGameTiming _timing = default!;
+
+ public override void Update(float frameTime)
+ {
+ base.Update(frameTime);
+
+ if (_glimmerSystem.Glimmer == 0)
+ return; // yes, return. Glimmer value is global.
+
+ var curTime = _timing.CurTime;
+
+ var query = EntityQueryEnumerator();
+ while (query.MoveNext(out var scribe, out var scribeComponent))
+ {
+ if (curTime < scribeComponent.NextAnnounceTime)
+ continue;
+
+ if (!TryComp(scribe, out var radio))
+ continue;
+
+ var message = Loc.GetString("glimmer-report", ("level", _glimmerSystem.Glimmer));
+ var channel = _prototypeManager.Index("Science");
+ _radioSystem.SendRadioMessage(scribe, message, channel, scribe);
+
+ scribeComponent.NextAnnounceTime = curTime + scribeComponent.AnnounceInterval;
+ }
+ }
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent(OnInteractHand);
+ SubscribeLocalEvent(OnGlimmerEventEnded);
+ }
+
+ private void OnInteractHand(EntityUid uid, SophicScribeComponent component, InteractHandEvent args)
+ {
+ //TODO: the update function should be removed eventually too.
+ if (_timing.CurTime < component.StateTime)
+ return;
+
+ component.StateTime = _timing.CurTime + component.StateCD;
+
+ _chat.TrySendInGameICMessage(uid, Loc.GetString("glimmer-report", ("level", _glimmerSystem.Glimmer)), InGameICChatType.Speak, true);
+ }
+
+ private void OnGlimmerEventEnded(GlimmerEventEndedEvent args)
+ {
+ var query = EntityQueryEnumerator();
+ while (query.MoveNext(out var scribe, out _))
+ {
+ if (!TryComp(scribe, out var radio)) return;
+
+ // mind entities when...
+ var speaker = scribe;
+ if (TryComp(scribe, out var swapped))
+ {
+ speaker = swapped.OriginalEntity;
+ }
+
+ var message = Loc.GetString(args.Message, ("decrease", args.GlimmerBurned), ("level", _glimmerSystem.Glimmer));
+ var channel = _prototypeManager.Index("Common");
+ _radioSystem.SendRadioMessage(speaker, message, channel, speaker);
+ }
+ }
+}
diff --git a/Resources/Locale/en-US/nyanotrasen/cartridge-loader/cartridges.ftl b/Resources/Locale/en-US/nyanotrasen/cartridge-loader/cartridges.ftl
new file mode 100644
index 0000000000..906466bc10
--- /dev/null
+++ b/Resources/Locale/en-US/nyanotrasen/cartridge-loader/cartridges.ftl
@@ -0,0 +1,4 @@
+glimmer-monitor-program-name = Glimmer Monitor
+glimmer-monitor-current-glimmer = Current Glimmer: {$glimmer}Ψ
+glimmer-monitor-interval = Interval
+glimmer-monitor-sync = Sync
diff --git a/Resources/Prototypes/Entities/Objects/Devices/pda.yml b/Resources/Prototypes/Entities/Objects/Devices/pda.yml
index e11254d768..5d384624c0 100644
--- a/Resources/Prototypes/Entities/Objects/Devices/pda.yml
+++ b/Resources/Prototypes/Entities/Objects/Devices/pda.yml
@@ -584,6 +584,12 @@
accentVColor: "#8900c9"
- type: Icon
state: pda-rd
+ - type: CartridgeLoader # Nyanotrasen - Glimmer Monitor
+ preinstalled:
+ - CrewManifestCartridge
+ - NotekeeperCartridge
+ - NewsReadCartridge
+ - GlimmerMonitorCartridge
- type: entity
parent: BasePDA
@@ -599,6 +605,12 @@
accentVColor: "#8900c9"
- type: Icon
state: pda-science
+ - type: CartridgeLoader # Nyanotrasen - Glimmer Monitor
+ preinstalled:
+ - CrewManifestCartridge
+ - NotekeeperCartridge
+ - NewsReadCartridge
+ - GlimmerMonitorCartridge
- type: entity
parent: BasePDA
diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Objects/Devices/cartridges.yml b/Resources/Prototypes/Nyanotrasen/Entities/Objects/Devices/cartridges.yml
new file mode 100644
index 0000000000..df61f1a382
--- /dev/null
+++ b/Resources/Prototypes/Nyanotrasen/Entities/Objects/Devices/cartridges.yml
@@ -0,0 +1,20 @@
+- type: entity
+ parent: BaseItem
+ id: GlimmerMonitorCartridge
+ name: glimmer monitor cartridge
+ description: A cartridge that keeps track of glimmer.
+ components:
+ - type: Sprite
+ sprite: Nyanotrasen/Objects/Devices/cartridge.rsi
+ state: cart-psi
+ - type: Icon
+ sprite: Nyanotrasen/Objects/Devices/cartridge.rsi
+ state: cart-psi
+ - type: UIFragment
+ ui: !type:GlimmerMonitorUi
+ - type: Cartridge
+ programName: glimmer-monitor-program-name
+ icon:
+ sprite: Nyanotrasen/Icons/psi.rsi
+ state: psi
+ - type: GlimmerMonitorCartridge
diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Objects/Devices/pda.yml b/Resources/Prototypes/Nyanotrasen/Entities/Objects/Devices/pda.yml
index 2fb24c6d42..dc09034e4a 100644
--- a/Resources/Prototypes/Nyanotrasen/Entities/Objects/Devices/pda.yml
+++ b/Resources/Prototypes/Nyanotrasen/Entities/Objects/Devices/pda.yml
@@ -100,9 +100,9 @@
state: pda-science
- type: Icon
state: pda-science
- # - type: CartridgeLoader
- # preinstalled:
- # - CrewManifestCartridge
- # - NotekeeperCartridge
- # - NewsReadCartridge
- # - GlimmerMonitorCartridge # Uncomment this entire thing if we re-implement the glimmer monitor.
+ - type: CartridgeLoader
+ preinstalled:
+ - CrewManifestCartridge
+ - NotekeeperCartridge
+ - NewsReadCartridge
+ - GlimmerMonitorCartridge
diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Structures/Research/sophicscribe.yml b/Resources/Prototypes/Nyanotrasen/Entities/Structures/Research/sophicscribe.yml
index 5aede59926..0c67579339 100644
--- a/Resources/Prototypes/Nyanotrasen/Entities/Structures/Research/sophicscribe.yml
+++ b/Resources/Prototypes/Nyanotrasen/Entities/Structures/Research/sophicscribe.yml
@@ -1,221 +1,39 @@
- - type: entity
- parent: BaseStructure
- id: SophicScribe
- name: sophic grammateus
- description: Latest reports on the Noösphere!
- components:
- - type: PotentialPsionic #this makes her easier to access for glimmer events, dw about it
- - type: Psionic
- - type: Sprite
- noRot: true
- drawdepth: Mobs
- offset: "0.0,0.5"
- sprite: DeltaV/Structures/Decoration/statues.rsi
- layers:
- - state: sophie
- - map: ["enum.SolutionContainerLayers.Fill"]
-# - type: SophicScribe
-# - type: Speech
-# speechSounds: Snake
-# - type: IntrinsicRadioReceiver
-# channels:
-# - Common
-# - Science
-# - type: IntrinsicRadioTransmitter
-# channels:
-# - Common
-# - Science
-# - type: ActiveRadio
-# channels:
-# - Common
-# - Science
-# - type: ActiveListener
-# - type: TypingIndicator
-# - type: NPCConversation
-# tree: SophiaTree
-# - type: Grammar
-# attributes:
-# gender: female
-# proper: true
-# - type: SpriteFade
-# - type: GuideHelp
-# guides:
-# - Psionics
-# - type: Tag
-# tags: []
-
-# - type: npcConversationTree
-# id: SophiaTree
-# dialogue:
-# - prompts: [glimmer, reading]
-# responses:
-# - is: !type:NPCConversationGetGlimmerEvent
-# text: sophia-response-glimmer
-
-# - prompts: [purpose, job, occupation, profession]
-# weight: 0.5
-# responses:
-# - text: sophia-response-job
-
-# - prompts: [help, topics]
-# weight: 0.5
-# hidden: true
-# responses:
-# - is: !type:NPCConversationHelpEvent
-# text: sophia-response-help
-
-# - prompts: [nature, statue, snake, being]
-# weight: 0.3
-# responses:
-# - text: sophia-response-nature
-
-# - prompts: [epistemics, epi]
-# weight: 0.2
-# responses:
-# - text: sophia-response-epi
-
-# - prompts: [mantis]
-# weight: 0.2
-# responses:
-# - text: sophia-response-mantis
-
-# - prompts: [mystagogue, mysta]
-# weight: 0.2
-# responses:
-# - text: sophia-response-mystagogue
-
-# - prompts: [psionics, psychic]
-# weight: 0.2
-# responses:
-# - text: sophia-response-psionics
-
-# - prompts: [noösphere, noosphere]
-# weight: 0.2
-# responses:
-# - text: sophia-response-noosphere
-
-# - prompts: [metempsychosis, metempsychoses, reincarnation, death, dying, afterlife]
-# weight: 0.2
-# responses:
-# - text: sophia-response-metempsychosis
-
-# - prompts: [calendar]
-# weight: 0.2
-# responses:
-# - text: sophia-response-calendar
-
-# - prompts: [morphotypes, morphotype, species]
-# weight: 0.2
-# responses:
-# - text: sophia-response-morphotype
-
-# - prompts: [gods, god]
-# weight: 0.1
-# hidden: true
-# responses:
-# - text: sophia-response-god
-
-# - prompts: [truth, "true", "false", falsity, falsehood]
-# weight: 0.1
-# hidden: true
-# responses:
-# - text: sophia-response-truth
-
-# - prompts: [human, humans, humanoid, unmutated]
-# weight: 0.1
-# hidden: true
-# responses:
-# - text: sophia-response-human
-
-# - prompts: [felinid, felinids, felid, felids, catperson, catpeople]
-# weight: 0.1
-# hidden: true
-# responses:
-# - text: sophia-response-felinid
-
-# - prompts: [oni, onis]
-# weight: 0.1
-# hidden: true
-# responses:
-# - text: sophia-response-oni
-
-# - prompts: [arachne, arachnid, arachnids, spiderperson, spiderpeople]
-# weight: 0.1
-# hidden: true
-# responses:
-# - text: sophia-response-arachne
-
-# - prompts: [moth, moths, moff, moths]
-# weight: 0.1
-# hidden: true
-# responses:
-# - text: sophia-response-moth
-
-# - prompts: [lamiae, lamia, lamias]
-# weight: 0.1
-# hidden: true
-# responses:
-# - text: sophia-response-lamiae
-
-# - prompts: [grue, grues, batperson, batpeople]
-# weight: 0.1
-# hidden: true
-# responses:
-# - text: sophia-response-grue
-
-# - prompts: [cynocephalus, cynocephali, cyno, cynos]
-# weight: 0.1
-# hidden: true
-# responses:
-# - text: sophia-response-cyno
-
-# - prompts: [crisis, causality]
-# weight: 0.1
-# hidden: true
-# responses:
-# - text: sophia-response-crisis
-
-# - prompts: [oracle]
-# weight: 0.1
-# hidden: true
-# responses:
-# - text: sophia-response-oracle
-
-# - prompts: [abraxas]
-# weight: 0.1
-# hidden: true
-# responses:
-# - text: sophia-response-abraxas
-
-# - prompts: [hi, hello, hey, greetings, salutations]
-# weight: 0.1
-# hidden: true
-# responses:
-# - text: sophia-response-hello-1
-# - text: sophia-response-hello-2
-
-# - prompts: [bye, goodbye, done, farewell, later, seeya]
-# weight: 0.1
-# hidden: true
-# responses:
-# - text: sophia-response-bye-1
-# event: !type:NPCConversationByeEvent
-# - text: sophia-response-bye-2
-# event: !type:NPCConversationByeEvent
-# - text: sophia-response-bye-3
-# event: !type:NPCConversationByeEvent
-
-# attention:
-# - text: sophia-response-attention-1
-# - text: sophia-response-attention-2
-# - text: sophia-response-attention-3
-
-# idle:
-# - text: sophia-idle-phrase-1
-# - text: sophia-idle-phrase-2
-# - text: sophia-idle-phrase-3
-
-# unknown:
-# - text: sophia-response-sorry-1
-# - text: sophia-response-sorry-2
-# - text: sophia-response-sorry-3
+- type: entity
+ parent: BaseStructure
+ id: SophicScribe
+ name: sophie
+ description: Latest reports on the Noösphere!
+ components:
+ - type: Sprite
+ noRot: true
+ drawdepth: Mobs
+ offset: "0.0,0.5"
+ sprite: DeltaV/Structures/Decoration/statues.rsi
+ layers:
+ - state: sophie
+ - map: ["enum.SolutionContainerLayers.Fill"]
+ - type: SophicScribe
+ - type: Speech
+ speechSounds: Tenor
+ - type: IntrinsicRadioReceiver
+ channels:
+ - Common
+ - Science
+ - type: IntrinsicRadioTransmitter
+ channels:
+ - Common
+ - Science
+ - type: ActiveRadio
+ channels:
+ - Common
+ - Science
+ - type: PotentialPsionic #this makes her easier to access for glimmer events, dw about it
+ - type: Psionic
+ - type: Grammar
+ attributes:
+ gender: female
+ proper: true
+ - type: SpriteFade
+ #- type: GuideHelp # For whenever someones readds this
+ # guides:
+ # - Psionics
diff --git a/Resources/Textures/Interface/glimmerGraph.png b/Resources/Textures/Interface/glimmerGraph.png
new file mode 100644
index 0000000000..967b4662cd
Binary files /dev/null and b/Resources/Textures/Interface/glimmerGraph.png differ
diff --git a/Resources/Textures/Nyanotrasen/Icons/psi.rsi/meta.json b/Resources/Textures/Nyanotrasen/Icons/psi.rsi/meta.json
new file mode 100644
index 0000000000..1daa18e0cc
--- /dev/null
+++ b/Resources/Textures/Nyanotrasen/Icons/psi.rsi/meta.json
@@ -0,0 +1,14 @@
+{
+ "version": 1,
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "license": "CC0-1.0",
+ "copyright": "the greeks",
+ "states": [
+ {
+ "name": "psi"
+ }
+ ]
+}
diff --git a/Resources/Textures/Nyanotrasen/Icons/psi.rsi/psi.png b/Resources/Textures/Nyanotrasen/Icons/psi.rsi/psi.png
new file mode 100644
index 0000000000..a53c723ebe
Binary files /dev/null and b/Resources/Textures/Nyanotrasen/Icons/psi.rsi/psi.png differ
diff --git a/Resources/Textures/Nyanotrasen/Objects/Devices/cartridge.rsi/cart-psi.png b/Resources/Textures/Nyanotrasen/Objects/Devices/cartridge.rsi/cart-psi.png
new file mode 100644
index 0000000000..876b480550
Binary files /dev/null and b/Resources/Textures/Nyanotrasen/Objects/Devices/cartridge.rsi/cart-psi.png differ
diff --git a/Resources/Textures/Nyanotrasen/Objects/Devices/cartridge.rsi/meta.json b/Resources/Textures/Nyanotrasen/Objects/Devices/cartridge.rsi/meta.json
new file mode 100644
index 0000000000..43891272a0
--- /dev/null
+++ b/Resources/Textures/Nyanotrasen/Objects/Devices/cartridge.rsi/meta.json
@@ -0,0 +1,14 @@
+{
+ "version": 1,
+ "license": "CC-BY-SA-3.0",
+ "copyright": "Rane",
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "states": [
+ {
+ "name": "cart-psi"
+ }
+ ]
+}