[feat]jukebox (#9)

This commit is contained in:
rhailrake
2023-05-04 13:43:03 +06:00
committed by Aviu00
parent 1a729666f4
commit 68ffe42dbb
35 changed files with 1626 additions and 0 deletions

View File

@@ -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) =>

View File

@@ -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
}
}

View File

@@ -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" />

View File

@@ -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
}
}

View File

@@ -0,0 +1,10 @@
namespace Content.Client.White;
//Система со смешным названием, чье предназначение заключается лишь в одном - отправке нетворк ивентов.
public sealed class CheZaHuetaSystem : EntitySystem
{
public void SendNetMessage(EntityEventArgs message)
{
RaiseNetworkEvent(message);
}
}

View File

@@ -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);
}
}

View 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();
}
}

View 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>

View 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);
}
}

View 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>

View 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);
}
}

View 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();
}
}

View 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();
}
}

View 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>

View 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;
}
}

View 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);
}
}