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:
@@ -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));
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
35
Content.Server/Speech/Components/VoiceOverrideComponent.cs
Normal file
35
Content.Server/Speech/Components/VoiceOverrideComponent.cs
Normal 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;
|
||||
}
|
||||
22
Content.Server/Speech/EntitySystems/VoiceOverrideSystem.cs
Normal file
22
Content.Server/Speech/EntitySystems/VoiceOverrideSystem.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
24
Content.Shared/Chat/SharedChatEvents.cs
Normal file
24
Content.Shared/Chat/SharedChatEvents.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -183,8 +183,6 @@
|
||||
- type: Stripping
|
||||
- type: UserInterface
|
||||
interfaces:
|
||||
enum.VoiceMaskUIKey.Key:
|
||||
type: VoiceMaskBoundUserInterface
|
||||
enum.HumanoidMarkingModifierKey.Key:
|
||||
type: HumanoidMarkingModifierBoundUserInterface
|
||||
enum.StrippingUiKey.Key:
|
||||
|
||||
@@ -28,8 +28,6 @@
|
||||
interfaces:
|
||||
enum.StorageUiKey.Key:
|
||||
type: StorageBoundUserInterface
|
||||
enum.VoiceMaskUIKey.Key:
|
||||
type: VoiceMaskBoundUserInterface
|
||||
enum.HumanoidMarkingModifierKey.Key:
|
||||
type: HumanoidMarkingModifierBoundUserInterface
|
||||
enum.StrippingUiKey.Key:
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user