Skip to content

Commit

Permalink
Voice mask refactor and fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
beck-thompson committed Oct 26, 2024
1 parent 00e374c commit 09983b7
Show file tree
Hide file tree
Showing 26 changed files with 242 additions and 339 deletions.
4 changes: 2 additions & 2 deletions Content.Server/Chat/Systems/ChatSystem.Emote.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ private void CacheEmotes()
/// <param name="emoteId">The id of emote prototype. Should has valid <see cref="EmotePrototype.ChatMessages"/></param>
/// <param name="hideLog">Whether or not this message should appear in the adminlog window</param>
/// <param name="range">Conceptual range of transmission, if it shows in the chat window, if it shows to far-away ghosts or ghosts at all...</param>
/// <param name="nameOverride">The name to use for the speaking entity. Usually this should just be modified via <see cref="TransformSpeakerNameEvent"/>. If this is set, the event will not get raised.</param>
/// <param name="nameOverride">The name to use for the speaking entity. Usually this should just be modified via <see cref="TransformSpeakerSpeechEvent"/>. If this is set, the event will not get raised.</param>
public void TryEmoteWithChat(
EntityUid source,
string emoteId,
Expand All @@ -74,7 +74,7 @@ public void TryEmoteWithChat(
/// <param name="hideLog">Whether or not this message should appear in the adminlog window</param>
/// <param name="hideChat">Whether or not this message should appear in the chat window</param>
/// <param name="range">Conceptual range of transmission, if it shows in the chat window, if it shows to far-away ghosts or ghosts at all...</param>
/// <param name="nameOverride">The name to use for the speaking entity. Usually this should just be modified via <see cref="TransformSpeakerNameEvent"/>. If this is set, the event will not get raised.</param>
/// <param name="nameOverride">The name to use for the speaking entity. Usually this should just be modified via <see cref="TransformSpeakerSpeechEvent"/>. If this is set, the event will not get raised.</param>
public void TryEmoteWithChat(
EntityUid source,
EmotePrototype emote,
Expand Down
29 changes: 7 additions & 22 deletions Content.Server/Chat/Systems/ChatSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
using Content.Server.Language;
using Content.Server.Speech.Components;
using Content.Server.Speech.EntitySystems;
using Content.Server.Chat;
using Content.Server.Station.Components;
using Content.Server.Station.Systems;
using Content.Shared.ActionBlocker;
Expand Down Expand Up @@ -147,7 +146,7 @@ private void OnGameChange(GameRunLevelChangedEvent ev)
/// <param name="hideLog">Whether or not this message should appear in the adminlog window</param>
/// <param name="shell"></param>
/// <param name="player">The player doing the speaking</param>
/// <param name="nameOverride">The name to use for the speaking entity. Usually this should just be modified via <see cref="TransformSpeakerNameEvent"/>. If this is set, the event will not get raised.</param>
/// <param name="nameOverride">The name to use for the speaking entity. Usually this should just be modified via <see cref="TransformSpeakerSpeechEvent"/>. If this is set, the event will not get raised.</param>
public void TrySendInGameICMessage(
EntityUid source,
string message,
Expand All @@ -170,7 +169,7 @@ public void TrySendInGameICMessage(
/// <param name="range">Conceptual range of transmission, if it shows in the chat window, if it shows to far-away ghosts or ghosts at all...</param>
/// <param name="shell"></param>
/// <param name="player">The player doing the speaking</param>
/// <param name="nameOverride">The name to use for the speaking entity. Usually this should just be modified via <see cref="TransformSpeakerNameEvent"/>. If this is set, the event will not get raised.</param>
/// <param name="nameOverride">The name to use for the speaking entity. Usually this should just be modified via <see cref="TransformSpeakerSpeechEvent"/>. If this is set, the event will not get raised.</param>
/// <param name="ignoreActionBlocker">If set to true, action blocker will not be considered for whether an entity can send this message.</param>
public void TrySendInGameICMessage(
EntityUid source,
Expand Down Expand Up @@ -421,11 +420,11 @@ private void SendEntitySpeak(
}
else
{
var nameEv = new TransformSpeakerNameEvent(source, Name(source));
var nameEv = new TransformSpeakerSpeechEvent(source, Name(source));
RaiseLocalEvent(source, nameEv);
name = nameEv.Name;
name = nameEv.VoiceName ?? Name(source);
// 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;
}

Expand Down Expand Up @@ -493,9 +492,9 @@ private void SendEntityWhisper(
}
else
{
var nameEv = new TransformSpeakerNameEvent(source, Name(source));
var nameEv = new TransformSpeakerSpeechEvent(source, Name(source));
RaiseLocalEvent(source, nameEv);
name = nameEv.Name;
name = nameEv.VoiceName ?? Name(source);
}
name = FormattedMessage.EscapeText(name);

Expand Down Expand Up @@ -990,20 +989,6 @@ public record ExpandICChatRecipientstEvent(EntityUid Source, float VoiceRange, D
{
}

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>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,110 +1,30 @@
using Content.Server.Administration.Logs;
using Content.Server.Chat.Systems;
using Content.Server.Popups;
using Content.Server.VoiceMask;
using Content.Shared.Database;
using Content.Server.VoiceMask;
using Content.Shared.Implants;
using Content.Shared.Implants.Components;
using Content.Shared.Popups;
using Content.Shared.Preferences;
using Content.Shared.Tag;
using Content.Shared.VoiceMask;
using Robust.Server.GameObjects;
using Robust.Shared.Containers;

namespace Content.Server.Implants;

public sealed class SubdermalBionicSyrinxImplantSystem : EntitySystem
{
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
[Dependency] private readonly TagSystem _tag = default!;

[ValidatePrototypeId<TagPrototype>]
public const string BionicSyrinxImplant = "BionicSyrinxImplant";

private const string BionicSyrinxImplant = "BionicSyrinxImplant";

public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<VoiceMaskerComponent, ImplantImplantedEvent>(OnInsert);
SubscribeLocalEvent<SyrinxVoiceMaskComponent, TransformSpeakerNameEvent>(OnSpeakerNameTransform);
SubscribeLocalEvent<SyrinxVoiceMaskComponent, VoiceMaskChangeNameMessage>(OnChangeName);
// We need to remove the SyrinxVoiceMaskComponent from the owner before the implant
// is removed, so we need to execute before the SubdermalImplantSystem.
SubscribeLocalEvent<VoiceMaskerComponent, EntGotRemovedFromContainerMessage>(OnRemove, before: new[] { typeof(SubdermalImplantSystem) });
SubscribeLocalEvent<VoiceMaskComponent, ImplantImplantedEvent>(OnInsert);
}

private void OnInsert(EntityUid uid, VoiceMaskerComponent component, ImplantImplantedEvent args)
private void OnInsert(EntityUid uid, VoiceMaskComponent component, ImplantImplantedEvent args)
{
if (!args.Implanted.HasValue ||
!_tag.HasTag(args.Implant, BionicSyrinxImplant))
return;

var voicemask = EnsureComp<SyrinxVoiceMaskComponent>(args.Implanted.Value);
voicemask.VoiceName = MetaData(args.Implanted.Value).EntityName;
Dirty(args.Implanted.Value, voicemask);
}

private void OnRemove(EntityUid uid, VoiceMaskerComponent component, EntGotRemovedFromContainerMessage args)
{
if (!TryComp<SubdermalImplantComponent>(uid, out var implanted) || implanted.ImplantedEntity == null)
return;

RemComp<SyrinxVoiceMaskComponent>(implanted.ImplantedEntity.Value);
}

/// <summary>
/// Copy from VoiceMaskSystem, adapted to work with SyrinxVoiceMaskComponent.
/// </summary>
private void OnChangeName(EntityUid uid, SyrinxVoiceMaskComponent component, 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);
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);
TrySetLastKnownName(uid, message.Name);
UpdateUI(uid, component);
}

/// <summary>
/// Copy from VoiceMaskSystem, adapted to work with SyrinxVoiceMaskComponent.
/// </summary>
private void TrySetLastKnownName(EntityUid implanted, string lastName)
{
if (!HasComp<VoiceMaskComponent>(implanted)
|| !TryComp<VoiceMaskerComponent>(implanted, out var maskComp))
return;

maskComp.LastSetName = lastName;
}

/// <summary>
/// Copy from VoiceMaskSystem, adapted to work with SyrinxVoiceMaskComponent.
/// </summary>
private void UpdateUI(EntityUid owner, SyrinxVoiceMaskComponent? component = null)
{
if (!Resolve(owner, ref component, logMissing: false))
return;

if (_uiSystem.TryGetOpenUi(owner, VoiceMaskUIKey.Key, out _))
_uiSystem.SetUiState(new(owner, null), VoiceMaskUIKey.Key, new VoiceMaskBuiState(component.VoiceName, null));
}

/// <summary>
/// Copy from VoiceMaskSystem, adapted to work with SyrinxVoiceMaskComponent.
/// </summary>
private void OnSpeakerNameTransform(EntityUid uid, SyrinxVoiceMaskComponent component, TransformSpeakerNameEvent args)
{
if (component.Enabled)
args.Name = component.VoiceName;
// Update the name so it's the entities default name. You can't take it off like a voice mask so it's important!
component.VoiceMaskName = Name(args.Implanted.Value);
}
}
9 changes: 0 additions & 9 deletions Content.Server/DeltaV/VoiceMask/SyrinxVoiceMaskComponent.cs

This file was deleted.

4 changes: 2 additions & 2 deletions Content.Server/Radio/EntitySystems/RadioDeviceSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -206,11 +206,11 @@ private void OnReceiveRadio(EntityUid uid, RadioSpeakerComponent component, ref
if (uid == args.RadioSource)
return;

var nameEv = new TransformSpeakerNameEvent(args.MessageSource, Name(args.MessageSource));
var nameEv = new TransformSpeakerSpeechEvent(args.MessageSource, Name(args.MessageSource));
RaiseLocalEvent(args.MessageSource, nameEv);

var name = Loc.GetString("speech-name-relay", ("speaker", Name(uid)),
("originalName", nameEv.Name));
("originalName", nameEv.VoiceName ?? Name(args.MessageSource)));

// log to chat so people can identity the speaker/source, but avoid clogging ghost chat if there are many radios
var message = args.OriginalChatMsg.Message; // The chat system will handle the rest and re-obfuscate if needed.
Expand Down
23 changes: 3 additions & 20 deletions Content.Server/Radio/EntitySystems/RadioSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
using Content.Server.Language;
using Content.Server.Power.Components;
using Content.Server.Radio.Components;
using Content.Server.Speech;
using Content.Server.VoiceMask;
using Content.Shared.Chat;
using Content.Shared.Database;
using Content.Shared.Language;
Expand Down Expand Up @@ -92,28 +90,13 @@ public void SendRadioMessage(EntityUid messageSource, string message, RadioChann
if (!_messages.Add(message))
return;

var name = TryComp(messageSource, out VoiceMaskComponent? mask) && mask.Enabled
? mask.VoiceName
: MetaData(messageSource).EntityName;

// Delta-V: Support syrinx voice mask on radio.
if (TryComp(messageSource, out SyrinxVoiceMaskComponent? syrinx) && syrinx.Enabled)
name = syrinx.VoiceName;
var evt = new TransformSpeakerSpeechEvent(messageSource, Name(messageSource));
RaiseLocalEvent(messageSource, evt);
var name = evt.VoiceName ?? Name(messageSource);

name = FormattedMessage.EscapeText(name);

// most radios are relayed to chat, so lets parse the chat message beforehand
SpeechVerbPrototype speech;
if (mask != null
&& mask.Enabled
&& mask.SpeechVerb != null
&& _prototype.TryIndex<SpeechVerbPrototype>(mask.SpeechVerb, out var proto))
{
speech = proto;
}
else
speech = _chat.GetSpeechVerb(messageSource, message);

var content = escapeMarkup
? FormattedMessage.EscapeText(message)
: message;
Expand Down
65 changes: 20 additions & 45 deletions Content.Server/Speech/Components/ListenWireAction.cs
Original file line number Diff line number Diff line change
@@ -1,27 +1,26 @@
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;

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";

Expand All @@ -37,6 +36,8 @@ public override void Initialize()

_wires = EntityManager.System<WiresSystem>();
_chat = EntityManager.System<ChatSystem>();
_radio = EntityManager.System<RadioSystem>();
_protoMan = IoCManager.Resolve<IPrototypeManager>();
}
public override StatusLightState? GetLightState(Wire wire)
{
Expand Down Expand Up @@ -72,46 +73,20 @@ public override void Pulse(EntityUid user, Wire wire)
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);
}
Expand Down
Loading

0 comments on commit 09983b7

Please sign in to comment.