From 3fcf58fcb5abcaf56dd7385fa7ef504ec9e90c5c Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 25 Apr 2018 02:47:07 -0400 Subject: rewrite input suppression again (#453) This uses the new Game1.input in SDV 1.3.0.37 to override the game's input more consistently, though it still doesn't intercept clicks correctly yet. --- src/SMAPI/Framework/Input/InputState.cs | 220 ------------------- src/SMAPI/Framework/Input/SInputState.cs | 359 +++++++++++++++++++++++++++++++ src/SMAPI/Framework/SGame.cs | 113 ++-------- src/SMAPI/SModHooks.cs | 48 ----- src/SMAPI/StardewModdingAPI.csproj | 3 +- 5 files changed, 376 insertions(+), 367 deletions(-) delete mode 100644 src/SMAPI/Framework/Input/InputState.cs create mode 100644 src/SMAPI/Framework/Input/SInputState.cs delete mode 100644 src/SMAPI/SModHooks.cs (limited to 'src') diff --git a/src/SMAPI/Framework/Input/InputState.cs b/src/SMAPI/Framework/Input/InputState.cs deleted file mode 100644 index 62337a6c..00000000 --- a/src/SMAPI/Framework/Input/InputState.cs +++ /dev/null @@ -1,220 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Input; -using StardewValley; - -namespace StardewModdingAPI.Framework.Input -{ - /// A summary of input changes during an update frame. - internal class InputState - { - /********* - ** Accessors - *********/ - /// The maximum amount of direction to ignore for the left thumbstick. - private const float LeftThumbstickDeadZone = 0.2f; - - /// The maximum amount of direction to ignore for the right thumbstick. - private const float RightThumbstickDeadZone = 0f; - - - /********* - ** Accessors - *********/ - /// The underlying controller state. - public GamePadState ControllerState { get; } - - /// The underlying keyboard state. - public KeyboardState KeyboardState { get; } - - /// The underlying mouse state. - public MouseState MouseState { get; } - - /// The mouse position on the screen adjusted for the zoom level. - public Point MousePosition { get; } - - /// The buttons which were pressed, held, or released. - public IDictionary ActiveButtons { get; } = new Dictionary(); - - - /********* - ** Public methods - *********/ - /// Construct an empty instance. - public InputState() { } - - /// Construct an instance. - /// The previous input state. - /// The current controller state. - /// The current keyboard state. - /// The current mouse state. - public InputState(InputState previousState, GamePadState controllerState, KeyboardState keyboardState, MouseState mouseState) - { - // init properties - this.ControllerState = controllerState; - this.KeyboardState = keyboardState; - this.MouseState = mouseState; - this.MousePosition = new Point((int)(mouseState.X * (1.0 / Game1.options.zoomLevel)), (int)(mouseState.Y * (1.0 / Game1.options.zoomLevel))); // derived from Game1::getMouseX - - // get button states - SButton[] down = this.GetPressedButtons(keyboardState, mouseState, controllerState).ToArray(); - foreach (SButton button in down) - this.ActiveButtons[button] = this.GetStatus(previousState.GetStatus(button), isDown: true); - foreach (KeyValuePair prev in previousState.ActiveButtons) - { - if (prev.Value.IsDown() && !this.ActiveButtons.ContainsKey(prev.Key)) - this.ActiveButtons[prev.Key] = InputStatus.Released; - } - } - - /// Get the status of a button. - /// The button to check. - public InputStatus GetStatus(SButton button) - { - return this.ActiveButtons.TryGetValue(button, out InputStatus status) ? status : InputStatus.None; - } - - /// Get whether a given button was pressed or held. - /// The button to check. - public bool IsDown(SButton button) - { - return this.GetStatus(button).IsDown(); - } - - /// Get whether any of the given buttons were pressed or held. - /// The buttons to check. - public bool IsAnyDown(InputButton[] buttons) - { - return buttons.Any(button => this.IsDown(button.ToSButton())); - } - - /// Get the current input state. - /// The previous input state. - public static InputState GetState(InputState previousState) - { - GamePadState controllerState = GamePad.GetState(PlayerIndex.One); - KeyboardState keyboardState = Keyboard.GetState(); - MouseState mouseState = Mouse.GetState(); - - return new InputState(previousState, controllerState, keyboardState, mouseState); - } - - /********* - ** Private methods - *********/ - /// Get the status of a button. - /// The previous button status. - /// Whether the button is currently down. - public InputStatus GetStatus(InputStatus oldStatus, bool isDown) - { - if (isDown && oldStatus.IsDown()) - return InputStatus.Held; - if (isDown) - return InputStatus.Pressed; - return InputStatus.Released; - } - - /// Get the buttons pressed in the given stats. - /// The keyboard state. - /// The mouse state. - /// The controller state. - /// Thumbstick direction logic derived from . - private IEnumerable GetPressedButtons(KeyboardState keyboard, MouseState mouse, GamePadState controller) - { - // keyboard - foreach (Keys key in keyboard.GetPressedKeys()) - yield return key.ToSButton(); - - // mouse - if (mouse.LeftButton == ButtonState.Pressed) - yield return SButton.MouseLeft; - if (mouse.RightButton == ButtonState.Pressed) - yield return SButton.MouseRight; - if (mouse.MiddleButton == ButtonState.Pressed) - yield return SButton.MouseMiddle; - if (mouse.XButton1 == ButtonState.Pressed) - yield return SButton.MouseX1; - if (mouse.XButton2 == ButtonState.Pressed) - yield return SButton.MouseX2; - - // controller - if (controller.IsConnected) - { - // main buttons - if (controller.Buttons.A == ButtonState.Pressed) - yield return SButton.ControllerA; - if (controller.Buttons.B == ButtonState.Pressed) - yield return SButton.ControllerB; - if (controller.Buttons.X == ButtonState.Pressed) - yield return SButton.ControllerX; - if (controller.Buttons.Y == ButtonState.Pressed) - yield return SButton.ControllerY; - if (controller.Buttons.LeftStick == ButtonState.Pressed) - yield return SButton.LeftStick; - if (controller.Buttons.RightStick == ButtonState.Pressed) - yield return SButton.RightStick; - if (controller.Buttons.Start == ButtonState.Pressed) - yield return SButton.ControllerStart; - - // directional pad - if (controller.DPad.Up == ButtonState.Pressed) - yield return SButton.DPadUp; - if (controller.DPad.Down == ButtonState.Pressed) - yield return SButton.DPadDown; - if (controller.DPad.Left == ButtonState.Pressed) - yield return SButton.DPadLeft; - if (controller.DPad.Right == ButtonState.Pressed) - yield return SButton.DPadRight; - - // secondary buttons - if (controller.Buttons.Back == ButtonState.Pressed) - yield return SButton.ControllerBack; - if (controller.Buttons.BigButton == ButtonState.Pressed) - yield return SButton.BigButton; - - // shoulders - if (controller.Buttons.LeftShoulder == ButtonState.Pressed) - yield return SButton.LeftShoulder; - if (controller.Buttons.RightShoulder == ButtonState.Pressed) - yield return SButton.RightShoulder; - - // triggers - if (controller.Triggers.Left > 0.2f) - yield return SButton.LeftTrigger; - if (controller.Triggers.Right > 0.2f) - yield return SButton.RightTrigger; - - // left thumbstick direction - if (controller.ThumbSticks.Left.Y > InputState.LeftThumbstickDeadZone) - yield return SButton.LeftThumbstickUp; - if (controller.ThumbSticks.Left.Y < -InputState.LeftThumbstickDeadZone) - yield return SButton.LeftThumbstickDown; - if (controller.ThumbSticks.Left.X > InputState.LeftThumbstickDeadZone) - yield return SButton.LeftThumbstickRight; - if (controller.ThumbSticks.Left.X < -InputState.LeftThumbstickDeadZone) - yield return SButton.LeftThumbstickLeft; - - // right thumbstick direction - if (this.IsRightThumbstickOutsideDeadZone(controller.ThumbSticks.Right)) - { - if (controller.ThumbSticks.Right.Y > 0) - yield return SButton.RightThumbstickUp; - if (controller.ThumbSticks.Right.Y < 0) - yield return SButton.RightThumbstickDown; - if (controller.ThumbSticks.Right.X > 0) - yield return SButton.RightThumbstickRight; - if (controller.ThumbSticks.Right.X < 0) - yield return SButton.RightThumbstickLeft; - } - } - } - - /// Get whether the right thumbstick should be considered outside the dead zone. - /// The right thumbstick value. - private bool IsRightThumbstickOutsideDeadZone(Vector2 direction) - { - return direction.Length() > 0.9f; - } - } -} diff --git a/src/SMAPI/Framework/Input/SInputState.cs b/src/SMAPI/Framework/Input/SInputState.cs new file mode 100644 index 00000000..62defa9f --- /dev/null +++ b/src/SMAPI/Framework/Input/SInputState.cs @@ -0,0 +1,359 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Input; +using StardewValley; + +#pragma warning disable 809 // obsolete override of non-obsolete method (this is deliberate) +namespace StardewModdingAPI.Framework.Input +{ + /// A summary of input changes during an update frame. + internal sealed class SInputState : InputState + { + /********* + ** Accessors + *********/ + /// The maximum amount of direction to ignore for the left thumbstick. + private const float LeftThumbstickDeadZone = 0.2f; + + + /********* + ** Accessors + *********/ + /// The controller state as of the last update. + public GamePadState RealController { get; private set; } + + /// The keyboard state as of the last update. + public KeyboardState RealKeyboard { get; private set; } + + /// The mouse state as of the last update. + public MouseState RealMouse { get; private set; } + + /// A derivative of which suppresses the buttons in . + public GamePadState SuppressedController { get; private set; } + + /// A derivative of which suppresses the buttons in . + public KeyboardState SuppressedKeyboard { get; private set; } + + /// A derivative of which suppresses the buttons in . + public MouseState SuppressedMouse { get; private set; } + + /// The mouse position on the screen adjusted for the zoom level. + public Point MousePosition { get; private set; } + + /// The buttons which were pressed, held, or released. + public IDictionary ActiveButtons { get; private set; } = new Dictionary(); + + /// The buttons to suppress when the game next handles input. Each button is suppressed until it's released. + public HashSet SuppressButtons { get; } = new HashSet(); + + + /********* + ** Public methods + *********/ + /// Get a copy of the current state. + public SInputState Clone() + { + return new SInputState + { + ActiveButtons = this.ActiveButtons, + RealController = this.RealController, + RealKeyboard = this.RealKeyboard, + RealMouse = this.RealMouse, + MousePosition = this.MousePosition + }; + } + + /// This method is called by the game, and does nothing since SMAPI will already have updated by that point. + [Obsolete("This method should only be called by the game itself.")] + public override void Update() { } + + /// Update the current button statuses for the given tick. + public void TrueUpdate() + { + try + { + // get new states + GamePadState realController = GamePad.GetState(PlayerIndex.One); + KeyboardState realKeyboard = Keyboard.GetState(); + MouseState realMouse = Mouse.GetState(); + Point mousePosition = new Point((int)(this.RealMouse.X * (1.0 / Game1.options.zoomLevel)), (int)(this.RealMouse.Y * (1.0 / Game1.options.zoomLevel))); // derived from Game1::getMouseX + var activeButtons = this.DeriveStatuses(this.ActiveButtons, realKeyboard, realMouse, realController); + + // get suppressed states + GamePadState suppressedController = realController; + KeyboardState suppressedKeyboard = realKeyboard; + MouseState suppressedMouse = realMouse; + if (this.SuppressButtons.Count > 0) + this.UpdateSuppression(activeButtons, ref suppressedKeyboard, ref suppressedMouse, ref suppressedController); + + // update + this.ActiveButtons = activeButtons; + this.RealController = realController; + this.RealKeyboard = realKeyboard; + this.RealMouse = realMouse; + this.SuppressedController = suppressedController; + this.SuppressedKeyboard = suppressedKeyboard; + this.SuppressedMouse = suppressedMouse; + this.MousePosition = mousePosition; + } + catch (InvalidOperationException) + { + // GetState() may crash for some players if window doesn't have focus but game1.IsActive == true + } + } + + /// Get the gamepad state visible to the game. + [Obsolete("This method should only be called by the game itself.")] + public override GamePadState GetGamePadState() + { + return this.ShouldSuppressNow() + ? this.SuppressedController + : this.RealController; + } + + /// Get the keyboard state visible to the game. + [Obsolete("This method should only be called by the game itself.")] + public override KeyboardState GetKeyboardState() + { + return this.ShouldSuppressNow() + ? this.SuppressedKeyboard + : this.RealKeyboard; + } + + /// Get the keyboard state visible to the game. + [Obsolete("This method should only be called by the game itself.")] + public override MouseState GetMouseState() + { + return this.ShouldSuppressNow() + ? this.SuppressedMouse + : this.RealMouse; + } + + /// Get whether a given button was pressed or held. + /// The button to check. + public bool IsDown(SButton button) + { + return this.GetStatus(this.ActiveButtons, button).IsDown(); + } + + /// Get whether any of the given buttons were pressed or held. + /// The buttons to check. + public bool IsAnyDown(InputButton[] buttons) + { + return buttons.Any(button => this.IsDown(button.ToSButton())); + } + + /// Apply input suppression for the given input states. + /// The current button states to check. + /// The game's keyboard state for the current tick. + /// The game's mouse state for the current tick. + /// The game's controller state for the current tick. + public void UpdateSuppression(IDictionary activeButtons, ref KeyboardState keyboardState, ref MouseState mouseState, ref GamePadState gamePadState) + { + // stop suppressing buttons once released + if (this.SuppressButtons.Count != 0) + this.SuppressButtons.RemoveWhere(p => !this.GetStatus(activeButtons, p).IsDown()); + if (this.SuppressButtons.Count == 0) + return; + + // gather info + HashSet keyboardButtons = new HashSet(); + HashSet controllerButtons = new HashSet(); + HashSet mouseButtons = new HashSet(); + foreach (SButton button in this.SuppressButtons) + { + if (button == SButton.MouseLeft || button == SButton.MouseMiddle || button == SButton.MouseRight || button == SButton.MouseX1 || button == SButton.MouseX2) + mouseButtons.Add(button); + else if (button.TryGetKeyboard(out Keys key)) + keyboardButtons.Add(key); + else if (gamePadState.IsConnected && button.TryGetController(out Buttons _)) + controllerButtons.Add(button); + } + + // suppress keyboard keys + if (keyboardState.GetPressedKeys().Any() && keyboardButtons.Any()) + keyboardState = new KeyboardState(keyboardState.GetPressedKeys().Except(keyboardButtons).ToArray()); + + // suppress controller keys + if (gamePadState.IsConnected && controllerButtons.Any()) + { + GamePadStateBuilder builder = new GamePadStateBuilder(gamePadState); + builder.SuppressButtons(controllerButtons); + gamePadState = builder.ToGamePadState(); + } + + // suppress mouse buttons + if (mouseButtons.Any()) + { + mouseState = new MouseState( + x: mouseState.X, + y: mouseState.Y, + scrollWheel: mouseState.ScrollWheelValue, + leftButton: mouseButtons.Contains(SButton.MouseLeft) ? ButtonState.Pressed : mouseState.LeftButton, + middleButton: mouseButtons.Contains(SButton.MouseMiddle) ? ButtonState.Pressed : mouseState.MiddleButton, + rightButton: mouseButtons.Contains(SButton.MouseRight) ? ButtonState.Pressed : mouseState.RightButton, + xButton1: mouseButtons.Contains(SButton.MouseX1) ? ButtonState.Pressed : mouseState.XButton1, + xButton2: mouseButtons.Contains(SButton.MouseX2) ? ButtonState.Pressed : mouseState.XButton2 + ); + } + } + + + /********* + ** Private methods + *********/ + /// Whether input should be suppressed in the current context. + private bool ShouldSuppressNow() + { + return Game1.chatBox != null && !Game1.chatBox.isActive(); + } + + /// Get the status of all pressed or released buttons relative to their previous status. + /// The previous button statuses. + /// The keyboard state. + /// The mouse state. + /// The controller state. + private IDictionary DeriveStatuses(IDictionary previousStatuses, KeyboardState keyboard, MouseState mouse, GamePadState controller) + { + IDictionary activeButtons = new Dictionary(); + + // handle pressed keys + SButton[] down = this.GetPressedButtons(keyboard, mouse, controller).ToArray(); + foreach (SButton button in down) + activeButtons[button] = this.DeriveStatus(this.GetStatus(previousStatuses, button), isDown: true); + + // handle released keys + foreach (KeyValuePair prev in activeButtons) + { + if (prev.Value.IsDown() && !activeButtons.ContainsKey(prev.Key)) + activeButtons[prev.Key] = InputStatus.Released; + } + + return activeButtons; + } + + /// Get the status of a button relative to its previous status. + /// The previous button status. + /// Whether the button is currently down. + private InputStatus DeriveStatus(InputStatus oldStatus, bool isDown) + { + if (isDown && oldStatus.IsDown()) + return InputStatus.Held; + if (isDown) + return InputStatus.Pressed; + return InputStatus.Released; + } + + /// Get the status of a button. + /// The current button states to check. + /// The button to check. + private InputStatus GetStatus(IDictionary activeButtons, SButton button) + { + return activeButtons.TryGetValue(button, out InputStatus status) ? status : InputStatus.None; + } + + /// Get the buttons pressed in the given stats. + /// The keyboard state. + /// The mouse state. + /// The controller state. + /// Thumbstick direction logic derived from . + private IEnumerable GetPressedButtons(KeyboardState keyboard, MouseState mouse, GamePadState controller) + { + // keyboard + foreach (Keys key in keyboard.GetPressedKeys()) + yield return key.ToSButton(); + + // mouse + if (mouse.LeftButton == ButtonState.Pressed) + yield return SButton.MouseLeft; + if (mouse.RightButton == ButtonState.Pressed) + yield return SButton.MouseRight; + if (mouse.MiddleButton == ButtonState.Pressed) + yield return SButton.MouseMiddle; + if (mouse.XButton1 == ButtonState.Pressed) + yield return SButton.MouseX1; + if (mouse.XButton2 == ButtonState.Pressed) + yield return SButton.MouseX2; + + // controller + if (controller.IsConnected) + { + // main buttons + if (controller.Buttons.A == ButtonState.Pressed) + yield return SButton.ControllerA; + if (controller.Buttons.B == ButtonState.Pressed) + yield return SButton.ControllerB; + if (controller.Buttons.X == ButtonState.Pressed) + yield return SButton.ControllerX; + if (controller.Buttons.Y == ButtonState.Pressed) + yield return SButton.ControllerY; + if (controller.Buttons.LeftStick == ButtonState.Pressed) + yield return SButton.LeftStick; + if (controller.Buttons.RightStick == ButtonState.Pressed) + yield return SButton.RightStick; + if (controller.Buttons.Start == ButtonState.Pressed) + yield return SButton.ControllerStart; + + // directional pad + if (controller.DPad.Up == ButtonState.Pressed) + yield return SButton.DPadUp; + if (controller.DPad.Down == ButtonState.Pressed) + yield return SButton.DPadDown; + if (controller.DPad.Left == ButtonState.Pressed) + yield return SButton.DPadLeft; + if (controller.DPad.Right == ButtonState.Pressed) + yield return SButton.DPadRight; + + // secondary buttons + if (controller.Buttons.Back == ButtonState.Pressed) + yield return SButton.ControllerBack; + if (controller.Buttons.BigButton == ButtonState.Pressed) + yield return SButton.BigButton; + + // shoulders + if (controller.Buttons.LeftShoulder == ButtonState.Pressed) + yield return SButton.LeftShoulder; + if (controller.Buttons.RightShoulder == ButtonState.Pressed) + yield return SButton.RightShoulder; + + // triggers + if (controller.Triggers.Left > 0.2f) + yield return SButton.LeftTrigger; + if (controller.Triggers.Right > 0.2f) + yield return SButton.RightTrigger; + + // left thumbstick direction + if (controller.ThumbSticks.Left.Y > SInputState.LeftThumbstickDeadZone) + yield return SButton.LeftThumbstickUp; + if (controller.ThumbSticks.Left.Y < -SInputState.LeftThumbstickDeadZone) + yield return SButton.LeftThumbstickDown; + if (controller.ThumbSticks.Left.X > SInputState.LeftThumbstickDeadZone) + yield return SButton.LeftThumbstickRight; + if (controller.ThumbSticks.Left.X < -SInputState.LeftThumbstickDeadZone) + yield return SButton.LeftThumbstickLeft; + + // right thumbstick direction + if (this.IsRightThumbstickOutsideDeadZone(controller.ThumbSticks.Right)) + { + if (controller.ThumbSticks.Right.Y > 0) + yield return SButton.RightThumbstickUp; + if (controller.ThumbSticks.Right.Y < 0) + yield return SButton.RightThumbstickDown; + if (controller.ThumbSticks.Right.X > 0) + yield return SButton.RightThumbstickRight; + if (controller.ThumbSticks.Right.X < 0) + yield return SButton.RightThumbstickLeft; + } + } + } + + /// Get whether the right thumbstick should be considered outside the dead zone. + /// The right thumbstick value. + private bool IsRightThumbstickOutsideDeadZone(Vector2 direction) + { + return direction.Length() > 0.9f; + } + } +} diff --git a/src/SMAPI/Framework/SGame.cs b/src/SMAPI/Framework/SGame.cs index 4f5bd96b..182b90fc 100644 --- a/src/SMAPI/Framework/SGame.cs +++ b/src/SMAPI/Framework/SGame.cs @@ -53,6 +53,9 @@ namespace StardewModdingAPI.Framework /// Manages SMAPI events for mods. private readonly EventManager Events; + /// Manages input visible to the game. + private SInputState Input => (SInputState)Game1.input; + /// The maximum number of consecutive attempts SMAPI should make to recover from a draw error. private readonly Countdown DrawCrashTimer = new Countdown(60); // 60 ticks = roughly one second @@ -78,9 +81,6 @@ namespace StardewModdingAPI.Framework /**** ** Game state ****/ - /// The player input as of the previous tick. - private InputState PreviousInput = new InputState(); - /// The underlying watchers for convenience. These are accessible individually as separate properties. private readonly List Watchers = new List(); @@ -120,9 +120,6 @@ namespace StardewModdingAPI.Framework /// Simplifies access to private game code. private readonly Reflector Reflection; - /// The buttons to suppress when the game next handles input. Each button is suppressed until it's released. - private readonly HashSet SuppressButtons = new HashSet(); - /********* ** Accessors @@ -157,7 +154,7 @@ namespace StardewModdingAPI.Framework this.OnGameExiting = onGameExiting; if (this.ContentCore == null) // shouldn't happen since CreateContentManager is called first, but let's init here just in case this.ContentCore = new ContentCore(this.Content.ServiceProvider, this.Content.RootDirectory, Thread.CurrentThread.CurrentUICulture, this.Monitor, reflection); - Game1.hooks = new SModHooks(this.UpdateControlInput); + Game1.input = new SInputState(); // init watchers Game1.locations = new ObservableCollection(); @@ -289,7 +286,7 @@ namespace StardewModdingAPI.Framework if (this.FirstUpdate) this.OnGameInitialised(); - + /********* ** Update context *********/ @@ -390,16 +387,9 @@ namespace StardewModdingAPI.Framework *********/ if (Game1.game1.IsActive) { - // get input state - InputState inputState; - try - { - inputState = InputState.GetState(this.PreviousInput); - } - catch (InvalidOperationException) // GetState() may crash for some players if window doesn't have focus but game1.IsActive == true - { - inputState = this.PreviousInput; - } + SInputState previousInputState = this.Input.Clone(); + SInputState inputState = this.Input; + inputState.TrueUpdate(); // raise events bool isChatInput = Game1.IsChatting || (Context.IsMultiplayer && Context.IsWorldReady && Game1.activeClickableMenu == null && Game1.currentMinigame == null && inputState.IsAnyDown(Game1.options.chatButton)); @@ -425,7 +415,7 @@ namespace StardewModdingAPI.Framework if (status == InputStatus.Pressed) { - this.Events.Input_ButtonPressed.Raise(new EventArgsInput(button, cursor, button.IsActionButton(), button.IsUseToolButton(), this.SuppressButtons)); + this.Events.Input_ButtonPressed.Raise(new EventArgsInput(button, cursor, button.IsActionButton(), button.IsUseToolButton(), inputState.SuppressButtons)); // legacy events if (button.TryGetKeyboard(out Keys key)) @@ -436,14 +426,14 @@ namespace StardewModdingAPI.Framework else if (button.TryGetController(out Buttons controllerButton)) { if (controllerButton == Buttons.LeftTrigger || controllerButton == Buttons.RightTrigger) - this.Events.Control_ControllerTriggerPressed.Raise(new EventArgsControllerTriggerPressed(PlayerIndex.One, controllerButton, controllerButton == Buttons.LeftTrigger ? inputState.ControllerState.Triggers.Left : inputState.ControllerState.Triggers.Right)); + this.Events.Control_ControllerTriggerPressed.Raise(new EventArgsControllerTriggerPressed(PlayerIndex.One, controllerButton, controllerButton == Buttons.LeftTrigger ? inputState.RealController.Triggers.Left : inputState.RealController.Triggers.Right)); else this.Events.Control_ControllerButtonPressed.Raise(new EventArgsControllerButtonPressed(PlayerIndex.One, controllerButton)); } } else if (status == InputStatus.Released) { - this.Events.Input_ButtonReleased.Raise(new EventArgsInput(button, cursor, button.IsActionButton(), button.IsUseToolButton(), this.SuppressButtons)); + this.Events.Input_ButtonReleased.Raise(new EventArgsInput(button, cursor, button.IsActionButton(), button.IsUseToolButton(), inputState.SuppressButtons)); // legacy events if (button.TryGetKeyboard(out Keys key)) @@ -454,7 +444,7 @@ namespace StardewModdingAPI.Framework else if (button.TryGetController(out Buttons controllerButton)) { if (controllerButton == Buttons.LeftTrigger || controllerButton == Buttons.RightTrigger) - this.Events.Control_ControllerTriggerReleased.Raise(new EventArgsControllerTriggerReleased(PlayerIndex.One, controllerButton, controllerButton == Buttons.LeftTrigger ? inputState.ControllerState.Triggers.Left : inputState.ControllerState.Triggers.Right)); + this.Events.Control_ControllerTriggerReleased.Raise(new EventArgsControllerTriggerReleased(PlayerIndex.One, controllerButton, controllerButton == Buttons.LeftTrigger ? inputState.RealController.Triggers.Left : inputState.RealController.Triggers.Right)); else this.Events.Control_ControllerButtonReleased.Raise(new EventArgsControllerButtonReleased(PlayerIndex.One, controllerButton)); } @@ -462,14 +452,11 @@ namespace StardewModdingAPI.Framework } // raise legacy state-changed events - if (inputState.KeyboardState != this.PreviousInput.KeyboardState) - this.Events.Control_KeyboardChanged.Raise(new EventArgsKeyboardStateChanged(this.PreviousInput.KeyboardState, inputState.KeyboardState)); - if (inputState.MouseState != this.PreviousInput.MouseState) - this.Events.Control_MouseChanged.Raise(new EventArgsMouseStateChanged(this.PreviousInput.MouseState, inputState.MouseState, this.PreviousInput.MousePosition, inputState.MousePosition)); + if (inputState.RealKeyboard != previousInputState.RealKeyboard) + this.Events.Control_KeyboardChanged.Raise(new EventArgsKeyboardStateChanged(previousInputState.RealKeyboard, inputState.RealKeyboard)); + if (inputState.RealMouse != previousInputState.RealMouse) + this.Events.Control_MouseChanged.Raise(new EventArgsMouseStateChanged(previousInputState.RealMouse, inputState.RealMouse, previousInputState.MousePosition, inputState.MousePosition)); } - - // track state - this.PreviousInput = inputState; } /********* @@ -624,17 +611,6 @@ namespace StardewModdingAPI.Framework } } - /// Read the current input state for handling. - /// The game's keyboard state for the current tick. - /// The game's mouse state for the current tick. - /// The game's controller state for the current tick. - /// The game's default logic. - public void UpdateControlInput(ref KeyboardState keyboardState, ref MouseState mouseState, ref GamePadState gamePadState, Action defaultLogic) - { - this.ApplySuppression(ref keyboardState, ref mouseState, ref gamePadState); - defaultLogic(); - } - /// The method called to draw everything to the screen. /// A snapshot of the game timing state. protected override void Draw(GameTime gameTime) @@ -1256,63 +1232,6 @@ namespace StardewModdingAPI.Framework /**** ** Methods ****/ - /// Apply input suppression for the given input states. - /// The game's keyboard state for the current tick. - /// The game's mouse state for the current tick. - /// The game's controller state for the current tick. - private void ApplySuppression(ref KeyboardState keyboardState, ref MouseState mouseState, ref GamePadState gamePadState) - { - // stop suppressing buttons once released - if (this.SuppressButtons.Count != 0) - { - InputState inputState = new InputState(this.PreviousInput, gamePadState, keyboardState, mouseState); - this.SuppressButtons.RemoveWhere(p => !inputState.IsDown(p)); - } - if (this.SuppressButtons.Count == 0) - return; - - // gather info - HashSet keyboardButtons = new HashSet(); - HashSet controllerButtons = new HashSet(); - HashSet mouseButtons = new HashSet(); - foreach (SButton button in this.SuppressButtons) - { - if (button == SButton.MouseLeft || button == SButton.MouseMiddle || button == SButton.MouseRight || button == SButton.MouseX1 || button == SButton.MouseX2) - mouseButtons.Add(button); - else if (button.TryGetKeyboard(out Keys key)) - keyboardButtons.Add(key); - else if (gamePadState.IsConnected && button.TryGetController(out Buttons _)) - controllerButtons.Add(button); - } - - // suppress keyboard keys - if (keyboardState.GetPressedKeys().Any() && keyboardButtons.Any()) - keyboardState = new KeyboardState(keyboardState.GetPressedKeys().Except(keyboardButtons).ToArray()); - - // suppress controller keys - if (gamePadState.IsConnected && controllerButtons.Any()) - { - GamePadStateBuilder builder = new GamePadStateBuilder(gamePadState); - builder.SuppressButtons(controllerButtons); - gamePadState = builder.ToGamePadState(); - } - - // suppress mouse buttons - if (mouseButtons.Any()) - { - mouseState = new MouseState( - x: mouseState.X, - y: mouseState.Y, - scrollWheel: mouseState.ScrollWheelValue, - leftButton: mouseButtons.Contains(SButton.MouseLeft) ? ButtonState.Pressed : mouseState.LeftButton, - middleButton: mouseButtons.Contains(SButton.MouseMiddle) ? ButtonState.Pressed : mouseState.MiddleButton, - rightButton: mouseButtons.Contains(SButton.MouseRight) ? ButtonState.Pressed : mouseState.RightButton, - xButton1: mouseButtons.Contains(SButton.MouseX1) ? ButtonState.Pressed : mouseState.XButton1, - xButton2: mouseButtons.Contains(SButton.MouseX2) ? ButtonState.Pressed : mouseState.XButton2 - ); - } - } - /// Perform any cleanup needed when the player unloads a save and returns to the title screen. private void CleanupAfterReturnToTitle() { diff --git a/src/SMAPI/SModHooks.cs b/src/SMAPI/SModHooks.cs deleted file mode 100644 index 1b88d606..00000000 --- a/src/SMAPI/SModHooks.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System; -using Microsoft.Xna.Framework.Input; -using StardewValley; - -namespace StardewModdingAPI -{ - /// Intercepts predefined Stardew Valley mod hooks. - internal class SModHooks : ModHooks - { - /********* - ** Delegates - *********/ - /// A delegate invoked by the hook. - /// The game's keyboard state for the current tick. - /// The game's mouse state for the current tick. - /// The game's controller state for the current tick. - /// The game's default logic. - public delegate void UpdateControlInputDelegate(ref KeyboardState keyboardState, ref MouseState mouseState, ref GamePadState gamePadState, Action action); - - - /********* - ** Properties - *********/ - /// The callback for . - private readonly UpdateControlInputDelegate UpdateControlInputHandler; - - - /********* - ** Public methods - *********/ - /// Construct an instance. - /// The callback for . - public SModHooks(UpdateControlInputDelegate updateControlInputHandler) - { - this.UpdateControlInputHandler = updateControlInputHandler; - } - - /// A hook invoked before the game processes player input. - /// The game's keyboard state for the current tick. - /// The game's mouse state for the current tick. - /// The game's controller state for the current tick. - /// The game's default logic. - public override void OnGame1_UpdateControlInput(ref KeyboardState keyboardState, ref MouseState mouseState, ref GamePadState gamePadState, Action action) - { - this.UpdateControlInputHandler(ref keyboardState, ref mouseState, ref gamePadState, action); - } - } -} diff --git a/src/SMAPI/StardewModdingAPI.csproj b/src/SMAPI/StardewModdingAPI.csproj index d7719e27..560d7bf4 100644 --- a/src/SMAPI/StardewModdingAPI.csproj +++ b/src/SMAPI/StardewModdingAPI.csproj @@ -92,7 +92,7 @@ - + @@ -263,7 +263,6 @@ - -- cgit