Hackable intercoms (#23984)
* 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
This commit is contained in:
@@ -847,6 +847,16 @@ public sealed partial class ChatSystem : SharedChatSystem
|
|||||||
return modifiedMessage.ToString();
|
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
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
11
Content.Server/Speech/Components/BlockListeningComponent.cs
Normal file
11
Content.Server/Speech/Components/BlockListeningComponent.cs
Normal file
@@ -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
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
111
Content.Server/Speech/Components/ListenWireAction.cs
Normal file
111
Content.Server/Speech/Components/ListenWireAction.cs
Normal file
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
18
Content.Server/Speech/EntitySystems/BlockListeningSystem.cs
Normal file
18
Content.Server/Speech/EntitySystems/BlockListeningSystem.cs
Normal file
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.Speech;
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public enum ListenWireActionKey : byte
|
||||||
|
{
|
||||||
|
StatusKey,
|
||||||
|
TimeoutKey,
|
||||||
|
}
|
||||||
3
Resources/Locale/en-US/speech/listen-wire-action.ftl
Normal file
3
Resources/Locale/en-US/speech/listen-wire-action.ftl
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
wire-listen-pulse-identifier = electricity
|
||||||
|
wire-listen-pulse-characters = eee EEo
|
||||||
|
wire-listen-pulse-error-name = ERROR
|
||||||
@@ -64,3 +64,4 @@ wire-name-bomb-proceed = PRCD
|
|||||||
wire-name-bomb-boom = BOOM
|
wire-name-bomb-boom = BOOM
|
||||||
wire-name-bomb-bolt = BOLT
|
wire-name-bomb-bolt = BOLT
|
||||||
wire-name-speech = SPKR
|
wire-name-speech = SPKR
|
||||||
|
wire-name-listen = MIC
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
- type: Clickable
|
- type: Clickable
|
||||||
- type: InteractionOutline
|
- type: InteractionOutline
|
||||||
- type: Appearance
|
- type: Appearance
|
||||||
|
- type: WiresVisuals
|
||||||
- type: ContainerFill
|
- type: ContainerFill
|
||||||
containers:
|
containers:
|
||||||
board: [ IntercomElectronics ]
|
board: [ IntercomElectronics ]
|
||||||
@@ -46,6 +47,10 @@
|
|||||||
map: ["enum.RadioDeviceVisualLayers.Speaker"]
|
map: ["enum.RadioDeviceVisualLayers.Speaker"]
|
||||||
shader: unshaded
|
shader: unshaded
|
||||||
visible: false
|
visible: false
|
||||||
|
- state: panel
|
||||||
|
map: ["enum.WiresVisualLayers.MaintenancePanel"]
|
||||||
|
shader: unshaded
|
||||||
|
visible: false
|
||||||
- type: Transform
|
- type: Transform
|
||||||
noRot: false
|
noRot: false
|
||||||
anchored: true
|
anchored: true
|
||||||
@@ -60,6 +65,8 @@
|
|||||||
interfaces:
|
interfaces:
|
||||||
- key: enum.IntercomUiKey.Key
|
- key: enum.IntercomUiKey.Key
|
||||||
type: IntercomBoundUserInterface
|
type: IntercomBoundUserInterface
|
||||||
|
- key: enum.WiresUiKey.Key
|
||||||
|
type: WiresBoundUserInterface
|
||||||
- type: Construction
|
- type: Construction
|
||||||
graph: Intercom
|
graph: Intercom
|
||||||
node: intercom
|
node: intercom
|
||||||
@@ -87,6 +94,10 @@
|
|||||||
volume: -4
|
volume: -4
|
||||||
- type: GenericVisualizer
|
- type: GenericVisualizer
|
||||||
visuals:
|
visuals:
|
||||||
|
enum.WiresVisualLayers.MaintenancePanel:
|
||||||
|
enum.WiresVisualLayers.MaintenancePanel:
|
||||||
|
True: { visible: true }
|
||||||
|
False: { visible: false }
|
||||||
enum.PowerDeviceVisuals.Powered:
|
enum.PowerDeviceVisuals.Powered:
|
||||||
enum.PowerDeviceVisualLayers.Powered:
|
enum.PowerDeviceVisualLayers.Powered:
|
||||||
True: { visible: true }
|
True: { visible: true }
|
||||||
|
|||||||
@@ -49,6 +49,13 @@
|
|||||||
- !type:AirAlarmPanicWire
|
- !type:AirAlarmPanicWire
|
||||||
- !type:AtmosMonitorDeviceNetWire
|
- !type:AtmosMonitorDeviceNetWire
|
||||||
|
|
||||||
|
- type: wireLayout
|
||||||
|
id: Intercom
|
||||||
|
wires:
|
||||||
|
- !type:PowerWireAction
|
||||||
|
- !type:SpeechWireAction
|
||||||
|
- !type:ListenWireAction
|
||||||
|
|
||||||
- type: wireLayout
|
- type: wireLayout
|
||||||
id: FireAlarm
|
id: FireAlarm
|
||||||
wires:
|
wires:
|
||||||
|
|||||||
@@ -26,6 +26,10 @@
|
|||||||
{
|
{
|
||||||
"name": "unshaded",
|
"name": "unshaded",
|
||||||
"directions": 4
|
"directions": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "panel",
|
||||||
|
"directions": 4
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
Resources/Textures/Structures/Wallmounts/intercom.rsi/panel.png
Normal file
BIN
Resources/Textures/Structures/Wallmounts/intercom.rsi/panel.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 656 B |
Reference in New Issue
Block a user