[feat]jukebox (#9)
This commit is contained in:
@@ -22,6 +22,7 @@ using Content.Client.Stylesheets;
|
|||||||
using Content.Client.Viewport;
|
using Content.Client.Viewport;
|
||||||
using Content.Client.Voting;
|
using Content.Client.Voting;
|
||||||
using Content.Client.White.JoinQueue;
|
using Content.Client.White.JoinQueue;
|
||||||
|
using Content.Client.White.Jukebox;
|
||||||
using Content.Client.White.Sponsors;
|
using Content.Client.White.Sponsors;
|
||||||
using Content.Shared.Ame;
|
using Content.Shared.Ame;
|
||||||
using Content.Client.White.Stalin;
|
using Content.Client.White.Stalin;
|
||||||
@@ -78,6 +79,7 @@ namespace Content.Client.Entry
|
|||||||
[Dependency] private readonly SponsorsManager _sponsorsManager = default!;
|
[Dependency] private readonly SponsorsManager _sponsorsManager = default!;
|
||||||
[Dependency] private readonly JoinQueueManager _queueManager = default!;
|
[Dependency] private readonly JoinQueueManager _queueManager = default!;
|
||||||
[Dependency] private readonly StalinManager _stalinManager = default!;
|
[Dependency] private readonly StalinManager _stalinManager = default!;
|
||||||
|
[Dependency] private readonly ClientJukeboxSongsSyncManager _jukeboxSyncManager = default!;
|
||||||
//WD-EDIT
|
//WD-EDIT
|
||||||
|
|
||||||
public override void Init()
|
public override void Init()
|
||||||
@@ -183,6 +185,7 @@ namespace Content.Client.Entry
|
|||||||
//WD-EDIT
|
//WD-EDIT
|
||||||
_sponsorsManager.Initialize();
|
_sponsorsManager.Initialize();
|
||||||
_queueManager.Initialize();
|
_queueManager.Initialize();
|
||||||
|
_jukeboxSyncManager.Initialize();
|
||||||
//WD-EDIT
|
//WD-EDIT
|
||||||
|
|
||||||
_baseClient.RunLevelChanged += (_, args) =>
|
_baseClient.RunLevelChanged += (_, args) =>
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ using Content.Shared.Administration.Logs;
|
|||||||
using Content.Client.Guidebook;
|
using Content.Client.Guidebook;
|
||||||
using Content.Client.Replay;
|
using Content.Client.Replay;
|
||||||
using Content.Client.White.JoinQueue;
|
using Content.Client.White.JoinQueue;
|
||||||
|
using Content.Client.White.Jukebox;
|
||||||
using Content.Client.White.Sponsors;
|
using Content.Client.White.Sponsors;
|
||||||
using Content.Client.White.Stalin;
|
using Content.Client.White.Stalin;
|
||||||
using Content.Shared.Administration.Managers;
|
using Content.Shared.Administration.Managers;
|
||||||
@@ -53,6 +54,7 @@ namespace Content.Client.IoC
|
|||||||
IoCManager.Register<JoinQueueManager>();
|
IoCManager.Register<JoinQueueManager>();
|
||||||
IoCManager.Register<SponsorsManager>();
|
IoCManager.Register<SponsorsManager>();
|
||||||
IoCManager.Register<StalinManager>();
|
IoCManager.Register<StalinManager>();
|
||||||
|
IoCManager.Register<ClientJukeboxSongsSyncManager>();
|
||||||
//WD-EDIT
|
//WD-EDIT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,6 +74,19 @@
|
|||||||
<Label Name="TtsVolumeLabel" MinSize="48 0" Align="Right" />
|
<Label Name="TtsVolumeLabel" MinSize="48 0" Align="Right" />
|
||||||
<Control MinSize="4 0"/>
|
<Control MinSize="4 0"/>
|
||||||
</BoxContainer>
|
</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">
|
<BoxContainer Orientation="Horizontal" Margin="5 0 0 0">
|
||||||
<Label Text="{Loc 'ui-options-lobby-volume'}" HorizontalExpand="True" />
|
<Label Text="{Loc 'ui-options-lobby-volume'}" HorizontalExpand="True" />
|
||||||
<Control MinSize="8 0" />
|
<Control MinSize="8 0" />
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ namespace Content.Client.Options.UI.Tabs
|
|||||||
LobbyVolumeSlider.OnValueChanged += OnLobbyVolumeSliderChanged;
|
LobbyVolumeSlider.OnValueChanged += OnLobbyVolumeSliderChanged;
|
||||||
TtsVolumeSlider.OnValueChanged += OnTtsVolumeSliderChanged;
|
TtsVolumeSlider.OnValueChanged += OnTtsVolumeSliderChanged;
|
||||||
InterfaceVolumeSlider.OnValueChanged += OnInterfaceVolumeSliderChanged;
|
InterfaceVolumeSlider.OnValueChanged += OnInterfaceVolumeSliderChanged;
|
||||||
|
JukeboxVolumeSlider.OnValueChanged += OnJukeboxVolumeSliderChanged;
|
||||||
LobbyMusicCheckBox.OnToggled += OnLobbyMusicCheckToggled;
|
LobbyMusicCheckBox.OnToggled += OnLobbyMusicCheckToggled;
|
||||||
RestartSoundsCheckBox.OnToggled += OnRestartSoundsCheckToggled;
|
RestartSoundsCheckBox.OnToggled += OnRestartSoundsCheckToggled;
|
||||||
EventMusicCheckBox.OnToggled += OnEventMusicCheckToggled;
|
EventMusicCheckBox.OnToggled += OnEventMusicCheckToggled;
|
||||||
@@ -63,6 +64,7 @@ namespace Content.Client.Options.UI.Tabs
|
|||||||
|
|
||||||
//WD-EDIT
|
//WD-EDIT
|
||||||
TtsVolumeSlider.OnValueChanged -= OnTtsVolumeSliderChanged;
|
TtsVolumeSlider.OnValueChanged -= OnTtsVolumeSliderChanged;
|
||||||
|
JukeboxVolumeSlider.OnValueChanged -= OnJukeboxVolumeSliderChanged;
|
||||||
//WD-EDIT
|
//WD-EDIT
|
||||||
|
|
||||||
base.Dispose(disposing);
|
base.Dispose(disposing);
|
||||||
@@ -76,6 +78,13 @@ namespace Content.Client.Options.UI.Tabs
|
|||||||
}
|
}
|
||||||
//TTS-End
|
//TTS-End
|
||||||
|
|
||||||
|
//JUKEBOX
|
||||||
|
private void OnJukeboxVolumeSliderChanged(Range obj)
|
||||||
|
{
|
||||||
|
UpdateChanges();
|
||||||
|
}
|
||||||
|
//JUKEBOX
|
||||||
|
|
||||||
private void OnLobbyVolumeSliderChanged(Range obj)
|
private void OnLobbyVolumeSliderChanged(Range obj)
|
||||||
{
|
{
|
||||||
UpdateChanges();
|
UpdateChanges();
|
||||||
@@ -150,6 +159,7 @@ namespace Content.Client.Options.UI.Tabs
|
|||||||
|
|
||||||
//WD-EDIT
|
//WD-EDIT
|
||||||
_cfg.SetCVar(WhiteCVars.TtsVolume, LV100ToDB(TtsVolumeSlider.Value));
|
_cfg.SetCVar(WhiteCVars.TtsVolume, LV100ToDB(TtsVolumeSlider.Value));
|
||||||
|
_cfg.SetCVar(WhiteCVars.JukeboxVolume, LV100ToDB(JukeboxVolumeSlider.Value));
|
||||||
//WD-EDIT
|
//WD-EDIT
|
||||||
|
|
||||||
_cfg.SaveToFile();
|
_cfg.SaveToFile();
|
||||||
@@ -179,6 +189,7 @@ namespace Content.Client.Options.UI.Tabs
|
|||||||
|
|
||||||
//WD-EDIT
|
//WD-EDIT
|
||||||
TtsVolumeSlider.Value = DBToLV100(_cfg.GetCVar(WhiteCVars.TtsVolume));
|
TtsVolumeSlider.Value = DBToLV100(_cfg.GetCVar(WhiteCVars.TtsVolume));
|
||||||
|
JukeboxVolumeSlider.Value = DBToLV100(_cfg.GetCVar(WhiteCVars.JukeboxVolume));
|
||||||
//WD-EDIT
|
//WD-EDIT
|
||||||
|
|
||||||
|
|
||||||
@@ -222,6 +233,8 @@ namespace Content.Client.Options.UI.Tabs
|
|||||||
//WD-EDIT
|
//WD-EDIT
|
||||||
var isTtsVolumeSame =
|
var isTtsVolumeSame =
|
||||||
Math.Abs(TtsVolumeSlider.Value - DBToLV100(_cfg.GetCVar(WhiteCVars.TtsVolume))) < 0.01f;
|
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
|
//WD-EDIT
|
||||||
|
|
||||||
var isRestartSoundsSame = RestartSoundsCheckBox.Pressed == _cfg.GetCVar(CCVars.RestartSoundsEnabled);
|
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
|
var isEverythingSame = isMasterVolumeSame && isMidiVolumeSame && isAmbientVolumeSame && isAmbientMusicVolumeSame && isAmbientSoundsSame && isLobbySame && isRestartSoundsSame && isEventSame
|
||||||
&& isAdminSoundsSame && isLobbyVolumeSame && isInterfaceVolumeSame;
|
&& isAdminSoundsSame && isLobbyVolumeSame && isInterfaceVolumeSame;
|
||||||
isEverythingSame = isEverythingSame && isTtsVolumeSame; //WD-EDIT
|
isEverythingSame = isEverythingSame && isTtsVolumeSame; //WD-EDIT
|
||||||
|
isEverythingSame = isEverythingSame && isTtsVolumeSame && isJukeboxVolumeSame; //WD-EDIT
|
||||||
ApplyButton.Disabled = isEverythingSame;
|
ApplyButton.Disabled = isEverythingSame;
|
||||||
ResetButton.Disabled = isEverythingSame;
|
ResetButton.Disabled = isEverythingSame;
|
||||||
MasterVolumeLabel.Text =
|
MasterVolumeLabel.Text =
|
||||||
@@ -249,6 +263,8 @@ namespace Content.Client.Options.UI.Tabs
|
|||||||
//WD-EDIT
|
//WD-EDIT
|
||||||
TtsVolumeLabel.Text =
|
TtsVolumeLabel.Text =
|
||||||
Loc.GetString("ui-options-volume-percent", ("volume", TtsVolumeSlider.Value / 100));
|
Loc.GetString("ui-options-volume-percent", ("volume", TtsVolumeSlider.Value / 100));
|
||||||
|
JukeboxVolumeLabel.Text =
|
||||||
|
Loc.GetString("ui-options-volume-percent", ("volume", JukeboxVolumeSlider.Value / 100));
|
||||||
//WD-EDIT
|
//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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -31,6 +31,7 @@ using Robust.Shared.Timing;
|
|||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
using Content.Server.UtkaIntegration;
|
using Content.Server.UtkaIntegration;
|
||||||
using Content.Server.White.JoinQueue;
|
using Content.Server.White.JoinQueue;
|
||||||
|
using Content.Server.White.Jukebox;
|
||||||
using Content.Server.White.Sponsors;
|
using Content.Server.White.Sponsors;
|
||||||
using Content.Server.White.Stalin;
|
using Content.Server.White.Stalin;
|
||||||
using Content.Server.White.TTS;
|
using Content.Server.White.TTS;
|
||||||
@@ -113,6 +114,7 @@ namespace Content.Server.Entry
|
|||||||
IoCManager.Resolve<JoinQueueManager>().Initialize();
|
IoCManager.Resolve<JoinQueueManager>().Initialize();
|
||||||
IoCManager.Resolve<TTSManager>().Initialize();
|
IoCManager.Resolve<TTSManager>().Initialize();
|
||||||
IoCManager.Resolve<StalinManager>().Initialize();
|
IoCManager.Resolve<StalinManager>().Initialize();
|
||||||
|
IoCManager.Resolve<ServerJukeboxSongsSyncManager>().Initialize();
|
||||||
//WD-EDIT
|
//WD-EDIT
|
||||||
|
|
||||||
_voteManager.Initialize();
|
_voteManager.Initialize();
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ using Content.Server.Voting.Managers;
|
|||||||
using Content.Server.Worldgen.Tools;
|
using Content.Server.Worldgen.Tools;
|
||||||
using Content.Server.UtkaIntegration;
|
using Content.Server.UtkaIntegration;
|
||||||
using Content.Server.White.JoinQueue;
|
using Content.Server.White.JoinQueue;
|
||||||
|
using Content.Server.White.Jukebox;
|
||||||
using Content.Server.White.Sponsors;
|
using Content.Server.White.Sponsors;
|
||||||
using Content.Server.White.Stalin;
|
using Content.Server.White.Stalin;
|
||||||
using Content.Server.White.TTS;
|
using Content.Server.White.TTS;
|
||||||
@@ -71,6 +72,7 @@ namespace Content.Server.IoC
|
|||||||
IoCManager.Register<UtkaTCPWrapper>();
|
IoCManager.Register<UtkaTCPWrapper>();
|
||||||
IoCManager.Register<TTSManager>();
|
IoCManager.Register<TTSManager>();
|
||||||
IoCManager.Register<StalinManager>();
|
IoCManager.Register<StalinManager>();
|
||||||
|
IoCManager.Register<ServerJukeboxSongsSyncManager>();
|
||||||
// WD-EDIT
|
// WD-EDIT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
207
Content.Server/White/Jukebox/JukeboxSystem.cs
Normal file
207
Content.Server/White/Jukebox/JukeboxSystem.cs
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using Content.Shared.GameTicking;
|
||||||
|
using Content.Shared.Hands.EntitySystems;
|
||||||
|
using Content.Shared.Interaction;
|
||||||
|
using Content.Shared.Verbs;
|
||||||
|
using Content.Shared.White.Jukebox;
|
||||||
|
using Robust.Server.GameObjects;
|
||||||
|
using Robust.Server.GameStates;
|
||||||
|
using Robust.Shared.Containers;
|
||||||
|
using Robust.Shared.ContentPack;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
|
namespace Content.Server.White.Jukebox;
|
||||||
|
|
||||||
|
public sealed class JukeboxSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly AudioSystem _audioSystem = default!;
|
||||||
|
[Dependency] private readonly IResourceManager _resourceManager = default!;
|
||||||
|
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
|
||||||
|
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
|
||||||
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||||
|
[Dependency] private readonly PVSOverrideSystem _pvsOverrideSystem = default!;
|
||||||
|
|
||||||
|
|
||||||
|
private readonly List<JukeboxComponent> _playingJukeboxes = new();
|
||||||
|
|
||||||
|
private float _updateTimerDefaultTime = 1f;
|
||||||
|
private float _updateTimer;
|
||||||
|
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
SubscribeNetworkEvent<JukeboxRequestSongPlay>(OnSongRequestPlay);
|
||||||
|
SubscribeLocalEvent<JukeboxComponent, InteractUsingEvent>(OnInteract);
|
||||||
|
SubscribeLocalEvent<JukeboxComponent, ComponentGetState>(OnGetState);
|
||||||
|
SubscribeLocalEvent<JukeboxComponent, JukeboxStopRequest>(OnRequestStop);
|
||||||
|
SubscribeLocalEvent<JukeboxComponent, JukeboxRepeatToggled>(OnRepeatToggled);
|
||||||
|
SubscribeLocalEvent<JukeboxComponent, JukeboxEjectRequest>(OnEjectRequest);
|
||||||
|
SubscribeLocalEvent<JukeboxComponent, GetVerbsEvent<Verb>>(OnGetVerb);
|
||||||
|
SubscribeLocalEvent<JukeboxComponent, ComponentInit>(OnInit);
|
||||||
|
SubscribeLocalEvent<RoundRestartCleanupEvent>(OnRoundRestart);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnInit(EntityUid uid, JukeboxComponent component, ComponentInit args)
|
||||||
|
{
|
||||||
|
_pvsOverrideSystem.AddGlobalOverride(uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRoundRestart(RoundRestartCleanupEvent ev)
|
||||||
|
{
|
||||||
|
_playingJukeboxes.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnEjectRequest(EntityUid uid, JukeboxComponent component, JukeboxEjectRequest args)
|
||||||
|
{
|
||||||
|
if(component.PlayingSongData != null) return;
|
||||||
|
|
||||||
|
var containedEntities = component.TapeContainer.ContainedEntities;
|
||||||
|
|
||||||
|
if (containedEntities.Count > 0)
|
||||||
|
{
|
||||||
|
_containerSystem.EmptyContainer(component.TapeContainer, true).ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGetVerb(EntityUid uid, JukeboxComponent jukeboxComponent, GetVerbsEvent<Verb> ev)
|
||||||
|
{
|
||||||
|
if (ev.Hands == null) return;
|
||||||
|
if (jukeboxComponent.PlayingSongData != null) return;
|
||||||
|
if (jukeboxComponent.TapeContainer.ContainedEntities.Count == 0) return;
|
||||||
|
|
||||||
|
var removeTapeVerb = new Verb()
|
||||||
|
{
|
||||||
|
Text = "Вытащить касету",
|
||||||
|
Priority = 10000,
|
||||||
|
Icon = new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/VerbIcons/remove_tape.png")),
|
||||||
|
Act = () =>
|
||||||
|
{
|
||||||
|
var tapes = jukeboxComponent.TapeContainer.ContainedEntities.ToList();
|
||||||
|
_containerSystem.EmptyContainer(jukeboxComponent.TapeContainer, true);
|
||||||
|
|
||||||
|
foreach (var tape in tapes)
|
||||||
|
{
|
||||||
|
_handsSystem.PickupOrDrop(ev.User, tape);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ev.Verbs.Add(removeTapeVerb);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRepeatToggled(EntityUid uid, JukeboxComponent component, JukeboxRepeatToggled args)
|
||||||
|
{
|
||||||
|
component.Repeating = args.NewState;
|
||||||
|
Dirty(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRequestStop(EntityUid uid, JukeboxComponent component, JukeboxStopRequest args)
|
||||||
|
{
|
||||||
|
component.PlayingSongData = null;
|
||||||
|
Dirty(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void OnInteract(EntityUid uid, JukeboxComponent component, InteractUsingEvent args)
|
||||||
|
{
|
||||||
|
if(component.PlayingSongData != null) return;
|
||||||
|
|
||||||
|
if (TryComp<TapeComponent>(args.Used, out var tape))
|
||||||
|
{
|
||||||
|
var containedEntities = component.TapeContainer.ContainedEntities;
|
||||||
|
|
||||||
|
if (containedEntities.Count >= 1)
|
||||||
|
{
|
||||||
|
var removedTapes = _containerSystem.EmptyContainer(component.TapeContainer, true).ToList();
|
||||||
|
component.TapeContainer.Insert(args.Used);
|
||||||
|
|
||||||
|
foreach (var tapeUid in removedTapes)
|
||||||
|
{
|
||||||
|
_handsSystem.PickupOrDrop(args.User, tapeUid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
component.TapeContainer.Insert(args.Used);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSongRequestPlay(JukeboxRequestSongPlay msg, EntitySessionEventArgs args)
|
||||||
|
{
|
||||||
|
var jukebox = Comp<JukeboxComponent>(msg.Jukebox!.Value);
|
||||||
|
jukebox.Repeating = true;
|
||||||
|
|
||||||
|
var songData = new PlayingSongData()
|
||||||
|
{
|
||||||
|
SongName = msg.SongName,
|
||||||
|
SongPath = msg.SongPath,
|
||||||
|
ActualSongLengthSeconds = msg.SongDuration,
|
||||||
|
PlaybackPosition = 0f
|
||||||
|
};
|
||||||
|
jukebox.PlayingSongData = songData;
|
||||||
|
|
||||||
|
_playingJukeboxes.Add(jukebox);
|
||||||
|
|
||||||
|
Dirty(jukebox);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Update(float frameTime)
|
||||||
|
{
|
||||||
|
base.Update(frameTime);
|
||||||
|
|
||||||
|
if (_updateTimer <= _updateTimerDefaultTime)
|
||||||
|
{
|
||||||
|
_updateTimer += frameTime;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProcessPlayingJukeboxes(frameTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessPlayingJukeboxes(float frameTime)
|
||||||
|
{
|
||||||
|
for (int i = _playingJukeboxes.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
var playingJukeboxData = _playingJukeboxes[i];
|
||||||
|
|
||||||
|
if (playingJukeboxData.PlayingSongData == null)
|
||||||
|
{
|
||||||
|
_playingJukeboxes.RemoveAt(i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
playingJukeboxData.PlayingSongData.PlaybackPosition += _updateTimer;
|
||||||
|
|
||||||
|
if (playingJukeboxData.PlayingSongData.PlaybackPosition >= playingJukeboxData.PlayingSongData.ActualSongLengthSeconds)
|
||||||
|
{
|
||||||
|
if (playingJukeboxData.Repeating)
|
||||||
|
{
|
||||||
|
playingJukeboxData.PlayingSongData.PlaybackPosition = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
RaiseNetworkEvent(new JukeboxStopPlaying());
|
||||||
|
_playingJukeboxes.RemoveAt(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Dirty(playingJukeboxData);
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateTimer = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGetState(EntityUid uid, JukeboxComponent component, ref ComponentGetState args)
|
||||||
|
{
|
||||||
|
args.State = new JukeboxComponentState()
|
||||||
|
{
|
||||||
|
SongData = component.PlayingSongData,
|
||||||
|
Playing = component.Repeating,
|
||||||
|
Volume = component.Volume
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
using Content.Shared.White.Jukebox;
|
||||||
|
using Robust.Shared.Network;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
|
namespace Content.Server.White.Jukebox;
|
||||||
|
|
||||||
|
public sealed class ServerJukeboxSongsSyncManager : JukeboxSongsSyncManager
|
||||||
|
{
|
||||||
|
[Dependency] private readonly INetManager _netManager = default!;
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
_netManager.Connected += OnClientConnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnClientConnected(object? sender, NetChannelArgs e)
|
||||||
|
{
|
||||||
|
foreach (var (path, data) in ContentRoot.GetAllFiles())
|
||||||
|
{
|
||||||
|
var msg = new JukeboxSongUploadNetMessage
|
||||||
|
{
|
||||||
|
RelativePath = path,
|
||||||
|
Data = data
|
||||||
|
};
|
||||||
|
|
||||||
|
e.Channel.SendMessage(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public (string songName, ResPath path) SyncSongData(string songName, List<byte> bytes)
|
||||||
|
{
|
||||||
|
if (ContentRoot.TryGetFile(new ResPath(songName + ".ogg"), out _))
|
||||||
|
{
|
||||||
|
songName += "a";
|
||||||
|
}
|
||||||
|
|
||||||
|
var msg = new JukeboxSongUploadNetMessage()
|
||||||
|
{
|
||||||
|
Data = bytes.ToArray(),
|
||||||
|
RelativePath = new ResPath(songName + ".ogg")
|
||||||
|
};
|
||||||
|
|
||||||
|
OnSongUploaded(msg);
|
||||||
|
var path = new ResPath($"{Prefix}/{songName}.ogg");
|
||||||
|
return (songName, path);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public override void OnSongUploaded(JukeboxSongUploadNetMessage message)
|
||||||
|
{
|
||||||
|
|
||||||
|
ContentRoot.AddOrUpdateFile(message.RelativePath, message.Data);
|
||||||
|
|
||||||
|
foreach (var channel in _netManager.Channels)
|
||||||
|
{
|
||||||
|
channel.SendMessage(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
181
Content.Server/White/Jukebox/TapeCreatorSystem.cs
Normal file
181
Content.Server/White/Jukebox/TapeCreatorSystem.cs
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Content.Shared.CCVar;
|
||||||
|
using Content.Shared.Hands.EntitySystems;
|
||||||
|
using Content.Shared.Interaction;
|
||||||
|
using Content.Shared.Popups;
|
||||||
|
using Content.Shared.Tag;
|
||||||
|
using Content.Shared.Verbs;
|
||||||
|
using Content.Shared.White.Jukebox;
|
||||||
|
using Robust.Shared.Configuration;
|
||||||
|
using Robust.Shared.Containers;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
|
namespace Content.Server.White.Jukebox;
|
||||||
|
|
||||||
|
public sealed class TapeCreatorSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
||||||
|
[Dependency] private readonly ServerJukeboxSongsSyncManager _songsSyncManager = default!;
|
||||||
|
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
|
||||||
|
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
|
||||||
|
[Dependency] private readonly TagSystem _tagSystem = default!;
|
||||||
|
|
||||||
|
|
||||||
|
private readonly int _recordTime = 25;
|
||||||
|
|
||||||
|
private static string TapeCreatorContainerName = "tape_creator_container";
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
SubscribeNetworkEvent<JukeboxSongUploadRequest>(OnSongUploaded);
|
||||||
|
SubscribeLocalEvent<TapeCreatorComponent, ComponentInit>(OnComponentInit);
|
||||||
|
SubscribeLocalEvent<TapeCreatorComponent, InteractUsingEvent>(OnInteract);
|
||||||
|
SubscribeLocalEvent<TapeCreatorComponent, GetVerbsEvent<Verb>>(OnTapeCreatorGetVerb);
|
||||||
|
SubscribeLocalEvent<TapeCreatorComponent, ComponentGetState>(OnTapeCreatorStateChanged);
|
||||||
|
SubscribeLocalEvent<TapeComponent, ComponentGetState>(OnTapeStateChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnTapeCreatorGetVerb(EntityUid uid, TapeCreatorComponent component, GetVerbsEvent<Verb> ev)
|
||||||
|
{
|
||||||
|
if (component.Recording) return;
|
||||||
|
if (ev.Hands == null) return;
|
||||||
|
if (component.TapeContainer.ContainedEntities.Count == 0) return;
|
||||||
|
|
||||||
|
var removeTapeVerb = new Verb()
|
||||||
|
{
|
||||||
|
Text = "Вытащить касету",
|
||||||
|
Priority = 10000,
|
||||||
|
Icon = new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/VerbIcons/remove_tape.png")),
|
||||||
|
Act = () =>
|
||||||
|
{
|
||||||
|
var tapes = component.TapeContainer.ContainedEntities.ToList();
|
||||||
|
_containerSystem.EmptyContainer(component.TapeContainer, true);
|
||||||
|
|
||||||
|
foreach (var tape in tapes)
|
||||||
|
{
|
||||||
|
_handsSystem.PickupOrDrop(ev.User, tape);
|
||||||
|
}
|
||||||
|
|
||||||
|
component.InsertedTape = null;
|
||||||
|
Dirty(component);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ev.Verbs.Add(removeTapeVerb);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnTapeStateChanged(EntityUid uid, TapeComponent component, ref ComponentGetState args)
|
||||||
|
{
|
||||||
|
args.State = new TapeComponentState()
|
||||||
|
{
|
||||||
|
Songs = component.Songs
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnTapeCreatorStateChanged(EntityUid uid, TapeCreatorComponent component, ref ComponentGetState args)
|
||||||
|
{
|
||||||
|
args.State = new TapeCreatorComponentState
|
||||||
|
{
|
||||||
|
Recording = component.Recording,
|
||||||
|
CoinBalance = component.CoinBalance,
|
||||||
|
InsertedTape = component.InsertedTape
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnComponentInit(EntityUid uid, TapeCreatorComponent component, ComponentInit args)
|
||||||
|
{
|
||||||
|
component.TapeContainer = _containerSystem.EnsureContainer<Container>(uid, TapeCreatorContainerName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnInteract(EntityUid uid, TapeCreatorComponent component, InteractUsingEvent args)
|
||||||
|
{
|
||||||
|
if(component.Recording) return;
|
||||||
|
|
||||||
|
if (TryComp<TapeComponent>(args.Used, out var tape))
|
||||||
|
{
|
||||||
|
var containedEntities = component.TapeContainer.ContainedEntities;
|
||||||
|
|
||||||
|
if (containedEntities.Count > 1)
|
||||||
|
{
|
||||||
|
var removedTapes = _containerSystem.EmptyContainer(component.TapeContainer, true).ToList();
|
||||||
|
component.TapeContainer.Insert(args.Used);
|
||||||
|
|
||||||
|
foreach (var tapes in removedTapes)
|
||||||
|
{
|
||||||
|
_handsSystem.PickupOrDrop(args.User, tapes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
component.TapeContainer.Insert(args.Used);
|
||||||
|
}
|
||||||
|
|
||||||
|
component.InsertedTape = tape.Owner;
|
||||||
|
Dirty(component);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_tagSystem.HasTag(args.Used, "TapeRecorderCoin"))
|
||||||
|
{
|
||||||
|
Del(args.Used);
|
||||||
|
component.CoinBalance += 1;
|
||||||
|
Dirty(component);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSongUploaded(JukeboxSongUploadRequest ev)
|
||||||
|
{
|
||||||
|
if(!TryComp<TapeCreatorComponent>(ev.TapeCreatorUid, out var tapeCreatorComponent)) return;
|
||||||
|
|
||||||
|
if (!tapeCreatorComponent.InsertedTape.HasValue || tapeCreatorComponent.CoinBalance <= 0)
|
||||||
|
{
|
||||||
|
_popupSystem.PopupEntity("Т# %ак@ э*^о сdf{ал б2я~b? Запись была прервана.", tapeCreatorComponent.Owner);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tapeCreatorComponent.CoinBalance -= 1;
|
||||||
|
tapeCreatorComponent.Recording = true;
|
||||||
|
|
||||||
|
var tapeComponent = Comp<TapeComponent>(tapeCreatorComponent.InsertedTape.Value);
|
||||||
|
var songData = _songsSyncManager.SyncSongData(ev.SongName, ev.SongBytes);
|
||||||
|
|
||||||
|
var song = new JukeboxSong()
|
||||||
|
{
|
||||||
|
SongName = songData.songName,
|
||||||
|
SongPath = songData.path
|
||||||
|
};
|
||||||
|
|
||||||
|
tapeComponent.Songs.Add(song);
|
||||||
|
_popupSystem.PopupEntity($"Запись началась, примерное время ожидания: {_recordTime} секунд", tapeCreatorComponent.Owner);
|
||||||
|
Dirty(ev.TapeCreatorUid);
|
||||||
|
Dirty(tapeComponent);
|
||||||
|
StartRecordDelayAsync(tapeCreatorComponent, _popupSystem, _containerSystem);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void StartRecordDelayAsync(TapeCreatorComponent component, SharedPopupSystem popupSystem, SharedContainerSystem containerSystem)
|
||||||
|
{
|
||||||
|
var recordTimeDelay = _recordTime * 1000 / 10;
|
||||||
|
|
||||||
|
await Task.Delay(1000);
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
popupSystem.PopupEntity($"Запись мозговой активности выполнена на {i * 10}%", component.Owner);
|
||||||
|
await Task.Delay(recordTimeDelay);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
containerSystem.EmptyContainer(component.TapeContainer, force: true).ToList();
|
||||||
|
|
||||||
|
component.Recording = false;
|
||||||
|
component.InsertedTape = null;
|
||||||
|
|
||||||
|
popupSystem.PopupEntity($"Запись мозговой активности завершена", component.Owner);
|
||||||
|
Dirty(component);
|
||||||
|
}
|
||||||
|
}
|
||||||
20
Content.Shared/White/Jukebox/JukeboxBUIMessagess.cs
Normal file
20
Content.Shared/White/Jukebox/JukeboxBUIMessagess.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.White.Jukebox;
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public class JukeboxStopRequest : BoundUserInterfaceMessage { }
|
||||||
|
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public class JukeboxRepeatToggled : BoundUserInterfaceMessage
|
||||||
|
{
|
||||||
|
public bool NewState { get; }
|
||||||
|
public JukeboxRepeatToggled(bool newState)
|
||||||
|
{
|
||||||
|
NewState = newState;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public class JukeboxEjectRequest : BoundUserInterfaceMessage { }
|
||||||
133
Content.Shared/White/Jukebox/JukeboxComponentsAndStuff.cs
Normal file
133
Content.Shared/White/Jukebox/JukeboxComponentsAndStuff.cs
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using Lidgren.Network;
|
||||||
|
using Robust.Shared.Containers;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Network;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
|
namespace Content.Shared.White.Jukebox;
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public enum JukeboxUIKey : byte
|
||||||
|
{
|
||||||
|
Key
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public enum TapeCreatorUIKey : byte
|
||||||
|
{
|
||||||
|
Key
|
||||||
|
}
|
||||||
|
|
||||||
|
[NetworkedComponent, RegisterComponent]
|
||||||
|
public class JukeboxComponent : Component
|
||||||
|
{
|
||||||
|
|
||||||
|
public static string JukeboxContainerName = "jukebox_tapes";
|
||||||
|
public static string JukeboxDefaultSongsName = "jukebox_default_tapes";
|
||||||
|
|
||||||
|
[ViewVariables(VVAccess.ReadOnly)]
|
||||||
|
public Container TapeContainer = default!;
|
||||||
|
|
||||||
|
[DataField("defaultTapes")]
|
||||||
|
public List<string> DefaultTapes = new();
|
||||||
|
[ViewVariables(VVAccess.ReadOnly)]
|
||||||
|
public Container DefaultSongsContainer = default!;
|
||||||
|
|
||||||
|
|
||||||
|
[ViewVariables(VVAccess.ReadOnly)]
|
||||||
|
public bool Repeating { get; set; } = true;
|
||||||
|
|
||||||
|
[ViewVariables(VVAccess.ReadOnly)]
|
||||||
|
public float Volume { get; set; }
|
||||||
|
|
||||||
|
public PlayingSongData? PlayingSongData { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TapeContainerComponent : Component
|
||||||
|
{
|
||||||
|
public int MaxTapeCount = 1;
|
||||||
|
public Container TapeContainer { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public class PlayingSongData
|
||||||
|
{
|
||||||
|
public ResPath? SongPath;
|
||||||
|
public string? SongName;
|
||||||
|
public float PlaybackPosition;
|
||||||
|
public float ActualSongLengthSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public class JukeboxComponentState : ComponentState
|
||||||
|
{
|
||||||
|
public bool Playing { get; set; }
|
||||||
|
|
||||||
|
public PlayingSongData? SongData { get; set; }
|
||||||
|
public float Volume { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable, DataDefinition]
|
||||||
|
public class JukeboxSong
|
||||||
|
{
|
||||||
|
[DataField("songName")]
|
||||||
|
public string? SongName;
|
||||||
|
[DataField("path")]
|
||||||
|
public ResPath? SongPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public class JukeboxRequestSongPlay : EntityEventArgs
|
||||||
|
{
|
||||||
|
public string? SongName { get; set; }
|
||||||
|
public ResPath? SongPath { get; set; }
|
||||||
|
public EntityUid? Jukebox { get; set; }
|
||||||
|
public float SongDuration { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public class JukeboxRequestStop : EntityEventArgs
|
||||||
|
{
|
||||||
|
public EntityUid? JukeboxUid { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public class JukeboxStopPlaying : EntityEventArgs
|
||||||
|
{
|
||||||
|
public EntityUid? JukeboxUid { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public class JukeboxSongUploadRequest : EntityEventArgs
|
||||||
|
{
|
||||||
|
public string SongName = string.Empty;
|
||||||
|
public List<byte> SongBytes = new();
|
||||||
|
public EntityUid TapeCreatorUid = default!;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class JukeboxSongUploadNetMessage : NetMessage
|
||||||
|
{
|
||||||
|
public override NetDeliveryMethod DeliveryMethod => NetDeliveryMethod.ReliableUnordered;
|
||||||
|
public override MsgGroups MsgGroup => MsgGroups.Command;
|
||||||
|
|
||||||
|
public ResPath RelativePath { get; set; } = ResPath.Self;
|
||||||
|
public byte[] Data { get; set; } = Array.Empty<byte>();
|
||||||
|
|
||||||
|
public override void ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer serializer)
|
||||||
|
{
|
||||||
|
var dataLength = buffer.ReadVariableInt32();
|
||||||
|
Data = buffer.ReadBytes(dataLength);
|
||||||
|
RelativePath = new ResPath(buffer.ReadString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer serializer)
|
||||||
|
{
|
||||||
|
buffer.WriteVariableInt32(Data.Length);
|
||||||
|
buffer.Write(Data);
|
||||||
|
buffer.Write(RelativePath.ToString());
|
||||||
|
buffer.Write(ResPath.Separator);
|
||||||
|
}
|
||||||
|
}
|
||||||
37
Content.Shared/White/Jukebox/JukeboxSharedSystem.cs
Normal file
37
Content.Shared/White/Jukebox/JukeboxSharedSystem.cs
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
using Robust.Shared.Containers;
|
||||||
|
using Robust.Shared.Network;
|
||||||
|
|
||||||
|
namespace Content.Shared.White.Jukebox;
|
||||||
|
|
||||||
|
public class JukeboxSharedSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
|
||||||
|
[Dependency] private readonly INetManager _netManager = default!;
|
||||||
|
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
SubscribeLocalEvent<JukeboxComponent, ComponentStartup>(OnJukeboxInit);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnJukeboxInit(EntityUid uid, JukeboxComponent component, ComponentStartup args)
|
||||||
|
{
|
||||||
|
component.TapeContainer = _containerSystem.EnsureContainer<Container>(uid, JukeboxComponent.JukeboxContainerName);
|
||||||
|
component.DefaultSongsContainer = _containerSystem.EnsureContainer<Container>(uid, JukeboxComponent.JukeboxDefaultSongsName);
|
||||||
|
|
||||||
|
if (_netManager.IsServer)
|
||||||
|
{
|
||||||
|
var transform = Transform(component.Owner);
|
||||||
|
|
||||||
|
foreach (var tapePrototype in component.DefaultTapes)
|
||||||
|
{
|
||||||
|
var tapeUid = EntityManager.SpawnEntity(tapePrototype, transform.MapPosition);
|
||||||
|
|
||||||
|
if(!TryComp<TapeComponent>(tapeUid, out _)) continue;
|
||||||
|
|
||||||
|
component.DefaultSongsContainer.Insert(tapeUid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
29
Content.Shared/White/Jukebox/JukeboxSongsSyncManager.cs
Normal file
29
Content.Shared/White/Jukebox/JukeboxSongsSyncManager.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
using Robust.Shared.ContentPack;
|
||||||
|
using Robust.Shared.Network;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
|
namespace Content.Shared.White.Jukebox;
|
||||||
|
|
||||||
|
public abstract class JukeboxSongsSyncManager : IDisposable
|
||||||
|
{
|
||||||
|
[Dependency] private readonly INetManager _netManager = default!;
|
||||||
|
[Dependency] protected readonly IResourceManager ResourceManager = default!;
|
||||||
|
|
||||||
|
public static readonly ResPath Prefix = ResPath.Root / "Jukebox";
|
||||||
|
|
||||||
|
protected readonly MemoryContentRoot ContentRoot = new();
|
||||||
|
|
||||||
|
public virtual void Initialize()
|
||||||
|
{
|
||||||
|
ResourceManager.AddRoot(Prefix, ContentRoot);
|
||||||
|
|
||||||
|
_netManager.RegisterNetMessage<JukeboxSongUploadNetMessage>(OnSongUploaded);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void OnSongUploaded(JukeboxSongUploadNetMessage message);
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
ContentRoot.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
17
Content.Shared/White/Jukebox/TapeComponent.cs
Normal file
17
Content.Shared/White/Jukebox/TapeComponent.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.White.Jukebox;
|
||||||
|
|
||||||
|
[RegisterComponent, NetworkedComponent]
|
||||||
|
public sealed class TapeComponent : Component
|
||||||
|
{
|
||||||
|
[DataField("songs")]
|
||||||
|
public List<JukeboxSong> Songs { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class TapeComponentState : ComponentState
|
||||||
|
{
|
||||||
|
public List<JukeboxSong> Songs { get; set; } = new();
|
||||||
|
}
|
||||||
29
Content.Shared/White/Jukebox/TapeCreatorComponent.cs
Normal file
29
Content.Shared/White/Jukebox/TapeCreatorComponent.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
using Robust.Shared.Containers;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.White.Jukebox;
|
||||||
|
|
||||||
|
[RegisterComponent, NetworkedComponent]
|
||||||
|
public sealed class TapeCreatorComponent : Component
|
||||||
|
{
|
||||||
|
[DataField("coins")]
|
||||||
|
public int CoinBalance { get; set; }
|
||||||
|
|
||||||
|
[ViewVariables(VVAccess.ReadOnly)]
|
||||||
|
public bool Recording { get; set; }
|
||||||
|
|
||||||
|
[ViewVariables(VVAccess.ReadOnly)]
|
||||||
|
public EntityUid? InsertedTape { get; set; }
|
||||||
|
|
||||||
|
[ViewVariables(VVAccess.ReadOnly)]
|
||||||
|
public Container TapeContainer { get; set; } = default!;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class TapeCreatorComponentState : ComponentState
|
||||||
|
{
|
||||||
|
public int CoinBalance { get; set; }
|
||||||
|
public bool Recording { get; set; }
|
||||||
|
public EntityUid? InsertedTape { get; set; }
|
||||||
|
}
|
||||||
@@ -158,6 +158,19 @@ public sealed class WhiteCVars
|
|||||||
public static readonly CVarDef<float> BwoinkVolume =
|
public static readonly CVarDef<float> BwoinkVolume =
|
||||||
CVarDef.Create("white.admin.bwoinkVolume", 0f, CVar.CLIENTONLY | CVar.ARCHIVE);
|
CVarDef.Create("white.admin.bwoinkVolume", 0f, CVar.CLIENTONLY | CVar.ARCHIVE);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Jukebox
|
||||||
|
*/
|
||||||
|
|
||||||
|
public static readonly CVarDef<float> MaxJukeboxSongSizeInMB = CVarDef.Create("white.max_jukebox_song_size",
|
||||||
|
3.5f, CVar.SERVER | CVar.REPLICATED | CVar.ARCHIVE);
|
||||||
|
|
||||||
|
public static readonly CVarDef<float> MaxJukeboxSoundRange = CVarDef.Create("white.max_jukebox_sound_range", 20f,
|
||||||
|
CVar.SERVER | CVar.REPLICATED | CVar.ARCHIVE);
|
||||||
|
|
||||||
|
public static readonly CVarDef<float> JukeboxVolume =
|
||||||
|
CVarDef.Create("white.jukebox_volume", 0f, CVar.CLIENTONLY | CVar.ARCHIVE);
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Chat
|
* Chat
|
||||||
|
|||||||
BIN
Resources/Audio/White/Jukebox/Tapes/tape_fall.ogg
Normal file
BIN
Resources/Audio/White/Jukebox/Tapes/tape_fall.ogg
Normal file
Binary file not shown.
52
Resources/Prototypes/White/JukeboxAndStuff/jukebox_stuff.yml
Normal file
52
Resources/Prototypes/White/JukeboxAndStuff/jukebox_stuff.yml
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
- type: entity
|
||||||
|
parent: BaseItem
|
||||||
|
id: Jukebox
|
||||||
|
name: jukebox
|
||||||
|
description: Альфа-Шкварки
|
||||||
|
suffix: Empty
|
||||||
|
components:
|
||||||
|
- type: Jukebox
|
||||||
|
- type: Sprite
|
||||||
|
sprite: White/Objects/Devices/jukebox.rsi
|
||||||
|
layers:
|
||||||
|
- state: boombox
|
||||||
|
- state: boombox_working_overlay
|
||||||
|
map: ["bars"]
|
||||||
|
netsync: false
|
||||||
|
- type: Item
|
||||||
|
sprite: White/Objects/Devices/jukebox.rsi
|
||||||
|
heldPrefix: boombox
|
||||||
|
size: 500
|
||||||
|
- type: UserInterface
|
||||||
|
interfaces:
|
||||||
|
- key: enum.JukeboxUIKey.Key
|
||||||
|
type: JukeboxBUI
|
||||||
|
- type: ActivatableUI
|
||||||
|
key: enum.JukeboxUIKey.Key
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
parent: BaseItem
|
||||||
|
id: TapeRecorder
|
||||||
|
name: tape recorder
|
||||||
|
suffix: Empty
|
||||||
|
components:
|
||||||
|
- type: TapeCreator
|
||||||
|
coins: 3
|
||||||
|
- type: Sprite
|
||||||
|
scale: 0.7, 0.7
|
||||||
|
sprite: White/Objects/Devices/tapeRecorder.rsi
|
||||||
|
layers:
|
||||||
|
- state: base
|
||||||
|
- state: tape
|
||||||
|
map: ["tape"]
|
||||||
|
visible: false
|
||||||
|
netsync: false
|
||||||
|
- type: Item
|
||||||
|
sprite: White/Objects/Devices/tapeRecorder.rsi
|
||||||
|
heldPrefix: tapeRecorder
|
||||||
|
- type: UserInterface
|
||||||
|
interfaces:
|
||||||
|
- key: enum.TapeCreatorUIKey.Key
|
||||||
|
type: TapeCreatorBUI
|
||||||
|
- type: ActivatableUI
|
||||||
|
key: enum.TapeCreatorUIKey.Key
|
||||||
27
Resources/Prototypes/White/JukeboxAndStuff/tapes.yml
Normal file
27
Resources/Prototypes/White/JukeboxAndStuff/tapes.yml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
- type: entity
|
||||||
|
parent: BaseItem
|
||||||
|
id: BaseTape
|
||||||
|
name: baseTape
|
||||||
|
abstract: true
|
||||||
|
noSpawn: true
|
||||||
|
description: это база
|
||||||
|
suffix: Empty
|
||||||
|
components:
|
||||||
|
- type: Tape
|
||||||
|
- type: Sprite
|
||||||
|
sprite: Objects/Devices/jukeboxStuff/tape.rsi
|
||||||
|
layers:
|
||||||
|
- state: tape
|
||||||
|
netsync: false
|
||||||
|
- type: Item
|
||||||
|
sprite: Objects/Devices/jukeboxStuff/tape.rsi
|
||||||
|
heldPrefix: tape
|
||||||
|
- type: EmitSoundOnLand
|
||||||
|
sound:
|
||||||
|
path: /Audio/White/Jukebox/Tapes/tape_fall.ogg
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
parent: BaseTape
|
||||||
|
id: TapeEmpty
|
||||||
|
name: Old tape
|
||||||
|
description: Ligma balls
|
||||||
@@ -1197,3 +1197,6 @@
|
|||||||
id: Crystal
|
id: Crystal
|
||||||
|
|
||||||
# WHITE END
|
# WHITE END
|
||||||
|
|
||||||
|
- type: Tag
|
||||||
|
id: TapeRecorderCoin
|
||||||
|
|||||||
BIN
Resources/Textures/Interface/VerbIcons/remove_tape.png
Normal file
BIN
Resources/Textures/Interface/VerbIcons/remove_tape.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 973 B |
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"license": "CC-BY-SA-3.0",
|
||||||
|
"copyright": "Taken from infinity baystation 12, https://github.com/infinitystation/Baystation12/blob/073f678cdce92edb8fcd55f9ffc9f0523bf31506/icons/obj/radio.dmi",
|
||||||
|
"size": {
|
||||||
|
"x": 14,
|
||||||
|
"y": 8
|
||||||
|
},
|
||||||
|
"states" : [
|
||||||
|
{"name": "tape"}
|
||||||
|
]
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 656 B |
Reference in New Issue
Block a user