]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Voice Mask refactor (#30798)
authorbeck-thompson <107373427+beck-thompson@users.noreply.github.com>
Thu, 26 Sep 2024 16:55:59 +0000 (09:55 -0700)
committerGitHub <noreply@github.com>
Thu, 26 Sep 2024 16:55:59 +0000 (18:55 +0200)
* First commit

* Added base.Initialize()

* Voice wire fix (Electricty name)

* Various minor cleanups

* Localized default voice mask name

* Added VoiceOverride stuff

* Removed unused stuff

* Typo

* Better localized stuff

* Typo / spelling stuff / comments

* Blessed

20 files changed:
Content.Client/VoiceMask/VoiceMaskBoundUserInterface.cs
Content.Client/VoiceMask/VoiceMaskNameChangeWindow.xaml.cs
Content.Server/Chat/Systems/ChatSystem.cs
Content.Server/Radio/EntitySystems/RadioDeviceSystem.cs
Content.Server/Radio/EntitySystems/RadioSystem.cs
Content.Server/Speech/Components/ListenWireAction.cs
Content.Server/Speech/Components/VoiceOverrideComponent.cs [new file with mode: 0644]
Content.Server/Speech/EntitySystems/VoiceOverrideSystem.cs [new file with mode: 0644]
Content.Server/SurveillanceCamera/Systems/SurveillanceCameraSpeakerSystem.cs
Content.Server/VoiceMask/VoiceMaskComponent.cs
Content.Server/VoiceMask/VoiceMaskSystem.Equip.cs [deleted file]
Content.Server/VoiceMask/VoiceMaskSystem.cs
Content.Server/VoiceMask/VoiceMaskerComponent.cs [deleted file]
Content.Shared/Chat/SharedChatEvents.cs [new file with mode: 0644]
Content.Shared/Inventory/InventorySystem.Relay.cs
Resources/Locale/en-US/voice-mask.ftl
Resources/Prototypes/Entities/Clothing/Masks/specific.yml
Resources/Prototypes/Entities/Mobs/Species/base.yml
Resources/Prototypes/Entities/Mobs/Species/slime.yml
Resources/Prototypes/Entities/Structures/Wallmounts/intercom.yml

index 891804674d3c9587d7b4bab00ef611c8a72d8561..e76ca1cf8f70356cb1d67f17c45d3b47a5a28db5 100644 (file)
@@ -22,6 +22,7 @@ public sealed class VoiceMaskBoundUserInterface : BoundUserInterface
 
         _window = this.CreateWindow<VoiceMaskNameChangeWindow>();
         _window.ReloadVerbs(_protomanager);
+        _window.AddVerbs();
 
         _window.OnNameChange += OnNameSelected;
         _window.OnVerbChange += verb => SendMessage(new VoiceMaskChangeVerbMessage(verb));
index 0dc41f807ab127d0e2491f6d3d96fe92191f8584..7ca4dd4b95701e71ff8febcae24d95d32677bdff 100644 (file)
@@ -31,8 +31,6 @@ public sealed partial class VoiceMaskNameChangeWindow : FancyWindow
             OnVerbChange?.Invoke((string?) args.Button.GetItemMetadata(args.Id));
             SpeechVerbSelector.SelectId(args.Id);
         };
-
-        AddVerbs();
     }
 
     public void ReloadVerbs(IPrototypeManager proto)
@@ -44,7 +42,7 @@ public sealed partial class VoiceMaskNameChangeWindow : FancyWindow
         _verbs.Sort((a, b) => a.Item1.CompareTo(b.Item1));
     }
 
-    private void AddVerbs()
+    public void AddVerbs()
     {
         SpeechVerbSelector.Clear();
 
index fb84c785d27638de75e930ad2f50d3c560934379..24937ea4b9f423150a7d6e71c6ecf4afee1470a9 100644 (file)
@@ -4,7 +4,6 @@ using System.Text;
 using Content.Server.Administration.Logs;
 using Content.Server.Administration.Managers;
 using Content.Server.Chat.Managers;
-using Content.Server.Examine;
 using Content.Server.GameTicking;
 using Content.Server.Players.RateLimiting;
 using Content.Server.Speech.Components;
@@ -18,13 +17,10 @@ using Content.Shared.Chat;
 using Content.Shared.Database;
 using Content.Shared.Examine;
 using Content.Shared.Ghost;
-using Content.Shared.Humanoid;
 using Content.Shared.IdentityManagement;
-using Content.Shared.Interaction;
 using Content.Shared.Mobs.Systems;
 using Content.Shared.Players;
 using Content.Shared.Radio;
-using Content.Shared.Speech;
 using Content.Shared.Whitelist;
 using Robust.Server.Player;
 using Robust.Shared.Audio;
@@ -440,9 +436,9 @@ public sealed partial class ChatSystem : SharedChatSystem
         {
             var nameEv = new TransformSpeakerNameEvent(source, Name(source));
             RaiseLocalEvent(source, nameEv);
-            name = nameEv.Name;
+            name = nameEv.VoiceName;
             // Check for a speech verb override
-            if (nameEv.SpeechVerb != null && _prototypeManager.TryIndex<SpeechVerbPrototype>(nameEv.SpeechVerb, out var proto))
+            if (nameEv.SpeechVerb != null && _prototypeManager.TryIndex(nameEv.SpeechVerb, out var proto))
                 speech = proto;
         }
 
@@ -514,7 +510,7 @@ public sealed partial class ChatSystem : SharedChatSystem
         {
             var nameEv = new TransformSpeakerNameEvent(source, Name(source));
             RaiseLocalEvent(source, nameEv);
-            name = nameEv.Name;
+            name = nameEv.VoiceName;
         }
         name = FormattedMessage.EscapeText(name);
 
@@ -911,20 +907,6 @@ public record ExpandICChatRecipientsEvent(EntityUid Source, float VoiceRange, Di
 {
 }
 
-public sealed class TransformSpeakerNameEvent : EntityEventArgs
-{
-    public EntityUid Sender;
-    public string Name;
-    public string? SpeechVerb;
-
-    public TransformSpeakerNameEvent(EntityUid sender, string name, string? speechVerb = null)
-    {
-        Sender = sender;
-        Name = name;
-        SpeechVerb = speechVerb;
-    }
-}
-
 /// <summary>
 ///     Raised broadcast in order to transform speech.transmit
 /// </summary>
index c977fbc0489af6189a57543822e60b5bf08fca94..c8867744a40b6b9fd33eb71defc03810d7e86873 100644 (file)
@@ -11,6 +11,7 @@ using Content.Shared.Examine;
 using Content.Shared.Interaction;
 using Content.Shared.Power;
 using Content.Shared.Radio;
+using Content.Shared.Chat;
 using Content.Shared.Radio.Components;
 using Robust.Shared.Prototypes;
 
@@ -213,7 +214,7 @@ public sealed class RadioDeviceSystem : EntitySystem
 
         var name = Loc.GetString("speech-name-relay",
             ("speaker", Name(uid)),
-            ("originalName", nameEv.Name));
+            ("originalName", nameEv.VoiceName));
 
         // log to chat so people can identity the speaker/source, but avoid clogging ghost chat if there are many radios
         _chat.TrySendInGameICMessage(uid, args.Message, InGameICChatType.Whisper, ChatTransmitRange.GhostRangeLimit, nameOverride: name, checkRadioPrefix: false);
index 3ad101e62dbd8ff311e8a28704fcdd0631cc5173..bdc368fa93299b778525f7556ead8b8d7131b0cd 100644 (file)
@@ -2,7 +2,6 @@ using Content.Server.Administration.Logs;
 using Content.Server.Chat.Systems;
 using Content.Server.Power.Components;
 using Content.Server.Radio.Components;
-using Content.Server.VoiceMask;
 using Content.Shared.Chat;
 using Content.Shared.Database;
 using Content.Shared.Radio;
@@ -78,20 +77,15 @@ public sealed class RadioSystem : EntitySystem
         if (!_messages.Add(message))
             return;
 
-        var name = TryComp(messageSource, out VoiceMaskComponent? mask) && mask.Enabled
-            ? mask.VoiceName
-            : MetaData(messageSource).EntityName;
+        var evt = new TransformSpeakerNameEvent(messageSource, MetaData(messageSource).EntityName);
+        RaiseLocalEvent(messageSource, evt);
 
+        var name = evt.VoiceName;
         name = FormattedMessage.EscapeText(name);
 
         SpeechVerbPrototype speech;
-        if (mask != null
-            && mask.Enabled
-            && mask.SpeechVerb != null
-            && _prototype.TryIndex<SpeechVerbPrototype>(mask.SpeechVerb, out var proto))
-        {
-            speech = proto;
-        }
+        if (evt.SpeechVerb != null && _prototype.TryIndex(evt.SpeechVerb, out var evntProto))
+            speech = evntProto;
         else
             speech = _chat.GetSpeechVerb(messageSource, message);
 
index 68d2201862ca79014802056aba46dedf97fafd48..b8b1c19e84b71c42ec71fdd19b2fd289abf0492b 100644 (file)
@@ -1,9 +1,12 @@
-using Content.Server.Speech.Components;
 using Content.Server.Chat.Systems;
-using Content.Server.VoiceMask;
+using Content.Shared.Radio;
+using Content.Server.Radio.Components;
+using Content.Server.Radio.EntitySystems;
+using Content.Server.Speech.Components;
 using Content.Server.Wires;
-using Content.Shared.Speech;
 using Content.Shared.Wires;
+using Content.Shared.Speech;
+using Robust.Shared.Prototypes;
 
 namespace Content.Server.Speech;
 
@@ -11,17 +14,13 @@ public sealed partial class ListenWireAction : BaseToggleWireAction
 {
     private WiresSystem _wires = default!;
     private ChatSystem _chat = default!;
+    private RadioSystem _radio = default!;
+    private IPrototypeManager _protoMan = default!;
 
     /// <summary>
     /// Length of the gibberish string sent when pulsing the wire
     /// </summary>
     private const int NoiseLength = 16;
-
-    /// <summary>
-    /// Identifier of the SpeechVerbPrototype to use when pulsing the wire
-    /// </summary>
-    [ValidatePrototypeId<SpeechVerbPrototype>]
-    private const string SpeechVerb = "Electricity";
     public override Color Color { get; set; } = Color.Green;
     public override string Name { get; set; } = "wire-name-listen";
 
@@ -37,6 +36,8 @@ public sealed partial class ListenWireAction : BaseToggleWireAction
 
         _wires = EntityManager.System<WiresSystem>();
         _chat = EntityManager.System<ChatSystem>();
+        _radio = EntityManager.System<RadioSystem>();
+        _protoMan = IoCManager.Resolve<IPrototypeManager>();
     }
     public override StatusLightState? GetLightState(Wire wire)
     {
@@ -72,46 +73,20 @@ public sealed partial class ListenWireAction : BaseToggleWireAction
         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");
-        string? oldSpeechVerb = null;
-        if (EntityManager.TryGetComponent<VoiceMaskComponent>(user, out var oldMask))
-        {
-            oldEnabled = oldMask.Enabled;
-            oldVoiceName = oldMask.VoiceName;
-            oldSpeechVerb = oldMask.SpeechVerb;
-        }
-
-        // Give the user a temporary voicemask component
-        var mask = EntityManager.EnsureComponent<VoiceMaskComponent>(user);
-        mask.Enabled = true;
-        mask.VoiceName = Loc.GetString("wire-listen-pulse-identifier");
-        mask.SpeechVerb = SpeechVerb;
-
         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);
-        }
+        if (!EntityManager.TryGetComponent<RadioMicrophoneComponent>(wire.Owner, out var radioMicroPhoneComp))
+            return;
 
-        // 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;
-            mask.SpeechVerb = oldSpeechVerb;
-        }
+        if (!EntityManager.TryGetComponent<VoiceOverrideComponent>(wire.Owner, out var voiceOverrideComp))
+            return;
+
+        // The reason for the override is to make the voice sound like its coming from electrity rather than the intercom.
+        voiceOverrideComp.NameOverride = Loc.GetString("wire-listen-pulse-identifier");
+        voiceOverrideComp.Enabled = true;
+        _radio.SendRadioMessage(wire.Owner, noiseMsg, _protoMan.Index<RadioChannelPrototype>(radioMicroPhoneComp.BroadcastChannel), wire.Owner);
+        voiceOverrideComp.Enabled = false;
 
         base.Pulse(user, wire);
     }
diff --git a/Content.Server/Speech/Components/VoiceOverrideComponent.cs b/Content.Server/Speech/Components/VoiceOverrideComponent.cs
new file mode 100644 (file)
index 0000000..349babc
--- /dev/null
@@ -0,0 +1,35 @@
+using Content.Shared.Speech;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server.Speech.Components;
+
+/// <summary>
+///     Will change the voice of the entity that has the component (e.g radio and speech).
+/// </summary>
+/// <remarks>
+///     Before using this component, please take a look at the the TransformSpeakerNameEvent (and the inventory relay version).
+///     Depending on what you're doing, it could be a better choice!
+/// </remarks>
+[RegisterComponent]
+public sealed partial class VoiceOverrideComponent : Component
+{
+    /// <summary>
+    ///     The name that will be used instead of an entities default one.
+    ///     Uses the localized version of the string and if null wont do anything.
+    /// </summary>
+    [DataField]
+    public string? NameOverride = null;
+
+    /// <summary>
+    ///     The verb that will be used insteand of an entities default one.
+    ///     If null, the defaut will be used.
+    /// </summary>
+    [DataField]
+    public ProtoId<SpeechVerbPrototype>? SpeechVerbOverride = null;
+
+    /// <summary>
+    ///     If true, the override values (if they are not null) will be applied.
+    /// </summary>
+    [DataField]
+    public bool Enabled = true;
+}
diff --git a/Content.Server/Speech/EntitySystems/VoiceOverrideSystem.cs b/Content.Server/Speech/EntitySystems/VoiceOverrideSystem.cs
new file mode 100644 (file)
index 0000000..daaad09
--- /dev/null
@@ -0,0 +1,22 @@
+using Content.Shared.Chat;
+using Content.Server.Speech.Components;
+
+namespace Content.Server.Speech.EntitySystems;
+
+public sealed partial class VoiceOverrideSystem : EntitySystem
+{
+    public override void Initialize()
+    {
+        base.Initialize();
+        SubscribeLocalEvent<VoiceOverrideComponent, TransformSpeakerNameEvent>(OnTransformSpeakerName);
+    }
+
+    private void OnTransformSpeakerName(Entity<VoiceOverrideComponent> entity, ref TransformSpeakerNameEvent args)
+    {
+        if (!entity.Comp.Enabled)
+            return;
+
+        args.VoiceName = entity.Comp.NameOverride ?? args.VoiceName;
+        args.SpeechVerb = entity.Comp.SpeechVerbOverride ?? args.SpeechVerb;
+    }
+}
index 0e694a801eb0deb64fc7668e09a571f53785e995..581ac197197f63aa94c5940a817cef8cd4d0359a 100644 (file)
@@ -1,6 +1,7 @@
 using Content.Server.Chat.Systems;
 using Content.Server.Speech;
 using Content.Shared.Speech;
+using Content.Shared.Chat;
 using Robust.Shared.Audio.Systems;
 using Robust.Shared.Timing;
 
@@ -48,7 +49,7 @@ public sealed class SurveillanceCameraSpeakerSystem : EntitySystem
         RaiseLocalEvent(args.Speaker, nameEv);
 
         var name = Loc.GetString("speech-name-relay", ("speaker", Name(uid)),
-            ("originalName", nameEv.Name));
+            ("originalName", nameEv.VoiceName));
 
         // log to chat so people can identity the speaker/source, but avoid clogging ghost chat if there are many radios
         _chatSystem.TrySendInGameICMessage(uid, args.Message, InGameICChatType.Speak, ChatTransmitRange.GhostRangeLimit, nameOverride: name);
index d0c920030063db9c760ba743e7ae06f0e10bf76d..d3116f94db1475f20a103a15ad82c168cfb340a2 100644 (file)
@@ -3,21 +3,38 @@ using Robust.Shared.Prototypes;
 
 namespace Content.Server.VoiceMask;
 
+/// <summary>
+///     This component is for voice mask items! Adding this component to clothing will give the the voice mask UI
+///     and allow the wearer to change their voice and verb at will.
+/// </summary>
+/// <remarks>
+///     DO NOT use this if you do not want the interface.
+///     The VoiceOverrideSystem is probably what your looking for (Or you might have to make something similar)!
+/// </remarks>
 [RegisterComponent]
 public sealed partial class VoiceMaskComponent : Component
 {
+    /// <summary>
+    ///     The name that will override an entities default name. If null, it will use the default override.
+    /// </summary>
+    [DataField]
+    public string? VoiceMaskName = null;
+
+    /// <summary>
+    ///     The speech verb that will override an entities default one. If null, it will use the entities default verb.
+    /// </summary>
     [DataField]
-    [ViewVariables(VVAccess.ReadWrite)]
-    public bool Enabled = true;
+    public ProtoId<SpeechVerbPrototype>? VoiceMaskSpeechVerb;
 
+    /// <summary>
+    ///     The action that gets displayed when the voice mask is equipped.
+    /// </summary>
     [DataField]
-    [ViewVariables(VVAccess.ReadWrite)]
-    public string VoiceName = "Unknown";
+    public EntProtoId Action = "ActionChangeVoiceMask";
 
     /// <summary>
-    /// If EnableSpeechVerbModification is true, overrides the speech verb used when this entity speaks.
+    ///     Reference to the action.
     /// </summary>
     [DataField]
-    [ViewVariables(VVAccess.ReadWrite)]
-    public ProtoId<SpeechVerbPrototype>? SpeechVerb;
+    public EntityUid? ActionEntity;
 }
diff --git a/Content.Server/VoiceMask/VoiceMaskSystem.Equip.cs b/Content.Server/VoiceMask/VoiceMaskSystem.Equip.cs
deleted file mode 100644 (file)
index b97c47c..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-using Content.Server.Actions;
-using Content.Shared.Clothing;
-using Content.Shared.Inventory;
-
-namespace Content.Server.VoiceMask;
-
-// This partial deals with equipment, i.e., the syndicate voice mask.
-public sealed partial class VoiceMaskSystem
-{
-    [Dependency] private readonly InventorySystem _inventory = default!;
-    [Dependency] private readonly ActionsSystem _actions = default!;
-
-    private const string MaskSlot = "mask";
-
-    private void OnEquip(EntityUid uid, VoiceMaskerComponent component, ClothingGotEquippedEvent args)
-    {
-        var user = args.Wearer;
-        var comp = EnsureComp<VoiceMaskComponent>(user);
-        comp.VoiceName = component.LastSetName;
-        comp.SpeechVerb = component.LastSpeechVerb;
-
-        _actions.AddAction(user, ref component.ActionEntity, component.Action, uid);
-    }
-
-    private void OnUnequip(EntityUid uid, VoiceMaskerComponent compnent, ClothingGotUnequippedEvent args)
-    {
-        RemComp<VoiceMaskComponent>(args.Wearer);
-    }
-
-    private VoiceMaskerComponent? TryGetMask(EntityUid user)
-    {
-        if (!HasComp<VoiceMaskComponent>(user) || !_inventory.TryGetSlotEntity(user, MaskSlot, out var maskEntity))
-            return null;
-
-        return CompOrNull<VoiceMaskerComponent>(maskEntity);
-    }
-
-    private void TrySetLastKnownName(EntityUid user, string name)
-    {
-        if (TryGetMask(user) is {} comp)
-            comp.LastSetName = name;
-    }
-
-    private void TrySetLastSpeechVerb(EntityUid user, string? verb)
-    {
-        if (TryGetMask(user) is {} comp)
-            comp.LastSpeechVerb = verb;
-    }
-}
index df972b9a14ae1d001a0b7e68ca1ebb07700549d0..47ea98d2ccfbda0abd0a74c108fb8ba49f00179c 100644 (file)
-using Content.Server.Administration.Logs;
-using Content.Server.Chat.Systems;
-using Content.Server.Popups;
+using Content.Shared.Actions;
+using Content.Shared.Administration.Logs;
+using Content.Shared.Chat;
 using Content.Shared.Clothing;
 using Content.Shared.Database;
+using Content.Shared.Inventory;
 using Content.Shared.Popups;
 using Content.Shared.Preferences;
 using Content.Shared.Speech;
 using Content.Shared.VoiceMask;
-using Robust.Server.GameObjects;
-using Robust.Shared.Player;
 using Robust.Shared.Prototypes;
 
 namespace Content.Server.VoiceMask;
 
 public sealed partial class VoiceMaskSystem : EntitySystem
 {
-    [Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
-    [Dependency] private readonly PopupSystem _popupSystem = default!;
-    [Dependency] private readonly IAdminLogManager _adminLogger = default!;
+    [Dependency] private readonly SharedUserInterfaceSystem _uiSystem = default!;
+    [Dependency] private readonly SharedPopupSystem _popupSystem = default!;
+    [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
     [Dependency] private readonly IPrototypeManager _proto = default!;
+    [Dependency] private readonly SharedActionsSystem _actions = default!;
 
     public override void Initialize()
     {
-        SubscribeLocalEvent<VoiceMaskComponent, TransformSpeakerNameEvent>(OnSpeakerNameTransform);
+        base.Initialize();
+        SubscribeLocalEvent<VoiceMaskComponent, InventoryRelayedEvent<TransformSpeakerNameEvent>>(OnTransformSpeakerName);
         SubscribeLocalEvent<VoiceMaskComponent, VoiceMaskChangeNameMessage>(OnChangeName);
         SubscribeLocalEvent<VoiceMaskComponent, VoiceMaskChangeVerbMessage>(OnChangeVerb);
-        SubscribeLocalEvent<VoiceMaskComponent, WearerMaskToggledEvent>(OnMaskToggled);
-        SubscribeLocalEvent<VoiceMaskerComponent, ClothingGotEquippedEvent>(OnEquip);
-        SubscribeLocalEvent<VoiceMaskerComponent, ClothingGotUnequippedEvent>(OnUnequip);
-        SubscribeLocalEvent<VoiceMaskSetNameEvent>(OnSetName);
-        // SubscribeLocalEvent<VoiceMaskerComponent, GetVerbsEvent<AlternativeVerb>>(GetVerbs);
+        SubscribeLocalEvent<VoiceMaskComponent, ClothingGotEquippedEvent>(OnEquip);
+        SubscribeLocalEvent<VoiceMaskSetNameEvent>(OpenUI);
     }
 
-    private void OnSetName(VoiceMaskSetNameEvent ev)
+    private void OnTransformSpeakerName(Entity<VoiceMaskComponent> entity, ref InventoryRelayedEvent<TransformSpeakerNameEvent> args)
     {
-        OpenUI(ev.Performer);
+        args.Args.VoiceName = GetCurrentVoiceName(entity);
+        args.Args.SpeechVerb = entity.Comp.VoiceMaskSpeechVerb ?? args.Args.SpeechVerb;
     }
 
-    private void OnChangeName(EntityUid uid, VoiceMaskComponent component, VoiceMaskChangeNameMessage message)
+    #region User inputs from UI
+    private void OnChangeVerb(Entity<VoiceMaskComponent> entity, ref VoiceMaskChangeVerbMessage msg)
     {
-        if (message.Name.Length > HumanoidCharacterProfile.MaxNameLength || message.Name.Length <= 0)
-        {
-            _popupSystem.PopupEntity(Loc.GetString("voice-mask-popup-failure"), uid, message.Actor, PopupType.SmallCaution);
+        if (msg.Verb is { } id && !_proto.HasIndex<SpeechVerbPrototype>(id))
             return;
-        }
 
-        component.VoiceName = message.Name;
-        _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(message.Actor):player} set voice of {ToPrettyString(uid):mask}: {component.VoiceName}");
-
-        _popupSystem.PopupEntity(Loc.GetString("voice-mask-popup-success"), uid, message.Actor);
+        entity.Comp.VoiceMaskSpeechVerb = msg.Verb;
+        // verb is only important to metagamers so no need to log as opposed to name
 
-        TrySetLastKnownName(uid, message.Name);
+        _popupSystem.PopupEntity(Loc.GetString("voice-mask-popup-success"), entity, msg.Actor);
 
-        UpdateUI(uid, component);
+        UpdateUI(entity);
     }
 
-    private void OnChangeVerb(Entity<VoiceMaskComponent> ent, ref VoiceMaskChangeVerbMessage msg)
+    private void OnChangeName(Entity<VoiceMaskComponent> entity, ref VoiceMaskChangeNameMessage message)
     {
-        if (msg.Verb is {} id && !_proto.HasIndex<SpeechVerbPrototype>(id))
+        if (message.Name.Length > HumanoidCharacterProfile.MaxNameLength || message.Name.Length <= 0)
+        {
+            _popupSystem.PopupEntity(Loc.GetString("voice-mask-popup-failure"), entity, message.Actor, PopupType.SmallCaution);
             return;
+        }
 
-        ent.Comp.SpeechVerb = msg.Verb;
-        // verb is only important to metagamers so no need to log as opposed to name
-
-        _popupSystem.PopupEntity(Loc.GetString("voice-mask-popup-success"), ent, msg.Actor);
+        entity.Comp.VoiceMaskName = message.Name;
+        _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(message.Actor):player} set voice of {ToPrettyString(entity):mask}: {entity.Comp.VoiceMaskName}");
 
-        TrySetLastSpeechVerb(ent, msg.Verb);
+        _popupSystem.PopupEntity(Loc.GetString("voice-mask-popup-success"), entity, message.Actor);
 
-        UpdateUI(ent, ent.Comp);
+        UpdateUI(entity);
     }
+    #endregion
 
-    private void OnSpeakerNameTransform(EntityUid uid, VoiceMaskComponent component, TransformSpeakerNameEvent args)
+    #region UI
+    private void OnEquip(EntityUid uid, VoiceMaskComponent component, ClothingGotEquippedEvent args)
     {
-        if (component.Enabled)
-        {
-            /*
-            args.Name = _idCard.TryGetIdCard(uid, out var card) && !string.IsNullOrEmpty(card.FullName)
-                ? card.FullName
-                : Loc.GetString("voice-mask-unknown");
-                */
-
-            args.Name = component.VoiceName;
-            if (component.SpeechVerb != null)
-                args.SpeechVerb = component.SpeechVerb;
-        }
+        _actions.AddAction(args.Wearer, ref component.ActionEntity, component.Action, uid);
     }
 
-    private void OnMaskToggled(Entity<VoiceMaskComponent> ent, ref WearerMaskToggledEvent args)
+    private void OpenUI(VoiceMaskSetNameEvent ev)
     {
-        ent.Comp.Enabled = !args.IsToggled;
-    }
+        var maskEntity = ev.Action.Comp.Container;
 
-    private void OpenUI(EntityUid player)
-    {
-        if (!_uiSystem.HasUi(player, VoiceMaskUIKey.Key))
+        if (!TryComp<VoiceMaskComponent>(maskEntity, out var voiceMaskComp))
+            return;
+
+        if (!_uiSystem.HasUi(maskEntity.Value, VoiceMaskUIKey.Key))
             return;
 
-        _uiSystem.OpenUi(player, VoiceMaskUIKey.Key, player);
-        UpdateUI(player);
+        _uiSystem.OpenUi(maskEntity.Value, VoiceMaskUIKey.Key, ev.Performer);
+        UpdateUI((maskEntity.Value, voiceMaskComp));
     }
 
-    private void UpdateUI(EntityUid owner, VoiceMaskComponent? component = null)
+    private void UpdateUI(Entity<VoiceMaskComponent> entity)
     {
-        if (!Resolve(owner, ref component))
-        {
-            return;
-        }
+        if (_uiSystem.HasUi(entity, VoiceMaskUIKey.Key))
+            _uiSystem.SetUiState(entity.Owner, VoiceMaskUIKey.Key, new VoiceMaskBuiState(GetCurrentVoiceName(entity), entity.Comp.VoiceMaskSpeechVerb));
+    }
+    #endregion
 
-        if (_uiSystem.HasUi(owner, VoiceMaskUIKey.Key))
-            _uiSystem.SetUiState(owner, VoiceMaskUIKey.Key, new VoiceMaskBuiState(component.VoiceName, component.SpeechVerb));
+    #region Helper functions
+    private string GetCurrentVoiceName(Entity<VoiceMaskComponent> entity)
+    {
+        return entity.Comp.VoiceMaskName ?? Loc.GetString("voice-mask-default-name-override");
     }
+    #endregion
 }
diff --git a/Content.Server/VoiceMask/VoiceMaskerComponent.cs b/Content.Server/VoiceMask/VoiceMaskerComponent.cs
deleted file mode 100644 (file)
index afea587..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-using Content.Shared.Speech;
-using Robust.Shared.Prototypes;
-
-namespace Content.Server.VoiceMask;
-
-[RegisterComponent]
-public sealed partial class VoiceMaskerComponent : Component
-{
-    [DataField]
-    public string LastSetName = "Unknown";
-
-    [DataField]
-    public ProtoId<SpeechVerbPrototype>? LastSpeechVerb;
-
-    [DataField]
-    public EntProtoId Action = "ActionChangeVoiceMask";
-
-    [DataField]
-    public EntityUid? ActionEntity;
-}
diff --git a/Content.Shared/Chat/SharedChatEvents.cs b/Content.Shared/Chat/SharedChatEvents.cs
new file mode 100644 (file)
index 0000000..c187fd5
--- /dev/null
@@ -0,0 +1,24 @@
+using Content.Shared.Speech;
+using Robust.Shared.Prototypes;
+using Content.Shared.Inventory;
+
+namespace Content.Shared.Chat;
+
+/// <summary>
+///     This event should be sent everytime an entity talks (Radio, local chat, etc...).
+///     The event is sent to both the entity itself, and all clothing (For stuff like voice masks).
+/// </summary>
+public sealed class TransformSpeakerNameEvent : EntityEventArgs, IInventoryRelayEvent
+{
+    public SlotFlags TargetSlots { get; } = SlotFlags.WITHOUT_POCKET;
+    public EntityUid Sender;
+    public string VoiceName;
+    public ProtoId<SpeechVerbPrototype>? SpeechVerb;
+
+    public TransformSpeakerNameEvent(EntityUid sender, string name)
+    {
+        Sender = sender;
+        VoiceName = name;
+        SpeechVerb = null;
+    }
+}
index fc300b24afed7472fbbe55fd11e726c439c8dbe2..c910a9ae772195081274d316aff31bb9cf9ffc50 100644 (file)
@@ -15,6 +15,7 @@ using Content.Shared.Slippery;
 using Content.Shared.Strip.Components;
 using Content.Shared.Temperature;
 using Content.Shared.Verbs;
+using Content.Shared.Chat;
 
 namespace Content.Shared.Inventory;
 
@@ -31,6 +32,7 @@ public partial class InventorySystem
         SubscribeLocalEvent<InventoryComponent, ModifyChangedTemperatureEvent>(RelayInventoryEvent);
         SubscribeLocalEvent<InventoryComponent, GetDefaultRadioChannelEvent>(RelayInventoryEvent);
         SubscribeLocalEvent<InventoryComponent, RefreshNameModifiersEvent>(RelayInventoryEvent);
+        SubscribeLocalEvent<InventoryComponent, TransformSpeakerNameEvent>(RelayInventoryEvent);
 
         // by-ref events
         SubscribeLocalEvent<InventoryComponent, GetExplosionResistanceEvent>(RefRelayInventoryEvent);
index 2f5acefee4118f8565330b098603675dd362b089..f3740cdafb68d70d55eec89d8a20ceb15cbb41d8 100644 (file)
@@ -1,3 +1,5 @@
+voice-mask-default-name-override = Unknown
+
 voice-mask-name-change-window = Voice Mask Name Change
 voice-mask-name-change-info = Type in the name you want to mimic.
 voice-mask-name-change-speech-style = Speech style
index 64a1adcebdf8a93770005b0013f2e8b9a1d2132d..90c648c9d8893ec51728c0da80c9b76103a99d8b 100644 (file)
   id: ClothingMaskGasVoiceChameleon
   suffix: Voice Mask, Chameleon
   components:
-    - type: VoiceMasker
+    - type: VoiceMask
     - type: HideLayerClothing
       slots:
       - Snout
+    - type: UserInterface
+      interfaces:
+        enum.VoiceMaskUIKey.Key:
+          type: VoiceMaskBoundUserInterface
 
 - type: entity
   parent: ClothingMaskBase
index 5cebd2cf853b441fdf99d507f67a1c406b5999a0..100a0ced8427512fe6cbd1d566e71646a0fc4923 100644 (file)
   - type: Stripping
   - type: UserInterface
     interfaces:
-      enum.VoiceMaskUIKey.Key:
-        type: VoiceMaskBoundUserInterface
       enum.HumanoidMarkingModifierKey.Key:
         type: HumanoidMarkingModifierBoundUserInterface
       enum.StrippingUiKey.Key:
index 23d68338398484730231adefbf83d3c8897c52fb..56acf52fb7178c738aec23d5eae6e9ad2703d28f 100644 (file)
@@ -28,8 +28,6 @@
     interfaces:
       enum.StorageUiKey.Key:
         type: StorageBoundUserInterface
-      enum.VoiceMaskUIKey.Key:
-        type: VoiceMaskBoundUserInterface
       enum.HumanoidMarkingModifierKey.Key:
         type: HumanoidMarkingModifierBoundUserInterface
       enum.StrippingUiKey.Key:
index ef3546981f98574e7623a976f3ff95d46aa5b286..d44bbb9647d0e4c642ccaf4f938bebfe785f386a 100644 (file)
@@ -24,6 +24,9 @@
   - type: Intercom
   - type: Speech
     speechVerb: Robotic
+  - type: VoiceOverride # This is for the wire that makes an electricity zapping noise.
+    speechVerbOverride: Electricity
+    enabled: false
   - type: ExtensionCableReceiver
   - type: Clickable
   - type: InteractionOutline