Files
wwdpublic/Content.Client/Instruments/UI/InstrumentMenu.xaml.cs
sleepyyapril 885ee5a831 Wizmerge for Station AI (#1351)
<!--
This is a semi-strict format, you can add/remove sections as needed but
the order/format should be kept the same
Remove these comments before submitting
-->

# Description

the adding AI is now up to y'all because i'm not touching loadout code
for name datasets, but it shouldn't be too bad from here

---------

Signed-off-by: sleepyyapril <123355664+sleepyyapril@users.noreply.github.com>
Signed-off-by: SolStar <44028047+ewokswagger@users.noreply.github.com>
Signed-off-by: deltanedas <39013340+deltanedas@users.noreply.github.com>
Co-authored-by: themias <89101928+themias@users.noreply.github.com>
Co-authored-by: Verm <32827189+Vermidia@users.noreply.github.com>
Co-authored-by: DrSmugleaf <10968691+DrSmugleaf@users.noreply.github.com>
Co-authored-by: Sphiral <145869023+SphiraI@users.noreply.github.com>
Co-authored-by: Ed <96445749+TheShuEd@users.noreply.github.com>
Co-authored-by: Mr. 27 <45323883+Dutch-VanDerLinde@users.noreply.github.com>
Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
Co-authored-by: Alzore <140123969+Blackern5000@users.noreply.github.com>
Co-authored-by: ravage <142820619+ravage123321@users.noreply.github.com>
Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>
Co-authored-by: Intoxicating-Innocence <188202277+Intoxicating-Innocence@users.noreply.github.com>
Co-authored-by: Saphire <lattice@saphi.re>
Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
Co-authored-by: Errant <35878406+Errant-4@users.noreply.github.com>
Co-authored-by: Tayrtahn <tayrtahn@gmail.com>
Co-authored-by: CaasGit <87243814+CaasGit@users.noreply.github.com>
Co-authored-by: BramvanZijp <56019239+BramvanZijp@users.noreply.github.com>
Co-authored-by: Boaz1111 <149967078+Boaz1111@users.noreply.github.com>
Co-authored-by: NakataRin <45946146+NakataRin@users.noreply.github.com>
Co-authored-by: Kara <lunarautomaton6@gmail.com>
Co-authored-by: Plykiya <58439124+Plykiya@users.noreply.github.com>
Co-authored-by: SlamBamActionman <slambamactionman@gmail.com>
Co-authored-by: Doomsdrayk <robotdoughnut@comcast.net>
Co-authored-by: Brandon Hu <103440971+Brandon-Huu@users.noreply.github.com>
Co-authored-by: SlamBamActionman <83650252+SlamBamActionman@users.noreply.github.com>
Co-authored-by: ElectroJr <leonsfriedrich@gmail.com>
Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com>
Co-authored-by: DrSmugleaf <DrSmugleaf@users.noreply.github.com>
Co-authored-by: Julian Giebel <juliangiebel@live.de>
Co-authored-by: nikthechampiongr <32041239+nikthechampiongr@users.noreply.github.com>
Co-authored-by: Repo <47093363+Titian3@users.noreply.github.com>
Co-authored-by: Chief-Engineer <119664036+Chief-Engineer@users.noreply.github.com>
Co-authored-by: icekot8 <93311212+icekot8@users.noreply.github.com>
Co-authored-by: AJCM-git <60196617+AJCM-git@users.noreply.github.com>
Co-authored-by: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com>
Co-authored-by: no <165581243+pissdemon@users.noreply.github.com>
Co-authored-by: Tornado Tech <54727692+Tornado-Technology@users.noreply.github.com>
Co-authored-by: osjarw <62134478+osjarw@users.noreply.github.com>
Co-authored-by: Simon <63975668+Simyon264@users.noreply.github.com>
Co-authored-by: TGRCDev <tgrc@tgrc.dev>
Co-authored-by: Milon <milonpl.git@proton.me>
Co-authored-by: deltanedas <39013340+deltanedas@users.noreply.github.com>
Co-authored-by: ShadowCommander <10494922+ShadowCommander@users.noreply.github.com>
Co-authored-by: Fildrance <fildrance@gmail.com>
Co-authored-by: pa.pecherskij <pa.pecherskij@interfax.ru>
Co-authored-by: chavonadelal <156101927+chavonadelal@users.noreply.github.com>
Co-authored-by: SolStar <44028047+ewokswagger@users.noreply.github.com>
Co-authored-by: K-Dynamic <20566341+K-Dynamic@users.noreply.github.com>
Co-authored-by: lzk <124214523+lzk228@users.noreply.github.com>
Co-authored-by: ArchRBX <5040911+ArchRBX@users.noreply.github.com>
Co-authored-by: archrbx <punk.gear5260@fastmail.com>
Co-authored-by: Radezolid <snappednexus@gmail.com>
Co-authored-by: Nemanja <98561806+EmoGarbage404@users.noreply.github.com>
Co-authored-by: EmoGarbage404 <retron404@gmail.com>
Co-authored-by: MilenVolf <63782763+MilenVolf@users.noreply.github.com>
Co-authored-by: Velcroboy <107660393+IamVelcroboy@users.noreply.github.com>
Co-authored-by: Velcroboy <velcroboy333@hotmail.com>
Co-authored-by: neuPanda <chriseparton@gmail.com>
Co-authored-by: neuPanda <spainman0@yahoo.com>
Co-authored-by: Dvir <39403717+dvir001@users.noreply.github.com>
Co-authored-by: Whatstone <whatston3@gmail.com>
Co-authored-by: VideoKompany <135313844+VlaDOS1408@users.noreply.github.com>

(cherry picked from commit 93ed70acfeda357133a701f637d3faeec02749bb)
2025-01-14 00:13:42 +03:00

287 lines
9.7 KiB
C#

using System.IO;
using System.Numerics;
using System.Threading.Tasks;
using Content.Client.Interactable;
using Content.Shared.ActionBlocker;
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.Input;
using Robust.Shared.Timing;
using static Robust.Client.UserInterface.Controls.BaseButton;
using Range = Robust.Client.UserInterface.Controls.Range;
namespace Content.Client.Instruments.UI
{
[GenerateTypedNameReferences]
public sealed partial class InstrumentMenu : DefaultWindow
{
[Dependency] private readonly IEntityManager _entManager = default!;
[Dependency] private readonly IFileDialogManager _dialogs = default!;
[Dependency] private readonly IPlayerManager _player = default!;
private bool _isMidiFileDialogueWindowOpen;
public event Action? OnOpenBand;
public event Action? OnOpenChannels;
public event Action? OnCloseBands;
public event Action? OnCloseChannels;
public EntityUid Entity;
public InstrumentMenu()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
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;
MinSize = SetSize = new Vector2(400, 150);
}
public void SetInstrument(Entity<InstrumentComponent> entity)
{
Entity = entity;
var component = entity.Comp;
component.OnMidiPlaybackEnded += InstrumentOnMidiPlaybackEnded;
LoopButton.Disabled = !component.IsMidiOpen;
LoopButton.Pressed = component.LoopMidi;
ChannelsButton.Disabled = !component.IsRendererAlive;
StopButton.Disabled = !component.IsMidiOpen;
PlaybackSlider.MouseFilter = component.IsMidiOpen ? MouseFilterMode.Pass : MouseFilterMode.Ignore;
}
public void RemoveInstrument(InstrumentComponent component)
{
component.OnMidiPlaybackEnded -= InstrumentOnMidiPlaybackEnded;
}
public void SetMIDI(bool available)
{
UnavailableOverlay.Visible = !available;
}
private void BandButtonOnPressed(ButtonEventArgs obj)
{
if (!PlayCheck())
return;
OnOpenBand?.Invoke();
}
private void BandButtonOnToggled(ButtonToggledEventArgs obj)
{
if (obj.Pressed)
return;
if (_entManager.TryGetComponent(Entity, out InstrumentComponent? instrument))
{
_entManager.System<InstrumentSystem>().SetMaster(Entity, instrument.Master);
}
}
private void ChannelsButtonOnPressed(ButtonEventArgs obj)
{
OnOpenChannels?.Invoke();
}
private void InstrumentOnMidiPlaybackEnded()
{
MidiPlaybackSetButtonsDisabled(true);
}
public void MidiPlaybackSetButtonsDisabled(bool disabled)
{
if (disabled)
{
OnCloseChannels?.Invoke();
}
LoopButton.Disabled = disabled;
StopButton.Disabled = disabled;
// Whether to allow the slider to receive events..
PlaybackSlider.MouseFilter = !disabled ? MouseFilterMode.Pass : MouseFilterMode.Ignore;
}
private async void MidiFileButtonOnOnPressed(ButtonEventArgs obj)
{
if (_isMidiFileDialogueWindowOpen)
return;
OnCloseBands?.Invoke();
var filters = new FileDialogFilters(new FileDialogFilters.Group("mid", "midi"));
// TODO: Once the file dialogue manager can handle focusing or closing windows, improve this logic to close
// or focus the previously-opened window.
_isMidiFileDialogueWindowOpen = true;
await using var file = await _dialogs.OpenFile(filters);
_isMidiFileDialogueWindowOpen = false;
// did the instrument menu get closed while waiting for the user to select a file?
if (Disposed)
return;
// 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 (!PlayCheck())
return;
await using var memStream = new MemoryStream((int) file.Length);
await file.CopyToAsync(memStream);
if (!_entManager.TryGetComponent<InstrumentComponent>(Entity, out var instrument))
{
return;
}
if (!_entManager.System<InstrumentSystem>()
.OpenMidi(Entity,
memStream.GetBuffer().AsSpan(0, (int) memStream.Length),
instrument))
{
return;
}
MidiPlaybackSetButtonsDisabled(false);
if (InputButton.Pressed)
InputButton.Pressed = false;
}
private void MidiInputButtonOnOnToggled(ButtonToggledEventArgs obj)
{
OnCloseBands?.Invoke();
if (obj.Pressed)
{
if (!PlayCheck())
return;
MidiStopButtonOnPressed(null);
if (_entManager.TryGetComponent(Entity, out InstrumentComponent? instrument))
_entManager.System<InstrumentSystem>().OpenInput(Entity, instrument);
}
else
{
_entManager.System<InstrumentSystem>().CloseInput(Entity, false);
OnCloseChannels?.Invoke();
}
}
private bool PlayCheck()
{
// TODO all of these checks should also be done server-side.
if (!_entManager.TryGetComponent(Entity, out InstrumentComponent? instrument))
return false;
var localEntity = _player.LocalEntity;
// If we don't have a player or controlled entity, we return.
if (localEntity == null)
return false;
// By default, allow an instrument to play itself and skip all other checks
if (localEntity == Entity)
return true;
var container = _entManager.System<SharedContainerSystem>();
// If we're a handheld instrument, we might be in a container. Get it just in case.
container.TryGetContainingContainer((Entity, null, null), out var conMan);
// If the instrument is handheld and we're not holding it, we return.
if (instrument.Handheld && (conMan == null || conMan.Owner != localEntity))
return false;
if (!_entManager.System<ActionBlockerSystem>().CanInteract(localEntity.Value, Entity))
return false;
// We check that we're in range unobstructed just in case.
return _entManager.System<InteractionSystem>().InRangeUnobstructed(localEntity.Value, Entity);
}
private void MidiStopButtonOnPressed(ButtonEventArgs? obj)
{
MidiPlaybackSetButtonsDisabled(true);
_entManager.System<InstrumentSystem>().CloseMidi(Entity, false);
OnCloseChannels?.Invoke();
}
private void MidiLoopButtonOnOnToggled(ButtonToggledEventArgs obj)
{
var instrument = _entManager.System<InstrumentSystem>();
if (_entManager.TryGetComponent(Entity, out InstrumentComponent? instrumentComp))
{
instrumentComp.LoopMidi = obj.Pressed;
}
instrument.UpdateRenderer(Entity);
}
private void PlaybackSliderSeek(Range _)
{
// Do not seek while still grabbing.
if (PlaybackSlider.Grabbed)
return;
_entManager.System<InstrumentSystem>().SetPlayerTick(Entity, (int)Math.Ceiling(PlaybackSlider.Value));
}
private void PlaybackSliderKeyUp(GUIBoundKeyEventArgs args)
{
if (args.Function != EngineKeyFunctions.UIClick)
return;
_entManager.System<InstrumentSystem>().SetPlayerTick(Entity, (int)Math.Ceiling(PlaybackSlider.Value));
}
protected override void FrameUpdate(FrameEventArgs args)
{
base.FrameUpdate(args);
if (!_entManager.TryGetComponent(Entity, out InstrumentComponent? instrument))
return;
var hasMaster = instrument.Master != null;
BandButton.ToggleMode = hasMaster;
BandButton.Pressed = hasMaster;
BandButton.Disabled = instrument.IsMidiOpen || instrument.IsInputOpen;
ChannelsButton.Disabled = !instrument.IsRendererAlive;
if (!instrument.IsMidiOpen)
{
PlaybackSlider.MaxValue = 1;
PlaybackSlider.SetValueWithoutEvent(0);
return;
}
if (PlaybackSlider.Grabbed)
return;
PlaybackSlider.MaxValue = instrument.PlayerTotalTick;
PlaybackSlider.SetValueWithoutEvent(instrument.PlayerTick);
}
}
}