Adds playable instruments, IDropped, IHandSelected and IHandDese… (#368)
* Instrument test. * Midi stuff * Some more work * This actually works now! * update * Midi Audio works! * Lots of stuff, and cool interfaces for items * Update * Fix a few things * It just works * Move textures to another folder, remove placeholder description from instruments * Fix warning * Use renderer enum * Instruments now use DisposeRenderer method, and send MidiEvents as they receive them. Deletes InstrumentSystem whoo. * Fix incorrect sprite paths * Instruments take midi file size check into account when enabling/disabling midi playback buttons * Fix crash when pressing drop on empty hand. * Use new renderer return values for midi/input * Xylophones are no longer handheld instruments, fix their sprites. * Use new API * Remove nfluidsynth from solution * Timing information * Use IGameTiming.CurTime for timestamps instead
@@ -0,0 +1,36 @@
|
|||||||
|
using Content.Client.Instruments;
|
||||||
|
using Robust.Client.GameObjects.Components.UserInterface;
|
||||||
|
using Robust.Shared.ViewVariables;
|
||||||
|
|
||||||
|
namespace Content.Client.GameObjects.Components.Instruments
|
||||||
|
{
|
||||||
|
public class InstrumentBoundUserInterface : BoundUserInterface
|
||||||
|
{
|
||||||
|
[ViewVariables]
|
||||||
|
private InstrumentMenu _instrumentMenu;
|
||||||
|
|
||||||
|
public InstrumentComponent Instrument { get; set; }
|
||||||
|
|
||||||
|
public InstrumentBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Open()
|
||||||
|
{
|
||||||
|
if (!Owner.Owner.TryGetComponent<InstrumentComponent>(out var instrument)) return;
|
||||||
|
|
||||||
|
Instrument = instrument;
|
||||||
|
_instrumentMenu = new InstrumentMenu(this);
|
||||||
|
_instrumentMenu.OnClose += Close;
|
||||||
|
|
||||||
|
_instrumentMenu.OpenCentered();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
base.Dispose(disposing);
|
||||||
|
if (!disposing) return;
|
||||||
|
_instrumentMenu?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,175 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using Content.Shared.GameObjects.Components.Instruments;
|
||||||
|
using OpenTK.Platform.Windows;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Client.Audio.Midi;
|
||||||
|
using Robust.Client.GameObjects.EntitySystems;
|
||||||
|
using Robust.Client.Interfaces.Graphics;
|
||||||
|
using Robust.Client.Interfaces.UserInterface;
|
||||||
|
using Robust.Client.Reflection;
|
||||||
|
using Robust.Shared.Audio.Midi;
|
||||||
|
using Robust.Shared.Interfaces.GameObjects;
|
||||||
|
using Robust.Shared.Interfaces.Network;
|
||||||
|
using Robust.Shared.Interfaces.Reflection;
|
||||||
|
using Robust.Shared.Interfaces.Serialization;
|
||||||
|
using Robust.Shared.Interfaces.Timing;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
using Robust.Shared.ViewVariables;
|
||||||
|
using Timer = Robust.Shared.Timers.Timer;
|
||||||
|
|
||||||
|
|
||||||
|
namespace Content.Client.GameObjects.Components.Instruments
|
||||||
|
{
|
||||||
|
[RegisterComponent]
|
||||||
|
public class InstrumentComponent : SharedInstrumentComponent
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Called when a midi song stops playing.
|
||||||
|
/// </summary>
|
||||||
|
public event Action OnMidiPlaybackEnded;
|
||||||
|
|
||||||
|
#pragma warning disable 649
|
||||||
|
[Dependency] private IMidiManager _midiManager;
|
||||||
|
[Dependency] private IFileDialogManager _fileDialogManager;
|
||||||
|
[Dependency] private readonly IGameTiming _timing;
|
||||||
|
#pragma warning restore 649
|
||||||
|
|
||||||
|
private IMidiRenderer _renderer;
|
||||||
|
private int _instrumentProgram = 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A queue of MidiEvents to be sent to the server.
|
||||||
|
/// </summary>
|
||||||
|
private Queue<MidiEvent> _eventQueue = new Queue<MidiEvent>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether a midi song will loop or not.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public bool LoopMidi
|
||||||
|
{
|
||||||
|
get => _renderer.LoopMidi;
|
||||||
|
set => _renderer.LoopMidi = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Changes the instrument the midi renderer will play.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public int InstrumentProgram
|
||||||
|
{
|
||||||
|
get => _instrumentProgram;
|
||||||
|
set {
|
||||||
|
_instrumentProgram = value;
|
||||||
|
_renderer.MidiProgram = _instrumentProgram;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether there's a midi song being played or not.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public bool IsMidiOpen => _renderer.Status == MidiRendererStatus.File;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the midi renderer is listening for midi input or not.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public bool IsInputOpen => _renderer.Status == MidiRendererStatus.Input;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
IoCManager.InjectDependencies(this);
|
||||||
|
_renderer = _midiManager.GetNewRenderer();
|
||||||
|
_renderer.MidiProgram = _instrumentProgram;
|
||||||
|
_renderer.TrackingEntity = Owner;
|
||||||
|
_renderer.OnMidiPlayerFinished += () => { OnMidiPlaybackEnded?.Invoke(); };
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Shutdown()
|
||||||
|
{
|
||||||
|
base.Shutdown();
|
||||||
|
_renderer?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ExposeData(ObjectSerializer serializer)
|
||||||
|
{
|
||||||
|
base.ExposeData(serializer);
|
||||||
|
serializer.DataField(ref _instrumentProgram, "program", 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null, IComponent component = null)
|
||||||
|
{
|
||||||
|
base.HandleMessage(message, netChannel, component);
|
||||||
|
switch (message)
|
||||||
|
{
|
||||||
|
case InstrumentMidiEventMessage midiEventMessage:
|
||||||
|
// If we're the ones sending the MidiEvents, we ignore this message.
|
||||||
|
if (IsInputOpen || IsMidiOpen) break;
|
||||||
|
Timer.Spawn((int) (500 + _timing.CurTime.TotalMilliseconds - midiEventMessage.Timestamp),
|
||||||
|
() => _renderer.SendMidiEvent(midiEventMessage.MidiEvent));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InstrumentStopMidiMessage _:
|
||||||
|
_renderer.StopAllNotes();
|
||||||
|
if(IsInputOpen) CloseInput();
|
||||||
|
if(IsMidiOpen) CloseMidi();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="MidiRenderer.OpenInput"/>
|
||||||
|
public bool OpenInput()
|
||||||
|
{
|
||||||
|
if (_renderer.OpenInput())
|
||||||
|
{
|
||||||
|
_renderer.OnMidiEvent += RendererOnMidiEvent;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="MidiRenderer.CloseInput"/>
|
||||||
|
public bool CloseInput()
|
||||||
|
{
|
||||||
|
if (!_renderer.CloseInput()) return false;
|
||||||
|
_renderer.OnMidiEvent -= RendererOnMidiEvent;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="MidiRenderer.OpenMidi(string)"/>
|
||||||
|
public bool OpenMidi(string filename)
|
||||||
|
{
|
||||||
|
if (!_renderer.OpenMidi(filename)) return false;
|
||||||
|
_renderer.OnMidiEvent += RendererOnMidiEvent;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="MidiRenderer.CloseMidi"/>
|
||||||
|
public bool CloseMidi()
|
||||||
|
{
|
||||||
|
if (!_renderer.CloseMidi()) return false;
|
||||||
|
_renderer.OnMidiEvent -= RendererOnMidiEvent;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called whenever the renderer receives a midi event.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="midiEvent">The received midi event</param>
|
||||||
|
private void RendererOnMidiEvent(MidiEvent midiEvent)
|
||||||
|
{
|
||||||
|
SendNetworkMessage(new InstrumentMidiEventMessage(midiEvent, _timing.CurTime.TotalMilliseconds));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
197
Content.Client/Instruments/InstrumentMenu.cs
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
using Content.Client.GameObjects.Components.Instruments;
|
||||||
|
using Robust.Client.Audio.Midi;
|
||||||
|
using Robust.Client.Interfaces.UserInterface;
|
||||||
|
using Robust.Client.UserInterface;
|
||||||
|
using Robust.Client.UserInterface.Controls;
|
||||||
|
using Robust.Client.UserInterface.CustomControls;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Log;
|
||||||
|
using Robust.Shared.Maths;
|
||||||
|
|
||||||
|
namespace Content.Client.Instruments
|
||||||
|
{
|
||||||
|
public class InstrumentMenu : SS14Window
|
||||||
|
{
|
||||||
|
#pragma warning disable 649
|
||||||
|
[Dependency] private IMidiManager _midiManager;
|
||||||
|
[Dependency] private IFileDialogManager _fileDialogManager;
|
||||||
|
#pragma warning restore 649
|
||||||
|
|
||||||
|
private InstrumentBoundUserInterface _owner;
|
||||||
|
private Button midiLoopButton;
|
||||||
|
private Button midiStopButton;
|
||||||
|
private Button midiInputButton;
|
||||||
|
|
||||||
|
protected override Vector2? CustomSize => (400, 150);
|
||||||
|
|
||||||
|
public InstrumentMenu(InstrumentBoundUserInterface owner)
|
||||||
|
{
|
||||||
|
IoCManager.InjectDependencies(this);
|
||||||
|
Title = "Instrument";
|
||||||
|
|
||||||
|
_owner = owner;
|
||||||
|
|
||||||
|
_owner.Instrument.OnMidiPlaybackEnded += InstrumentOnMidiPlaybackEnded;
|
||||||
|
|
||||||
|
var margin = new MarginContainer()
|
||||||
|
{
|
||||||
|
SizeFlagsVertical = SizeFlags.FillExpand,
|
||||||
|
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||||
|
MarginTop = 5f,
|
||||||
|
MarginLeft = 5f,
|
||||||
|
MarginRight = -5f,
|
||||||
|
MarginBottom = -5f,
|
||||||
|
};
|
||||||
|
|
||||||
|
margin.SetAnchorAndMarginPreset(LayoutPreset.Wide);
|
||||||
|
|
||||||
|
var vBox = new VBoxContainer()
|
||||||
|
{
|
||||||
|
SizeFlagsVertical = SizeFlags.FillExpand,
|
||||||
|
SeparationOverride = 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
vBox.SetAnchorAndMarginPreset(LayoutPreset.Wide);
|
||||||
|
|
||||||
|
var hBoxTopButtons = new HBoxContainer()
|
||||||
|
{
|
||||||
|
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||||
|
SizeFlagsVertical = SizeFlags.FillExpand,
|
||||||
|
SizeFlagsStretchRatio = 1,
|
||||||
|
Align = BoxContainer.AlignMode.Center
|
||||||
|
};
|
||||||
|
|
||||||
|
midiInputButton = new Button()
|
||||||
|
{
|
||||||
|
Text = "MIDI Input",
|
||||||
|
TextAlign = Button.AlignMode.Center,
|
||||||
|
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||||
|
SizeFlagsStretchRatio = 1,
|
||||||
|
ToggleMode = true,
|
||||||
|
Pressed = _owner.Instrument.IsInputOpen,
|
||||||
|
};
|
||||||
|
|
||||||
|
midiInputButton.OnToggled += MidiInputButtonOnOnToggled;
|
||||||
|
|
||||||
|
var topSpacer = new Control()
|
||||||
|
{
|
||||||
|
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||||
|
SizeFlagsStretchRatio = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
var midiFileButton = new Button()
|
||||||
|
{
|
||||||
|
Text = "Open File",
|
||||||
|
TextAlign = Button.AlignMode.Center,
|
||||||
|
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||||
|
SizeFlagsStretchRatio = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
midiFileButton.OnPressed += MidiFileButtonOnOnPressed;
|
||||||
|
|
||||||
|
var hBoxBottomButtons = new HBoxContainer()
|
||||||
|
{
|
||||||
|
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||||
|
SizeFlagsVertical = SizeFlags.FillExpand,
|
||||||
|
SizeFlagsStretchRatio = 1,
|
||||||
|
Align = BoxContainer.AlignMode.Center
|
||||||
|
};
|
||||||
|
|
||||||
|
midiLoopButton = new Button()
|
||||||
|
{
|
||||||
|
Text = "Loop",
|
||||||
|
TextAlign = Button.AlignMode.Center,
|
||||||
|
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||||
|
SizeFlagsStretchRatio = 1,
|
||||||
|
ToggleMode = true,
|
||||||
|
Disabled = !_owner.Instrument.IsMidiOpen,
|
||||||
|
Pressed = _owner.Instrument.LoopMidi,
|
||||||
|
};
|
||||||
|
|
||||||
|
midiLoopButton.OnToggled += MidiLoopButtonOnOnToggled;
|
||||||
|
|
||||||
|
var bottomSpacer = new Control()
|
||||||
|
{
|
||||||
|
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||||
|
SizeFlagsStretchRatio = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
midiStopButton = new Button()
|
||||||
|
{
|
||||||
|
Text = "Stop",
|
||||||
|
TextAlign = Button.AlignMode.Center,
|
||||||
|
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||||
|
SizeFlagsStretchRatio = 1,
|
||||||
|
Disabled = !_owner.Instrument.IsMidiOpen,
|
||||||
|
};
|
||||||
|
|
||||||
|
midiStopButton.OnPressed += MidiStopButtonOnPressed;
|
||||||
|
|
||||||
|
hBoxBottomButtons.AddChild(midiLoopButton);
|
||||||
|
hBoxBottomButtons.AddChild(bottomSpacer);
|
||||||
|
hBoxBottomButtons.AddChild(midiStopButton);
|
||||||
|
|
||||||
|
hBoxTopButtons.AddChild(midiInputButton);
|
||||||
|
hBoxTopButtons.AddChild(topSpacer);
|
||||||
|
hBoxTopButtons.AddChild(midiFileButton);
|
||||||
|
|
||||||
|
vBox.AddChild(hBoxTopButtons);
|
||||||
|
vBox.AddChild(hBoxBottomButtons);
|
||||||
|
|
||||||
|
margin.AddChild(vBox);
|
||||||
|
|
||||||
|
Contents.AddChild(margin);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InstrumentOnMidiPlaybackEnded()
|
||||||
|
{
|
||||||
|
MidiPlaybackSetButtonsDisabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void MidiPlaybackSetButtonsDisabled(bool disabled)
|
||||||
|
{
|
||||||
|
midiLoopButton.Disabled = disabled;
|
||||||
|
midiStopButton.Disabled = disabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void MidiFileButtonOnOnPressed(BaseButton.ButtonEventArgs obj)
|
||||||
|
{
|
||||||
|
var filename = await _fileDialogManager.OpenFile();
|
||||||
|
|
||||||
|
if (filename == null) return;
|
||||||
|
|
||||||
|
if (!_midiManager.IsMidiFile(filename))
|
||||||
|
{
|
||||||
|
Logger.Warning($"Not a midi file! Chosen file: {filename}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_owner.Instrument.OpenMidi(filename)) return;
|
||||||
|
MidiPlaybackSetButtonsDisabled(false);
|
||||||
|
if(midiInputButton.Pressed)
|
||||||
|
midiInputButton.Pressed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MidiInputButtonOnOnToggled(BaseButton.ButtonToggledEventArgs obj)
|
||||||
|
{
|
||||||
|
if (obj.Pressed)
|
||||||
|
{
|
||||||
|
MidiStopButtonOnPressed(null);
|
||||||
|
_owner.Instrument.OpenInput();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
_owner.Instrument.CloseInput();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MidiStopButtonOnPressed(BaseButton.ButtonEventArgs obj)
|
||||||
|
{
|
||||||
|
MidiPlaybackSetButtonsDisabled(true);
|
||||||
|
_owner.Instrument.CloseMidi();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MidiLoopButtonOnOnToggled(BaseButton.ButtonToggledEventArgs obj)
|
||||||
|
{
|
||||||
|
_owner.Instrument.LoopMidi = obj.Pressed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -145,6 +145,8 @@ namespace Content.Server.GameObjects
|
|||||||
item.Owner.Transform.LocalPosition = Vector2.Zero;
|
item.Owner.Transform.LocalPosition = Vector2.Zero;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_entitySystemManager.GetEntitySystem<InteractionSystem>().HandSelectedInteraction(Owner, item.Owner);
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,126 @@
|
|||||||
|
using Content.Server.GameObjects.EntitySystems;
|
||||||
|
using Content.Shared.GameObjects.Components.Instruments;
|
||||||
|
using Robust.Server.GameObjects;
|
||||||
|
using Robust.Server.GameObjects.Components.UserInterface;
|
||||||
|
using Robust.Server.Interfaces.GameObjects;
|
||||||
|
using Robust.Server.Interfaces.Player;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.Interfaces.GameObjects;
|
||||||
|
using Robust.Shared.Interfaces.Network;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
using Robust.Shared.ViewVariables;
|
||||||
|
|
||||||
|
namespace Content.Server.GameObjects.Components.Instruments
|
||||||
|
{
|
||||||
|
[RegisterComponent]
|
||||||
|
[ComponentReference(typeof(IActivate))]
|
||||||
|
public class InstrumentComponent : SharedInstrumentComponent,
|
||||||
|
IDropped, IHandSelected, IHandDeselected, IActivate, IUse, IThrown
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The client channel currently playing the instrument, or null if there's none.
|
||||||
|
/// </summary>
|
||||||
|
private INetChannel _instrumentPlayer;
|
||||||
|
private bool _handheld;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
private BoundUserInterface _userInterface;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the instrument is an item which can be held or not.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public bool Handheld => _handheld;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
_userInterface = Owner.GetComponent<ServerUserInterfaceComponent>().GetBoundUserInterface(InstrumentUiKey.Key);
|
||||||
|
_userInterface.OnClosed += UserInterfaceOnClosed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ExposeData(ObjectSerializer serializer)
|
||||||
|
{
|
||||||
|
base.ExposeData(serializer);
|
||||||
|
serializer.DataField(ref _handheld, "handheld", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null, IComponent component = null)
|
||||||
|
{
|
||||||
|
base.HandleMessage(message, netChannel, component);
|
||||||
|
// If the client that sent the message isn't the client playing this instrument, we ignore it.
|
||||||
|
if (netChannel != _instrumentPlayer) return;
|
||||||
|
switch (message)
|
||||||
|
{
|
||||||
|
case InstrumentMidiEventMessage midiEventMsg:
|
||||||
|
SendNetworkMessage(midiEventMsg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dropped(DroppedEventArgs eventArgs)
|
||||||
|
{
|
||||||
|
SendNetworkMessage(new InstrumentStopMidiMessage());
|
||||||
|
_instrumentPlayer = null;
|
||||||
|
_userInterface.CloseAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Thrown(ThrownEventArgs eventArgs)
|
||||||
|
{
|
||||||
|
SendNetworkMessage(new InstrumentStopMidiMessage());
|
||||||
|
_instrumentPlayer = null;
|
||||||
|
_userInterface.CloseAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void HandSelected(HandSelectedEventArgs eventArgs)
|
||||||
|
{
|
||||||
|
var session = eventArgs.User?.GetComponent<BasicActorComponent>()?.playerSession;
|
||||||
|
|
||||||
|
if (session == null) return;
|
||||||
|
|
||||||
|
_instrumentPlayer = session.ConnectedClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void HandDeselected(HandDeselectedEventArgs eventArgs)
|
||||||
|
{
|
||||||
|
SendNetworkMessage(new InstrumentStopMidiMessage());
|
||||||
|
_userInterface.CloseAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Activate(ActivateEventArgs eventArgs)
|
||||||
|
{
|
||||||
|
if (Handheld || !eventArgs.User.TryGetComponent(out IActorComponent actor))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (_instrumentPlayer != null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_instrumentPlayer = actor.playerSession.ConnectedClient;
|
||||||
|
OpenUserInterface(actor.playerSession);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool UseEntity(UseEntityEventArgs eventArgs)
|
||||||
|
{
|
||||||
|
if (!eventArgs.User.TryGetComponent(out IActorComponent actor))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(_instrumentPlayer == actor.playerSession.ConnectedClient)
|
||||||
|
OpenUserInterface(actor.playerSession);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UserInterfaceOnClosed(ServerBoundUserInterfaceMessage obj)
|
||||||
|
{
|
||||||
|
if (!Handheld && obj.Session.ConnectedClient == _instrumentPlayer)
|
||||||
|
{
|
||||||
|
_instrumentPlayer = null;
|
||||||
|
SendNetworkMessage(new InstrumentStopMidiMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OpenUserInterface(IPlayerSession session)
|
||||||
|
{
|
||||||
|
_userInterface.Open(session);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -57,6 +57,11 @@ namespace Content.Server.GameObjects
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IActionBlocker.CanDrop()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool IActionBlocker.CanEmote()
|
bool IActionBlocker.CanEmote()
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
@@ -103,6 +108,11 @@ namespace Content.Server.GameObjects
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IActionBlocker.CanDrop()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool IActionBlocker.CanEmote()
|
bool IActionBlocker.CanEmote()
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@@ -169,6 +179,11 @@ namespace Content.Server.GameObjects
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IActionBlocker.CanDrop()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool IActionBlocker.CanEmote()
|
bool IActionBlocker.CanEmote()
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -103,6 +103,11 @@ namespace Content.Server.GameObjects
|
|||||||
return CurrentDamageState.CanSpeak();
|
return CurrentDamageState.CanSpeak();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IActionBlocker.CanDrop()
|
||||||
|
{
|
||||||
|
return CurrentDamageState.CanDrop();
|
||||||
|
}
|
||||||
|
|
||||||
bool IActionBlocker.CanEmote()
|
bool IActionBlocker.CanEmote()
|
||||||
{
|
{
|
||||||
return CurrentDamageState.CanEmote();
|
return CurrentDamageState.CanEmote();
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ namespace Content.Server.GameObjects.EntitySystems
|
|||||||
|
|
||||||
bool CanSpeak();
|
bool CanSpeak();
|
||||||
|
|
||||||
|
bool CanDrop();
|
||||||
|
|
||||||
bool CanEmote();
|
bool CanEmote();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,6 +72,16 @@ namespace Content.Server.GameObjects.EntitySystems
|
|||||||
return canspeak;
|
return canspeak;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool CanDrop(IEntity entity)
|
||||||
|
{
|
||||||
|
bool candrop = true;
|
||||||
|
foreach (var actionblockercomponents in entity.GetAllComponents<IActionBlocker>())
|
||||||
|
{
|
||||||
|
candrop &= actionblockercomponents.CanDrop();
|
||||||
|
}
|
||||||
|
return candrop;
|
||||||
|
}
|
||||||
|
|
||||||
public static bool CanEmote(IEntity entity)
|
public static bool CanEmote(IEntity entity)
|
||||||
{
|
{
|
||||||
bool canemote = true;
|
bool canemote = true;
|
||||||
|
|||||||
@@ -183,6 +183,60 @@ namespace Content.Server.GameObjects.EntitySystems
|
|||||||
public GridCoordinates ClickLocation { get; }
|
public GridCoordinates ClickLocation { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This interface gives components behavior when they're held on the selected hand.
|
||||||
|
/// </summary>
|
||||||
|
public interface IHandSelected
|
||||||
|
{
|
||||||
|
void HandSelected(HandSelectedEventArgs eventArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class HandSelectedEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
public HandSelectedEventArgs(IEntity user)
|
||||||
|
{
|
||||||
|
User = user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEntity User { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This interface gives components behavior when they're held on a deselected hand.
|
||||||
|
/// </summary>
|
||||||
|
public interface IHandDeselected
|
||||||
|
{
|
||||||
|
void HandDeselected(HandDeselectedEventArgs eventArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class HandDeselectedEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
public HandDeselectedEventArgs(IEntity user)
|
||||||
|
{
|
||||||
|
User = user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEntity User { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This interface gives components behavior when they're dropped by a mob.
|
||||||
|
/// </summary>
|
||||||
|
public interface IDropped
|
||||||
|
{
|
||||||
|
void Dropped(DroppedEventArgs eventArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DroppedEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
public DroppedEventArgs(IEntity user)
|
||||||
|
{
|
||||||
|
User = user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEntity User { get; }
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Governs interactions during clicking on entities
|
/// Governs interactions during clicking on entities
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -512,8 +566,8 @@ namespace Content.Server.GameObjects.EntitySystems
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Activates the Use behavior of an object
|
/// Activates the Throw behavior of an object
|
||||||
/// Verifies that the user is capable of doing the use interaction first
|
/// Verifies that the user is capable of doing the throw interaction first
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool TryThrowInteraction(IEntity user, IEntity item)
|
public bool TryThrowInteraction(IEntity user, IEntity item)
|
||||||
{
|
{
|
||||||
@@ -568,6 +622,86 @@ namespace Content.Server.GameObjects.EntitySystems
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Activates the Dropped behavior of an object
|
||||||
|
/// Verifies that the user is capable of doing the drop interaction first
|
||||||
|
/// </summary>
|
||||||
|
public bool TryDroppedInteraction(IEntity user, IEntity item)
|
||||||
|
{
|
||||||
|
if (user == null || item == null || !ActionBlockerSystem.CanDrop(user)) return false;
|
||||||
|
|
||||||
|
DroppedInteraction(user, item);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calls Dropped on all components that implement the IDropped interface
|
||||||
|
/// on an entity that has been dropped.
|
||||||
|
/// </summary>
|
||||||
|
public void DroppedInteraction(IEntity user, IEntity item)
|
||||||
|
{
|
||||||
|
var dropMsg = new DroppedMessage(user, item);
|
||||||
|
RaiseEvent(dropMsg);
|
||||||
|
if (dropMsg.Handled)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var comps = item.GetAllComponents<IDropped>().ToList();
|
||||||
|
|
||||||
|
// Call Land on all components that implement the interface
|
||||||
|
foreach (var comp in comps)
|
||||||
|
{
|
||||||
|
comp.Dropped(new DroppedEventArgs(user));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calls HandSelected on all components that implement the IHandSelected interface
|
||||||
|
/// on an item entity on a hand that has just been selected.
|
||||||
|
/// </summary>
|
||||||
|
public void HandSelectedInteraction(IEntity user, IEntity item)
|
||||||
|
{
|
||||||
|
var dropMsg = new HandSelectedMessage(user, item);
|
||||||
|
RaiseEvent(dropMsg);
|
||||||
|
if (dropMsg.Handled)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var comps = item.GetAllComponents<IHandSelected>().ToList();
|
||||||
|
|
||||||
|
// Call Land on all components that implement the interface
|
||||||
|
foreach (var comp in comps)
|
||||||
|
{
|
||||||
|
comp.HandSelected(new HandSelectedEventArgs(user));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calls HandDeselected on all components that implement the IHandDeselected interface
|
||||||
|
/// on an item entity on a hand that has just been deselected.
|
||||||
|
/// </summary>
|
||||||
|
public void HandDeselectedInteraction(IEntity user, IEntity item)
|
||||||
|
{
|
||||||
|
var dropMsg = new HandDeselectedMessage(user, item);
|
||||||
|
RaiseEvent(dropMsg);
|
||||||
|
if (dropMsg.Handled)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var comps = item.GetAllComponents<IHandDeselected>().ToList();
|
||||||
|
|
||||||
|
// Call Land on all components that implement the interface
|
||||||
|
foreach (var comp in comps)
|
||||||
|
{
|
||||||
|
comp.HandDeselected(new HandDeselectedEventArgs(user));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Will have two behaviors, either "uses" the weapon at range on the entity if it is capable of accepting that action
|
/// Will have two behaviors, either "uses" the weapon at range on the entity if it is capable of accepting that action
|
||||||
/// Or it will use the weapon itself on the position clicked, regardless of what was there
|
/// Or it will use the weapon itself on the position clicked, regardless of what was there
|
||||||
@@ -883,6 +1017,90 @@ namespace Content.Server.GameObjects.EntitySystems
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised when an entity that was thrown lands.
|
||||||
|
/// </summary>
|
||||||
|
[PublicAPI]
|
||||||
|
public class DroppedMessage : EntitySystemMessage
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// If this message has already been "handled" by a previous system.
|
||||||
|
/// </summary>
|
||||||
|
public bool Handled { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Entity that dropped the item.
|
||||||
|
/// </summary>
|
||||||
|
public IEntity User { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Item that was dropped.
|
||||||
|
/// </summary>
|
||||||
|
public IEntity Dropped { get; }
|
||||||
|
|
||||||
|
public DroppedMessage(IEntity user, IEntity dropped)
|
||||||
|
{
|
||||||
|
User = user;
|
||||||
|
Dropped = dropped;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised when an entity item in a hand is selected.
|
||||||
|
/// </summary>
|
||||||
|
[PublicAPI]
|
||||||
|
public class HandSelectedMessage : EntitySystemMessage
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// If this message has already been "handled" by a previous system.
|
||||||
|
/// </summary>
|
||||||
|
public bool Handled { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Entity that owns the selected hand.
|
||||||
|
/// </summary>
|
||||||
|
public IEntity User { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The item in question.
|
||||||
|
/// </summary>
|
||||||
|
public IEntity Item { get; }
|
||||||
|
|
||||||
|
public HandSelectedMessage(IEntity user, IEntity item)
|
||||||
|
{
|
||||||
|
User = user;
|
||||||
|
Item = item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised when an entity item in a hand is deselected.
|
||||||
|
/// </summary>
|
||||||
|
[PublicAPI]
|
||||||
|
public class HandDeselectedMessage : EntitySystemMessage
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// If this message has already been "handled" by a previous system.
|
||||||
|
/// </summary>
|
||||||
|
public bool Handled { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Entity that owns the deselected hand.
|
||||||
|
/// </summary>
|
||||||
|
public IEntity User { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The item in question.
|
||||||
|
/// </summary>
|
||||||
|
public IEntity Item { get; }
|
||||||
|
|
||||||
|
public HandDeselectedMessage(IEntity user, IEntity item)
|
||||||
|
{
|
||||||
|
User = user;
|
||||||
|
Item = item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Raised when an entity is activated in the world.
|
/// Raised when an entity is activated in the world.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ using Robust.Server.Interfaces.Player;
|
|||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.GameObjects.Systems;
|
using Robust.Shared.GameObjects.Systems;
|
||||||
using Robust.Shared.Input;
|
using Robust.Shared.Input;
|
||||||
|
using Robust.Shared.Interfaces.GameObjects;
|
||||||
using Robust.Shared.Interfaces.Map;
|
using Robust.Shared.Interfaces.Map;
|
||||||
using Robust.Shared.Interfaces.Timing;
|
using Robust.Shared.Interfaces.Timing;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
@@ -27,6 +28,7 @@ namespace Content.Server.GameObjects.EntitySystems
|
|||||||
{
|
{
|
||||||
#pragma warning disable 649
|
#pragma warning disable 649
|
||||||
[Dependency] private readonly IMapManager _mapManager;
|
[Dependency] private readonly IMapManager _mapManager;
|
||||||
|
[Dependency] private readonly IEntitySystemManager _entitySystemManager;
|
||||||
#pragma warning restore 649
|
#pragma warning restore 649
|
||||||
|
|
||||||
private const float ThrowForce = 1.5f; // Throwing force of mobs in Newtons
|
private const float ThrowForce = 1.5f; // Throwing force of mobs in Newtons
|
||||||
@@ -94,7 +96,19 @@ namespace Content.Server.GameObjects.EntitySystems
|
|||||||
if (!TryGetAttachedComponent(session as IPlayerSession, out HandsComponent handsComp))
|
if (!TryGetAttachedComponent(session as IPlayerSession, out HandsComponent handsComp))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
var interactionSystem = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<InteractionSystem>();
|
||||||
|
|
||||||
|
var oldItem = handsComp.GetActiveHand;
|
||||||
|
|
||||||
handsComp.SwapHands();
|
handsComp.SwapHands();
|
||||||
|
|
||||||
|
var newItem = handsComp.GetActiveHand;
|
||||||
|
|
||||||
|
if(oldItem != null)
|
||||||
|
interactionSystem.HandDeselectedInteraction(handsComp.Owner, oldItem.Owner);
|
||||||
|
|
||||||
|
if(newItem != null)
|
||||||
|
interactionSystem.HandSelectedInteraction(handsComp.Owner, newItem.Owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool HandleDrop(ICommonSession session, GridCoordinates coords, EntityUid uid)
|
private bool HandleDrop(ICommonSession session, GridCoordinates coords, EntityUid uid)
|
||||||
@@ -102,14 +116,19 @@ namespace Content.Server.GameObjects.EntitySystems
|
|||||||
var ent = ((IPlayerSession) session).AttachedEntity;
|
var ent = ((IPlayerSession) session).AttachedEntity;
|
||||||
|
|
||||||
if (ent == null || !ent.IsValid())
|
if (ent == null || !ent.IsValid())
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
if (!ent.TryGetComponent(out HandsComponent handsComp))
|
if (!ent.TryGetComponent(out HandsComponent handsComp))
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
if (handsComp.GetActiveHand == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!_entitySystemManager.GetEntitySystem<InteractionSystem>().TryDroppedInteraction(ent, handsComp.GetActiveHand.Owner))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(handsComp.GetActiveHand != null && !_entitySystemManager.GetEntitySystem<InteractionSystem>().TryDroppedInteraction(ent, handsComp.GetActiveHand.Owner))
|
||||||
|
return false;
|
||||||
|
|
||||||
if (coords.InRange(_mapManager, ent.Transform.GridPosition, InteractionSystem.InteractionRange))
|
if (coords.InRange(_mapManager, ent.Transform.GridPosition, InteractionSystem.InteractionRange))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
using System;
|
||||||
|
using Robust.Shared.Audio.Midi;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.GameObjects.Components.Instruments
|
||||||
|
{
|
||||||
|
public class SharedInstrumentComponent : Component
|
||||||
|
{
|
||||||
|
public override string Name => "Instrument";
|
||||||
|
public override uint? NetID => ContentNetIDs.INSTRUMENTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This message is sent to the client to completely stop midi input and midi playback.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public class InstrumentStopMidiMessage : ComponentMessage
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This message carries a MidiEvent to be played on clients.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public class InstrumentMidiEventMessage : ComponentMessage
|
||||||
|
{
|
||||||
|
public MidiEvent MidiEvent;
|
||||||
|
public double Timestamp;
|
||||||
|
|
||||||
|
public InstrumentMidiEventMessage(MidiEvent midiEvent, double timestamp)
|
||||||
|
{
|
||||||
|
MidiEvent = midiEvent;
|
||||||
|
Timestamp = timestamp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[NetSerializable, Serializable]
|
||||||
|
public enum InstrumentUiKey
|
||||||
|
{
|
||||||
|
Key,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -35,5 +35,6 @@
|
|||||||
public const uint CARGO_ORDER_DATABASE = 1030;
|
public const uint CARGO_ORDER_DATABASE = 1030;
|
||||||
public const uint GALACTIC_MARKET = 1031;
|
public const uint GALACTIC_MARKET = 1031;
|
||||||
public const uint HAIR = 1032;
|
public const uint HAIR = 1032;
|
||||||
|
public const uint INSTRUMENTS = 1033;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
69
Resources/Prototypes/Entities/buildings/instruments.yml
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
- type: entity
|
||||||
|
name: BaseInstrument
|
||||||
|
id: BaseInstrument
|
||||||
|
components:
|
||||||
|
- type: Instrument
|
||||||
|
handheld: false
|
||||||
|
|
||||||
|
- type: Clickable
|
||||||
|
|
||||||
|
- type: Collidable
|
||||||
|
shapes:
|
||||||
|
- !type:PhysShapeAabb
|
||||||
|
mask: 19
|
||||||
|
layer: 16
|
||||||
|
|
||||||
|
- type: SnapGrid
|
||||||
|
offset: Center
|
||||||
|
|
||||||
|
- type: Damageable
|
||||||
|
- type: Destructible
|
||||||
|
thresholdvalue: 50
|
||||||
|
|
||||||
|
- type: UserInterface
|
||||||
|
interfaces:
|
||||||
|
- key: enum.InstrumentUiKey.Key
|
||||||
|
type: InstrumentBoundUserInterface
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
name: Piano
|
||||||
|
parent: BaseInstrument
|
||||||
|
id: PianoInstrument
|
||||||
|
description: Play Needles Piano Now.
|
||||||
|
components:
|
||||||
|
- type: Instrument
|
||||||
|
program: 1
|
||||||
|
- type: Sprite
|
||||||
|
sprite: Objects/Instruments/musician.rsi
|
||||||
|
state: piano
|
||||||
|
- type: Icon
|
||||||
|
sprite: Objects/Instruments/musician.rsi
|
||||||
|
state: piano
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
name: Minimoog
|
||||||
|
parent: BaseInstrument
|
||||||
|
id: MinimoogInstrument
|
||||||
|
components:
|
||||||
|
- type: Instrument
|
||||||
|
program: 7
|
||||||
|
- type: Sprite
|
||||||
|
sprite: Objects/Instruments/musician.rsi
|
||||||
|
state: minimoog
|
||||||
|
- type: Icon
|
||||||
|
sprite: Objects/Instruments/musician.rsi
|
||||||
|
state: minimoog
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
name: Xylophone
|
||||||
|
parent: BaseInstrument
|
||||||
|
id: XylophoneInstrument
|
||||||
|
components:
|
||||||
|
- type: Instrument
|
||||||
|
program: 13
|
||||||
|
- type: Sprite
|
||||||
|
sprite: Objects/Instruments/musician.rsi
|
||||||
|
state: xylophone
|
||||||
|
- type: Icon
|
||||||
|
sprite: Objects/Instruments/musician.rsi
|
||||||
|
state: xylophone
|
||||||
131
Resources/Prototypes/Entities/items/Instruments.yml
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
- type: entity
|
||||||
|
name: BaseHandheldInstrument
|
||||||
|
parent: BaseItem
|
||||||
|
id: BaseHandheldInstrument
|
||||||
|
components:
|
||||||
|
- type: Instrument
|
||||||
|
handheld: true
|
||||||
|
- type: UserInterface
|
||||||
|
interfaces:
|
||||||
|
- key: enum.InstrumentUiKey.Key
|
||||||
|
type: InstrumentBoundUserInterface
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
name: Synthesizer
|
||||||
|
parent: BaseHandheldInstrument
|
||||||
|
id: SynthesizerInstrument
|
||||||
|
components:
|
||||||
|
- type: Instrument
|
||||||
|
program: 2
|
||||||
|
- type: Sprite
|
||||||
|
texture: Objects/Instruments/musician.rsi/h_synthesizer.png
|
||||||
|
- type: Icon
|
||||||
|
texture: Objects/Instruments/musician.rsi/h_synthesizer.png
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
name: Violin
|
||||||
|
parent: BaseHandheldInstrument
|
||||||
|
id: ViolinInstrument
|
||||||
|
components:
|
||||||
|
- type: Instrument
|
||||||
|
program: 40
|
||||||
|
- type: Sprite
|
||||||
|
texture: Objects/Instruments/musician.rsi/violin.png
|
||||||
|
- type: Icon
|
||||||
|
texture: Objects/Instruments/musician.rsi/violin.png
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
name: Trumpet
|
||||||
|
parent: BaseHandheldInstrument
|
||||||
|
id: TrumpetInstrument
|
||||||
|
components:
|
||||||
|
- type: Instrument
|
||||||
|
program: 56
|
||||||
|
- type: Sprite
|
||||||
|
texture: Objects/Instruments/musician.rsi/trumpet.png
|
||||||
|
- type: Icon
|
||||||
|
texture: Objects/Instruments/musician.rsi/trumpet.png
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
name: Electric Guitar
|
||||||
|
parent: BaseHandheldInstrument
|
||||||
|
id: ElectricGuitarInstrument
|
||||||
|
components:
|
||||||
|
- type: Instrument
|
||||||
|
program: 27
|
||||||
|
- type: Sprite
|
||||||
|
texture: Objects/Instruments/musician.rsi/eguitar.png
|
||||||
|
- type: Icon
|
||||||
|
texture: Objects/Instruments/musician.rsi/eguitar.png
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
name: Accordion
|
||||||
|
parent: BaseHandheldInstrument
|
||||||
|
id: AccordionInstrument
|
||||||
|
components:
|
||||||
|
- type: Instrument
|
||||||
|
program: 21
|
||||||
|
- type: Sprite
|
||||||
|
texture: Objects/Instruments/musician.rsi/accordion.png
|
||||||
|
- type: Icon
|
||||||
|
texture: Objects/Instruments/musician.rsi/accordion.png
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
name: Harmonica
|
||||||
|
parent: BaseHandheldInstrument
|
||||||
|
id: HarmonicaInstrument
|
||||||
|
components:
|
||||||
|
- type: Instrument
|
||||||
|
program: 22
|
||||||
|
- type: Sprite
|
||||||
|
texture: Objects/Instruments/musician.rsi/harmonica.png
|
||||||
|
- type: Icon
|
||||||
|
texture: Objects/Instruments/musician.rsi/harmonica.png
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
name: Recorder
|
||||||
|
parent: BaseHandheldInstrument
|
||||||
|
id: RecorderInstrument
|
||||||
|
components:
|
||||||
|
- type: Instrument
|
||||||
|
program: 74
|
||||||
|
- type: Sprite
|
||||||
|
texture: Objects/Instruments/musician.rsi/recorder.png
|
||||||
|
- type: Icon
|
||||||
|
texture: Objects/Instruments/musician.rsi/recorder.png
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
name: Trombone
|
||||||
|
parent: BaseHandheldInstrument
|
||||||
|
id: TromboneInstrument
|
||||||
|
components:
|
||||||
|
- type: Instrument
|
||||||
|
program: 57
|
||||||
|
- type: Sprite
|
||||||
|
texture: Objects/Instruments/musician.rsi/trombone.png
|
||||||
|
- type: Icon
|
||||||
|
texture: Objects/Instruments/musician.rsi/trombone.png
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
name: Saxophone
|
||||||
|
parent: BaseHandheldInstrument
|
||||||
|
id: SaxophoneInstrument
|
||||||
|
components:
|
||||||
|
- type: Instrument
|
||||||
|
program: 67
|
||||||
|
- type: Sprite
|
||||||
|
texture: Objects/Instruments/musician.rsi/saxophone.png
|
||||||
|
- type: Icon
|
||||||
|
texture: Objects/Instruments/musician.rsi/saxophone.png
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
name: Glockenspiel
|
||||||
|
parent: BaseHandheldInstrument
|
||||||
|
id: GlockenspielInstrument
|
||||||
|
components:
|
||||||
|
- type: Instrument
|
||||||
|
program: 9
|
||||||
|
- type: Sprite
|
||||||
|
texture: Objects/Instruments/musician.rsi/glockenspiel.png
|
||||||
|
- type: Icon
|
||||||
|
texture: Objects/Instruments/musician.rsi/glockenspiel.png
|
||||||
|
After Width: | Height: | Size: 772 B |
|
After Width: | Height: | Size: 411 B |
BIN
Resources/Textures/Objects/Instruments/musician.rsi/drum.png
Normal file
|
After Width: | Height: | Size: 550 B |
|
After Width: | Height: | Size: 518 B |
|
After Width: | Height: | Size: 520 B |
BIN
Resources/Textures/Objects/Instruments/musician.rsi/eguitar.png
Normal file
|
After Width: | Height: | Size: 808 B |
|
After Width: | Height: | Size: 551 B |
BIN
Resources/Textures/Objects/Instruments/musician.rsi/guitar.png
Normal file
|
After Width: | Height: | Size: 926 B |
|
After Width: | Height: | Size: 565 B |
|
After Width: | Height: | Size: 780 B |
@@ -0,0 +1 @@
|
|||||||
|
{"version": 1, "size": {"x": 32, "y": 32}, "license": "CC-BY-SA-3.0", "copyright": "https://github.com/vgstation-coders/vgstation13 at 8d9c91e19cb52713c7f7f1804c2b6f7203f8d331", "states": [{"name": "accordion", "directions": 1, "delays": [[1.0]]}, {"name": "bike_horn", "directions": 1, "delays": [[1.0]]}, {"name": "drum", "directions": 1, "delays": [[1.0]]}, {"name": "drum_bongo", "directions": 1, "delays": [[1.0]]}, {"name": "drum_makeshift", "directions": 1, "delays": [[1.0]]}, {"name": "glockenspiel", "directions": 1, "delays": [[1.0]]}, {"name": "guitar", "directions": 1, "delays": [[1.0]]}, {"name": "harmonica", "directions": 1, "delays": [[1.0]]}, {"name": "minimoog", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "minimoog-broken", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "piano", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "piano-broken", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "recorder", "directions": 1, "delays": [[1.0]]}, {"name": "saxophone", "directions": 1, "delays": [[1.0]]}, {"name": "trombone", "directions": 1, "delays": [[1.0]]}, {"name": "violin", "directions": 1, "delays": [[1.0]]}, {"name": "xylophone", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "xylophone-broken", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}]}
|
||||||
|
After Width: | Height: | Size: 3.1 KiB |
BIN
Resources/Textures/Objects/Instruments/musician.rsi/minimoog.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
BIN
Resources/Textures/Objects/Instruments/musician.rsi/piano.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
BIN
Resources/Textures/Objects/Instruments/musician.rsi/recorder.png
Normal file
|
After Width: | Height: | Size: 414 B |
|
After Width: | Height: | Size: 707 B |
|
After Width: | Height: | Size: 779 B |
BIN
Resources/Textures/Objects/Instruments/musician.rsi/trombone.png
Normal file
|
After Width: | Height: | Size: 373 B |
BIN
Resources/Textures/Objects/Instruments/musician.rsi/trumpet.png
Normal file
|
After Width: | Height: | Size: 965 B |
BIN
Resources/Textures/Objects/Instruments/musician.rsi/violin.png
Normal file
|
After Width: | Height: | Size: 389 B |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
@@ -12,10 +12,14 @@
|
|||||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=UV/@EntryIndexedValue">UV</s:String>
|
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=UV/@EntryIndexedValue">UV</s:String>
|
||||||
<s:String x:Key="/Default/FilterSettingsManager/AttributeFilterXml/@EntryValue"><data /></s:String>
|
<s:String x:Key="/Default/FilterSettingsManager/AttributeFilterXml/@EntryValue"><data /></s:String>
|
||||||
<s:String x:Key="/Default/FilterSettingsManager/CoverageFilterXml/@EntryValue"><data><IncludeFilters /><ExcludeFilters><Filter ModuleMask="*.UnitTesting" ModuleVersionMask="*" ClassMask="*" FunctionMask="*" IsEnabled="True" /></ExcludeFilters></data></s:String>
|
<s:String x:Key="/Default/FilterSettingsManager/CoverageFilterXml/@EntryValue"><data><IncludeFilters /><ExcludeFilters><Filter ModuleMask="*.UnitTesting" ModuleVersionMask="*" ClassMask="*" FunctionMask="*" IsEnabled="True" /></ExcludeFilters></data></s:String>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=autoconnect/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=BYOND/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=BYOND/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:String x:Key="/Default/FilterSettingsManager/AttributeFilterXml/@EntryValue"><data /></s:String>
|
<s:String x:Key="/Default/FilterSettingsManager/AttributeFilterXml/@EntryValue"><data /></s:String>
|
||||||
<s:String x:Key="/Default/FilterSettingsManager/CoverageFilterXml/@EntryValue"><data><IncludeFilters /><ExcludeFilters><Filter ModuleMask="Lidgren.Network" ModuleVersionMask="*" ClassMask="*" FunctionMask="*" IsEnabled="True" /></ExcludeFilters></data></s:String>
|
<s:String x:Key="/Default/FilterSettingsManager/CoverageFilterXml/@EntryValue"><data><IncludeFilters /><ExcludeFilters><Filter ModuleMask="Lidgren.Network" ModuleVersionMask="*" ClassMask="*" FunctionMask="*" IsEnabled="True" /></ExcludeFilters></data></s:String>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Fluidsynth/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=freepats/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Lerp/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Lerp/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Noto/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Noto/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=preemptively/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=preemptively/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=soundfonts/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=swsl/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=swsl/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||||
|
|||||||