diff --git a/Content.Client/Audio/Jukebox/JukeboxBoundUserInterface.cs b/Content.Client/Audio/Jukebox/JukeboxBoundUserInterface.cs new file mode 100644 index 0000000000..072730d65d --- /dev/null +++ b/Content.Client/Audio/Jukebox/JukeboxBoundUserInterface.cs @@ -0,0 +1,119 @@ +using Content.Shared.Audio.Jukebox; +using Robust.Client.Audio; +using Robust.Client.Player; +using Robust.Shared.Audio.Components; +using Robust.Shared.Player; +using Robust.Shared.Prototypes; + +namespace Content.Client.Audio.Jukebox; + +public sealed class JukeboxBoundUserInterface : BoundUserInterface +{ + [Dependency] private readonly IPlayerManager _player = default!; + [Dependency] private readonly IPrototypeManager _protoManager = default!; + + [ViewVariables] + private JukeboxMenu? _menu; + + public JukeboxBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) + { + IoCManager.InjectDependencies(this); + } + + protected override void Open() + { + base.Open(); + + _menu = new JukeboxMenu(); + _menu.OnClose += Close; + _menu.OpenCentered(); + + _menu.OnPlayPressed += args => + { + if (args) + { + SendMessage(new JukeboxPlayingMessage()); + } + else + { + SendMessage(new JukeboxPauseMessage()); + } + }; + + _menu.OnStopPressed += () => + { + SendMessage(new JukeboxStopMessage()); + }; + + _menu.OnSongSelected += SelectSong; + + _menu.SetTime += SetTime; + PopulateMusic(); + Reload(); + } + + /// + /// Reloads the attached menu if it exists. + /// + public void Reload() + { + if (_menu == null || !EntMan.TryGetComponent(Owner, out JukeboxComponent? jukebox)) + return; + + _menu.SetAudioStream(jukebox.AudioStream); + + if (_protoManager.TryIndex(jukebox.SelectedSongId, out var songProto)) + { + var length = EntMan.System().GetAudioLength(songProto.Path.Path.ToString()); + _menu.SetSelectedSong(songProto.Name, (float) length.TotalSeconds); + } + else + { + _menu.SetSelectedSong(string.Empty, 0f); + } + } + + public void PopulateMusic() + { + _menu?.Populate(_protoManager.EnumeratePrototypes()); + } + + public void SelectSong(ProtoId songid) + { + SendMessage(new JukeboxSelectedMessage(songid)); + } + + public void SetTime(float time) + { + var sentTime = time; + + // You may be wondering, what the fuck is this + // Well we want to be able to predict the playback slider change, of which there are many ways to do it + // We can't just use SendPredictedMessage because it will reset every tick and audio updates every frame + // so it will go BRRRRT + // Using ping gets us close enough that it SHOULD, MOST OF THE TIME, fall within the 0.1 second tolerance + // that's still on engine so our playback position never gets corrected. + if (EntMan.TryGetComponent(Owner, out JukeboxComponent? jukebox) && + EntMan.TryGetComponent(jukebox.AudioStream, out AudioComponent? audioComp)) + { + audioComp.PlaybackPosition = time; + } + + SendMessage(new JukeboxSetTimeMessage(sentTime)); + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + if (!disposing) + return; + + if (_menu == null) + return; + + _menu.OnClose -= Close; + _menu.Dispose(); + _menu = null; + } +} + diff --git a/Content.Client/Audio/Jukebox/JukeboxMenu.xaml b/Content.Client/Audio/Jukebox/JukeboxMenu.xaml new file mode 100644 index 0000000000..e8d39a9b11 --- /dev/null +++ b/Content.Client/Audio/Jukebox/JukeboxMenu.xaml @@ -0,0 +1,18 @@ + + + + + + +