From e48cdc65067425ceb5ec3ff45714ce8d22c133d3 Mon Sep 17 00:00:00 2001 From: bonnibel Date: Thu, 9 Jan 2025 16:21:51 -0800 Subject: [PATCH] Support mocking game input --- bang | 2 +- src/Murder.Editor/Architect.cs | 3 ++ src/Murder/Core/Input/PlayerInput.cs | 73 ++++++++++++++++++++++++-- src/Murder/Core/Input/VirtualAxis.cs | 27 ++++++++++ src/Murder/Core/Input/VirtualButton.cs | 12 +++++ 5 files changed, 112 insertions(+), 5 deletions(-) diff --git a/bang b/bang index 9f5523eda..114e1913c 160000 --- a/bang +++ b/bang @@ -1 +1 @@ -Subproject commit 9f5523edad25a448c64ad8601b76e35e6c97620b +Subproject commit 114e1913c90931bf1816a0c90de93402a275f623 diff --git a/src/Murder.Editor/Architect.cs b/src/Murder.Editor/Architect.cs index c9384f4ad..7b591004b 100644 --- a/src/Murder.Editor/Architect.cs +++ b/src/Murder.Editor/Architect.cs @@ -163,7 +163,10 @@ public override void SetWindowSize(Point screenSize, bool remember) private void QuitToEditor() { Microsoft.Xna.Framework.Media.MediaPlayer.Stop(); + Resume(); + Input.LockInput(false); + SoundPlayer.Stop(Murder.Core.Sounds.SoundLayer.Any, fadeOut: false); GameLogger.Verify(_sceneLoader is not null); diff --git a/src/Murder/Core/Input/PlayerInput.cs b/src/Murder/Core/Input/PlayerInput.cs index 166dc80eb..efd161a71 100644 --- a/src/Murder/Core/Input/PlayerInput.cs +++ b/src/Murder/Core/Input/PlayerInput.cs @@ -4,16 +4,21 @@ using Murder.Utilities; using System.Collections.Immutable; using System.Diagnostics; +using System.Numerics; using System.Text; namespace Murder.Core.Input { public class PlayerInput { - public int[] AllButtons => _buttons.Keys.ToArray(); - public int[] AllAxis => _axis.Keys.ToArray(); - private readonly Dictionary _buttons = new(); - private readonly Dictionary _axis = new(); + // Debug only + public int[] AllButtons => [.. _buttons.Keys]; + + // Debug only + public int[] AllAxis => [.. _axis.Keys]; + + private readonly Dictionary _buttons = []; + private readonly Dictionary _axis = []; private KeyboardState _rawPreviousKeyboardState; private KeyboardState _rawCurrentKeyboardState; @@ -38,6 +43,11 @@ public class PlayerInput public bool MouseConsumed = false; + /// + /// This will freeze any external input from the user. + /// + private bool _lockInput = false; + /// /// Scrollwheel delta /// @@ -55,6 +65,39 @@ public int ScrollWheel private readonly KeyboardState _emptyKeyboardState = new KeyboardState(); private readonly MouseState _emptyMouseState = new MouseState(); + public void LockInput(bool @lock) + { + if (!@lock) + { + _lockInput = false; + return; + } + + UpdateOnEmpty(); + _lockInput = true; + } + + public void MockInput(int button) + { + if (_buttons.TryGetValue(button, out VirtualButton? virtualButton)) + { + virtualButton.Press(); + } + } + + public void MockInput(int axis, Vector2 value) + { + if (_axis.TryGetValue(axis, out VirtualAxis? virtualAxis)) + { + virtualAxis.Press(value); + } + } + + public void MockNoInput() + { + UpdateOnEmpty(); + } + public VirtualButton GetOrCreateButton(int button) { if (!_buttons.ContainsKey(button) || _buttons[button] == null) @@ -149,6 +192,11 @@ public void Register(int button, params MouseButtons[] buttons) public void Update() { + if (_lockInput) + { + return; + } + _previousKeyboardState = _currentKeyboardState; if (!KeyboardConsumed) { @@ -201,6 +249,22 @@ public void Update() Calculator.RoundToInt(mouseState.Y)); } + public void UpdateOnEmpty() + { + _currentKeyboardState = _emptyKeyboardState; + + InputState inputState = new(_currentKeyboardState, gamePadState: new(), _emptyMouseState); + foreach (var button in _buttons) + { + button.Value.Update(inputState); + } + + foreach (var axis in _axis) + { + axis.Value.Update(inputState); + } + } + public void Bind(int button, Action action) { GetOrCreateButton(button).OnPress += action; @@ -734,6 +798,7 @@ public void ListenToKeyboardInput(bool enable, int maxCharacters = 32) } public string GetKeyboardInput() => _userKeyboardInput.ToString(); + public void SetKeyboardInput(string value) { _userKeyboardInput.Clear(); diff --git a/src/Murder/Core/Input/VirtualAxis.cs b/src/Murder/Core/Input/VirtualAxis.cs index 314076edf..e6f52a1d9 100644 --- a/src/Murder/Core/Input/VirtualAxis.cs +++ b/src/Murder/Core/Input/VirtualAxis.cs @@ -136,6 +136,33 @@ public void Update(InputState inputState) Consumed = false; } + public void Press(Vector2 value) + { + PreviousValue = Value; + IntPreviousValue = IntValue; + + Down = true; + Consumed = false; + + Value = value; + PressedValue = new Point(MathF.Sign(Value.X), MathF.Sign(Value.Y)); + IntValue = new Point(Calculator.PolarSnapToInt(Value.X), Calculator.PolarSnapToInt(Value.Y)); + + if (PressedX) + { + _pressedXStart = Game.NowUnscaled; + _nextXTick = Game.NowUnscaled + _firstTickDelay; + _tickX = true; + } + + if (PressedY) + { + _pressedYStart = Game.NowUnscaled; + _nextYTick = Game.NowUnscaled + _firstTickDelay; + _tickY = true; + } + } + internal string GetDescriptor() { return StringHelper.ToHumanList(GetActiveButtonDescriptions(), ",", "or"); diff --git a/src/Murder/Core/Input/VirtualButton.cs b/src/Murder/Core/Input/VirtualButton.cs index 16b6075de..096f9998e 100644 --- a/src/Murder/Core/Input/VirtualButton.cs +++ b/src/Murder/Core/Input/VirtualButton.cs @@ -52,6 +52,18 @@ public void Update(InputState inputState) } } + /// + /// Force pressing the button. + /// + public void Press() + { + Previous = false; + Down = true; + Consumed = false; + + LastPressed = Game.NowUnscaled; + } + public string GetDescriptor() { return StringHelper.ToHumanList(GetActiveDescriptors(), ",", "or");