diff --git a/docs/release-notes.md b/docs/release-notes.md index ece388c7..d1a78aaa 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -23,6 +23,7 @@ * Fixed error if a mod loads a PNG while the game is loading (e.g. custom map tilesheets via `IAssetLoader`). * Fixed assets loaded by temporary content managers not being editable by mods. * Fixed assets not reloaded consistently when the player switches language. + * Fixed input suppression not working consistently for clicks. * Fixed console command input not saved to the log. * Fixed `helper.ModRegistry.GetApi` interface validation errors not mentioning which interface caused the issue. * **Breaking changes** (see [migration guide](https://stardewvalleywiki.com/Modding:Migrate_to_Stardew_Valley_1.3)): diff --git a/src/SMAPI/Framework/Input/SInputState.cs b/src/SMAPI/Framework/Input/SInputState.cs index 1b224737..5e8efa62 100644 --- a/src/SMAPI/Framework/Input/SInputState.cs +++ b/src/SMAPI/Framework/Input/SInputState.cs @@ -81,22 +81,16 @@ namespace StardewModdingAPI.Framework.Input 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 + // update real states this.ActiveButtons = activeButtons; this.RealController = realController; this.RealKeyboard = realKeyboard; this.RealMouse = realMouse; - this.SuppressedController = suppressedController; - this.SuppressedKeyboard = suppressedKeyboard; - this.SuppressedMouse = suppressedMouse; this.MousePosition = mousePosition; + + // update suppressed states + this.SuppressButtons.RemoveWhere(p => !this.GetStatus(activeButtons, p).IsDown()); + this.UpdateSuppression(); } catch (InvalidOperationException) { @@ -104,6 +98,20 @@ namespace StardewModdingAPI.Framework.Input } } + /// Apply input suppression to current input. + public void UpdateSuppression() + { + GamePadState suppressedController = this.RealController; + KeyboardState suppressedKeyboard = this.RealKeyboard; + MouseState suppressedMouse = this.RealMouse; + + this.SuppressGivenStates(this.ActiveButtons, ref suppressedKeyboard, ref suppressedMouse, ref suppressedController); + + this.SuppressedController = suppressedController; + this.SuppressedKeyboard = suppressedKeyboard; + this.SuppressedMouse = suppressedMouse; + } + /// Get the gamepad state visible to the game. [Obsolete("This method should only be called by the game itself.")] public override GamePadState GetGamePadState() @@ -145,61 +153,6 @@ namespace StardewModdingAPI.Framework.Input 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 @@ -210,6 +163,58 @@ namespace StardewModdingAPI.Framework.Input return Game1.chatBox != null && !Game1.chatBox.isActive(); } + /// Apply input suppression to 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. + private void SuppressGivenStates(IDictionary activeButtons, ref KeyboardState keyboardState, ref MouseState mouseState, ref GamePadState gamePadState) + { + if (this.SuppressButtons.Count == 0) + return; + + // gather info + HashSet suppressKeys = new HashSet(); + HashSet suppressButtons = new HashSet(); + HashSet suppressMouse = new HashSet(); + foreach (SButton button in this.SuppressButtons) + { + if (button == SButton.MouseLeft || button == SButton.MouseMiddle || button == SButton.MouseRight || button == SButton.MouseX1 || button == SButton.MouseX2) + suppressMouse.Add(button); + else if (button.TryGetKeyboard(out Keys key)) + suppressKeys.Add(key); + else if (gamePadState.IsConnected && button.TryGetController(out Buttons _)) + suppressButtons.Add(button); + } + + // suppress keyboard keys + if (keyboardState.GetPressedKeys().Any() && suppressKeys.Any()) + keyboardState = new KeyboardState(keyboardState.GetPressedKeys().Except(suppressKeys).ToArray()); + + // suppress controller keys + if (gamePadState.IsConnected && suppressButtons.Any()) + { + GamePadStateBuilder builder = new GamePadStateBuilder(gamePadState); + builder.SuppressButtons(suppressButtons); + gamePadState = builder.ToGamePadState(); + } + + // suppress mouse buttons + if (suppressMouse.Any()) + { + mouseState = new MouseState( + x: mouseState.X, + y: mouseState.Y, + scrollWheel: mouseState.ScrollWheelValue, + leftButton: suppressMouse.Contains(SButton.MouseLeft) ? ButtonState.Released : mouseState.LeftButton, + middleButton: suppressMouse.Contains(SButton.MouseMiddle) ? ButtonState.Released : mouseState.MiddleButton, + rightButton: suppressMouse.Contains(SButton.MouseRight) ? ButtonState.Released : mouseState.RightButton, + xButton1: suppressMouse.Contains(SButton.MouseX1) ? ButtonState.Released : mouseState.XButton1, + xButton2: suppressMouse.Contains(SButton.MouseX2) ? ButtonState.Released : mouseState.XButton2 + ); + } + } + /// Get the status of all pressed or released buttons relative to their previous status. /// The previous button statuses. /// The keyboard state. diff --git a/src/SMAPI/Framework/SGame.cs b/src/SMAPI/Framework/SGame.cs index c8c30834..63f7f073 100644 --- a/src/SMAPI/Framework/SGame.cs +++ b/src/SMAPI/Framework/SGame.cs @@ -613,6 +613,7 @@ namespace StardewModdingAPI.Framework /********* ** Game update *********/ + this.Input.UpdateSuppression(); try { base.Update(gameTime);