diff --git a/Content.Client/VoiceMask/VoiceMaskBoundUserInterface.cs b/Content.Client/VoiceMask/VoiceMaskBoundUserInterface.cs
index 0650482b108a..f700c6663b9a 100644
--- a/Content.Client/VoiceMask/VoiceMaskBoundUserInterface.cs
+++ b/Content.Client/VoiceMask/VoiceMaskBoundUserInterface.cs
@@ -1,10 +1,13 @@
using Content.Shared.VoiceMask;
using Robust.Client.GameObjects;
+using Robust.Shared.Prototypes;
namespace Content.Client.VoiceMask;
public sealed class VoiceMaskBoundUserInterface : BoundUserInterface
{
+ [Dependency] private readonly IPrototypeManager _proto = default!;
+
[ViewVariables]
private VoiceMaskNameChangeWindow? _window;
@@ -16,10 +19,11 @@ protected override void Open()
{
base.Open();
- _window = new();
+ _window = new(_proto);
_window.OpenCentered();
_window.OnNameChange += OnNameSelected;
+ _window.OnVerbChange += verb => SendMessage(new VoiceMaskChangeVerbMessage(verb));
_window.OnClose += Close;
}
@@ -35,7 +39,7 @@ protected override void UpdateState(BoundUserInterfaceState state)
return;
}
- _window.UpdateState(cast.Name);
+ _window.UpdateState(cast.Name, cast.Verb);
}
protected override void Dispose(bool disposing)
diff --git a/Content.Client/VoiceMask/VoiceMaskNameChangeWindow.xaml b/Content.Client/VoiceMask/VoiceMaskNameChangeWindow.xaml
index 2316ec9c7d85..e23aca123919 100644
--- a/Content.Client/VoiceMask/VoiceMaskNameChangeWindow.xaml
+++ b/Content.Client/VoiceMask/VoiceMaskNameChangeWindow.xaml
@@ -1,11 +1,16 @@
-
-
+ MinSize="5 30">
+
-
+
+
+
+
+
-
+
diff --git a/Content.Client/VoiceMask/VoiceMaskNameChangeWindow.xaml.cs b/Content.Client/VoiceMask/VoiceMaskNameChangeWindow.xaml.cs
index e373acbd0a01..16a28f9d9b3e 100644
--- a/Content.Client/VoiceMask/VoiceMaskNameChangeWindow.xaml.cs
+++ b/Content.Client/VoiceMask/VoiceMaskNameChangeWindow.xaml.cs
@@ -1,26 +1,85 @@
+using Content.Client.UserInterface.Controls;
+using Content.Shared.Speech;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
+using Robust.Shared.Prototypes;
namespace Content.Client.VoiceMask;
[GenerateTypedNameReferences]
-public sealed partial class VoiceMaskNameChangeWindow : DefaultWindow
+public sealed partial class VoiceMaskNameChangeWindow : FancyWindow
{
public Action? OnNameChange;
+ public Action? OnVerbChange;
- public VoiceMaskNameChangeWindow()
+ private List<(string, string)> _verbs = new();
+
+ private string? _verb;
+
+ public VoiceMaskNameChangeWindow(IPrototypeManager proto)
{
RobustXamlLoader.Load(this);
NameSelectorSet.OnPressed += _ =>
{
- OnNameChange!(NameSelector.Text);
+ OnNameChange?.Invoke(NameSelector.Text);
+ };
+
+ SpeechVerbSelector.OnItemSelected += args =>
+ {
+ OnVerbChange?.Invoke((string?) args.Button.GetItemMetadata(args.Id));
+ SpeechVerbSelector.SelectId(args.Id);
};
+
+ ReloadVerbs(proto);
+
+ AddVerbs();
+ }
+
+ private void ReloadVerbs(IPrototypeManager proto)
+ {
+ foreach (var verb in proto.EnumeratePrototypes())
+ {
+ _verbs.Add((Loc.GetString(verb.Name), verb.ID));
+ }
+ _verbs.Sort((a, b) => a.Item1.CompareTo(b.Item1));
}
- public void UpdateState(string name)
+ private void AddVerbs()
+ {
+ SpeechVerbSelector.Clear();
+
+ AddVerb(Loc.GetString("chat-speech-verb-name-none"), null);
+ foreach (var (name, id) in _verbs)
+ {
+ AddVerb(name, id);
+ }
+ }
+
+ private void AddVerb(string name, string? verb)
+ {
+ var id = SpeechVerbSelector.ItemCount;
+ SpeechVerbSelector.AddItem(name);
+ if (verb is {} metadata)
+ SpeechVerbSelector.SetItemMetadata(id, metadata);
+
+ if (verb == _verb)
+ SpeechVerbSelector.SelectId(id);
+ }
+
+ public void UpdateState(string name, string? verb)
{
NameSelector.Text = name;
+ _verb = verb;
+
+ for (int id = 0; id < SpeechVerbSelector.ItemCount; id++)
+ {
+ if (string.Equals(verb, SpeechVerbSelector.GetItemMetadata(id)))
+ {
+ SpeechVerbSelector.SelectId(id);
+ break;
+ }
+ }
}
}
diff --git a/Content.Server/VoiceMask/VoiceMaskSystem.Equip.cs b/Content.Server/VoiceMask/VoiceMaskSystem.Equip.cs
index 87b3b9ef323c..bd0c40f26a1d 100644
--- a/Content.Server/VoiceMask/VoiceMaskSystem.Equip.cs
+++ b/Content.Server/VoiceMask/VoiceMaskSystem.Equip.cs
@@ -21,6 +21,7 @@ private void OnEquip(EntityUid uid, VoiceMaskerComponent component, GotEquippedE
var comp = EnsureComp(user);
comp.VoiceName = component.LastSetName;
+ comp.SpeechVerb = component.LastSpeechVerb;
_actions.AddAction(user, ref component.ActionEntity, component.Action, uid);
}
@@ -30,15 +31,23 @@ private void OnUnequip(EntityUid uid, VoiceMaskerComponent compnent, GotUnequipp
RemComp(args.Equipee);
}
- private void TrySetLastKnownName(EntityUid maskWearer, string lastName)
+ private VoiceMaskerComponent? TryGetMask(EntityUid user)
{
- if (!HasComp(maskWearer)
- || !_inventory.TryGetSlotEntity(maskWearer, MaskSlot, out var maskEntity)
- || !TryComp(maskEntity, out var maskComp))
- {
- return;
- }
+ if (!HasComp(user) || !_inventory.TryGetSlotEntity(user, MaskSlot, out var maskEntity))
+ return null;
+
+ return CompOrNull(maskEntity);
+ }
- maskComp.LastSetName = lastName;
+ 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;
}
}
diff --git a/Content.Server/VoiceMask/VoiceMaskSystem.cs b/Content.Server/VoiceMask/VoiceMaskSystem.cs
index 380eb7e70134..ac16e9225919 100644
--- a/Content.Server/VoiceMask/VoiceMaskSystem.cs
+++ b/Content.Server/VoiceMask/VoiceMaskSystem.cs
@@ -6,9 +6,11 @@
using Content.Shared.Inventory.Events;
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;
@@ -17,11 +19,13 @@ 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 IPrototypeManager _proto = default!;
public override void Initialize()
{
SubscribeLocalEvent(OnSpeakerNameTransform);
SubscribeLocalEvent(OnChangeName);
+ SubscribeLocalEvent(OnChangeVerb);
SubscribeLocalEvent(OnMaskToggled);
SubscribeLocalEvent(OnEquip);
SubscribeLocalEvent(OnUnequip);
@@ -55,6 +59,21 @@ private void OnChangeName(EntityUid uid, VoiceMaskComponent component, VoiceMask
UpdateUI(uid, component);
}
+ private void OnChangeVerb(Entity ent, ref VoiceMaskChangeVerbMessage msg)
+ {
+ if (msg.Verb is {} id && !_proto.HasIndex(id))
+ 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.Session);
+
+ TrySetLastSpeechVerb(ent, msg.Verb);
+
+ UpdateUI(ent, ent.Comp);
+ }
+
private void OnSpeakerNameTransform(EntityUid uid, VoiceMaskComponent component, TransformSpeakerNameEvent args)
{
if (component.Enabled)
@@ -95,6 +114,6 @@ private void UpdateUI(EntityUid owner, VoiceMaskComponent? component = null)
}
if (_uiSystem.TryGetUi(owner, VoiceMaskUIKey.Key, out var bui))
- _uiSystem.SetUiState(bui, new VoiceMaskBuiState(component.VoiceName));
+ _uiSystem.SetUiState(bui, new VoiceMaskBuiState(component.VoiceName, component.SpeechVerb));
}
}
diff --git a/Content.Server/VoiceMask/VoiceMaskerComponent.cs b/Content.Server/VoiceMask/VoiceMaskerComponent.cs
index c3cc29c52717..afea5877df24 100644
--- a/Content.Server/VoiceMask/VoiceMaskerComponent.cs
+++ b/Content.Server/VoiceMask/VoiceMaskerComponent.cs
@@ -1,15 +1,20 @@
+using Content.Shared.Speech;
using Robust.Shared.Prototypes;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server.VoiceMask;
[RegisterComponent]
public sealed partial class VoiceMaskerComponent : Component
{
- [ViewVariables(VVAccess.ReadWrite)] public string LastSetName = "Unknown";
+ [DataField]
+ public string LastSetName = "Unknown";
- [DataField("action", customTypeSerializer: typeof(PrototypeIdSerializer))]
- public string Action = "ActionChangeVoiceMask";
+ [DataField]
+ public ProtoId? LastSpeechVerb;
- [DataField("actionEntity")] public EntityUid? ActionEntity;
+ [DataField]
+ public EntProtoId Action = "ActionChangeVoiceMask";
+
+ [DataField]
+ public EntityUid? ActionEntity;
}
diff --git a/Content.Shared/Speech/SpeechVerbPrototype.cs b/Content.Shared/Speech/SpeechVerbPrototype.cs
index 7ed1705f1156..951cac64f765 100644
--- a/Content.Shared/Speech/SpeechVerbPrototype.cs
+++ b/Content.Shared/Speech/SpeechVerbPrototype.cs
@@ -43,4 +43,10 @@ public sealed partial class SpeechVerbPrototype : IPrototype
///
[DataField("priority")]
public int Priority = 0;
+
+ ///
+ /// Name shown in the voicemask UI for this verb.
+ ///
+ [DataField(required: true)]
+ public LocId Name = string.Empty;
}
diff --git a/Content.Shared/VoiceMask/SharedVoiceMaskSystem.cs b/Content.Shared/VoiceMask/SharedVoiceMaskSystem.cs
index 2bb87819e30b..285589194617 100644
--- a/Content.Shared/VoiceMask/SharedVoiceMaskSystem.cs
+++ b/Content.Shared/VoiceMask/SharedVoiceMaskSystem.cs
@@ -11,21 +11,37 @@ public enum VoiceMaskUIKey : byte
[Serializable, NetSerializable]
public sealed class VoiceMaskBuiState : BoundUserInterfaceState
{
- public string Name { get; }
+ public readonly string Name;
+ public readonly string? Verb;
- public VoiceMaskBuiState(string name)
+ public VoiceMaskBuiState(string name, string? verb)
{
Name = name;
+ Verb = verb;
}
}
[Serializable, NetSerializable]
public sealed class VoiceMaskChangeNameMessage : BoundUserInterfaceMessage
{
- public string Name { get; }
+ public readonly string Name;
public VoiceMaskChangeNameMessage(string name)
{
Name = name;
}
}
+
+///
+/// Change the speech verb prototype to override, or null to use the user's verb.
+///
+[Serializable, NetSerializable]
+public sealed class VoiceMaskChangeVerbMessage : BoundUserInterfaceMessage
+{
+ public readonly string? Verb;
+
+ public VoiceMaskChangeVerbMessage(string? verb)
+ {
+ Verb = verb;
+ }
+}
diff --git a/Resources/Locale/en-US/chat/managers/chat-manager.ftl b/Resources/Locale/en-US/chat/managers/chat-manager.ftl
index cf8c384dee28..1abe55da7ee8 100644
--- a/Resources/Locale/en-US/chat/managers/chat-manager.ftl
+++ b/Resources/Locale/en-US/chat/managers/chat-manager.ftl
@@ -58,57 +58,79 @@ chat-speech-verb-suffix-question = ?
chat-speech-verb-suffix-stutter = -
chat-speech-verb-suffix-mumble = ..
+chat-speech-verb-name-none = None
+chat-speech-verb-name-default = Default
chat-speech-verb-default = says
+chat-speech-verb-name-exclamation = Exclaiming
chat-speech-verb-exclamation = exclaims
+chat-speech-verb-name-exclamation-strong = Yelling
chat-speech-verb-exclamation-strong = yells
+chat-speech-verb-name-question = Asking
chat-speech-verb-question = asks
+chat-speech-verb-name-stutter = Stuttering
chat-speech-verb-stutter = stutters
+chat-speech-verb-name-mumble = Mumbling
chat-speech-verb-mumble = mumbles
+chat-speech-verb-name-arachnid = Arachnid
chat-speech-verb-insect-1 = chitters
chat-speech-verb-insect-2 = chirps
chat-speech-verb-insect-3 = clicks
+chat-speech-verb-name-moth = Moth
chat-speech-verb-winged-1 = flutters
chat-speech-verb-winged-2 = flaps
chat-speech-verb-winged-3 = buzzes
+chat-speech-verb-name-slime = Slime
chat-speech-verb-slime-1 = sloshes
chat-speech-verb-slime-2 = burbles
chat-speech-verb-slime-3 = oozes
+chat-speech-verb-name-plant = Diona
chat-speech-verb-plant-1 = rustles
chat-speech-verb-plant-2 = sways
chat-speech-verb-plant-3 = creaks
+chat-speech-verb-name-robotic = Robotic
chat-speech-verb-robotic-1 = states
chat-speech-verb-robotic-2 = beeps
+chat-speech-verb-robotic-3 = boops
+chat-speech-verb-name-reptilian = Reptilian
chat-speech-verb-reptilian-1 = hisses
chat-speech-verb-reptilian-2 = snorts
chat-speech-verb-reptilian-3 = huffs
+chat-speech-verb-name-skeleton = Skeleton
chat-speech-verb-skeleton-1 = rattles
chat-speech-verb-skeleton-2 = clacks
chat-speech-verb-skeleton-3 = gnashes
+chat-speech-verb-name-vox = Vox
chat-speech-verb-vox-1 = screeches
chat-speech-verb-vox-2 = shrieks
chat-speech-verb-vox-3 = croaks
+chat-speech-verb-name-canine = Canine
chat-speech-verb-canine-1 = barks
chat-speech-verb-canine-2 = woofs
chat-speech-verb-canine-3 = howls
+chat-speech-verb-name-small-mob = Mouse
chat-speech-verb-small-mob-1 = squeaks
chat-speech-verb-small-mob-2 = pieps
+chat-speech-verb-name-large-mob = Carp
chat-speech-verb-large-mob-1 = roars
chat-speech-verb-large-mob-2 = growls
+chat-speech-verb-name-monkey = Monkey
chat-speech-verb-monkey-1 = chimpers
chat-speech-verb-monkey-2 = screeches
+chat-speech-verb-name-cluwne = Cluwne
+
chat-speech-verb-parrot-1 = squawks
chat-speech-verb-parrot-2 = tweets
chat-speech-verb-parrot-3 = chirps
@@ -117,11 +139,13 @@ chat-speech-verb-cluwne-1 = giggles
chat-speech-verb-cluwne-2 = guffaws
chat-speech-verb-cluwne-3 = laughs
+chat-speech-verb-name-ghost = Ghost
chat-speech-verb-ghost-1 = complains
chat-speech-verb-ghost-2 = breathes
chat-speech-verb-ghost-3 = hums
chat-speech-verb-ghost-4 = mutters
+chat-speech-verb-name-electricity = Electricity
chat-speech-verb-electricity-1 = crackles
chat-speech-verb-electricity-2 = buzzes
chat-speech-verb-electricity-3 = screeches
diff --git a/Resources/Locale/en-US/voice-mask.ftl b/Resources/Locale/en-US/voice-mask.ftl
index cb6eb7768e5e..2f5acefee411 100644
--- a/Resources/Locale/en-US/voice-mask.ftl
+++ b/Resources/Locale/en-US/voice-mask.ftl
@@ -1,5 +1,6 @@
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
voice-mask-name-change-set = Set name
voice-mask-name-change-set-description = Change the name others hear to something else.
diff --git a/Resources/Prototypes/Voice/speech_verbs.yml b/Resources/Prototypes/Voice/speech_verbs.yml
index 26e9370c0171..43fefe04e314 100644
--- a/Resources/Prototypes/Voice/speech_verbs.yml
+++ b/Resources/Prototypes/Voice/speech_verbs.yml
@@ -1,30 +1,36 @@
- type: speechVerb
id: Default
+ name: chat-speech-verb-name-default
speechVerbStrings:
- chat-speech-verb-default
- type: speechVerb
id: DefaultQuestion
+ name: chat-speech-verb-name-question
speechVerbStrings:
- chat-speech-verb-question
- type: speechVerb
id: DefaultStutter
+ name: chat-speech-verb-name-stutter
speechVerbStrings:
- chat-speech-verb-stutter
- type: speechVerb
id: DefaultMumble
+ name: chat-speech-verb-name-mumble
speechVerbStrings:
- chat-speech-verb-mumble
- type: speechVerb
id: DefaultExclamation
+ name: chat-speech-verb-name-exclamation
speechVerbStrings:
- chat-speech-verb-exclamation
- type: speechVerb
id: DefaultExclamationStrong
+ name: chat-speech-verb-name-exclamation-strong
bold: true
speechVerbStrings:
- chat-speech-verb-exclamation-strong
@@ -32,6 +38,7 @@
- type: speechVerb
id: Arachnid
+ name: chat-speech-verb-name-arachnid
speechVerbStrings:
- chat-speech-verb-insect-1
- chat-speech-verb-insect-2
@@ -40,6 +47,7 @@
- type: speechVerb
id: Moth
+ name: chat-speech-verb-name-moth
speechVerbStrings:
- chat-speech-verb-winged-1
- chat-speech-verb-winged-2
@@ -48,12 +56,14 @@
- type: speechVerb
id: Robotic
+ name: chat-speech-verb-name-robotic
speechVerbStrings:
- chat-speech-verb-robotic-1
- chat-speech-verb-robotic-2
- type: speechVerb
id: Reptilian
+ name: chat-speech-verb-name-reptilian
speechVerbStrings:
- chat-speech-verb-reptilian-1
- chat-speech-verb-reptilian-2
@@ -62,6 +72,7 @@
- type: speechVerb
id: Skeleton
+ name: chat-speech-verb-name-skeleton
speechVerbStrings:
- chat-speech-verb-skeleton-1
- chat-speech-verb-skeleton-2
@@ -69,6 +80,7 @@
- type: speechVerb
id: Slime
+ name: chat-speech-verb-name-slime
speechVerbStrings:
- chat-speech-verb-slime-1
- chat-speech-verb-slime-2
@@ -76,13 +88,15 @@
- type: speechVerb
id: Vox
+ name: chat-speech-verb-name-vox
speechVerbStrings:
- - chat-speech-verb-vox-1
- - chat-speech-verb-vox-2
- - chat-speech-verb-vox-3
+ - chat-speech-verb-vox-1
+ - chat-speech-verb-vox-2
+ - chat-speech-verb-vox-3
- type: speechVerb
id: Plant
+ name: chat-speech-verb-name-plant
speechVerbStrings:
- chat-speech-verb-plant-1
- chat-speech-verb-plant-2
@@ -90,6 +104,7 @@
- type: speechVerb
id: Canine
+ name: chat-speech-verb-name-canine
speechVerbStrings:
- chat-speech-verb-canine-1
- chat-speech-verb-canine-2
@@ -97,18 +112,21 @@
- type: speechVerb
id: LargeMob
+ name: chat-speech-verb-name-large-mob
speechVerbStrings:
- chat-speech-verb-large-mob-1
- chat-speech-verb-large-mob-2
- type: speechVerb
id: SmallMob
+ name: chat-speech-verb-name-small-mob
speechVerbStrings:
- chat-speech-verb-small-mob-1
- chat-speech-verb-small-mob-2
- type: speechVerb
id: Monkey
+ name: chat-speech-verb-name-monkey
speechVerbStrings:
- chat-speech-verb-monkey-1
- chat-speech-verb-monkey-2
@@ -122,6 +140,7 @@
- type: speechVerb
id: Cluwne
+ name: chat-speech-verb-name-cluwne
speechVerbStrings:
- chat-speech-verb-cluwne-1
- chat-speech-verb-cluwne-2
@@ -129,6 +148,7 @@
- type: speechVerb
id: Ghost
+ name: chat-speech-verb-name-ghost
speechVerbStrings:
- chat-speech-verb-ghost-1
- chat-speech-verb-ghost-2
@@ -138,6 +158,7 @@
- type: speechVerb
id: Electricity
+ name: chat-speech-verb-name-electricity
speechVerbStrings:
- chat-speech-verb-electricity-1
- chat-speech-verb-electricity-2