[feat]jukebox (#9)
This commit is contained in:
@@ -22,6 +22,7 @@ using Content.Client.Stylesheets;
|
||||
using Content.Client.Viewport;
|
||||
using Content.Client.Voting;
|
||||
using Content.Client.White.JoinQueue;
|
||||
using Content.Client.White.Jukebox;
|
||||
using Content.Client.White.Sponsors;
|
||||
using Content.Shared.Ame;
|
||||
using Content.Client.White.Stalin;
|
||||
@@ -78,6 +79,7 @@ namespace Content.Client.Entry
|
||||
[Dependency] private readonly SponsorsManager _sponsorsManager = default!;
|
||||
[Dependency] private readonly JoinQueueManager _queueManager = default!;
|
||||
[Dependency] private readonly StalinManager _stalinManager = default!;
|
||||
[Dependency] private readonly ClientJukeboxSongsSyncManager _jukeboxSyncManager = default!;
|
||||
//WD-EDIT
|
||||
|
||||
public override void Init()
|
||||
@@ -183,6 +185,7 @@ namespace Content.Client.Entry
|
||||
//WD-EDIT
|
||||
_sponsorsManager.Initialize();
|
||||
_queueManager.Initialize();
|
||||
_jukeboxSyncManager.Initialize();
|
||||
//WD-EDIT
|
||||
|
||||
_baseClient.RunLevelChanged += (_, args) =>
|
||||
|
||||
@@ -18,6 +18,7 @@ using Content.Shared.Administration.Logs;
|
||||
using Content.Client.Guidebook;
|
||||
using Content.Client.Replay;
|
||||
using Content.Client.White.JoinQueue;
|
||||
using Content.Client.White.Jukebox;
|
||||
using Content.Client.White.Sponsors;
|
||||
using Content.Client.White.Stalin;
|
||||
using Content.Shared.Administration.Managers;
|
||||
@@ -53,6 +54,7 @@ namespace Content.Client.IoC
|
||||
IoCManager.Register<JoinQueueManager>();
|
||||
IoCManager.Register<SponsorsManager>();
|
||||
IoCManager.Register<StalinManager>();
|
||||
IoCManager.Register<ClientJukeboxSongsSyncManager>();
|
||||
//WD-EDIT
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,6 +74,19 @@
|
||||
<Label Name="TtsVolumeLabel" MinSize="48 0" Align="Right" />
|
||||
<Control MinSize="4 0"/>
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal" Margin="5 0 0 0">
|
||||
<Label Text="{Loc 'ui-options-jukebox-volume'}" HorizontalExpand="True" />
|
||||
<Control MinSize="8 0" />
|
||||
<Slider Name="JukeboxVolumeSlider"
|
||||
MinValue="25"
|
||||
MaxValue="200"
|
||||
HorizontalExpand="True"
|
||||
MinSize="80 0"
|
||||
Rounded="True" />
|
||||
<Control MinSize="8 0" />
|
||||
<Label Name="JukeboxVolumeLabel" MinSize="48 0" Align="Right" />
|
||||
<Control MinSize="4 0"/>
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal" Margin="5 0 0 0">
|
||||
<Label Text="{Loc 'ui-options-lobby-volume'}" HorizontalExpand="True" />
|
||||
<Control MinSize="8 0" />
|
||||
|
||||
@@ -39,6 +39,7 @@ namespace Content.Client.Options.UI.Tabs
|
||||
LobbyVolumeSlider.OnValueChanged += OnLobbyVolumeSliderChanged;
|
||||
TtsVolumeSlider.OnValueChanged += OnTtsVolumeSliderChanged;
|
||||
InterfaceVolumeSlider.OnValueChanged += OnInterfaceVolumeSliderChanged;
|
||||
JukeboxVolumeSlider.OnValueChanged += OnJukeboxVolumeSliderChanged;
|
||||
LobbyMusicCheckBox.OnToggled += OnLobbyMusicCheckToggled;
|
||||
RestartSoundsCheckBox.OnToggled += OnRestartSoundsCheckToggled;
|
||||
EventMusicCheckBox.OnToggled += OnEventMusicCheckToggled;
|
||||
@@ -63,6 +64,7 @@ namespace Content.Client.Options.UI.Tabs
|
||||
|
||||
//WD-EDIT
|
||||
TtsVolumeSlider.OnValueChanged -= OnTtsVolumeSliderChanged;
|
||||
JukeboxVolumeSlider.OnValueChanged -= OnJukeboxVolumeSliderChanged;
|
||||
//WD-EDIT
|
||||
|
||||
base.Dispose(disposing);
|
||||
@@ -76,6 +78,13 @@ namespace Content.Client.Options.UI.Tabs
|
||||
}
|
||||
//TTS-End
|
||||
|
||||
//JUKEBOX
|
||||
private void OnJukeboxVolumeSliderChanged(Range obj)
|
||||
{
|
||||
UpdateChanges();
|
||||
}
|
||||
//JUKEBOX
|
||||
|
||||
private void OnLobbyVolumeSliderChanged(Range obj)
|
||||
{
|
||||
UpdateChanges();
|
||||
@@ -150,6 +159,7 @@ namespace Content.Client.Options.UI.Tabs
|
||||
|
||||
//WD-EDIT
|
||||
_cfg.SetCVar(WhiteCVars.TtsVolume, LV100ToDB(TtsVolumeSlider.Value));
|
||||
_cfg.SetCVar(WhiteCVars.JukeboxVolume, LV100ToDB(JukeboxVolumeSlider.Value));
|
||||
//WD-EDIT
|
||||
|
||||
_cfg.SaveToFile();
|
||||
@@ -179,6 +189,7 @@ namespace Content.Client.Options.UI.Tabs
|
||||
|
||||
//WD-EDIT
|
||||
TtsVolumeSlider.Value = DBToLV100(_cfg.GetCVar(WhiteCVars.TtsVolume));
|
||||
JukeboxVolumeSlider.Value = DBToLV100(_cfg.GetCVar(WhiteCVars.JukeboxVolume));
|
||||
//WD-EDIT
|
||||
|
||||
|
||||
@@ -222,6 +233,8 @@ namespace Content.Client.Options.UI.Tabs
|
||||
//WD-EDIT
|
||||
var isTtsVolumeSame =
|
||||
Math.Abs(TtsVolumeSlider.Value - DBToLV100(_cfg.GetCVar(WhiteCVars.TtsVolume))) < 0.01f;
|
||||
var isJukeboxVolumeSame =
|
||||
Math.Abs(JukeboxVolumeSlider.Value - DBToLV100(_cfg.GetCVar(WhiteCVars.JukeboxVolume))) < 0.01f;
|
||||
//WD-EDIT
|
||||
|
||||
var isRestartSoundsSame = RestartSoundsCheckBox.Pressed == _cfg.GetCVar(CCVars.RestartSoundsEnabled);
|
||||
@@ -230,6 +243,7 @@ namespace Content.Client.Options.UI.Tabs
|
||||
var isEverythingSame = isMasterVolumeSame && isMidiVolumeSame && isAmbientVolumeSame && isAmbientMusicVolumeSame && isAmbientSoundsSame && isLobbySame && isRestartSoundsSame && isEventSame
|
||||
&& isAdminSoundsSame && isLobbyVolumeSame && isInterfaceVolumeSame;
|
||||
isEverythingSame = isEverythingSame && isTtsVolumeSame; //WD-EDIT
|
||||
isEverythingSame = isEverythingSame && isTtsVolumeSame && isJukeboxVolumeSame; //WD-EDIT
|
||||
ApplyButton.Disabled = isEverythingSame;
|
||||
ResetButton.Disabled = isEverythingSame;
|
||||
MasterVolumeLabel.Text =
|
||||
@@ -249,6 +263,8 @@ namespace Content.Client.Options.UI.Tabs
|
||||
//WD-EDIT
|
||||
TtsVolumeLabel.Text =
|
||||
Loc.GetString("ui-options-volume-percent", ("volume", TtsVolumeSlider.Value / 100));
|
||||
JukeboxVolumeLabel.Text =
|
||||
Loc.GetString("ui-options-volume-percent", ("volume", JukeboxVolumeSlider.Value / 100));
|
||||
//WD-EDIT
|
||||
}
|
||||
}
|
||||
|
||||
10
Content.Client/White/CheZaHuetaSystem.cs
Normal file
10
Content.Client/White/CheZaHuetaSystem.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace Content.Client.White;
|
||||
|
||||
//Система со смешным названием, чье предназначение заключается лишь в одном - отправке нетворк ивентов.
|
||||
public sealed class CheZaHuetaSystem : EntitySystem
|
||||
{
|
||||
public void SendNetMessage(EntityEventArgs message)
|
||||
{
|
||||
RaiseNetworkEvent(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using Content.Shared.White.Jukebox;
|
||||
|
||||
namespace Content.Client.White.Jukebox;
|
||||
|
||||
public sealed class ClientJukeboxSongsSyncManager : JukeboxSongsSyncManager
|
||||
{
|
||||
public override void OnSongUploaded(JukeboxSongUploadNetMessage message)
|
||||
{
|
||||
ContentRoot.AddOrUpdateFile(message.RelativePath!, message.Data);
|
||||
}
|
||||
}
|
||||
70
Content.Client/White/Jukebox/JukeboxBUI.cs
Normal file
70
Content.Client/White/Jukebox/JukeboxBUI.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.White.Jukebox;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
|
||||
namespace Content.Client.White.Jukebox;
|
||||
|
||||
public sealed class JukeboxBUI : BoundUserInterface
|
||||
{
|
||||
[Dependency] private readonly EntityManager _entityManager = default!;
|
||||
private readonly SharedPopupSystem _sharedPopupSystem = default!;
|
||||
|
||||
private JukeboxMenu? _window;
|
||||
public JukeboxBUI(ClientUserInterfaceComponent owner, Enum uiKey) : base(owner, uiKey)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
_sharedPopupSystem = _entityManager.System<SharedPopupSystem>();
|
||||
|
||||
var uid = owner.Owner;
|
||||
|
||||
if (!_entityManager.TryGetComponent<JukeboxComponent>(uid, out var jukeboxComponent))
|
||||
{
|
||||
_sharedPopupSystem.PopupEntity($"Тут нет JukeboxComponent, звоните кодерам", uid);
|
||||
return;
|
||||
}
|
||||
|
||||
_window = new JukeboxMenu(jukeboxComponent);
|
||||
_window.RepeatButton.OnToggled += OnRepeatButtonToggled;
|
||||
_window.StopButton.OnPressed += OnStopButtonPressed;
|
||||
_window.EjectButton.OnPressed += OnEjectButtonPressed;
|
||||
}
|
||||
|
||||
private void OnEjectButtonPressed(BaseButton.ButtonEventArgs obj)
|
||||
{
|
||||
SendMessage(new JukeboxEjectRequest());
|
||||
}
|
||||
|
||||
private void OnStopButtonPressed(BaseButton.ButtonEventArgs obj)
|
||||
{
|
||||
SendMessage(new JukeboxStopRequest());
|
||||
}
|
||||
|
||||
private void OnRepeatButtonToggled(BaseButton.ButtonToggledEventArgs obj)
|
||||
{
|
||||
SendMessage(new JukeboxRepeatToggled(obj.Pressed));
|
||||
}
|
||||
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
if (_window == null)
|
||||
{
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
|
||||
_window.OpenCentered();
|
||||
_window.OnClose += Close;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
if (!disposing) return;
|
||||
|
||||
_window?.Dispose();
|
||||
}
|
||||
}
|
||||
29
Content.Client/White/Jukebox/JukeboxMenu.xaml
Normal file
29
Content.Client/White/Jukebox/JukeboxMenu.xaml
Normal file
@@ -0,0 +1,29 @@
|
||||
<DefaultWindow xmlns="https://spacestation14.io" MinSize="600 700" Name="BoomBoxWindow" Title="Альфа-Шкварки">
|
||||
<BoxContainer Orientation="Vertical" HorizontalExpand="True" VerticalExpand="True">
|
||||
<Label Name="CurrenSongLabel" HorizontalAlignment="Center" HorizontalExpand="True"></Label>
|
||||
<BoxContainer Orientation="Horizontal" HorizontalAlignment="Center">
|
||||
<Button Access="Internal" Name="StopButton" Text="Остановить" HorizontalAlignment="Right"></Button>
|
||||
<Button Access="Internal" Name="RepeatButton" Text="Повторять" HorizontalAlignment="Right" ToggleMode="True"></Button>
|
||||
<Button Access="Internal" Name="EjectButton" Text="Извлечь"></Button>
|
||||
</BoxContainer>
|
||||
|
||||
<TabContainer VerticalExpand="True" HorizontalExpand="True">
|
||||
<!-- <ScrollContainer Name="DefaultSongsTab"
|
||||
HScrollEnabled="False">
|
||||
<BoxContainer Name="DefaultSongsContainer"
|
||||
Orientation="Vertical"
|
||||
Margin="2 2 0 0"
|
||||
HorizontalExpand="True">
|
||||
</BoxContainer>
|
||||
</ScrollContainer> -->
|
||||
<ScrollContainer Name="TapeSongsTab"
|
||||
HScrollEnabled="False">
|
||||
<BoxContainer Name="TapeSongsContainer"
|
||||
Orientation="Vertical"
|
||||
Margin="2 2 0 0"
|
||||
HorizontalExpand="True">
|
||||
</BoxContainer>
|
||||
</ScrollContainer>
|
||||
</TabContainer>
|
||||
</BoxContainer>
|
||||
</DefaultWindow>
|
||||
100
Content.Client/White/Jukebox/JukeboxMenu.xaml.cs
Normal file
100
Content.Client/White/Jukebox/JukeboxMenu.xaml.cs
Normal file
@@ -0,0 +1,100 @@
|
||||
using System.Linq;
|
||||
using Content.Shared.White.Jukebox;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Timing;
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
namespace Content.Client.White.Jukebox;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class JukeboxMenu : DefaultWindow
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
private readonly JukeboxSystem _jukeboxSystem = default!;
|
||||
|
||||
private readonly JukeboxComponent _component;
|
||||
|
||||
private List<JukeboxSongEntry> _defaultSongsEntries = new();
|
||||
private List<JukeboxSongEntry> _tapeSongsEntries = new();
|
||||
|
||||
public JukeboxMenu(JukeboxComponent component)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
_jukeboxSystem = _entityManager.System<JukeboxSystem>();
|
||||
|
||||
_component = component;
|
||||
|
||||
/*TabContainer.SetTabTitle(DefaultSongsTab, "Прямо с завода");*/
|
||||
TabContainer.SetTabTitle(TapeSongsTab, "Песенки с кассеты");
|
||||
|
||||
/*PopulateDefaultSongsContainer(DefaultSongsContainer);*/
|
||||
PopulateTapeSongsContainer(TapeSongsContainer);
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
CurrenSongLabel.Text = _component.PlayingSongData == null ? "..." : _component.PlayingSongData.SongName!;
|
||||
|
||||
RepeatButton.Pressed = _component.Repeating;
|
||||
|
||||
base.FrameUpdate(args);
|
||||
}
|
||||
|
||||
private void PopulateDefaultSongsContainer(BoxContainer defaultSongsContainer)
|
||||
{
|
||||
var tapes = _component.DefaultSongsContainer.ContainedEntities.ToList();
|
||||
|
||||
foreach (var tapeUid in tapes)
|
||||
{
|
||||
if (_entityManager.TryGetComponent<TapeComponent>(tapeUid, out var tape))
|
||||
{
|
||||
foreach (var song in tape.Songs)
|
||||
{
|
||||
var songEntry = new JukeboxSongEntry(song, RequestSongPlay);
|
||||
|
||||
_defaultSongsEntries.Add(songEntry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_defaultSongsEntries.Sort((x,y ) => string.Compare(x.Name!, y.Name!, StringComparison.Ordinal));
|
||||
|
||||
foreach (var defaultSongsEntry in _defaultSongsEntries)
|
||||
{
|
||||
defaultSongsContainer.AddChild(defaultSongsEntry);
|
||||
}
|
||||
}
|
||||
|
||||
private void PopulateTapeSongsContainer(BoxContainer tapeSongsContainer)
|
||||
{
|
||||
var tapes = _component.TapeContainer.ContainedEntities.ToList();
|
||||
|
||||
foreach (var tapeUid in tapes)
|
||||
{
|
||||
if (_entityManager.TryGetComponent<TapeComponent>(tapeUid, out var tape))
|
||||
{
|
||||
foreach (var song in tape.Songs)
|
||||
{
|
||||
var songEntry = new JukeboxSongEntry(song, RequestSongPlay);
|
||||
|
||||
_tapeSongsEntries.Add(songEntry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var tapeSongsEntry in _tapeSongsEntries)
|
||||
{
|
||||
tapeSongsContainer.AddChild(tapeSongsEntry);
|
||||
}
|
||||
}
|
||||
|
||||
private void RequestSongPlay(JukeboxSong song)
|
||||
{
|
||||
_jukeboxSystem.RequestSongToPlay(_component, song);
|
||||
}
|
||||
}
|
||||
13
Content.Client/White/Jukebox/JukeboxSongEntry.xaml
Normal file
13
Content.Client/White/Jukebox/JukeboxSongEntry.xaml
Normal file
@@ -0,0 +1,13 @@
|
||||
<Control xmlns="https://spacestation14.io"
|
||||
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client">
|
||||
<PanelContainer MaxHeight="32" HorizontalExpand="True" Margin="2 2 2 2">
|
||||
<PanelContainer.PanelOverride>
|
||||
<gfx:StyleBoxFlat BackgroundColor="#7B7E7E"></gfx:StyleBoxFlat>
|
||||
</PanelContainer.PanelOverride>
|
||||
|
||||
<BoxContainer Orientation="Horizontal" HorizontalExpand="True" >
|
||||
<Label Name="SongNameLabel" HorizontalAlignment="Left" HorizontalExpand="True"></Label>
|
||||
<Button Name="PlaySongButton" HorizontalAlignment="Right" Text="Запустить"></Button>
|
||||
</BoxContainer>
|
||||
</PanelContainer>
|
||||
</Control>
|
||||
25
Content.Client/White/Jukebox/JukeboxSongEntry.xaml.cs
Normal file
25
Content.Client/White/Jukebox/JukeboxSongEntry.xaml.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using Content.Shared.White.Jukebox;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
namespace Content.Client.White.Jukebox;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class JukeboxSongEntry : Control
|
||||
{
|
||||
public JukeboxSong? Song { get; private set; }
|
||||
|
||||
private JukeboxSongEntry()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
}
|
||||
|
||||
public JukeboxSongEntry(JukeboxSong song, Action<JukeboxSong> callback) : this()
|
||||
{
|
||||
Song = song;
|
||||
SongNameLabel.Text = Song.SongName;
|
||||
PlaySongButton.OnPressed += _ => callback.Invoke(Song);
|
||||
}
|
||||
}
|
||||
277
Content.Client/White/Jukebox/JukeboxSystem.cs
Normal file
277
Content.Client/White/Jukebox/JukeboxSystem.cs
Normal file
@@ -0,0 +1,277 @@
|
||||
using System.Resources;
|
||||
using Content.Shared.GameTicking;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.White;
|
||||
using Content.Shared.White.Jukebox;
|
||||
using Robust.Client.Audio;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Physics;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.White.Jukebox;
|
||||
|
||||
public sealed class JukeboxSystem : EntitySystem
|
||||
{
|
||||
|
||||
[Dependency] private readonly SharedAudioSystem _audioSystem = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IResourceCache _resource = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
|
||||
|
||||
|
||||
|
||||
private readonly Dictionary<JukeboxComponent, JukeboxAudio> _playingJukeboxes = new();
|
||||
|
||||
private float _maxAudioRange;
|
||||
private float _jukeboxVolume;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_cfg.OnValueChanged(WhiteCVars.MaxJukeboxSoundRange, range => _maxAudioRange = range, true);
|
||||
_cfg.OnValueChanged(WhiteCVars.JukeboxVolume, volume => JukeboxVolumeChanged(volume), true);
|
||||
|
||||
SubscribeLocalEvent<JukeboxComponent, ComponentHandleState>(OnStateChanged);
|
||||
SubscribeLocalEvent<JukeboxComponent, ComponentRemove>(OnComponentRemoved);
|
||||
SubscribeNetworkEvent<RoundRestartCleanupEvent>(OnRoundRestart);
|
||||
SubscribeNetworkEvent<TickerJoinLobbyEvent>(JoinLobby);
|
||||
SubscribeNetworkEvent<JukeboxStopPlaying>(OnStopPlaying);
|
||||
}
|
||||
|
||||
private void JukeboxVolumeChanged(float volume)
|
||||
{
|
||||
_jukeboxVolume = volume;
|
||||
|
||||
CleanUp();
|
||||
}
|
||||
|
||||
private void JoinLobby(TickerJoinLobbyEvent ev)
|
||||
{
|
||||
CleanUp();
|
||||
}
|
||||
|
||||
private void OnRoundRestart(RoundRestartCleanupEvent ev)
|
||||
{
|
||||
CleanUp();
|
||||
}
|
||||
|
||||
private void OnComponentRemoved(EntityUid uid, JukeboxComponent component, ComponentRemove args)
|
||||
{
|
||||
if (!_playingJukeboxes.TryGetValue(component, out var playingData)) return;
|
||||
playingData.PlayingStream.Stop();
|
||||
_playingJukeboxes.Remove(component);
|
||||
}
|
||||
|
||||
private void OnStopPlaying(JukeboxStopPlaying ev)
|
||||
{
|
||||
if (!ev.JukeboxUid.HasValue) return;
|
||||
if(!TryComp<JukeboxComponent>(ev.JukeboxUid, out var jukeboxComponent)) return;
|
||||
|
||||
if(!_playingJukeboxes.TryGetValue(jukeboxComponent, out var jukeboxAudio)) return;
|
||||
|
||||
jukeboxAudio.PlayingStream.Stop();
|
||||
_playingJukeboxes.Remove(jukeboxComponent);
|
||||
}
|
||||
|
||||
public void RequestSongToPlay(JukeboxComponent component, JukeboxSong jukeboxSong)
|
||||
{
|
||||
if (!_resource.TryGetResource<AudioResource>((ResPath) jukeboxSong.SongPath!, out var songResource))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RaiseNetworkEvent(new JukeboxRequestSongPlay()
|
||||
{
|
||||
Jukebox = component.Owner,
|
||||
SongName = jukeboxSong.SongName,
|
||||
SongPath = jukeboxSong.SongPath,
|
||||
SongDuration = (float)songResource.AudioStream.Length.TotalSeconds
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
var localPlayerEntity = _playerManager.LocalPlayer!.ControlledEntity;
|
||||
if(!localPlayerEntity.HasValue) return;
|
||||
|
||||
ProcessJukeboxes();
|
||||
}
|
||||
|
||||
private void OnStateChanged(EntityUid uid, JukeboxComponent component, ref ComponentHandleState args)
|
||||
{
|
||||
if (args.Current is JukeboxComponentState state)
|
||||
{
|
||||
component.Repeating = state.Playing;
|
||||
component.Volume = state.Volume;
|
||||
component.PlayingSongData = state.SongData;
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessJukeboxes()
|
||||
{
|
||||
var jukeboxes = EntityQuery<JukeboxComponent, TransformComponent>();
|
||||
var playerXform = Comp<TransformComponent>(_playerManager.LocalPlayer!.ControlledEntity!.Value);
|
||||
|
||||
foreach (var (jukeboxComponent, jukeboxXform) in jukeboxes)
|
||||
{
|
||||
|
||||
if(jukeboxXform.MapID != playerXform.MapID) continue;
|
||||
if ((jukeboxXform.MapPosition.Position - playerXform.MapPosition.Position).Length > _maxAudioRange) continue;
|
||||
|
||||
if (_playingJukeboxes.TryGetValue(jukeboxComponent, out var jukeboxAudio))
|
||||
{
|
||||
if (jukeboxAudio.PlayingStream.Done)
|
||||
{
|
||||
HandleDoneStream(jukeboxAudio, jukeboxComponent);
|
||||
return;
|
||||
}
|
||||
|
||||
if (jukeboxAudio.SongData.SongPath != jukeboxComponent.PlayingSongData?.SongPath)
|
||||
{
|
||||
HandleSongChanged(jukeboxAudio, jukeboxComponent);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (jukeboxComponent.PlayingSongData == null)
|
||||
{
|
||||
SetBarsLayerVisible(jukeboxComponent, false);
|
||||
continue;
|
||||
}
|
||||
|
||||
var stream = TryCreateStream(jukeboxComponent);
|
||||
|
||||
if (stream == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_playingJukeboxes.Add(jukeboxComponent, stream);
|
||||
SetBarsLayerVisible(jukeboxComponent, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleSongChanged(JukeboxAudio jukeboxAudio, JukeboxComponent jukeboxComponent)
|
||||
{
|
||||
jukeboxAudio.PlayingStream.Stop();
|
||||
|
||||
if (jukeboxComponent.PlayingSongData != null && jukeboxComponent.PlayingSongData.SongPath == jukeboxAudio.SongData.SongPath)
|
||||
{
|
||||
var newStream = TryCreateStream(jukeboxComponent);
|
||||
if(newStream == null) return;
|
||||
|
||||
_playingJukeboxes[jukeboxComponent] = newStream;
|
||||
SetBarsLayerVisible(jukeboxComponent, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
_playingJukeboxes.Remove(jukeboxComponent);
|
||||
SetBarsLayerVisible(jukeboxComponent, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleDoneStream(JukeboxAudio jukeboxAudio, JukeboxComponent jukeboxComponent)
|
||||
{
|
||||
if (!jukeboxComponent.Repeating)
|
||||
{
|
||||
jukeboxAudio.PlayingStream.Stop();
|
||||
_playingJukeboxes.Remove(jukeboxComponent);
|
||||
SetBarsLayerVisible(jukeboxComponent, false);
|
||||
return;
|
||||
}
|
||||
|
||||
if(jukeboxComponent.PlayingSongData == null) return;
|
||||
|
||||
|
||||
var newStream = TryCreateStream(jukeboxComponent);
|
||||
|
||||
if (newStream == null)
|
||||
{
|
||||
_playingJukeboxes.Remove(jukeboxComponent);
|
||||
SetBarsLayerVisible(jukeboxComponent, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
_playingJukeboxes[jukeboxComponent] = newStream;
|
||||
SetBarsLayerVisible(jukeboxComponent, true);
|
||||
}
|
||||
}
|
||||
|
||||
private JukeboxAudio? TryCreateStream(JukeboxComponent jukeboxComponent)
|
||||
{
|
||||
if (jukeboxComponent.PlayingSongData == null) return null;
|
||||
|
||||
var resourcePath = jukeboxComponent.PlayingSongData.SongPath!;
|
||||
var localSession = _playerManager.LocalPlayer!.Session;
|
||||
|
||||
if(!_resource.TryGetResource<AudioResource>((ResPath) resourcePath, out var audio))
|
||||
return null!;
|
||||
|
||||
if (audio!.AudioStream.Length.TotalSeconds < jukeboxComponent.PlayingSongData!.PlaybackPosition)
|
||||
{
|
||||
return null!;
|
||||
}
|
||||
|
||||
var audioParams = new AudioParams
|
||||
{
|
||||
PlayOffsetSeconds = jukeboxComponent.PlayingSongData.PlaybackPosition,
|
||||
Volume = _jukeboxVolume,
|
||||
MaxDistance = _maxAudioRange
|
||||
};
|
||||
|
||||
AudioSystem.PlayingStream? playingStream = null!;
|
||||
|
||||
playingStream = _audioSystem.PlayEntity(resourcePath.ToString()!, localSession, jukeboxComponent.Owner, audioParams) as AudioSystem.PlayingStream;
|
||||
|
||||
if (playingStream == null)
|
||||
return null!;
|
||||
|
||||
return new JukeboxAudio(playingStream, audio!, jukeboxComponent.PlayingSongData);
|
||||
}
|
||||
|
||||
private class JukeboxAudio
|
||||
{
|
||||
public PlayingSongData SongData { get; }
|
||||
public AudioSystem.PlayingStream PlayingStream { get; }
|
||||
public AudioResource AudioStream { get; }
|
||||
|
||||
public JukeboxAudio(AudioSystem.PlayingStream playingStream, AudioResource audioStream, PlayingSongData songData)
|
||||
{
|
||||
PlayingStream = playingStream;
|
||||
AudioStream = audioStream;
|
||||
SongData = songData;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetBarsLayerVisible(JukeboxComponent jukeboxComponent, bool visible)
|
||||
{
|
||||
var spriteComponent = Comp<SpriteComponent>(jukeboxComponent.Owner);
|
||||
spriteComponent.LayerMapTryGet("bars", out var layer);
|
||||
spriteComponent.LayerSetVisible(layer, visible);
|
||||
}
|
||||
|
||||
private void CleanUp()
|
||||
{
|
||||
foreach (var playingJukebox in _playingJukeboxes.Values)
|
||||
{
|
||||
playingJukebox.PlayingStream.Stop();
|
||||
}
|
||||
|
||||
_playingJukeboxes.Clear();
|
||||
}
|
||||
}
|
||||
51
Content.Client/White/Jukebox/TapeCreatorBUI.cs
Normal file
51
Content.Client/White/Jukebox/TapeCreatorBUI.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.White.Jukebox;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
namespace Content.Client.White.Jukebox;
|
||||
|
||||
public sealed class TapeCreatorBUI : BoundUserInterface
|
||||
{
|
||||
[Dependency] private readonly EntityManager _entityManager = default!;
|
||||
private readonly SharedPopupSystem _sharedPopupSystem = default!;
|
||||
|
||||
private TapeCreatorMenu? _window;
|
||||
|
||||
public TapeCreatorBUI(ClientUserInterfaceComponent owner, Enum uiKey) : base(owner, uiKey)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
_sharedPopupSystem = _entityManager.System<SharedPopupSystem>();
|
||||
|
||||
var uid = owner.Owner;
|
||||
|
||||
if (!_entityManager.TryGetComponent<TapeCreatorComponent>(uid, out var tapeCreatorComponent))
|
||||
{
|
||||
_sharedPopupSystem.PopupEntity($"Тут нет TapeCreatorComponent, звоните кодерам", uid);
|
||||
return;
|
||||
}
|
||||
|
||||
_window = new TapeCreatorMenu(tapeCreatorComponent);
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
if (_window == null)
|
||||
{
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
|
||||
_window.OpenCentered();
|
||||
_window.OnClose += Close;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
if (!disposing) return;
|
||||
|
||||
_window?.Dispose();
|
||||
}
|
||||
}
|
||||
16
Content.Client/White/Jukebox/TapeCreatorMenu.xaml
Normal file
16
Content.Client/White/Jukebox/TapeCreatorMenu.xaml
Normal file
@@ -0,0 +1,16 @@
|
||||
<DefaultWindow xmlns="https://spacestation14.io" MinSize="200 300" Title="Мысль">
|
||||
<BoxContainer Orientation="Vertical" HorizontalExpand="True" VerticalExpand="True">
|
||||
<BoxContainer Orientation="Horizontal" HorizontalExpand="True" SetHeight="30">
|
||||
<Label Text="Осталось циклов записи: "></Label>
|
||||
<Label Name="CoinsLabel" Text="0"></Label>
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Vertical" SeparationOverride="5">
|
||||
<BoxContainer Orientation="Horizontal" HorizontalExpand="True" SetHeight="30">
|
||||
<Label Text="Название"></Label>
|
||||
<LineEdit Name="SongNameField" MinWidth="200"></LineEdit>
|
||||
<Button Name="LoadSongButton" Access="Internal" Text="Загрузить песню"></Button>
|
||||
</BoxContainer>
|
||||
<Button Name="UploadSong" Access="Internal" Text="Записать мозговую активность" MinHeight="50" HorizontalExpand="True"></Button>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</DefaultWindow>
|
||||
125
Content.Client/White/Jukebox/TapeCreatorMenu.xaml.cs
Normal file
125
Content.Client/White/Jukebox/TapeCreatorMenu.xaml.cs
Normal file
@@ -0,0 +1,125 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.White;
|
||||
using Content.Shared.White.Jukebox;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.White.Jukebox;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class TapeCreatorMenu : DefaultWindow
|
||||
{
|
||||
[Dependency] private readonly EntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IFileDialogManager _fileDialogManager = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
private readonly CheZaHuetaSystem _huetaSystem = default!;
|
||||
private readonly SharedPopupSystem _popupSystem = default!;
|
||||
|
||||
private bool _fileDialogOpened;
|
||||
private double _maxFileSize;
|
||||
private double _currentFileSize;
|
||||
private readonly List<byte> _songBytes = new();
|
||||
private TapeCreatorComponent _component;
|
||||
|
||||
private const double BytesToMegabytes = 0.000001d;
|
||||
|
||||
public TapeCreatorMenu(TapeCreatorComponent component)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
_huetaSystem = _entityManager.System<CheZaHuetaSystem>();
|
||||
_popupSystem = _entityManager.System<SharedPopupSystem>();
|
||||
|
||||
_cfg.OnValueChanged(WhiteCVars.MaxJukeboxSongSizeInMB, x => _maxFileSize = x, true);
|
||||
|
||||
_component = component;
|
||||
|
||||
LoadSongButton.OnPressed += TryLoadSong;
|
||||
UploadSong.OnPressed += OnUploadButtonPressed;
|
||||
}
|
||||
|
||||
private void OnUploadButtonPressed(BaseButton.ButtonEventArgs obj)
|
||||
{
|
||||
if(!CanUploadSong()) return;
|
||||
|
||||
var input = SongNameField.Text;
|
||||
|
||||
string pattern = @"[^a-zA-Zа-яА-Я ]+";
|
||||
string replacement = "";
|
||||
|
||||
var songName = Regex.Replace(input, pattern, replacement);
|
||||
songName = Regex.Replace(songName, @"\s+", " ");
|
||||
|
||||
var songBytes = _songBytes;
|
||||
|
||||
var msg = new JukeboxSongUploadRequest()
|
||||
{
|
||||
SongName = songName,
|
||||
SongBytes = songBytes,
|
||||
TapeCreatorUid = _component.Owner
|
||||
};
|
||||
|
||||
_huetaSystem.SendNetMessage(msg);
|
||||
|
||||
_currentFileSize = 0;
|
||||
_songBytes.Clear();
|
||||
SongNameField.Clear();
|
||||
|
||||
_popupSystem.PopupEntity("Внимание. Начинается запись мозговой активности.", _component.Owner);
|
||||
}
|
||||
|
||||
private async void TryLoadSong(BaseButton.ButtonEventArgs obj)
|
||||
{
|
||||
var fileFilter = new FileDialogFilters(new FileDialogFilters.Group("ogg"));
|
||||
|
||||
var file = await _fileDialogManager.OpenFile(fileFilter);
|
||||
|
||||
if (Disposed) return;
|
||||
|
||||
if(file == null) return;
|
||||
|
||||
_currentFileSize = file.Length * BytesToMegabytes;
|
||||
|
||||
if (_currentFileSize > _maxFileSize)
|
||||
{
|
||||
_popupSystem.PopupEntity($"Лимит активности мозговых волн превышен на {_currentFileSize - _maxFileSize} мегахрюков", _component.Owner);
|
||||
return;
|
||||
}
|
||||
|
||||
//TODO: Песня слишком длинная пиздец
|
||||
|
||||
_songBytes.AddRange(file.CopyToArray());
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
|
||||
CoinsLabel.Text = _component.CoinBalance.ToString();
|
||||
|
||||
if (CanUploadSong())
|
||||
{
|
||||
UploadSong.Disabled = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
UploadSong.Disabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private bool CanUploadSong()
|
||||
{
|
||||
return SongNameField.Text.Length > 0 && _songBytes.Count > 0 && _component.CoinBalance > 0 && _component.InsertedTape.HasValue && !_component.Recording;
|
||||
}
|
||||
}
|
||||
41
Content.Client/White/Jukebox/TapeCreatorSystem.cs
Normal file
41
Content.Client/White/Jukebox/TapeCreatorSystem.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using Content.Shared.White.Jukebox;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Client.White.Jukebox;
|
||||
|
||||
public sealed class TapeCreatorSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<TapeCreatorComponent, ComponentHandleState>(OnStateChanged);
|
||||
SubscribeLocalEvent<TapeComponent, ComponentHandleState>(OnTapeStateChanged);
|
||||
}
|
||||
|
||||
private void OnTapeStateChanged(EntityUid uid, TapeComponent component, ref ComponentHandleState args)
|
||||
{
|
||||
if (args.Current is not TapeComponentState state) return;
|
||||
|
||||
component.Songs = state.Songs;
|
||||
}
|
||||
|
||||
private void OnStateChanged(EntityUid uid, TapeCreatorComponent component, ref ComponentHandleState args)
|
||||
{
|
||||
if (args.Current is not TapeCreatorComponentState state) return;
|
||||
|
||||
component.Recording = state.Recording;
|
||||
component.CoinBalance = state.CoinBalance;
|
||||
component.InsertedTape = state.InsertedTape;
|
||||
|
||||
SetTapeLayerVisible(component, state.InsertedTape.HasValue);
|
||||
}
|
||||
|
||||
private void SetTapeLayerVisible(TapeCreatorComponent component, bool visible)
|
||||
{
|
||||
var spriteComponent = Comp<SpriteComponent>(component.Owner);
|
||||
spriteComponent.LayerMapTryGet("tape", out var layer);
|
||||
spriteComponent.LayerSetVisible(layer, visible);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user