]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Hackable intercoms (#23984)
authorTayrtahn <tayrtahn@gmail.com>
Sun, 14 Jan 2024 05:37:28 +0000 (00:37 -0500)
committerGitHub <noreply@github.com>
Sun, 14 Jan 2024 05:37:28 +0000 (16:37 +1100)
* Enable wire interface for intercom

* Implement BlockListening component and system

* Implement ListenWireAction

* Added cooldown/overload to mic wire pulse

* Properly persist voicemask settings when user already has one.

* Addressed requested changes

* Added wire panel open/closed visuals

Content.Server/Chat/Systems/ChatSystem.cs
Content.Server/Speech/Components/BlockListeningComponent.cs [new file with mode: 0644]
Content.Server/Speech/Components/ListenWireAction.cs [new file with mode: 0644]
Content.Server/Speech/EntitySystems/BlockListeningSystem.cs [new file with mode: 0644]
Content.Shared/Speech/EntitySystems/SharedListenWireAction.cs [new file with mode: 0644]
Resources/Locale/en-US/speech/listen-wire-action.ftl [new file with mode: 0644]
Resources/Locale/en-US/wires/wire-names.ftl
Resources/Prototypes/Entities/Structures/Wallmounts/intercom.yml
Resources/Prototypes/Wires/layouts.yml
Resources/Textures/Structures/Wallmounts/intercom.rsi/meta.json
Resources/Textures/Structures/Wallmounts/intercom.rsi/panel.png [new file with mode: 0644]

index 2ca1404bc5353ff5be9a5d1c320d323c177de8bf..194844997b983b6ebf71d281ce35f5182f309fa3 100644 (file)
@@ -847,6 +847,16 @@ public sealed partial class ChatSystem : SharedChatSystem
         return modifiedMessage.ToString();
     }
 
+    public string BuildGibberishString(IReadOnlyList<char> charOptions, int length)
+    {
+        var sb = new StringBuilder();
+        for (var i = 0; i < length; i++)
+        {
+            sb.Append(_random.Pick(charOptions));
+        }
+        return sb.ToString();
+    }
+
     #endregion
 }
 
diff --git a/Content.Server/Speech/Components/BlockListeningComponent.cs b/Content.Server/Speech/Components/BlockListeningComponent.cs
new file mode 100644 (file)
index 0000000..3c74725
--- /dev/null
@@ -0,0 +1,11 @@
+
+namespace Content.Server.Speech.Components;
+
+/// <summary>
+///     Causes all ListenAttemptEvents to fail on the entity.
+/// </summary>
+[RegisterComponent]
+public sealed partial class BlockListeningComponent : Component
+{
+
+}
diff --git a/Content.Server/Speech/Components/ListenWireAction.cs b/Content.Server/Speech/Components/ListenWireAction.cs
new file mode 100644 (file)
index 0000000..1748ab3
--- /dev/null
@@ -0,0 +1,111 @@
+using System.Text;
+
+using Content.Server.Speech.Components;
+using Content.Server.Chat.Systems;
+using Content.Server.Speech.EntitySystems;
+using Content.Server.VoiceMask;
+using Content.Server.Wires;
+using Content.Shared.Speech;
+using Content.Shared.Wires;
+
+namespace Content.Server.Speech;
+
+public sealed partial class ListenWireAction : BaseToggleWireAction
+{
+    private WiresSystem _wires = default!;
+    private ChatSystem _chat = default!;
+
+    /// <summary>
+    /// Length of the gibberish string sent when pulsing the wire
+    /// </summary>
+    private int _noiseLength = 16;
+    public override Color Color { get; set; } = Color.Green;
+    public override string Name { get; set; } = "wire-name-listen";
+
+    public override object? StatusKey { get; } = ListenWireActionKey.StatusKey;
+
+    public override object? TimeoutKey { get; } = ListenWireActionKey.TimeoutKey;
+
+    public override int Delay { get; } = 10;
+
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        _wires = EntityManager.System<WiresSystem>();
+        _chat = EntityManager.System<ChatSystem>();
+    }
+    public override StatusLightState? GetLightState(Wire wire)
+    {
+        if (GetValue(wire.Owner))
+            return StatusLightState.On;
+        else
+        {
+            if (TimeoutKey != null && _wires.HasData(wire.Owner, TimeoutKey))
+                return StatusLightState.BlinkingSlow;
+            return StatusLightState.Off;
+        }
+    }
+    public override void ToggleValue(EntityUid owner, bool setting)
+    {
+        if (setting)
+        {
+            // If we defer removal, the status light gets out of sync
+            EntityManager.RemoveComponent<BlockListeningComponent>(owner);
+        }
+        else
+        {
+            EntityManager.EnsureComponent<BlockListeningComponent>(owner);
+        }
+    }
+
+    public override bool GetValue(EntityUid owner)
+    {
+        return !EntityManager.HasComponent<BlockListeningComponent>(owner);
+    }
+
+    public override void Pulse(EntityUid user, Wire wire)
+    {
+        if (!GetValue(wire.Owner) || !IsPowered(wire.Owner))
+            return;
+
+        // We have to use a valid euid in the ListenEvent. The user seems
+        // like a sensible choice, but we need to mask their name.
+
+        // Save the user's existing voicemask if they have one
+        var oldEnabled = true;
+        var oldVoiceName = Loc.GetString("wire-listen-pulse-error-name");
+        if (EntityManager.TryGetComponent<VoiceMaskComponent>(user, out var oldMask))
+        {
+            oldEnabled = oldMask.Enabled;
+            oldVoiceName = oldMask.VoiceName;
+        }
+
+        // Give the user a temporary voicemask component
+        var mask = EntityManager.EnsureComponent<VoiceMaskComponent>(user);
+        mask.Enabled = true;
+        mask.VoiceName = Loc.GetString("wire-listen-pulse-identifier");
+
+        var chars = Loc.GetString("wire-listen-pulse-characters").ToCharArray();
+        var noiseMsg = _chat.BuildGibberishString(chars, _noiseLength);
+
+        var attemptEv = new ListenAttemptEvent(wire.Owner);
+        EntityManager.EventBus.RaiseLocalEvent(wire.Owner, attemptEv);
+        if (!attemptEv.Cancelled)
+        {
+            var ev = new ListenEvent(noiseMsg, user);
+            EntityManager.EventBus.RaiseLocalEvent(wire.Owner, ev);
+        }
+
+        // Remove the voicemask component, or set it back to what it was before
+        if (oldMask == null)
+            EntityManager.RemoveComponent(user, mask);
+        else
+        {
+            mask.Enabled = oldEnabled;
+            mask.VoiceName = oldVoiceName;
+        }
+
+        base.Pulse(user, wire);
+    }
+}
diff --git a/Content.Server/Speech/EntitySystems/BlockListeningSystem.cs b/Content.Server/Speech/EntitySystems/BlockListeningSystem.cs
new file mode 100644 (file)
index 0000000..a13eda1
--- /dev/null
@@ -0,0 +1,18 @@
+using Content.Server.Speech.Components;
+
+namespace Content.Server.Speech.EntitySystems;
+
+public sealed class BlockListeningSystem : EntitySystem
+{
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<BlockListeningComponent, ListenAttemptEvent>(OnListenAttempt);
+    }
+
+    private void OnListenAttempt(EntityUid uid, BlockListeningComponent component, ListenAttemptEvent args)
+    {
+        args.Cancel();
+    }
+}
diff --git a/Content.Shared/Speech/EntitySystems/SharedListenWireAction.cs b/Content.Shared/Speech/EntitySystems/SharedListenWireAction.cs
new file mode 100644 (file)
index 0000000..4ffa13e
--- /dev/null
@@ -0,0 +1,10 @@
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.Speech;
+
+[Serializable, NetSerializable]
+public enum ListenWireActionKey : byte
+{
+    StatusKey,
+    TimeoutKey,
+}
diff --git a/Resources/Locale/en-US/speech/listen-wire-action.ftl b/Resources/Locale/en-US/speech/listen-wire-action.ftl
new file mode 100644 (file)
index 0000000..4e8d211
--- /dev/null
@@ -0,0 +1,3 @@
+wire-listen-pulse-identifier = electricity
+wire-listen-pulse-characters = eee EEo
+wire-listen-pulse-error-name = ERROR
index 3204426fdb647c5b4c60417ae4ed25ad5454b40d..8b760ca60f04e9196c002e72bbfe30ee6a01ea2c 100644 (file)
@@ -64,3 +64,4 @@ wire-name-bomb-proceed = PRCD
 wire-name-bomb-boom = BOOM
 wire-name-bomb-bolt = BOLT
 wire-name-speech = SPKR
+wire-name-listen = MIC
index 4ff7638594f64b441c915689d27cade271b876b3..9848993b082fb880415ccbc37a161fe40a18dcf3 100644 (file)
@@ -23,6 +23,7 @@
   - type: Clickable
   - type: InteractionOutline
   - type: Appearance
+  - type: WiresVisuals
   - type: ContainerFill
     containers:
       board: [ IntercomElectronics ]
       map: ["enum.RadioDeviceVisualLayers.Speaker"]
       shader: unshaded
       visible: false
+    - state: panel
+      map: ["enum.WiresVisualLayers.MaintenancePanel"]
+      shader: unshaded
+      visible: false
   - type: Transform
     noRot: false
     anchored: true
@@ -60,6 +65,8 @@
     interfaces:
       - key: enum.IntercomUiKey.Key
         type: IntercomBoundUserInterface
+      - key: enum.WiresUiKey.Key
+        type: WiresBoundUserInterface
   - type: Construction
     graph: Intercom
     node: intercom
                 volume: -4
   - type: GenericVisualizer
     visuals:
+      enum.WiresVisualLayers.MaintenancePanel:
+        enum.WiresVisualLayers.MaintenancePanel:
+          True: { visible: true }
+          False: { visible: false }
       enum.PowerDeviceVisuals.Powered:
         enum.PowerDeviceVisualLayers.Powered:
           True: { visible: true }
index d54965d88784537d4153e24313b32f3983974cb1..338bf188ba09c13a2b288faf4cf6da723742166d 100644 (file)
   - !type:AirAlarmPanicWire
   - !type:AtmosMonitorDeviceNetWire
 
+- type: wireLayout
+  id: Intercom
+  wires:
+  - !type:PowerWireAction
+  - !type:SpeechWireAction
+  - !type:ListenWireAction
+
 - type: wireLayout
   id: FireAlarm
   wires:
index 78fe02a9bbafb2522d58608674eea2b20d5c8561..f5a9d7b89c245d2d21f3691555d55537ea7ab282 100644 (file)
         {
             "name": "unshaded",
             "directions": 4
+        },
+        {
+            "name": "panel",
+            "directions": 4
         }
     ]
 }
diff --git a/Resources/Textures/Structures/Wallmounts/intercom.rsi/panel.png b/Resources/Textures/Structures/Wallmounts/intercom.rsi/panel.png
new file mode 100644 (file)
index 0000000..3bfeb8d
Binary files /dev/null and b/Resources/Textures/Structures/Wallmounts/intercom.rsi/panel.png differ