Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 25 additions & 2 deletions Content.Server/Chat/Systems/ChatSystem.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Content.Shared._Goobstation.Loudspeaker.Events; // goob - loudspeakers
using System.Globalization;
using System.Linq;
using System.Text;
Expand Down Expand Up @@ -62,7 +63,7 @@ public sealed partial class ChatSystem : SharedChatSystem
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly ReplacementAccentSystem _wordreplacement = default!;
[Dependency] private readonly ExamineSystemShared _examineSystem = default!;

//Nyano - Summary: pulls in the nyano chat system for psionics.
[Dependency] private readonly NyanoChatSystem _nyanoChatSystem = default!;

Expand Down Expand Up @@ -445,11 +446,33 @@ private void SendEntitySpeak(

name = FormattedMessage.EscapeText(name);

// goob start - loudspeakers

int? loudSpeakFont = null;

var getLoudspeakerEv = new GetLoudspeakerEvent();
RaiseLocalEvent(source, ref getLoudspeakerEv);

if (getLoudspeakerEv.Loudspeakers != null)
foreach (var loudspeaker in getLoudspeakerEv.Loudspeakers)
{
var loudSpeakerEv = new GetLoudspeakerDataEvent();
RaiseLocalEvent(loudspeaker, ref loudSpeakerEv);

if (loudSpeakerEv.IsActive && loudSpeakerEv.AffectChat)
{
loudSpeakFont = loudSpeakerEv.FontSize;
break;
}
}

// goob end

var wrappedMessage = Loc.GetString(speech.Bold ? "chat-manager-entity-say-bold-wrap-message" : "chat-manager-entity-say-wrap-message",
("entityName", name),
("verb", Loc.GetString(_random.Pick(speech.SpeechVerbStrings))),
("fontType", speech.FontId),
("fontSize", speech.FontSize),
("fontSize", loudSpeakFont ?? speech.FontSize),
("message", FormattedMessage.EscapeText(message)));

SendInVoiceRange(ChatChannel.Local, message, wrappedMessage, source, range);
Expand Down
25 changes: 24 additions & 1 deletion Content.Server/Radio/EntitySystems/RadioSystem.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Content.Shared._Goobstation.Loudspeaker.Events; // goob - loudspeakers
using Content.Server.Administration.Logs;
using Content.Server.Chat.Managers;
using Content.Server.Chat.Systems;
Expand Down Expand Up @@ -125,6 +126,28 @@ public void SendRadioMessage(EntityUid messageSource, string message, RadioChann
? FormattedMessage.EscapeText(message)
: message;

// goob start - loudspeakers

int? loudSpeakFont = null;

var getLoudspeakerEv = new GetLoudspeakerEvent();
RaiseLocalEvent(messageSource, ref getLoudspeakerEv);

if (getLoudspeakerEv.Loudspeakers != null)
foreach (var loudspeaker in getLoudspeakerEv.Loudspeakers)
{
var loudSpeakerEv = new GetLoudspeakerDataEvent();
RaiseLocalEvent(loudspeaker, ref loudSpeakerEv);

if (loudSpeakerEv.IsActive && loudSpeakerEv.AffectRadio)
{
loudSpeakFont = loudSpeakerEv.FontSize;
break;
}
}

// goob end

// DeltaV - This change is to change up how the messages are wrapped up. Basically changing the formatting depending on the emote type.
string wrappedMessage;

Expand All @@ -144,7 +167,7 @@ public void SendRadioMessage(EntityUid messageSource, string message, RadioChann
wrappedMessage = Loc.GetString(speech.Bold ? "chat-radio-message-wrap-bold" : "chat-radio-message-wrap",
("color", channel.Color),
("fontType", speech.FontId),
("fontSize", speech.FontSize),
("fontSize", loudSpeakFont ?? speech.FontSize),
("verb", Loc.GetString(_random.Pick(speech.SpeechVerbStrings))),
("channel", $"\\[{channel.LocalizedName}\\]"),
("name", name),
Expand Down
22 changes: 19 additions & 3 deletions Content.Server/Speech/SpeechNoiseSystem.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Content.Shared._Goobstation.Speech;
using Content.Shared.Chat;
using Content.Shared.Speech;
using Robust.Shared.Audio;
Expand All @@ -24,12 +25,27 @@ public override void Initialize()

public SoundSpecifier? GetSpeechSound(Entity<SpeechComponent> ent, string message)
{
if (ent.Comp.SpeechSounds == null)
return null;
// Goobstation start
var getSpeechSoundEv = new GetSpeechSoundEvent();
RaiseLocalEvent(ent, ref getSpeechSoundEv);
SpeechSoundsPrototype? prototype;
if (getSpeechSoundEv.Handled)
{
if (getSpeechSoundEv.SpeechSoundProtoId is not { } protoId ||
!_protoManager.TryIndex(protoId, out prototype))
return null;
}
else
{
if (ent.Comp.SpeechSounds == null)
return null;

prototype = _protoManager.Index<SpeechSoundsPrototype>(ent.Comp.SpeechSounds);
}
// Goobstation end

// Play speech sound
SoundSpecifier? contextSound;
var prototype = _protoManager.Index<SpeechSoundsPrototype>(ent.Comp.SpeechSounds);

// Different sounds for ask/exclaim based on last character
contextSound = message[^1] switch
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using Content.Shared.Inventory;
using Content.Shared.Speech;
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;

namespace Content.Shared._Goobstation.Loudspeaker.Components;

/// <summary>
/// Used for items (or entities) that have loudspeaker capabilities.
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class LoudspeakerComponent : Component
{
/// <summary>
/// Should it work in your hands?
/// </summary>
[DataField, AutoNetworkedField]
public bool WorksInHand;

/// <summary>
/// Can it be toggled on/off?
/// </summary>
[DataField, AutoNetworkedField]
public bool CanToggle;

/// <summary>
/// Is the loudspeaker active?
/// </summary>
[DataField, AutoNetworkedField]
public bool IsActive;

/// <summary>
/// Should it affect regular speaking?
/// </summary>
[DataField, AutoNetworkedField]
public bool AffectChat;

/// <summary>
/// Should it affect speaking via radio?
/// </summary>
[DataField, AutoNetworkedField]
public bool AffectRadio;

/// <summary>
/// How big should the new text font be?
/// </summary>
[DataField, AutoNetworkedField]
public int FontSize = 18;

/// <summary>
/// The slot it should take up to work.
/// </summary>
[DataField]
public SlotFlags RequiredSlot = SlotFlags.EARS;

/// <summary>
/// The sound it should play for the user when toggling.
/// </summary>
[DataField]
public SoundPathSpecifier ToggleSound = new("/Audio/Items/pen_click.ogg");

/// <summary>
/// The sounds the user will make when speaking.
/// </summary>
[DataField]
public ProtoId<SpeechSoundsPrototype>? SpeechSounds;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace Content.Shared._Goobstation.Loudspeaker.Components;

/// <summary>
/// Marks an entity that is holding equipped loudspeaker(s).
/// </summary>
[RegisterComponent]
public sealed partial class LoudspeakerHolderComponent : Component
{
[DataField]
public List<EntityUid> Loudspeakers = new();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using Content.Shared.Speech;
using Robust.Shared.Prototypes;

namespace Content.Shared._Goobstation.Loudspeaker.Events;

[ByRefEvent]
public record struct GetLoudspeakerDataEvent(
bool IsActive = false,
int? FontSize = null,
bool AffectRadio = false,
bool AffectChat = false,
ProtoId<SpeechSoundsPrototype>? SpeechSounds = null);
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
namespace Content.Shared._Goobstation.Loudspeaker.Events;

[ByRefEvent]
public record struct GetLoudspeakerEvent(
List<EntityUid> Loudspeakers);
161 changes: 161 additions & 0 deletions Content.Shared/_Goobstation/Loudspeaker/Systems/LoudspeakerSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
using Content.Shared._Goobstation.Loudspeaker.Events;
using Content.Shared._Goobstation.Speech;
using Content.Shared.Examine;
using Content.Shared.Hands;
using Content.Shared.Inventory.Events;
using Content.Shared.Popups;
using Content.Shared.Verbs;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Utility;

namespace Content.Shared._Goobstation.Loudspeaker.Systems;

public sealed class LoudSpeakerSystem : EntitySystem
{

[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;

public override void Initialize()
{

base.Initialize();

SubscribeLocalEvent<Components.LoudspeakerComponent, GotEquippedEvent>(OnEquipped);
SubscribeLocalEvent<Components.LoudspeakerComponent, GotUnequippedEvent>(OnUnequipped);
SubscribeLocalEvent<Components.LoudspeakerComponent, GotEquippedHandEvent>(OnEquippedHands);
SubscribeLocalEvent<Components.LoudspeakerComponent, GotUnequippedHandEvent>(OnUnequippedHands);

SubscribeLocalEvent<Components.LoudspeakerHolderComponent, GetLoudspeakerEvent>(GetLoudSpeakers);
SubscribeLocalEvent<Components.LoudspeakerComponent, GetLoudspeakerDataEvent>(OnGetLoudspeakerData);
SubscribeLocalEvent<Components.LoudspeakerHolderComponent, GetSpeechSoundEvent>(OnGetSpeechSound);

SubscribeLocalEvent<Components.LoudspeakerComponent, ExaminedEvent>(OnExamined);
SubscribeLocalEvent<Components.LoudspeakerComponent, GetVerbsEvent<AlternativeVerb>>(OnGetVerbs);

}

private void OnEquipped(EntityUid uid, Components.LoudspeakerComponent comp, GotEquippedEvent args)
{
if (!args.SlotFlags.HasFlag(comp.RequiredSlot))
return;

EnsureComp<Components.LoudspeakerHolderComponent>(args.Equipee).Loudspeakers.Add(uid);
}

private void OnUnequipped(EntityUid uid, Components.LoudspeakerComponent comp, GotUnequippedEvent args)
{
if (!TryComp<Components.LoudspeakerHolderComponent>(args.Equipee, out var holder))
return;

holder.Loudspeakers.Remove(uid);

DoRemovalCheck(args.Equipee, holder);
}

private void OnEquippedHands(EntityUid uid, Components.LoudspeakerComponent comp, GotEquippedHandEvent args)
{
if (!comp.WorksInHand)
return;

EnsureComp<Components.LoudspeakerHolderComponent>(args.User).Loudspeakers.Add(uid);
}

private void OnUnequippedHands(EntityUid uid, Components.LoudspeakerComponent comp, GotUnequippedHandEvent args)
{
if (!TryComp<Components.LoudspeakerHolderComponent>(args.User, out var holder))
return;

holder.Loudspeakers.Remove(uid);

DoRemovalCheck(args.User, holder);
}

private void GetLoudSpeakers(Entity<Components.LoudspeakerHolderComponent> ent, ref GetLoudspeakerEvent args)
{
args.Loudspeakers = ent.Comp.Loudspeakers;
}

private void OnGetLoudspeakerData(Entity<Components.LoudspeakerComponent> ent, ref GetLoudspeakerDataEvent args)
{
args.IsActive = ent.Comp.IsActive;

args.FontSize = ent.Comp.FontSize;
args.AffectRadio = ent.Comp.AffectRadio;
args.AffectChat = ent.Comp.AffectChat;
args.SpeechSounds = ent.Comp.SpeechSounds;
}

private void OnGetSpeechSound(Entity<Components.LoudspeakerHolderComponent> ent, ref GetSpeechSoundEvent args)
{

foreach (var loudspeaker in ent.Comp.Loudspeakers)
{
var speechEv = new GetLoudspeakerDataEvent();
RaiseLocalEvent(loudspeaker, ref speechEv);

if (speechEv.SpeechSounds != null)
{
args.SpeechSoundProtoId = speechEv.SpeechSounds;
args.Handled = true; // Delta V - bugfix
return;
}

}
}

private void OnExamined(Entity<Components.LoudspeakerComponent> ent, ref ExaminedEvent args)
{
var state = ent.Comp.IsActive ? "on" : "off";

var message = ent.Comp.CanToggle
? Loc.GetString("loudspeaker-examine-toggleable", ("state", state))
: Loc.GetString("loudspeaker-examine-generic");

args.PushMarkup(message);
}

private void OnGetVerbs(Entity<Components.LoudspeakerComponent> ent, ref GetVerbsEvent<AlternativeVerb> args)
{
if (!args.CanInteract
|| !ent.Comp.CanToggle)
return;

var user = args.User;

AlternativeVerb toggleLoudspeakerVerb = new()
{
Act = () =>
{
ToggleLoudspeakerEffect(user, ent);
ent.Comp.IsActive = !ent.Comp.IsActive;
Dirty(ent);
},
Text = Loc.GetString("loudspeaker-toggle"),
Icon = new SpriteSpecifier.Rsi(new("/Textures/Effects/text.rsi"), "exclamation"),
};

args.Verbs.Add(toggleLoudspeakerVerb);
}

#region Helper methods

private void DoRemovalCheck(EntityUid equipee, Components.LoudspeakerHolderComponent comp)
{
if (comp.Loudspeakers.Count == 0) // only remove when theres no loudspeakers
{
RemComp<Components.LoudspeakerHolderComponent>(equipee);
return;
}
}

private void ToggleLoudspeakerEffect(EntityUid user, Entity<Components.LoudspeakerComponent> loudspeaker)
{
var state = !loudspeaker.Comp.IsActive ? "on" : "off";

_audio.PlayPredicted(loudspeaker.Comp.ToggleSound, user, user);
_popup.PopupClient(Loc.GetString("loudspeaker-toggle-popup", ("state", state)), user, user);
}

#endregion
}
4 changes: 4 additions & 0 deletions Content.Shared/_Goobstation/Speech/GetSpeechSoundEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
namespace Content.Shared._Goobstation.Speech;

[ByRefEvent]
public record struct GetSpeechSoundEvent(string? SpeechSoundProtoId = null, bool Handled = false);
4 changes: 4 additions & 0 deletions Resources/Audio/_Goobstation/Loudspeaker/attributions.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- files: ["megaphone.ogg"]
license: "CC-BY-SA-3.0"
copyright: "Taken from TG"
source: "https://github.com/tgstation/tgstation/blob/master/sound/items/megaphone.ogg"
Binary file not shown.
Loading
Loading