Instrument band support, submodule update to 138.0.0 (#17995)

This commit is contained in:
Vera Aguilera Puerto
2023-07-16 21:12:53 +02:00
committed by GitHub
parent 69ff0ae2e6
commit a2893dd6c3
16 changed files with 722 additions and 178 deletions

View File

@@ -0,0 +1,9 @@
<DefaultWindow Title="{Loc 'instruments-component-band-menu'}" MinSize="250 350" xmlns="https://spacestation14.io"
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client">
<BoxContainer Orientation="Vertical" Align="Center" HorizontalExpand="true" VerticalExpand="true">
<ItemList Name="BandList" SelectMode="Button" Margin="3 3 3 3"
HorizontalExpand="true" VerticalExpand="true" SizeFlagsStretchRatio="8"/>
<Button Name="RefreshButton" Text="{Loc 'instrument-component-band-refresh'}" TextAlign="Center"
HorizontalExpand="true" Margin="5 5 5 5" SizeFlagsStretchRatio="1"/>
</BoxContainer>
</DefaultWindow>

View File

@@ -0,0 +1,45 @@
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Timing;
namespace Content.Client.Instruments.UI;
[GenerateTypedNameReferences]
public sealed partial class BandMenu : DefaultWindow
{
private readonly InstrumentBoundUserInterface _owner;
public BandMenu(InstrumentBoundUserInterface owner) : base()
{
RobustXamlLoader.Load(this);
_owner = owner;
BandList.OnItemSelected += OnItemSelected;
RefreshButton.OnPressed += OnRefreshPressed;
}
private void OnRefreshPressed(BaseButton.ButtonEventArgs obj)
{
_owner.RefreshBands();
}
private void OnItemSelected(ItemList.ItemListSelectedEventArgs args)
{
_owner.Instruments.SetMaster(_owner.Owner, (EntityUid)args.ItemList[args.ItemIndex].Metadata!);
BandList.Clear();
Timer.Spawn(0, Close);
}
public void Populate((EntityUid, string)[] nearby)
{
BandList.Clear();
foreach (var (uid, name) in nearby)
{
var item = BandList.AddItem(name, null, true, uid);
item.Selected = _owner.Instrument?.Master == uid;
}
}
}

View File

@@ -0,0 +1,11 @@
<DefaultWindow Title="{Loc 'instruments-component-channels-menu'}" MinSize="250 350" xmlns="https://spacestation14.io"
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client">
<BoxContainer Orientation="Vertical" HorizontalExpand="true" VerticalExpand="true" Align="Center">
<ItemList Name="ChannelList" SelectMode="Multiple" Margin="3 3 3 3" HorizontalExpand="true" VerticalExpand="true" SizeFlagsStretchRatio="8"/>
<BoxContainer Orientation="Horizontal" HorizontalExpand="true" VerticalExpand="true" Align="Center"
SizeFlagsStretchRatio="1">
<Button Name="AllButton" Text="{Loc 'instruments-component-channels-all-button'}" HorizontalExpand="true" VerticalExpand="true" SizeFlagsStretchRatio="1"/>
<Button Name="ClearButton" Text="{Loc 'instruments-component-channels-clear-button'}" HorizontalExpand="true" VerticalExpand="true" SizeFlagsStretchRatio="1"/>
</BoxContainer>
</BoxContainer>
</DefaultWindow>

View File

@@ -0,0 +1,66 @@
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Audio.Midi;
using Robust.Shared.Timing;
namespace Content.Client.Instruments.UI;
[GenerateTypedNameReferences]
public sealed partial class ChannelsMenu : DefaultWindow
{
private readonly InstrumentBoundUserInterface _owner;
public ChannelsMenu(InstrumentBoundUserInterface owner) : base()
{
RobustXamlLoader.Load(this);
_owner = owner;
ChannelList.OnItemSelected += OnItemSelected;
ChannelList.OnItemDeselected += OnItemDeselected;
AllButton.OnPressed += OnAllPressed;
ClearButton.OnPressed += OnClearPressed;
}
private void OnItemSelected(ItemList.ItemListSelectedEventArgs args)
{
_owner.Instruments.SetFilteredChannel(_owner.Owner, (int)ChannelList[args.ItemIndex].Metadata!, false);
}
private void OnItemDeselected(ItemList.ItemListDeselectedEventArgs args)
{
_owner.Instruments.SetFilteredChannel(_owner.Owner, (int)ChannelList[args.ItemIndex].Metadata!, true);
}
private void OnAllPressed(BaseButton.ButtonEventArgs obj)
{
foreach (var item in ChannelList)
{
// TODO: Make this efficient jfc
item.Selected = true;
}
}
private void OnClearPressed(BaseButton.ButtonEventArgs obj)
{
foreach (var item in ChannelList)
{
// TODO: Make this efficient jfc
item.Selected = false;
}
}
public void Populate()
{
ChannelList.Clear();
for (int i = 0; i < RobustMidiEvent.MaxChannels; i++)
{
var item = ChannelList.AddItem(_owner.Loc.GetString("instrument-component-channel-name",
("number", i)), null, true, i);
item.Selected = !_owner.Instrument?.FilteredChannels[i] ?? false;
}
}
}

View File

@@ -1,22 +1,56 @@
using Content.Shared.ActionBlocker;
using Content.Shared.Instruments.UI;
using Content.Shared.Interaction;
using Robust.Client.Audio.Midi;
using Robust.Client.GameObjects;
using Robust.Client.Player;
using Robust.Client.UserInterface;
namespace Content.Client.Instruments.UI
{
public sealed class InstrumentBoundUserInterface : BoundUserInterface
{
[ViewVariables]
private InstrumentMenu? _instrumentMenu;
[Dependency] public readonly IEntityManager Entities = default!;
[Dependency] public readonly IMidiManager MidiManager = default!;
[Dependency] public readonly IFileDialogManager FileDialogManager = default!;
[Dependency] public readonly IPlayerManager PlayerManager = default!;
[Dependency] public readonly ILocalizationManager Loc = default!;
[ViewVariables]
public InstrumentComponent? Instrument { get; set; }
public readonly InstrumentSystem Instruments = default!;
public readonly ActionBlockerSystem ActionBlocker = default!;
public readonly SharedInteractionSystem Interactions = default!;
[ViewVariables] private InstrumentMenu? _instrumentMenu;
[ViewVariables] private BandMenu? _bandMenu;
[ViewVariables] private ChannelsMenu? _channelsMenu;
[ViewVariables] public InstrumentComponent? Instrument { get; private set; }
public InstrumentBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
IoCManager.InjectDependencies(this);
Instruments = Entities.System<InstrumentSystem>();
ActionBlocker = Entities.System<ActionBlockerSystem>();
Interactions = Entities.System<SharedInteractionSystem>();
}
protected override void ReceiveMessage(BoundUserInterfaceMessage message)
{
switch (message)
{
case InstrumentBandResponseBuiMessage bandRx:
_bandMenu?.Populate(bandRx.Nearby);
break;
default:
break;
}
}
protected override void Open()
{
if (!EntMan.TryGetComponent<InstrumentComponent?>(Owner, out var instrument)) return;
if (!EntMan.TryGetComponent<InstrumentComponent?>(Owner, out var instrument))
return;
Instrument = instrument;
_instrumentMenu = new InstrumentMenu(this);
@@ -28,8 +62,45 @@ namespace Content.Client.Instruments.UI
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (!disposing) return;
if (!disposing)
return;
_instrumentMenu?.Dispose();
_bandMenu?.Dispose();
_channelsMenu?.Dispose();
}
public void RefreshBands()
{
SendMessage(new InstrumentBandRequestBuiMessage());
}
public void OpenBandMenu()
{
_bandMenu ??= new BandMenu(this);
// Refresh cache...
RefreshBands();
_bandMenu.OpenCenteredLeft();
}
public void CloseBandMenu()
{
if(_bandMenu?.IsOpen ?? false)
_bandMenu.Close();
}
public void OpenChannelsMenu()
{
_channelsMenu ??= new ChannelsMenu(this);
_channelsMenu.Populate();
_channelsMenu.OpenCenteredRight();
}
public void CloseChannelsMenu()
{
if(_channelsMenu?.IsOpen ?? false)
_channelsMenu.Close();
}
}
}

View File

@@ -4,14 +4,20 @@
<BoxContainer Orientation="Horizontal" VerticalExpand="True">
<Button Name="InputButton" ToggleMode="True" Text="{Loc 'instruments-component-menu-input-button'}" TextAlign="Center"
HorizontalExpand="True" SizeFlagsStretchRatio="1" />
<Control HorizontalExpand="True" SizeFlagsStretchRatio="2" />
<Control HorizontalExpand="True" SizeFlagsStretchRatio="0.5" />
<Button Name="BandButton" Text="{Loc 'instruments-component-menu-band-button'}" TextAlign="Center" HorizontalExpand="True"
SizeFlagsStretchRatio="1"/>
<Control HorizontalExpand="True" SizeFlagsStretchRatio="0.5" />
<Button Name="FileButton" Text="{Loc 'instruments-component-menu-play-button'}" TextAlign="Center" HorizontalExpand="True"
SizeFlagsStretchRatio="1" />
</BoxContainer>
<BoxContainer Orientation="Horizontal" VerticalExpand="True">
<Button Name="LoopButton" ToggleMode="True" Text="{Loc 'instruments-component-menu-loop-button'}" TextAlign="Center" HorizontalExpand="True"
SizeFlagsStretchRatio="1" />
<Control HorizontalExpand="True" SizeFlagsStretchRatio="2" />
<Control HorizontalExpand="True" SizeFlagsStretchRatio="0.5" />
<Button Name="ChannelsButton" Text="{Loc 'instruments-component-menu-channels-button'}" TextAlign="Center" HorizontalExpand="True"
SizeFlagsStretchRatio="1"/>
<Control HorizontalExpand="True" SizeFlagsStretchRatio="0.5" />
<Button Name="StopButton" Text="{Loc 'instruments-component-menu-stop-button'}" TextAlign="Center" HorizontalExpand="True"
SizeFlagsStretchRatio="1" />
</BoxContainer>

View File

@@ -1,20 +1,12 @@
using System;
using System.IO;
using System.Numerics;
using System.Threading.Tasks;
using Content.Client.Interactable;
using Content.Shared.ActionBlocker;
using Content.Shared.Interaction;
using Robust.Client.Audio.Midi;
using Robust.Client.AutoGenerated;
using Robust.Client.Player;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.Input;
using Robust.Shared.IoC;
using Robust.Shared.Timing;
using static Robust.Client.UserInterface.Controls.BaseButton;
using Range = Robust.Client.UserInterface.Controls.Range;
@@ -24,29 +16,26 @@ namespace Content.Client.Instruments.UI
[GenerateTypedNameReferences]
public sealed partial class InstrumentMenu : DefaultWindow
{
[Dependency] private readonly IMidiManager _midiManager = default!;
[Dependency] private readonly IFileDialogManager _fileDialogManager = default!;
private readonly InstrumentBoundUserInterface _owner;
public InstrumentMenu(InstrumentBoundUserInterface owner)
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
_owner = owner;
if (_owner.Instrument != null)
{
_owner.Instrument.OnMidiPlaybackEnded += InstrumentOnMidiPlaybackEnded;
Title = IoCManager.Resolve<IEntityManager>().GetComponent<MetaDataComponent>(_owner.Instrument.Owner).EntityName;
Title = _owner.Entities.GetComponent<MetaDataComponent>(_owner.Owner).EntityName;
LoopButton.Disabled = !_owner.Instrument.IsMidiOpen;
LoopButton.Pressed = _owner.Instrument.LoopMidi;
ChannelsButton.Disabled = !_owner.Instrument.IsRendererAlive;
StopButton.Disabled = !_owner.Instrument.IsMidiOpen;
PlaybackSlider.MouseFilter = _owner.Instrument.IsMidiOpen ? MouseFilterMode.Pass : MouseFilterMode.Ignore;
}
if (!_midiManager.IsAvailable)
if (!_owner.MidiManager.IsAvailable)
{
UnavailableOverlay.Visible = true;
// We return early as to not give the buttons behavior.
@@ -54,8 +43,11 @@ namespace Content.Client.Instruments.UI
}
InputButton.OnToggled += MidiInputButtonOnOnToggled;
BandButton.OnPressed += BandButtonOnPressed;
BandButton.OnToggled += BandButtonOnToggled;
FileButton.OnPressed += MidiFileButtonOnOnPressed;
LoopButton.OnToggled += MidiLoopButtonOnOnToggled;
ChannelsButton.OnPressed += ChannelsButtonOnPressed;
StopButton.OnPressed += MidiStopButtonOnPressed;
PlaybackSlider.OnValueChanged += PlaybackSliderSeek;
PlaybackSlider.OnKeyBindUp += PlaybackSliderKeyUp;
@@ -63,6 +55,27 @@ namespace Content.Client.Instruments.UI
MinSize = SetSize = new Vector2(400, 150);
}
private void BandButtonOnPressed(ButtonEventArgs obj)
{
if (!PlayCheck())
return;
_owner.OpenBandMenu();
}
private void BandButtonOnToggled(ButtonToggledEventArgs obj)
{
if (obj.Pressed)
return;
_owner.Instruments.SetMaster(_owner.Owner, null);
}
private void ChannelsButtonOnPressed(ButtonEventArgs obj)
{
_owner.OpenChannelsMenu();
}
private void InstrumentOnMidiPlaybackEnded()
{
MidiPlaybackSetButtonsDisabled(true);
@@ -70,6 +83,9 @@ namespace Content.Client.Instruments.UI
public void MidiPlaybackSetButtonsDisabled(bool disabled)
{
if(disabled)
_owner.CloseChannelsMenu();
LoopButton.Disabled = disabled;
StopButton.Disabled = disabled;
@@ -79,8 +95,10 @@ namespace Content.Client.Instruments.UI
private async void MidiFileButtonOnOnPressed(ButtonEventArgs obj)
{
_owner.CloseBandMenu();
var filters = new FileDialogFilters(new FileDialogFilters.Group("mid", "midi"));
await using var file = await _fileDialogManager.OpenFile(filters);
await using var file = await _owner.FileDialogManager.OpenFile(filters);
// did the instrument menu get closed while waiting for the user to select a file?
if (Disposed)
@@ -89,7 +107,8 @@ namespace Content.Client.Instruments.UI
// The following checks are only in place to prevent players from playing MIDI songs locally.
// There are equivalents for these checks on the server.
if (file == null) return;
if (file == null)
return;
/*if (!_midiManager.IsMidiFile(filename))
{
@@ -107,7 +126,7 @@ namespace Content.Client.Instruments.UI
await Task.WhenAll(Timer.Delay(100), file.CopyToAsync(memStream));
if (_owner.Instrument is not {} instrument
|| !EntitySystem.Get<InstrumentSystem>().OpenMidi(instrument.Owner, memStream.GetBuffer().AsSpan(0, (int) memStream.Length), instrument))
|| !_owner.Instruments.OpenMidi(_owner.Owner, memStream.GetBuffer().AsSpan(0, (int) memStream.Length), instrument))
return;
MidiPlaybackSetButtonsDisabled(false);
@@ -117,7 +136,7 @@ namespace Content.Client.Instruments.UI
private void MidiInputButtonOnOnToggled(ButtonToggledEventArgs obj)
{
var instrumentSystem = EntitySystem.Get<InstrumentSystem>();
_owner.CloseBandMenu();
if (obj.Pressed)
{
@@ -126,55 +145,58 @@ namespace Content.Client.Instruments.UI
MidiStopButtonOnPressed(null);
if(_owner.Instrument is {} instrument)
instrumentSystem.OpenInput(instrument.Owner, instrument);
_owner.Instruments.OpenInput(_owner.Owner, instrument);
}
else if (_owner.Instrument is { } instrument)
{
_owner.Instruments.CloseInput(_owner.Owner, false, instrument);
_owner.CloseChannelsMenu();
}
else if(_owner.Instrument is {} instrument)
instrumentSystem.CloseInput(instrument.Owner, false, instrument);
}
private bool PlayCheck()
{
// TODO all of these checks should also be done server-side.
var instrumentEnt = _owner.Instrument?.Owner;
var instrumentEnt = _owner.Owner;
var instrument = _owner.Instrument;
// If either the entity or component are null, return.
if (instrumentEnt == null || instrument == null)
if (instrument == null)
return false;
var localPlayer = IoCManager.Resolve<IPlayerManager>().LocalPlayer;
var localPlayer = _owner.PlayerManager.LocalPlayer;
// If we don't have a player or controlled entity, we return.
if (localPlayer?.ControlledEntity == null) return false;
if (localPlayer?.ControlledEntity == null)
return false;
// By default, allow an instrument to play itself and skip all other checks
if (localPlayer.ControlledEntity == instrumentEnt)
return true;
// If we're a handheld instrument, we might be in a container. Get it just in case.
instrumentEnt.Value.TryGetContainerMan(out var conMan);
instrumentEnt.TryGetContainerMan(out var conMan);
// If the instrument is handheld and we're not holding it, we return.
if ((instrument.Handheld && (conMan == null
|| conMan.Owner != localPlayer.ControlledEntity))) return false;
if ((instrument.Handheld && (conMan == null || conMan.Owner != localPlayer.ControlledEntity)))
return false;
var entSysMan = IoCManager.Resolve<IEntitySystemManager>();
if (!entSysMan.GetEntitySystem<ActionBlockerSystem>().CanInteract(localPlayer.ControlledEntity.Value, instrumentEnt))
if (!_owner.ActionBlocker.CanInteract(localPlayer.ControlledEntity.Value, instrumentEnt))
return false;
// We check that we're in range unobstructed just in case.
return entSysMan.GetEntitySystem<SharedInteractionSystem>().InRangeUnobstructed(localPlayer.ControlledEntity.Value, instrumentEnt.Value);
return _owner.Interactions.InRangeUnobstructed(localPlayer.ControlledEntity.Value, instrumentEnt);
}
private void MidiStopButtonOnPressed(ButtonEventArgs? obj)
{
MidiPlaybackSetButtonsDisabled(true);
if (_owner.Instrument is not { } instrument)
if (_owner.Instrument is not {} instrument)
return;
EntitySystem.Get<InstrumentSystem>().CloseMidi(instrument.Owner, false, instrument);
_owner.Instruments.CloseMidi(_owner.Owner, false, instrument);
_owner.CloseChannelsMenu();
}
private void MidiLoopButtonOnOnToggled(ButtonToggledEventArgs obj)
@@ -183,29 +205,45 @@ namespace Content.Client.Instruments.UI
return;
_owner.Instrument.LoopMidi = obj.Pressed;
_owner.Instrument.DirtyRenderer = true;
_owner.Instruments.UpdateRenderer(_owner.Owner, _owner.Instrument);
}
private void PlaybackSliderSeek(Range _)
{
// Do not seek while still grabbing.
if (PlaybackSlider.Grabbed || _owner.Instrument is not {} instrument) return;
if (PlaybackSlider.Grabbed || _owner.Instrument is not {} instrument)
return;
EntitySystem.Get<InstrumentSystem>().SetPlayerTick(instrument.Owner, (int)Math.Ceiling(PlaybackSlider.Value), instrument);
_owner.Instruments.SetPlayerTick(_owner.Owner, (int)Math.Ceiling(PlaybackSlider.Value), instrument);
}
private void PlaybackSliderKeyUp(GUIBoundKeyEventArgs args)
{
if (args.Function != EngineKeyFunctions.UIClick || _owner.Instrument is not {} instrument) return;
if (args.Function != EngineKeyFunctions.UIClick || _owner.Instrument is not {} instrument)
return;
EntitySystem.Get<InstrumentSystem>().SetPlayerTick(instrument.Owner, (int)Math.Ceiling(PlaybackSlider.Value), instrument);
_owner.Instruments.SetPlayerTick(_owner.Owner, (int)Math.Ceiling(PlaybackSlider.Value), instrument);
}
public override void Close()
{
base.Close();
_owner.CloseBandMenu();
_owner.CloseChannelsMenu();
}
protected override void FrameUpdate(FrameEventArgs args)
{
base.FrameUpdate(args);
if (_owner.Instrument == null) return;
if (_owner.Instrument == null)
return;
var hasMaster = _owner.Instrument.Master != null;
BandButton.ToggleMode = hasMaster;
BandButton.Pressed = hasMaster;
BandButton.Disabled = _owner.Instrument.IsMidiOpen || _owner.Instrument.IsInputOpen;
ChannelsButton.Disabled = !_owner.Instrument.IsRendererAlive;
if (!_owner.Instrument.IsMidiOpen)
{
@@ -214,7 +252,8 @@ namespace Content.Client.Instruments.UI
return;
}
if (PlaybackSlider.Grabbed) return;
if (PlaybackSlider.Grabbed)
return;
PlaybackSlider.MaxValue = _owner.Instrument.PlayerTotalTick;
PlaybackSlider.SetValueWithoutEvent(_owner.Instrument.PlayerTick);