Voice Mask refactor (#30798)

* 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
This commit is contained in:
beck-thompson
2024-09-26 09:55:59 -07:00
committed by GitHub
parent c2bc821e55
commit fe2145d3b9
20 changed files with 214 additions and 236 deletions

View 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));

View 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();

View 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>

View 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);

View 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);

View 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);
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View 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);

View 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
{
[DataField]
[ViewVariables(VVAccess.ReadWrite)]
public bool Enabled = true;
[DataField]
[ViewVariables(VVAccess.ReadWrite)]
public string VoiceName = "Unknown";
/// <summary>
/// If EnableSpeechVerbModification is true, overrides the speech verb used when this entity speaks.
/// The name that will override an entities default name. If null, it will use the default override.
/// </summary>
[DataField]
[ViewVariables(VVAccess.ReadWrite)]
public ProtoId<SpeechVerbPrototype>? SpeechVerb;
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]
public ProtoId<SpeechVerbPrototype>? VoiceMaskSpeechVerb;
/// <summary>
/// The action that gets displayed when the voice mask is equipped.
/// </summary>
[DataField]
public EntProtoId Action = "ActionChangeVoiceMask";
/// <summary>
/// Reference to the action.
/// </summary>
[DataField]
public EntityUid? ActionEntity;
}

View File

@@ -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;
}
}

View File

@@ -1,113 +1,103 @@
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 (msg.Verb is { } id && !_proto.HasIndex<SpeechVerbPrototype>(id))
return;
entity.Comp.VoiceMaskSpeechVerb = 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"), entity, msg.Actor);
UpdateUI(entity);
}
private void OnChangeName(Entity<VoiceMaskComponent> entity, ref VoiceMaskChangeNameMessage message)
{
if (message.Name.Length > HumanoidCharacterProfile.MaxNameLength || message.Name.Length <= 0)
{
_popupSystem.PopupEntity(Loc.GetString("voice-mask-popup-failure"), uid, message.Actor, PopupType.SmallCaution);
_popupSystem.PopupEntity(Loc.GetString("voice-mask-popup-failure"), entity, message.Actor, PopupType.SmallCaution);
return;
}
component.VoiceName = message.Name;
_adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(message.Actor):player} set voice of {ToPrettyString(uid):mask}: {component.VoiceName}");
entity.Comp.VoiceMaskName = message.Name;
_adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(message.Actor):player} set voice of {ToPrettyString(entity):mask}: {entity.Comp.VoiceMaskName}");
_popupSystem.PopupEntity(Loc.GetString("voice-mask-popup-success"), uid, message.Actor);
_popupSystem.PopupEntity(Loc.GetString("voice-mask-popup-success"), entity, message.Actor);
TrySetLastKnownName(uid, message.Name);
UpdateUI(entity);
}
#endregion
UpdateUI(uid, component);
#region UI
private void OnEquip(EntityUid uid, VoiceMaskComponent component, ClothingGotEquippedEvent args)
{
_actions.AddAction(args.Wearer, ref component.ActionEntity, component.Action, uid);
}
private void OnChangeVerb(Entity<VoiceMaskComponent> ent, ref VoiceMaskChangeVerbMessage msg)
private void OpenUI(VoiceMaskSetNameEvent ev)
{
if (msg.Verb is {} id && !_proto.HasIndex<SpeechVerbPrototype>(id))
var maskEntity = ev.Action.Comp.Container;
if (!TryComp<VoiceMaskComponent>(maskEntity, out var voiceMaskComp))
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);
TrySetLastSpeechVerb(ent, msg.Verb);
UpdateUI(ent, ent.Comp);
}
private void OnSpeakerNameTransform(EntityUid uid, VoiceMaskComponent component, TransformSpeakerNameEvent 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;
}
}
private void OnMaskToggled(Entity<VoiceMaskComponent> ent, ref WearerMaskToggledEvent args)
{
ent.Comp.Enabled = !args.IsToggled;
}
private void OpenUI(EntityUid player)
{
if (!_uiSystem.HasUi(player, VoiceMaskUIKey.Key))
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(owner, VoiceMaskUIKey.Key))
_uiSystem.SetUiState(owner, VoiceMaskUIKey.Key, new VoiceMaskBuiState(component.VoiceName, component.SpeechVerb));
if (_uiSystem.HasUi(entity, VoiceMaskUIKey.Key))
_uiSystem.SetUiState(entity.Owner, VoiceMaskUIKey.Key, new VoiceMaskBuiState(GetCurrentVoiceName(entity), entity.Comp.VoiceMaskSpeechVerb));
}
#endregion
#region Helper functions
private string GetCurrentVoiceName(Entity<VoiceMaskComponent> entity)
{
return entity.Comp.VoiceMaskName ?? Loc.GetString("voice-mask-default-name-override");
}
#endregion
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View 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);

View 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

View File

@@ -29,10 +29,14 @@
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

View File

@@ -183,8 +183,6 @@
- type: Stripping
- type: UserInterface
interfaces:
enum.VoiceMaskUIKey.Key:
type: VoiceMaskBoundUserInterface
enum.HumanoidMarkingModifierKey.Key:
type: HumanoidMarkingModifierBoundUserInterface
enum.StrippingUiKey.Key:

View File

@@ -28,8 +28,6 @@
interfaces:
enum.StorageUiKey.Key:
type: StorageBoundUserInterface
enum.VoiceMaskUIKey.Key:
type: VoiceMaskBoundUserInterface
enum.HumanoidMarkingModifierKey.Key:
type: HumanoidMarkingModifierBoundUserInterface
enum.StrippingUiKey.Key:

View 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