Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Random Announcer System [PORT] #2652

Open
wants to merge 28 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
ba41ae6
random announcer system port from einstein engines
Darkmajia Aug 15, 2024
0442f49
robust fixes for announcement issues
Darkmajia Aug 18, 2024
56ff915
doing it sensible style
Darkmajia Aug 18, 2024
a3198f0
multiple announcement sounds, announcer volume fix
Darkmajia Aug 29, 2024
7563c2e
CVars, yay.
Monotheonist Jan 8, 2025
bebd361
The great Einsteins Engineing
Monotheonist Jan 9, 2025
978e690
I am in misery (more Cvars)
Monotheonist Jan 9, 2025
f8809f8
quick fixes for shuttle dock/meteor swarm announcements
Darkmajia Aug 18, 2024
9384659
we're still in hell
Monotheonist Jan 10, 2025
6a65692
end of file newline ops
Monotheonist Jan 14, 2025
6cbfcd6
Oh my goodness it's almost not broken
Monotheonist Jan 14, 2025
3912e45
works now, apparently...?
Monotheonist Jan 14, 2025
a51b3c7
Y'know what, fuck everyone else, lmao!!
Monotheonist Jan 14, 2025
0412d02
Namespaces locale for announcers
Monotheonist Jan 14, 2025
031d7c9
Untested but this might fix the test fails :tm:
Monotheonist Jan 14, 2025
68e5411
don't forget the snails fool
Monotheonist Jan 14, 2025
7e9c1c1
Un-nullable, therefore false.
Monotheonist Jan 14, 2025
94269ea
Gas leak
Monotheonist Jan 14, 2025
437e38b
Sinking ships often leak and I'm plugging the holes with these commits
Monotheonist Jan 14, 2025
afd13e6
Is that everything, Linter?!
Monotheonist Jan 14, 2025
b463728
IT SAID NO!!!!!!!! RAHHHHHHHHHH
Monotheonist Jan 15, 2025
272e448
Probably the final attempt to appease the linter for this (at least I…
Monotheonist Jan 15, 2025
09e025a
Conflictless locale gaming part one (and also adding greytide virus t…
Monotheonist Jan 17, 2025
5b26441
I FORGOT!
Monotheonist Jan 17, 2025
15fd731
Conflictless locale gaming part two
Monotheonist Jan 17, 2025
a701e48
Comments (CS / XAML)
Monotheonist Jan 17, 2025
bc4e520
Finishes locale gaming for real
Monotheonist Jan 17, 2025
a4c6a7f
Pure Idiocy
Monotheonist Jan 17, 2025
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
1 change: 1 addition & 0 deletions Content.Client/Audio/ContentAudioSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public sealed partial class ContentAudioSystem : SharedContentAudioSystem
public const float AmbientMusicMultiplier = 3f;
public const float LobbyMultiplier = 3f;
public const float InterfaceMultiplier = 2f;
public const float AnnouncerMultiplier = 3f; // Impstation Random Announcer System

public override void Initialize()
{
Expand Down
6 changes: 5 additions & 1 deletion Content.Client/Options/UI/Tabs/AudioTab.xaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Control xmlns="https://spacestation14.io"
<Control xmlns="https://spacestation14.io"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ui="clr-namespace:Content.Client.Options.UI">
<BoxContainer Orientation="Vertical">
Expand All @@ -13,11 +13,15 @@
<ui:OptionSlider Name="SliderVolumeAmbience" Title="{Loc 'ui-options-ambience-volume'}" />
<ui:OptionSlider Name="SliderVolumeLobby" Title="{Loc 'ui-options-lobby-volume'}" />
<ui:OptionSlider Name="SliderVolumeInterface" Title="{Loc 'ui-options-interface-volume'}" />
<ui:OptionSlider Name="SliderVolumeAnnouncer" Title="{Loc 'ui-options-announcer-volume'}" /> <!--Impstation Random Announcer System port-->
<ui:OptionSlider Name="SliderMaxAmbienceSounds" Title="{Loc 'ui-options-ambience-max-sounds'}"
Margin="0 0 0 8" />
<CheckBox Name="LobbyMusicCheckBox" Text="{Loc 'ui-options-lobby-music'}" />
<CheckBox Name="RestartSoundsCheckBox" Text="{Loc 'ui-options-restart-sounds'}" />
<CheckBox Name="EventMusicCheckBox" Text="{Loc 'ui-options-event-music'}" />
<CheckBox Name="AnnouncerDisableMultipleSoundsCheckBox"
Text="{Loc 'ui-options-announcer-disable-multiple-sounds'}"
ToolTip="{Loc 'ui-options-announcer-disable-multiple-sounds-tooltip'}" /> <!--Impstation Random Announcer System port-->
<CheckBox Name="AdminSoundsCheckBox" Text="{Loc 'ui-options-admin-sounds'}" />
</BoxContainer>
</BoxContainer>
Expand Down
7 changes: 7 additions & 0 deletions Content.Client/Options/UI/Tabs/AudioTab.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Content.Client.Audio;
using Content.Shared.CCVar;
using Content.Shared._Impstation.CCVar;
using Robust.Client.Audio;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface;
Expand Down Expand Up @@ -51,6 +52,11 @@ public AudioTab()
SliderVolumeInterface,
scale: ContentAudioSystem.InterfaceMultiplier);

Control.AddOptionPercentSlider( //Impstation Random Announcer System port start
ImpCCVars.AnnouncerVolume,
SliderVolumeAnnouncer,
scale: ContentAudioSystem.AnnouncerMultiplier); //Impstation Random Announcer System port end

Control.AddOptionSlider(
CCVars.MaxAmbientSources,
SliderMaxAmbienceSounds,
Expand All @@ -60,6 +66,7 @@ public AudioTab()
Control.AddOptionCheckBox(CCVars.LobbyMusicEnabled, LobbyMusicCheckBox);
Control.AddOptionCheckBox(CCVars.RestartSoundsEnabled, RestartSoundsCheckBox);
Control.AddOptionCheckBox(CCVars.EventMusicEnabled, EventMusicCheckBox);
Control.AddOptionCheckBox(ImpCCVars.AnnouncerDisableMultipleSounds, AnnouncerDisableMultipleSoundsCheckBox); //Impstation Random Announcer System
Control.AddOptionCheckBox(CCVars.AdminSoundsEnabled, AdminSoundsCheckBox);

Control.Initialize();
Expand Down
100 changes: 100 additions & 0 deletions Content.Client/_EE/Announcements/Systems/AnnouncerSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
using System.Linq;
using Content.Client.Audio;
using Content.Shared._EE.Announcements.Events;
using Content.Shared._EE.Announcements.Systems;
using Content.Shared.CCVar;
using Content.Shared._Impstation.CCVar;
using Robust.Client.Audio;
using Robust.Client.Player;
using Robust.Client.ResourceManagement;
using Robust.Shared.Audio.Sources;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Configuration;

namespace Content.Client._EE.Announcements.Systems;

public sealed class AnnouncerSystem : SharedAnnouncerSystem
{
[Dependency] private readonly IPlayerManager _player = default!;
[Dependency] private readonly IConfigurationManager _config = default!;
[Dependency] private readonly IResourceCache _cache = default!;
[Dependency] private readonly IAudioManager _audioManager = default!;

public List<IAudioSource> AnnouncerSources { get; } = new();
public float AnnouncerVolume { get; private set; }


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

AnnouncerVolume = _config.GetCVar(ImpCCVars.AnnouncerVolume) * 100f / ContentAudioSystem.AnnouncerMultiplier;

_config.OnValueChanged(ImpCCVars.AnnouncerVolume, OnAnnouncerVolumeChanged); // Impstation: Namespacing
_config.OnValueChanged(ImpCCVars.AnnouncerDisableMultipleSounds, OnAnnouncerDisableMultipleSounds);

SubscribeNetworkEvent<AnnouncementSendEvent>(OnAnnouncementReceived);
}

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

_config.UnsubValueChanged(ImpCCVars.AnnouncerVolume, OnAnnouncerVolumeChanged); // Impstation: Namespacing
_config.UnsubValueChanged(ImpCCVars.AnnouncerDisableMultipleSounds, OnAnnouncerDisableMultipleSounds);
}


private void OnAnnouncerVolumeChanged(float value)
{
AnnouncerVolume = value;

foreach (var source in AnnouncerSources)
source.Gain = AnnouncerVolume;
}

private void OnAnnouncerDisableMultipleSounds(bool value)
{
if (!value)
return;

foreach (var audioSource in AnnouncerSources.ToList())
{
audioSource.Dispose();
AnnouncerSources.Remove(audioSource);
}
}

private void OnAnnouncementReceived(AnnouncementSendEvent ev)
{
if (!ev.Recipients.Contains(_player.LocalSession!.UserId)
|| !_cache.TryGetResource<AudioResource>(GetAnnouncementPath(ev.AnnouncementId, ev.AnnouncerId),
out var resource))
return;

var source = _audioManager.CreateAudioSource(resource);
if (source == null)
return;

source.Gain = AnnouncerVolume * SharedAudioSystem.VolumeToGain(ev.AudioParams.Volume);
source.Global = true;

if (_config.GetCVar(ImpCCVars.AnnouncerDisableMultipleSounds)) // Impstation: Namespacing
{
foreach (var audioSource in AnnouncerSources.ToList())
{
audioSource.Dispose();
AnnouncerSources.Remove(audioSource);
}
}

foreach (var audioSource in AnnouncerSources.ToList().Where(audioSource => !audioSource.Playing))
{
audioSource.Dispose();
AnnouncerSources.Remove(audioSource);
}

AnnouncerSources.Add(source);
source.StartPlaying();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
using System.Collections.Generic;
using System.Linq;
using Content.Server._EE.Announcements.Systems;
using Content.Server.StationEvents;
using Content.Shared._EE.Announcements.Prototypes;
using Robust.Client.ResourceManagement;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC.Exceptions;
using Robust.Shared.Localization;
using Robust.Shared.Prototypes;
using Serilog;

namespace Content.IntegrationTests.Tests._EE.Announcers;

/// <summary>
/// Checks if every station event using the announcerSystem has a valid audio file associated with it
/// </summary>
[TestFixture]
[TestOf(typeof(AnnouncerPrototype))]
public sealed class AnnouncerAudioTest
{
/// <inheritdoc cref="AnnouncerAudioTest" />
[Test]
public async Task TestEventSounds()
{
await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true });
var server = pair.Server;
var client = pair.Client;

var entSysMan = server.ResolveDependency<IEntitySystemManager>();
var proto = server.ResolveDependency<IPrototypeManager>();
var cache = client.ResolveDependency<IResourceCache>();
var announcer = entSysMan.GetEntitySystem<AnnouncerSystem>();
var events = entSysMan.GetEntitySystem<EventManagerSystem>();

await server.WaitAssertion(() =>
{
var succeeded = true;
var why = new List<string>();

foreach (var announcerProto in proto.EnumeratePrototypes<AnnouncerPrototype>().OrderBy(a => a.ID))
{
foreach (var ev in events.AllEvents())
{
if (ev.Value.StartAnnouncement)
{
var announcementId = announcer.GetAnnouncementId(ev.Key.ID);
var path = announcer.GetAnnouncementPath(announcementId, announcerProto);

if (!cache.ContentFileExists(path))
{
succeeded = false;
why.Add($"\"{announcerProto.ID}\", \"{announcementId}\": \"{path}\"");
}
}

if (ev.Value.EndAnnouncement)
{
var announcementId = announcer.GetAnnouncementId(ev.Key.ID, true);
var path = announcer.GetAnnouncementPath(announcementId, announcerProto);

if (!cache.ContentFileExists(path))
{
succeeded = false;
why.Add($"\"{announcerProto.ID}\", \"{announcementId}\": \"{path}\"");
}
}
}
}

Assert.That(succeeded, Is.True, $"The following announcements do not have a valid announcement audio:\n {string.Join("\n ", why)}");
});

await pair.CleanReturnAsync();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using System.Collections.Generic;
using System.Linq;
using Content.Shared._EE.Announcements.Prototypes;
using Robust.Shared.Prototypes;

namespace Content.IntegrationTests.Tests._EE.Announcers;

/// <summary>
/// Checks if every announcer has a fallback announcement
/// </summary>
[TestFixture]
[TestOf(typeof(AnnouncerPrototype))]
public sealed class AnnouncerPrototypeTest
{
/// <inheritdoc cref="AnnouncerPrototypeTest"/>
[Test]
public async Task TestAnnouncerFallbacks()
{
await using var pair = await PoolManager.GetServerClient();
var server = pair.Server;

var prototype = server.ResolveDependency<IPrototypeManager>();

await server.WaitAssertion(() =>
{
var success = true;
var why = new List<string>();

foreach (var announcer in prototype.EnumeratePrototypes<AnnouncerPrototype>().OrderBy(a => a.ID))
{
if (announcer.Announcements.Any(a => a.ID.ToLower() == "fallback"))
continue;

success = false;
why.Add(announcer.ID);
}

Assert.That(success, Is.True, $"The following announcers do not have a fallback announcement:\n {string.Join("\n ", why)}");
});

await pair.CleanReturnAsync();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
using System.Collections.Generic;
using System.Linq;
using Content.Server._EE.Announcements.Systems;
using Content.Server.StationEvents;
using Content.Shared._EE.Announcements.Prototypes;
using Robust.Shared.GameObjects;
using Robust.Shared.Localization;
using Robust.Shared.Prototypes;

namespace Content.IntegrationTests.Tests._EE.Announcers;

/// <summary>
/// Checks if every station event wanting the announcerSystem to send messages has a localization string
/// If an event doesn't have startAnnouncement or endAnnouncement set to true
/// it will be expected for that system to handle the announcements if it wants them
/// </summary>
[TestFixture]
[TestOf(typeof(AnnouncerPrototype))]
public sealed class AnnouncerLocalizationTest
{
/// <inheritdoc cref="AnnouncerLocalizationTest"/>
[Test]
public async Task TestEventLocalization()
{
await using var pair = await PoolManager.GetServerClient();
var server = pair.Server;

var locale = server.ResolveDependency<ILocalizationManager>();
var entSysMan = server.ResolveDependency<IEntitySystemManager>();
var proto = server.ResolveDependency<IPrototypeManager>();
var announcer = entSysMan.GetEntitySystem<AnnouncerSystem>();
var events = entSysMan.GetEntitySystem<EventManagerSystem>();

await server.WaitAssertion(() =>
{
var succeeded = true;
var why = new List<string>();

foreach (var announcerProto in proto.EnumeratePrototypes<AnnouncerPrototype>().OrderBy(a => a.ID))
{
foreach (var ev in events.AllEvents())
{
if (ev.Value.StartAnnouncement)
{
var announcementId = announcer.GetAnnouncementId(ev.Key.ID);
var eventLocaleString = announcer.GetAnnouncementMessage(announcementId, announcerProto.ID)
?? announcer.GetEventLocaleString(announcementId);

if (locale.GetString(eventLocaleString) == eventLocaleString)
{
succeeded = false;
why.Add($"\"{announcerProto.ID}\", \"{announcementId}\": \"{eventLocaleString}\"");
}
}

if (ev.Value.EndAnnouncement)
{
var announcementId = announcer.GetAnnouncementId(ev.Key.ID, true);
var eventLocaleString = announcer.GetAnnouncementMessage(announcementId, announcerProto.ID)
?? announcer.GetEventLocaleString(announcementId);

if (locale.GetString(eventLocaleString) == eventLocaleString)
{
succeeded = false;
why.Add($"\"{announcerProto.ID}\", \"{announcementId}\": \"{eventLocaleString}\"");
}
}
}
}

Assert.That(succeeded, Is.True, $"The following announcements do not have a localization string:\n {string.Join("\n ", why)}");
});

await pair.CleanReturnAsync();
}
}
7 changes: 6 additions & 1 deletion Content.Server/Administration/UI/AdminAnnounceEui.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,22 @@
using Content.Server.EUI;
using Content.Shared.Administration;
using Content.Shared.Eui;
using Content.Server._EE.Announcements.Systems; // Impstation Random Announcer System
using Robust.Shared.Player; // Impstation Random Announcer System

namespace Content.Server.Administration.UI
{
public sealed class AdminAnnounceEui : BaseEui
{
[Dependency] private readonly IAdminManager _adminManager = default!;
[Dependency] private readonly IChatManager _chatManager = default!;
private readonly AnnouncerSystem _announcer; // Impstation Random Announcer System
private readonly ChatSystem _chatSystem;

public AdminAnnounceEui()
{
IoCManager.InjectDependencies(this);
_announcer = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<AnnouncerSystem>(); // Impstation Random Announcer System
_chatSystem = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<ChatSystem>();
}

Expand Down Expand Up @@ -50,7 +54,8 @@ public override void HandleMessage(EuiMessageBase msg)
break;
// TODO: Per-station announcement support
case AdminAnnounceType.Station:
_chatSystem.DispatchGlobalAnnouncement(doAnnounce.Announcement, doAnnounce.Announcer, colorOverride: Color.Gold);
_announcer.SendAnnouncement(_announcer.GetAnnouncementId("Announce"), Filter.Broadcast(), // Impstation Random Announcer System
doAnnounce.Announcement, doAnnounce.Announcer, Color.Gold);
break;
}

Expand Down
Loading
Loading