From b567c34e927d38a2bccd672f6e92b50bef7b613a Mon Sep 17 00:00:00 2001
From: themias <89101928+themias@users.noreply.github.com>
Date: Sun, 16 Oct 2022 13:44:50 -0400
Subject: [PATCH] Add voice trigger for modular grenades (#11449)
---
.../Components/TriggerOnVoiceComponent.cs | 76 ++++++++++++++++++
.../EntitySystems/TriggerSystem.Voice.cs | 61 ++++++++++++++
.../Explosion/EntitySystems/TriggerSystem.cs | 1 +
.../en-US/weapons/grenades/voice-trigger.ftl | 8 ++
.../Catalog/Research/technologies.yml | 1 +
.../Objects/Devices/Electronics/triggers.yml | 15 ++++
.../Entities/Structures/Machines/lathe.yml | 1 +
.../Prototypes/Recipes/Lathes/devices.yml | 11 +++
.../Objects/Devices/voice.rsi/meta.json | 17 ++++
.../Objects/Devices/voice.rsi/voice.png | Bin 0 -> 204 bytes
10 files changed, 191 insertions(+)
create mode 100644 Content.Server/Explosion/Components/TriggerOnVoiceComponent.cs
create mode 100644 Content.Server/Explosion/EntitySystems/TriggerSystem.Voice.cs
create mode 100644 Resources/Locale/en-US/weapons/grenades/voice-trigger.ftl
create mode 100644 Resources/Textures/Objects/Devices/voice.rsi/meta.json
create mode 100644 Resources/Textures/Objects/Devices/voice.rsi/voice.png
diff --git a/Content.Server/Explosion/Components/TriggerOnVoiceComponent.cs b/Content.Server/Explosion/Components/TriggerOnVoiceComponent.cs
new file mode 100644
index 0000000000..f7b9f15636
--- /dev/null
+++ b/Content.Server/Explosion/Components/TriggerOnVoiceComponent.cs
@@ -0,0 +1,76 @@
+using Content.Shared.Interaction;
+using Content.Server.Radio.Components;
+using Content.Shared.Radio;
+using System.Text.RegularExpressions;
+using Content.Server.Explosion.EntitySystems;
+using Content.Server.Radio.EntitySystems;
+using System;
+
+namespace Content.Server.Explosion.Components
+{
+ ///
+ /// Sends a trigger when the keyphrase is heard
+ ///
+ [RegisterComponent]
+ [ComponentReference(typeof(IListen))]
+ public sealed class TriggerOnVoiceComponent : Component, IListen
+ {
+ private SharedInteractionSystem _sharedInteractionSystem = default!;
+ private TriggerSystem _triggerSystem = default!;
+
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("keyPhrase")]
+ public string? KeyPhrase;
+
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("listenRange")]
+ public int ListenRange { get; private set; } = 4;
+
+ [ViewVariables]
+ public bool IsRecording = false;
+
+ [ViewVariables]
+ [DataField("minLength")]
+ public int MinLength = 3;
+
+ [ViewVariables]
+ [DataField("maxLength")]
+ public int MaxLength = 50;
+
+ ///
+ /// Displays 'recorded' popup only for the one who activated
+ /// it in order to allow for stealthily recording others
+ ///
+ [ViewVariables]
+ public EntityUid Activator;
+
+ protected override void Initialize()
+ {
+ base.Initialize();
+
+ _sharedInteractionSystem = EntitySystem.Get();
+ _triggerSystem = EntitySystem.Get();
+ }
+
+ bool IListen.CanListen(string message, EntityUid source, RadioChannelPrototype? channelPrototype)
+ {
+ //will hear standard speech and radio messages originating nearby but not independant whispers
+ return _sharedInteractionSystem.InRangeUnobstructed(Owner, source, range: ListenRange);
+ }
+
+ void IListen.Listen(string message, EntityUid speaker, RadioChannelPrototype? channel)
+ {
+ message = message.Trim();
+
+ if (IsRecording && message.Length >= MinLength && message.Length <= MaxLength)
+ {
+ KeyPhrase = message;
+ _triggerSystem.ToggleRecord(this, Activator, true);
+ }
+ else if (KeyPhrase != null && message.Contains(KeyPhrase, StringComparison.InvariantCultureIgnoreCase))
+ {
+ _triggerSystem.Trigger(Owner, speaker);
+ }
+ }
+ }
+}
diff --git a/Content.Server/Explosion/EntitySystems/TriggerSystem.Voice.cs b/Content.Server/Explosion/EntitySystems/TriggerSystem.Voice.cs
new file mode 100644
index 0000000000..5140d296ff
--- /dev/null
+++ b/Content.Server/Explosion/EntitySystems/TriggerSystem.Voice.cs
@@ -0,0 +1,61 @@
+using Content.Server.Explosion.Components;
+using Content.Server.Nutrition.Components;
+using Content.Shared.Examine;
+using Content.Shared.Interaction.Events;
+using Content.Shared.Popups;
+using Content.Shared.Verbs;
+using Microsoft.CodeAnalysis.Options;
+using Robust.Shared.GameObjects;
+using Robust.Shared.Physics.Events;
+using Robust.Shared.Player;
+using Robust.Shared.Utility;
+
+namespace Content.Server.Explosion.EntitySystems
+{
+ public sealed partial class TriggerSystem
+ {
+ private void InitializeVoice()
+ {
+ SubscribeLocalEvent(OnVoiceExamine);
+ SubscribeLocalEvent>(OnVoiceGetAltVerbs);
+ }
+
+ private void OnVoiceGetAltVerbs(EntityUid uid, TriggerOnVoiceComponent component, GetVerbsEvent args)
+ {
+ if (!args.CanInteract || !args.CanAccess)
+ return;
+
+ args.Verbs.Add(new AlternativeVerb()
+ {
+ Text = Loc.GetString("verb-trigger-voice-record"),
+ Act = () => ToggleRecord(component, args.User),
+ Priority = 1
+ });
+ }
+
+ public void ToggleRecord(TriggerOnVoiceComponent component, EntityUid user, bool recorded = false)
+ {
+ component.IsRecording ^= true;
+
+ if (recorded) //recording success popup
+ {
+ _popupSystem.PopupEntity(Loc.GetString("popup-trigger-voice-recorded"), component.Owner, Filter.Entities(user));
+ }
+ else if (component.IsRecording) //recording start popup
+ {
+ component.Activator = user;
+ _popupSystem.PopupEntity(Loc.GetString("popup-trigger-voice-start-recording"), component.Owner, Filter.Entities(user));
+ }
+ else //recording stopped manually popup
+ {
+ _popupSystem.PopupEntity(Loc.GetString("popup-trigger-voice-stop-recording"), component.Owner, Filter.Entities(user));
+ }
+ }
+
+ private void OnVoiceExamine(EntityUid uid, TriggerOnVoiceComponent component, ExaminedEvent args)
+ {
+ if (args.IsInDetailsRange)
+ args.PushText(Loc.GetString("examine-trigger-voice", ("keyphrase", component.KeyPhrase?? Loc.GetString("trigger-voice-uninitialized"))));
+ }
+ }
+}
diff --git a/Content.Server/Explosion/EntitySystems/TriggerSystem.cs b/Content.Server/Explosion/EntitySystems/TriggerSystem.cs
index cd02f5321a..c0d019805a 100644
--- a/Content.Server/Explosion/EntitySystems/TriggerSystem.cs
+++ b/Content.Server/Explosion/EntitySystems/TriggerSystem.cs
@@ -57,6 +57,7 @@ namespace Content.Server.Explosion.EntitySystems
InitializeOnUse();
InitializeSignal();
InitializeTimedCollide();
+ InitializeVoice();
SubscribeLocalEvent(OnTriggerCollide);
SubscribeLocalEvent(OnActivate);
diff --git a/Resources/Locale/en-US/weapons/grenades/voice-trigger.ftl b/Resources/Locale/en-US/weapons/grenades/voice-trigger.ftl
new file mode 100644
index 0000000000..52be5c7cb2
--- /dev/null
+++ b/Resources/Locale/en-US/weapons/grenades/voice-trigger.ftl
@@ -0,0 +1,8 @@
+examine-trigger-voice = The display reads "{$keyphrase}"
+trigger-voice-uninitialized = Uninitialized...
+
+verb-trigger-voice-record = Record
+
+popup-trigger-voice-start-recording = Started recording
+popup-trigger-voice-stop-recording = Stopped recording
+popup-trigger-voice-recorded = Recorded
\ No newline at end of file
diff --git a/Resources/Prototypes/Catalog/Research/technologies.yml b/Resources/Prototypes/Catalog/Research/technologies.yml
index 0cf6a63a4c..fb28619f65 100644
--- a/Resources/Prototypes/Catalog/Research/technologies.yml
+++ b/Resources/Prototypes/Catalog/Research/technologies.yml
@@ -319,6 +319,7 @@
- GeneratorPlasmaMachineCircuitboard
- Signaller
- SignalTrigger
+ - VoiceTrigger
- type: technology
name: technologies-compact-power-technology
diff --git a/Resources/Prototypes/Entities/Objects/Devices/Electronics/triggers.yml b/Resources/Prototypes/Entities/Objects/Devices/Electronics/triggers.yml
index f8bae2881c..aa4ae65c3b 100644
--- a/Resources/Prototypes/Entities/Objects/Devices/Electronics/triggers.yml
+++ b/Resources/Prototypes/Entities/Objects/Devices/Electronics/triggers.yml
@@ -43,3 +43,18 @@
- type: SignalReceiver
- type: StaticPrice
price: 40
+
+- type: entity
+ parent: BaseItem
+ id: VoiceTrigger
+ name: voice trigger
+ description: Adds a machine link that is triggered by vocal keywords
+ components:
+ - type: Sprite
+ sprite: Objects/Devices/voice.rsi
+ state: voice
+ - type: PayloadTrigger
+ components:
+ - type: TriggerOnVoice
+ - type: StaticPrice
+ price: 40
diff --git a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml
index 47d5600450..180cbf441b 100644
--- a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml
+++ b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml
@@ -217,6 +217,7 @@
- FlashPayload
- Signaller
- SignalTrigger
+ - VoiceTrigger
- PowerCellSmall
- PowerCellMedium
- PowerCellHigh
diff --git a/Resources/Prototypes/Recipes/Lathes/devices.yml b/Resources/Prototypes/Recipes/Lathes/devices.yml
index 4aaa6d197d..c0dfdfc209 100644
--- a/Resources/Prototypes/Recipes/Lathes/devices.yml
+++ b/Resources/Prototypes/Recipes/Lathes/devices.yml
@@ -20,6 +20,17 @@
Steel: 300
Plastic: 200
+- type: latheRecipe
+ id: VoiceTrigger
+ icon:
+ sprite: Objects/Devices/voice.rsi
+ state: voice
+ result: VoiceTrigger
+ completetime: 2
+ materials:
+ Steel: 300
+ Plastic: 200
+
- type: latheRecipe
id: ChemicalPayload
icon:
diff --git a/Resources/Textures/Objects/Devices/voice.rsi/meta.json b/Resources/Textures/Objects/Devices/voice.rsi/meta.json
new file mode 100644
index 0000000000..d4a4b57ddf
--- /dev/null
+++ b/Resources/Textures/Objects/Devices/voice.rsi/meta.json
@@ -0,0 +1,17 @@
+{
+ "version": 1,
+
+ "license": "CC-BY-SA-3.0",
+ "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/c71e03214013191760284361aaff7af90505aaab",
+
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "states": [
+ {
+ "name": "voice",
+ "directions": 1
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Resources/Textures/Objects/Devices/voice.rsi/voice.png b/Resources/Textures/Objects/Devices/voice.rsi/voice.png
new file mode 100644
index 0000000000000000000000000000000000000000..42defce8d441ed572bb357066cd11a51c1e85904
GIT binary patch
literal 204
zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfv^#Gp`*8>L*=;-Lk$;svA<+-@H
zL`FuYq@*w~q!}9<2L=XeXlO8`&15(;vtZ6&U7!}mk|4ie28U-i(tw;`PZ!4!jfu$#
z68{_+VjUQJ&!@DgEl63y!s5Z@5Th#EVz(?n!6VpRsAGm<_A!%OmBtfGy{GG~)^rFG
x6cub;m6ZDYlv#@Lfi|Vc(+u0h?rtq%WymuUX19&V@d6sp;OXk;vd$@?2>`RXJYoO<
literal 0
HcmV?d00001