From 4ff7411fb786c1288db1d2feab080729d2551018 Mon Sep 17 00:00:00 2001
From: alexalexmax <149889301+alexalexmax@users.noreply.github.com>
Date: Thu, 25 Dec 2025 18:47:05 -0800
Subject: [PATCH] Voice mask effects are toggleable and hide your accent
(#41965)
* apply negate accents system
* add toggle to voice mask ui
* roll negateaccents into voice mask system, delete negate accents comp&system, update yml entries
* convert button to ToggleButton and some cleanup
* retry for heisenfail
* accent toggle
* update names and add mask active check for accent hiding
---
.../VoiceMask/VoiceMaskBoundUserInterface.cs | 14 ++++-
.../VoiceMask/VoiceMaskNameChangeWindow.xaml | 2 +
.../VoiceMaskNameChangeWindow.xaml.cs | 9 ++-
Content.Server/Chat/Systems/ChatSystem.cs | 2 +-
Content.Server/Speech/AccentSystem.cs | 3 +
.../VoiceMask/VoiceMaskComponent.cs | 12 ++++
Content.Server/VoiceMask/VoiceMaskSystem.cs | 57 +++++++++++++++++--
Content.Shared/Chat/SharedChatEvents.cs | 3 +-
.../SharedSubdermalImplantSystem.Relays.cs | 1 +
.../Inventory/InventorySystem.Relay.cs | 1 +
.../VoiceMask/SharedVoiceMaskSystem.cs | 18 +++++-
Resources/Locale/en-US/voice-mask.ftl | 5 ++
12 files changed, 118 insertions(+), 9 deletions(-)
diff --git a/Content.Client/VoiceMask/VoiceMaskBoundUserInterface.cs b/Content.Client/VoiceMask/VoiceMaskBoundUserInterface.cs
index e76ca1cf8f..b2b374cac5 100644
--- a/Content.Client/VoiceMask/VoiceMaskBoundUserInterface.cs
+++ b/Content.Client/VoiceMask/VoiceMaskBoundUserInterface.cs
@@ -26,6 +26,8 @@ public sealed class VoiceMaskBoundUserInterface : BoundUserInterface
_window.OnNameChange += OnNameSelected;
_window.OnVerbChange += verb => SendMessage(new VoiceMaskChangeVerbMessage(verb));
+ _window.OnToggle += OnToggle;
+ _window.OnAccentToggle += OnAccentToggle;
}
private void OnNameSelected(string name)
@@ -33,6 +35,16 @@ public sealed class VoiceMaskBoundUserInterface : BoundUserInterface
SendMessage(new VoiceMaskChangeNameMessage(name));
}
+ private void OnToggle()
+ {
+ SendMessage(new VoiceMaskToggleMessage());
+ }
+
+ private void OnAccentToggle()
+ {
+ SendMessage(new VoiceMaskAccentToggleMessage());
+ }
+
protected override void UpdateState(BoundUserInterfaceState state)
{
if (state is not VoiceMaskBuiState cast || _window == null)
@@ -40,7 +52,7 @@ public sealed class VoiceMaskBoundUserInterface : BoundUserInterface
return;
}
- _window.UpdateState(cast.Name, cast.Verb);
+ _window.UpdateState(cast.Name, cast.Verb, cast.Active, cast.AccentHide);
}
protected override void Dispose(bool disposing)
diff --git a/Content.Client/VoiceMask/VoiceMaskNameChangeWindow.xaml b/Content.Client/VoiceMask/VoiceMaskNameChangeWindow.xaml
index e23aca1239..18416757b9 100644
--- a/Content.Client/VoiceMask/VoiceMaskNameChangeWindow.xaml
+++ b/Content.Client/VoiceMask/VoiceMaskNameChangeWindow.xaml
@@ -12,5 +12,7 @@
+
+
diff --git a/Content.Client/VoiceMask/VoiceMaskNameChangeWindow.xaml.cs b/Content.Client/VoiceMask/VoiceMaskNameChangeWindow.xaml.cs
index 7ca4dd4b95..a5e7036283 100644
--- a/Content.Client/VoiceMask/VoiceMaskNameChangeWindow.xaml.cs
+++ b/Content.Client/VoiceMask/VoiceMaskNameChangeWindow.xaml.cs
@@ -12,6 +12,8 @@ public sealed partial class VoiceMaskNameChangeWindow : FancyWindow
{
public Action? OnNameChange;
public Action? OnVerbChange;
+ public Action? OnToggle;
+ public Action? OnAccentToggle;
private List<(string, string)> _verbs = new();
@@ -31,6 +33,9 @@ public sealed partial class VoiceMaskNameChangeWindow : FancyWindow
OnVerbChange?.Invoke((string?) args.Button.GetItemMetadata(args.Id));
SpeechVerbSelector.SelectId(args.Id);
};
+
+ ToggleButton.OnPressed += args => OnToggle?.Invoke();
+ ToggleAccentButton.OnPressed += args => OnAccentToggle?.Invoke();
}
public void ReloadVerbs(IPrototypeManager proto)
@@ -64,10 +69,12 @@ public sealed partial class VoiceMaskNameChangeWindow : FancyWindow
SpeechVerbSelector.SelectId(id);
}
- public void UpdateState(string name, string? verb)
+ public void UpdateState(string name, string? verb, bool active, bool accentHide)
{
NameSelector.Text = name;
_verb = verb;
+ ToggleButton.Pressed = active;
+ ToggleAccentButton.Pressed = accentHide;
for (int id = 0; id < SpeechVerbSelector.ItemCount; id++)
{
diff --git a/Content.Server/Chat/Systems/ChatSystem.cs b/Content.Server/Chat/Systems/ChatSystem.cs
index 581b678b84..9ea04ca3bc 100644
--- a/Content.Server/Chat/Systems/ChatSystem.cs
+++ b/Content.Server/Chat/Systems/ChatSystem.cs
@@ -736,7 +736,7 @@ public sealed partial class ChatSystem : SharedChatSystem
public string TransformSpeech(EntityUid sender, string message)
{
var ev = new TransformSpeechEvent(sender, message);
- RaiseLocalEvent(ev);
+ RaiseLocalEvent(sender, ev, true);
return ev.Message;
}
diff --git a/Content.Server/Speech/AccentSystem.cs b/Content.Server/Speech/AccentSystem.cs
index 275a4175c8..6f89a7d14d 100644
--- a/Content.Server/Speech/AccentSystem.cs
+++ b/Content.Server/Speech/AccentSystem.cs
@@ -15,6 +15,9 @@ public sealed class AccentSystem : EntitySystem
private void AccentHandler(TransformSpeechEvent args)
{
+ if (args.Cancelled)
+ return;
+
var accentEvent = new AccentGetEvent(args.Sender, args.Message);
RaiseLocalEvent(args.Sender, accentEvent, true);
diff --git a/Content.Server/VoiceMask/VoiceMaskComponent.cs b/Content.Server/VoiceMask/VoiceMaskComponent.cs
index f7e07f2bd1..042b78a859 100644
--- a/Content.Server/VoiceMask/VoiceMaskComponent.cs
+++ b/Content.Server/VoiceMask/VoiceMaskComponent.cs
@@ -43,5 +43,17 @@ public sealed partial class VoiceMaskComponent : Component
///
[DataField]
public EntityUid? ActionEntity;
+
+ ///
+ /// If user's voice is getting changed when they speak.
+ ///
+ [DataField]
+ public bool Active = true;
+
+ ///
+ /// If user's accent is getting hidden when they speak.
+ ///
+ [DataField]
+ public bool AccentHide = true;
}
diff --git a/Content.Server/VoiceMask/VoiceMaskSystem.cs b/Content.Server/VoiceMask/VoiceMaskSystem.cs
index 170bddf082..a67bfb8b66 100644
--- a/Content.Server/VoiceMask/VoiceMaskSystem.cs
+++ b/Content.Server/VoiceMask/VoiceMaskSystem.cs
@@ -1,3 +1,4 @@
+using Content.Server.Speech;
using Content.Shared.Actions;
using Content.Shared.Administration.Logs;
using Content.Shared.CCVar;
@@ -10,7 +11,6 @@ using Content.Shared.Implants;
using Content.Shared.Inventory;
using Content.Shared.Lock;
using Content.Shared.Popups;
-using Content.Shared.Preferences;
using Content.Shared.Speech;
using Content.Shared.VoiceMask;
using Robust.Shared.Configuration;
@@ -45,12 +45,41 @@ public sealed partial class VoiceMaskSystem : EntitySystem
SubscribeLocalEvent(OnLockToggled);
SubscribeLocalEvent(OnChangeName);
SubscribeLocalEvent(OnChangeVerb);
+ SubscribeLocalEvent(OnToggle);
+ SubscribeLocalEvent(OnAccentToggle);
SubscribeLocalEvent(OnEquip);
SubscribeLocalEvent(OpenUI);
+ SubscribeLocalEvent(OnTransformSpeech, before: [typeof(AccentSystem)]);
+ SubscribeLocalEvent>(OnTransformSpeechInventory, before: [typeof(AccentSystem)]);
+ SubscribeLocalEvent>(OnTransformSpeechImplant, before: [typeof(AccentSystem)]);
Subs.CVar(_cfgManager, CCVars.MaxNameLength, value => _maxNameLength = value, true);
}
+ ///
+ /// Hides accent if the voice mask is on and the option to block accents is on
+ ///
+ private void TransformSpeech(Entity entity, TransformSpeechEvent args)
+ {
+ if (entity.Comp.AccentHide && entity.Comp.Active)
+ args.Cancel();
+ }
+
+ private void OnTransformSpeech(Entity entity, ref TransformSpeechEvent args)
+ {
+ TransformSpeech(entity, args);
+ }
+
+ private void OnTransformSpeechInventory(Entity entity, ref InventoryRelayedEvent args)
+ {
+ TransformSpeech(entity, args.Args);
+ }
+
+ private void OnTransformSpeechImplant(Entity entity, ref ImplantRelayEvent args)
+ {
+ TransformSpeech(entity, args.Event);
+ }
+
private void OnTransformSpeakerNameInventory(Entity entity, ref InventoryRelayedEvent args)
{
TransformVoice(entity, args.Args);
@@ -63,8 +92,10 @@ public sealed partial class VoiceMaskSystem : EntitySystem
private void OnSeeIdentityAttemptEvent(Entity entity, ref ImplantRelayEvent args)
{
- if (entity.Comp.OverrideIdentity)
- args.Event.NameOverride = GetCurrentVoiceName(entity);
+ if (!entity.Comp.OverrideIdentity || !entity.Comp.Active)
+ return;
+
+ args.Event.NameOverride = GetCurrentVoiceName(entity);
}
private void OnImplantImplantedEvent(Entity entity, ref ImplantImplantedEvent ev)
@@ -117,6 +148,21 @@ public sealed partial class VoiceMaskSystem : EntitySystem
UpdateUI(entity);
}
+
+ private void OnToggle(Entity entity, ref VoiceMaskToggleMessage args)
+ {
+ _popupSystem.PopupEntity(Loc.GetString("voice-mask-popup-toggle"), entity, args.Actor);
+ entity.Comp.Active = !entity.Comp.Active;
+
+ // Update identity because of possible name override
+ _identity.QueueIdentityUpdate(args.Actor);
+ }
+
+ private void OnAccentToggle(Entity entity, ref VoiceMaskAccentToggleMessage args)
+ {
+ _popupSystem.PopupEntity(Loc.GetString("voice-mask-popup-accent-toggle"), entity, args.Actor);
+ entity.Comp.AccentHide = !entity.Comp.AccentHide;
+ }
#endregion
#region UI
@@ -145,7 +191,7 @@ public sealed partial class VoiceMaskSystem : EntitySystem
private void UpdateUI(Entity entity)
{
if (_uiSystem.HasUi(entity, VoiceMaskUIKey.Key))
- _uiSystem.SetUiState(entity.Owner, VoiceMaskUIKey.Key, new VoiceMaskBuiState(GetCurrentVoiceName(entity), entity.Comp.VoiceMaskSpeechVerb));
+ _uiSystem.SetUiState(entity.Owner, VoiceMaskUIKey.Key, new VoiceMaskBuiState(GetCurrentVoiceName(entity), entity.Comp.VoiceMaskSpeechVerb, entity.Comp.Active, entity.Comp.AccentHide));
}
#endregion
@@ -157,6 +203,9 @@ public sealed partial class VoiceMaskSystem : EntitySystem
private void TransformVoice(Entity entity, TransformSpeakerNameEvent args)
{
+ if (!entity.Comp.Active)
+ return;
+
args.VoiceName = GetCurrentVoiceName(entity);
args.SpeechVerb = entity.Comp.VoiceMaskSpeechVerb ?? args.SpeechVerb;
}
diff --git a/Content.Shared/Chat/SharedChatEvents.cs b/Content.Shared/Chat/SharedChatEvents.cs
index f9b706e57d..5701081b56 100644
--- a/Content.Shared/Chat/SharedChatEvents.cs
+++ b/Content.Shared/Chat/SharedChatEvents.cs
@@ -27,8 +27,9 @@ public sealed class TransformSpeakerNameEvent : EntityEventArgs, IInventoryRelay
///
/// Raised broadcast in order to transform speech.transmit
///
-public sealed class TransformSpeechEvent : EntityEventArgs
+public sealed class TransformSpeechEvent : CancellableEntityEventArgs, IInventoryRelayEvent
{
+ public SlotFlags TargetSlots { get; } = SlotFlags.WITHOUT_POCKET;
public EntityUid Sender;
public string Message;
diff --git a/Content.Shared/Implants/SharedSubdermalImplantSystem.Relays.cs b/Content.Shared/Implants/SharedSubdermalImplantSystem.Relays.cs
index 774be5d9b2..24b76e15c5 100644
--- a/Content.Shared/Implants/SharedSubdermalImplantSystem.Relays.cs
+++ b/Content.Shared/Implants/SharedSubdermalImplantSystem.Relays.cs
@@ -15,6 +15,7 @@ public abstract partial class SharedSubdermalImplantSystem
SubscribeLocalEvent(RelayToImplantEvent);
SubscribeLocalEvent(RelayToImplantEvent);
SubscribeLocalEvent(RelayToImplantEvent);
+ SubscribeLocalEvent(RelayToImplantEvent);
SubscribeLocalEvent(RelayToImplantEvent);
}
diff --git a/Content.Shared/Inventory/InventorySystem.Relay.cs b/Content.Shared/Inventory/InventorySystem.Relay.cs
index 242e8d0de9..280a99cd32 100644
--- a/Content.Shared/Inventory/InventorySystem.Relay.cs
+++ b/Content.Shared/Inventory/InventorySystem.Relay.cs
@@ -47,6 +47,7 @@ public partial class InventorySystem
SubscribeLocalEvent(RelayInventoryEvent);
SubscribeLocalEvent(RelayInventoryEvent);
SubscribeLocalEvent(RelayInventoryEvent);
+ SubscribeLocalEvent(RelayInventoryEvent);
SubscribeLocalEvent(RelayInventoryEvent);
SubscribeLocalEvent(RelayInventoryEvent);
SubscribeLocalEvent(RelayInventoryEvent);
diff --git a/Content.Shared/VoiceMask/SharedVoiceMaskSystem.cs b/Content.Shared/VoiceMask/SharedVoiceMaskSystem.cs
index 2855891946..9d586a5af8 100644
--- a/Content.Shared/VoiceMask/SharedVoiceMaskSystem.cs
+++ b/Content.Shared/VoiceMask/SharedVoiceMaskSystem.cs
@@ -13,11 +13,15 @@ public sealed class VoiceMaskBuiState : BoundUserInterfaceState
{
public readonly string Name;
public readonly string? Verb;
+ public readonly bool Active;
+ public readonly bool AccentHide;
- public VoiceMaskBuiState(string name, string? verb)
+ public VoiceMaskBuiState(string name, string? verb, bool active, bool accentHide)
{
Name = name;
Verb = verb;
+ Active = active;
+ AccentHide = accentHide;
}
}
@@ -45,3 +49,15 @@ public sealed class VoiceMaskChangeVerbMessage : BoundUserInterfaceMessage
Verb = verb;
}
}
+
+///
+/// Toggle the effects of the voice mask.
+///
+[Serializable, NetSerializable]
+public sealed class VoiceMaskToggleMessage : BoundUserInterfaceMessage;
+
+///
+/// Toggle the effects of accent negation.
+///
+[Serializable, NetSerializable]
+public sealed class VoiceMaskAccentToggleMessage : BoundUserInterfaceMessage;
diff --git a/Resources/Locale/en-US/voice-mask.ftl b/Resources/Locale/en-US/voice-mask.ftl
index f3740cdafb..cd06e61eb2 100644
--- a/Resources/Locale/en-US/voice-mask.ftl
+++ b/Resources/Locale/en-US/voice-mask.ftl
@@ -5,6 +5,11 @@ voice-mask-name-change-info = Type in the name you want to mimic.
voice-mask-name-change-speech-style = Speech style
voice-mask-name-change-set = Set name
voice-mask-name-change-set-description = Change the name others hear to something else.
+voice-mask-name-change-toggle = Toggle voice mask
+voice-mask-name-change-accent-toggle = Block accent
+
+voice-mask-popup-toggle = Toggled voice mask.
+voice-mask-popup-accent-toggle = Toggled accent.
voice-mask-popup-success = Name set successfully.
voice-mask-popup-failure = Name could not be set.
--
2.52.0