From 9c1617c9ee51a0f6b93242fe8fc789336957460c Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 11 Apr 2018 21:15:16 -0400 Subject: drop support for Stardew Valley 1.2 (#453) --- src/SMAPI/Events/EventArgsInput.cs | 6 ------ 1 file changed, 6 deletions(-) (limited to 'src/SMAPI/Events/EventArgsInput.cs') diff --git a/src/SMAPI/Events/EventArgsInput.cs b/src/SMAPI/Events/EventArgsInput.cs index 75b9b8cd..b2bd978b 100644 --- a/src/SMAPI/Events/EventArgsInput.cs +++ b/src/SMAPI/Events/EventArgsInput.cs @@ -18,12 +18,6 @@ namespace StardewModdingAPI.Events /// The current cursor position. public ICursorPosition Cursor { get; } -#if !STARDEW_VALLEY_1_3 - /// Whether the input is considered a 'click' by the game for enabling action. - [Obsolete("Use " + nameof(EventArgsInput.IsActionButton) + " or " + nameof(EventArgsInput.IsUseToolButton) + " instead")] // deprecated in SMAPI 2.1 - public bool IsClick => this.IsActionButton; -#endif - /// Whether the input should trigger actions on the affected tile. public bool IsActionButton { get; } -- cgit From 052ef9683a473ce2253f56ca6323abbc70ba9d76 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sat, 14 Apr 2018 02:40:55 -0400 Subject: fix typo in code docs (#473) --- src/SMAPI/Events/EventArgsInput.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/SMAPI/Events/EventArgsInput.cs') diff --git a/src/SMAPI/Events/EventArgsInput.cs b/src/SMAPI/Events/EventArgsInput.cs index b2bd978b..0cf0828b 100644 --- a/src/SMAPI/Events/EventArgsInput.cs +++ b/src/SMAPI/Events/EventArgsInput.cs @@ -44,7 +44,7 @@ namespace StardewModdingAPI.Events this.IsUseToolButton = isUseToolButton; } - /// Prevent the game from handling the vurrent button press. This doesn't prevent other mods from receiving the event. + /// Prevent the game from handling the current button press. This doesn't prevent other mods from receiving the event. public void SuppressButton() { this.SuppressButton(this.Button); -- cgit From 5e7eaf9f75d72d8cbb338c35b43f2974440b3456 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 22 Apr 2018 19:59:03 -0400 Subject: rewrite input suppression (#453) This lets SMAPI intercept all input using the new Game1.hooks in SDV 1.3.0.32. However, intercepting mouse clicks needs a few more changes in the game code. --- docs/release-notes.md | 3 +- src/SMAPI/Events/EventArgsInput.cs | 109 ++-------------- src/SMAPI/Framework/Input/GamePadStateBuilder.cs | 153 +++++++++++++++++++++++ src/SMAPI/Framework/Input/InputState.cs | 78 +++++++++--- src/SMAPI/Framework/SGame.cs | 78 +++++++++++- src/SMAPI/SModHooks.cs | 48 +++++++ src/SMAPI/StardewModdingAPI.csproj | 2 + 7 files changed, 357 insertions(+), 114 deletions(-) create mode 100644 src/SMAPI/Framework/Input/GamePadStateBuilder.cs create mode 100644 src/SMAPI/SModHooks.cs (limited to 'src/SMAPI/Events/EventArgsInput.cs') diff --git a/docs/release-notes.md b/docs/release-notes.md index ee2d0aa1..565ed58c 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -22,7 +22,8 @@ * Added prerelease versions to the mod update-check API response where available (GitHub only). * Added support for beta releases on the home page. * Split mod DB out of `StardewModdingAPI.config.json`, so we can load config earlier and reduce unnecessary memory usage later. - * Overhauled world/player state tracking: + * Rewrote input suppression using new SDV 1.3 APIs. + * Rewrote world/player state tracking: * much more efficient than previous method; * uses net field events where available; * lays groundwork for tracking events for multiple players. diff --git a/src/SMAPI/Events/EventArgsInput.cs b/src/SMAPI/Events/EventArgsInput.cs index 0cf0828b..d60f4017 100644 --- a/src/SMAPI/Events/EventArgsInput.cs +++ b/src/SMAPI/Events/EventArgsInput.cs @@ -1,14 +1,18 @@ using System; -using System.Linq; -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Input; -using StardewValley; +using System.Collections.Generic; namespace StardewModdingAPI.Events { /// Event arguments when a button is pressed or released. public class EventArgsInput : EventArgs { + /********* + ** Properties + *********/ + /// The buttons to suppress. + private readonly HashSet SuppressButtons; + + /********* ** Accessors *********/ @@ -25,7 +29,7 @@ namespace StardewModdingAPI.Events public bool IsUseToolButton { get; } /// Whether a mod has indicated the key was already handled. - public bool IsSuppressed { get; private set; } + public bool IsSuppressed => this.SuppressButtons.Contains(this.Button); /********* @@ -36,12 +40,14 @@ namespace StardewModdingAPI.Events /// The cursor position. /// Whether the input should trigger actions on the affected tile. /// Whether the input should use tools on the affected tile. - public EventArgsInput(SButton button, ICursorPosition cursor, bool isActionButton, bool isUseToolButton) + /// The buttons to suppress. + public EventArgsInput(SButton button, ICursorPosition cursor, bool isActionButton, bool isUseToolButton, HashSet suppressButtons) { this.Button = button; this.Cursor = cursor; this.IsActionButton = isActionButton; this.IsUseToolButton = isUseToolButton; + this.SuppressButtons = suppressButtons; } /// Prevent the game from handling the current button press. This doesn't prevent other mods from receiving the event. @@ -54,96 +60,7 @@ namespace StardewModdingAPI.Events /// The button to suppress. public void SuppressButton(SButton button) { - if (button == this.Button) - this.IsSuppressed = true; - - // keyboard - if (button.TryGetKeyboard(out Keys key)) - Game1.oldKBState = new KeyboardState(Game1.oldKBState.GetPressedKeys().Union(new[] { key }).ToArray()); - - // controller - else if (button.TryGetController(out Buttons controllerButton)) - { - var newState = GamePad.GetState(PlayerIndex.One); - var thumbsticks = Game1.oldPadState.ThumbSticks; - var triggers = Game1.oldPadState.Triggers; - var buttons = Game1.oldPadState.Buttons; - var dpad = Game1.oldPadState.DPad; - - switch (controllerButton) - { - // d-pad - case Buttons.DPadDown: - dpad = new GamePadDPad(dpad.Up, newState.DPad.Down, dpad.Left, dpad.Right); - break; - case Buttons.DPadLeft: - dpad = new GamePadDPad(dpad.Up, dpad.Down, newState.DPad.Left, dpad.Right); - break; - case Buttons.DPadRight: - dpad = new GamePadDPad(dpad.Up, dpad.Down, dpad.Left, newState.DPad.Right); - break; - case Buttons.DPadUp: - dpad = new GamePadDPad(newState.DPad.Up, dpad.Down, dpad.Left, dpad.Right); - break; - - // trigger - case Buttons.LeftTrigger: - triggers = new GamePadTriggers(newState.Triggers.Left, triggers.Right); - break; - case Buttons.RightTrigger: - triggers = new GamePadTriggers(triggers.Left, newState.Triggers.Right); - break; - - // thumbstick - case Buttons.LeftThumbstickDown: - case Buttons.LeftThumbstickLeft: - case Buttons.LeftThumbstickRight: - case Buttons.LeftThumbstickUp: - thumbsticks = new GamePadThumbSticks(newState.ThumbSticks.Left, thumbsticks.Right); - break; - case Buttons.RightThumbstickDown: - case Buttons.RightThumbstickLeft: - case Buttons.RightThumbstickRight: - case Buttons.RightThumbstickUp: - thumbsticks = new GamePadThumbSticks(newState.ThumbSticks.Right, thumbsticks.Left); - break; - - // buttons - default: - var mask = - (buttons.A == ButtonState.Pressed ? Buttons.A : 0) - | (buttons.B == ButtonState.Pressed ? Buttons.B : 0) - | (buttons.Back == ButtonState.Pressed ? Buttons.Back : 0) - | (buttons.BigButton == ButtonState.Pressed ? Buttons.BigButton : 0) - | (buttons.LeftShoulder == ButtonState.Pressed ? Buttons.LeftShoulder : 0) - | (buttons.LeftStick == ButtonState.Pressed ? Buttons.LeftStick : 0) - | (buttons.RightShoulder == ButtonState.Pressed ? Buttons.RightShoulder : 0) - | (buttons.RightStick == ButtonState.Pressed ? Buttons.RightStick : 0) - | (buttons.Start == ButtonState.Pressed ? Buttons.Start : 0) - | (buttons.X == ButtonState.Pressed ? Buttons.X : 0) - | (buttons.Y == ButtonState.Pressed ? Buttons.Y : 0); - mask = mask ^ controllerButton; - buttons = new GamePadButtons(mask); - break; - } - - Game1.oldPadState = new GamePadState(thumbsticks, triggers, buttons, dpad); - } - - // mouse - else if (button == SButton.MouseLeft || button == SButton.MouseMiddle || button == SButton.MouseRight || button == SButton.MouseX1 || button == SButton.MouseX2) - { - Game1.oldMouseState = new MouseState( - x: Game1.oldMouseState.X, - y: Game1.oldMouseState.Y, - scrollWheel: Game1.oldMouseState.ScrollWheelValue, - leftButton: button == SButton.MouseLeft ? ButtonState.Pressed : Game1.oldMouseState.LeftButton, - middleButton: button == SButton.MouseMiddle ? ButtonState.Pressed : Game1.oldMouseState.MiddleButton, - rightButton: button == SButton.MouseRight ? ButtonState.Pressed : Game1.oldMouseState.RightButton, - xButton1: button == SButton.MouseX1 ? ButtonState.Pressed : Game1.oldMouseState.XButton1, - xButton2: button == SButton.MouseX2 ? ButtonState.Pressed : Game1.oldMouseState.XButton2 - ); - } + this.SuppressButtons.Add(button); } } } diff --git a/src/SMAPI/Framework/Input/GamePadStateBuilder.cs b/src/SMAPI/Framework/Input/GamePadStateBuilder.cs new file mode 100644 index 00000000..5eeb7ef6 --- /dev/null +++ b/src/SMAPI/Framework/Input/GamePadStateBuilder.cs @@ -0,0 +1,153 @@ +using System.Collections.Generic; +using System.Linq; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Input; + +namespace StardewModdingAPI.Framework.Input +{ + /// An abstraction for manipulating controller state. + internal class GamePadStateBuilder + { + /********* + ** Properties + *********/ + /// The current button states. + private readonly IDictionary ButtonStates; + + /// The left trigger value. + private float LeftTrigger; + + /// The right trigger value. + private float RightTrigger; + + /// The left thumbstick position. + private Vector2 LeftStickPos; + + /// The left thumbstick position. + private Vector2 RightStickPos; + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// The initial controller state. + public GamePadStateBuilder(GamePadState state) + { + this.ButtonStates = new Dictionary + { + [SButton.DPadUp] = state.DPad.Up, + [SButton.DPadDown] = state.DPad.Down, + [SButton.DPadLeft] = state.DPad.Left, + [SButton.DPadRight] = state.DPad.Right, + + [SButton.ControllerA] = state.Buttons.A, + [SButton.ControllerB] = state.Buttons.B, + [SButton.ControllerX] = state.Buttons.X, + [SButton.ControllerY] = state.Buttons.Y, + [SButton.LeftStick] = state.Buttons.LeftStick, + [SButton.RightStick] = state.Buttons.RightStick, + [SButton.LeftShoulder] = state.Buttons.LeftShoulder, + [SButton.RightShoulder] = state.Buttons.RightShoulder, + [SButton.ControllerBack] = state.Buttons.Back, + [SButton.ControllerStart] = state.Buttons.Start, + [SButton.BigButton] = state.Buttons.BigButton + }; + this.LeftTrigger = state.Triggers.Left; + this.RightTrigger = state.Triggers.Right; + this.LeftStickPos = state.ThumbSticks.Left; + this.RightStickPos = state.ThumbSticks.Right; + } + + /// Mark all matching buttons unpressed. + /// The buttons. + public void SuppressButtons(IEnumerable buttons) + { + foreach (SButton button in buttons) + this.SuppressButton(button); + } + + /// Mark a button unpressed. + /// The button. + public void SuppressButton(SButton button) + { + switch (button) + { + // left thumbstick + case SButton.LeftThumbstickUp: + if (this.LeftStickPos.Y > 0) + this.LeftStickPos.Y = 0; + break; + case SButton.LeftThumbstickDown: + if (this.LeftStickPos.Y < 0) + this.LeftStickPos.Y = 0; + break; + case SButton.LeftThumbstickLeft: + if (this.LeftStickPos.X < 0) + this.LeftStickPos.X = 0; + break; + case SButton.LeftThumbstickRight: + if (this.LeftStickPos.X > 0) + this.LeftStickPos.X = 0; + break; + + // right thumbstick + case SButton.RightThumbstickUp: + if (this.RightStickPos.Y > 0) + this.RightStickPos.Y = 0; + break; + case SButton.RightThumbstickDown: + if (this.RightStickPos.Y < 0) + this.RightStickPos.Y = 0; + break; + case SButton.RightThumbstickLeft: + if (this.RightStickPos.X < 0) + this.RightStickPos.X = 0; + break; + case SButton.RightThumbstickRight: + if (this.RightStickPos.X > 0) + this.RightStickPos.X = 0; + break; + + // triggers + case SButton.LeftTrigger: + this.LeftTrigger = 0; + break; + case SButton.RightTrigger: + this.RightTrigger = 0; + break; + + // buttons + default: + if (this.ButtonStates.ContainsKey(button)) + this.ButtonStates[button] = ButtonState.Released; + break; + } + } + + /// Construct an equivalent gamepad state. + public GamePadState ToGamePadState() + { + return new GamePadState( + leftThumbStick: this.LeftStickPos, + rightThumbStick: this.RightStickPos, + leftTrigger: this.LeftTrigger, + rightTrigger: this.RightTrigger, + buttons: this.GetPressedButtons().ToArray() + ); + } + + /********* + ** Private methods + *********/ + /// Get all pressed buttons. + private IEnumerable GetPressedButtons() + { + foreach (var pair in this.ButtonStates) + { + if (pair.Value == ButtonState.Pressed && pair.Key.TryGetController(out Buttons button)) + yield return button; + } + } + } +} diff --git a/src/SMAPI/Framework/Input/InputState.cs b/src/SMAPI/Framework/Input/InputState.cs index 7c8676e9..62337a6c 100644 --- a/src/SMAPI/Framework/Input/InputState.cs +++ b/src/SMAPI/Framework/Input/InputState.cs @@ -9,6 +9,16 @@ 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 *********/ @@ -48,7 +58,7 @@ namespace StardewModdingAPI.Framework.Input 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 = InputState.GetPressedButtons(keyboardState, mouseState, controllerState).ToArray(); + 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) @@ -109,7 +119,8 @@ namespace StardewModdingAPI.Framework.Input /// The keyboard state. /// The mouse state. /// The controller state. - private static IEnumerable GetPressedButtons(KeyboardState keyboard, MouseState mouse, GamePadState controller) + /// Thumbstick direction logic derived from . + private IEnumerable GetPressedButtons(KeyboardState keyboard, MouseState mouse, GamePadState controller) { // keyboard foreach (Keys key in keyboard.GetPressedKeys()) @@ -130,28 +141,23 @@ namespace StardewModdingAPI.Framework.Input // 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.Back == ButtonState.Pressed) - yield return SButton.ControllerBack; - if (controller.Buttons.BigButton == ButtonState.Pressed) - yield return SButton.BigButton; - if (controller.Buttons.LeftShoulder == ButtonState.Pressed) - yield return SButton.LeftShoulder; + 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.RightShoulder == ButtonState.Pressed) - yield return SButton.RightShoulder; if (controller.Buttons.RightStick == ButtonState.Pressed) yield return SButton.RightStick; if (controller.Buttons.Start == ButtonState.Pressed) yield return SButton.ControllerStart; - if (controller.Buttons.X == ButtonState.Pressed) - yield return SButton.ControllerX; - if (controller.Buttons.Y == ButtonState.Pressed) - yield return SButton.ControllerY; + + // directional pad if (controller.DPad.Up == ButtonState.Pressed) yield return SButton.DPadUp; if (controller.DPad.Down == ButtonState.Pressed) @@ -160,11 +166,55 @@ namespace StardewModdingAPI.Framework.Input 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/SGame.cs b/src/SMAPI/Framework/SGame.cs index 35e027d8..3b9a159f 100644 --- a/src/SMAPI/Framework/SGame.cs +++ b/src/SMAPI/Framework/SGame.cs @@ -120,6 +120,9 @@ 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 @@ -154,6 +157,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); // init watchers Game1.locations = new ObservableCollection(); @@ -420,7 +424,7 @@ namespace StardewModdingAPI.Framework if (status == InputStatus.Pressed) { - this.Events.Input_ButtonPressed.Raise(new EventArgsInput(button, cursor, button.IsActionButton(), button.IsUseToolButton())); + this.Events.Input_ButtonPressed.Raise(new EventArgsInput(button, cursor, button.IsActionButton(), button.IsUseToolButton(), this.SuppressButtons)); // legacy events if (button.TryGetKeyboard(out Keys key)) @@ -438,7 +442,7 @@ namespace StardewModdingAPI.Framework } else if (status == InputStatus.Released) { - this.Events.Input_ButtonReleased.Raise(new EventArgsInput(button, cursor, button.IsActionButton(), button.IsUseToolButton())); + this.Events.Input_ButtonReleased.Raise(new EventArgsInput(button, cursor, button.IsActionButton(), button.IsUseToolButton(), this.SuppressButtons)); // legacy events if (button.TryGetKeyboard(out Keys key)) @@ -539,7 +543,7 @@ namespace StardewModdingAPI.Framework if (curPlayer.TryGetLocationChanges(out IDictionaryWatcher _)) { if (this.VerboseLogging) - this.Monitor.Log($"Context: current location objects changed.", LogLevel.Trace); + this.Monitor.Log("Context: current location objects changed.", LogLevel.Trace); this.Events.Location_LocationObjectsChanged.Raise(new EventArgsLocationObjectsChanged(curPlayer.GetCurrentLocation().objects.FieldDict)); } @@ -619,6 +623,17 @@ 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) @@ -1240,6 +1255,63 @@ 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 new file mode 100644 index 00000000..1b88d606 --- /dev/null +++ b/src/SMAPI/SModHooks.cs @@ -0,0 +1,48 @@ +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 5fe3e32c..d7719e27 100644 --- a/src/SMAPI/StardewModdingAPI.csproj +++ b/src/SMAPI/StardewModdingAPI.csproj @@ -91,6 +91,7 @@ + @@ -262,6 +263,7 @@ + -- cgit From 0df7a967a6980db7f4da8d393feae97c968e3375 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sat, 2 Jun 2018 01:48:35 -0400 Subject: add new-style input events (#310) --- src/SMAPI/Events/ControlEvents.cs | 28 ++++++------ src/SMAPI/Events/EventArgsInput.cs | 10 ++--- src/SMAPI/Events/IInputEvents.cs | 14 ++++++ src/SMAPI/Events/IModEvents.cs | 3 ++ src/SMAPI/Events/InputButtonPressedEventArgs.cs | 56 ++++++++++++++++++++++++ src/SMAPI/Events/InputButtonReleasedEventArgs.cs | 56 ++++++++++++++++++++++++ src/SMAPI/Events/InputEvents.cs | 8 ++-- src/SMAPI/Framework/Events/EventManager.cs | 48 ++++++++++++-------- src/SMAPI/Framework/Events/ModEvents.cs | 4 ++ src/SMAPI/Framework/Events/ModInputEvents.cs | 36 +++++++++++++++ src/SMAPI/Framework/SGame.cs | 20 +++++---- src/SMAPI/StardewModdingAPI.csproj | 4 ++ 12 files changed, 235 insertions(+), 52 deletions(-) create mode 100644 src/SMAPI/Events/IInputEvents.cs create mode 100644 src/SMAPI/Events/InputButtonPressedEventArgs.cs create mode 100644 src/SMAPI/Events/InputButtonReleasedEventArgs.cs create mode 100644 src/SMAPI/Framework/Events/ModInputEvents.cs (limited to 'src/SMAPI/Events/EventArgsInput.cs') diff --git a/src/SMAPI/Events/ControlEvents.cs b/src/SMAPI/Events/ControlEvents.cs index 973bb245..77bbf3ab 100644 --- a/src/SMAPI/Events/ControlEvents.cs +++ b/src/SMAPI/Events/ControlEvents.cs @@ -20,22 +20,22 @@ namespace StardewModdingAPI.Events /// Raised when the changes. That happens when the player presses or releases a key. public static event EventHandler KeyboardChanged { - add => ControlEvents.EventManager.Control_KeyboardChanged.Add(value); - remove => ControlEvents.EventManager.Control_KeyboardChanged.Remove(value); + add => ControlEvents.EventManager.Legacy_Control_KeyboardChanged.Add(value); + remove => ControlEvents.EventManager.Legacy_Control_KeyboardChanged.Remove(value); } /// Raised when the player presses a keyboard key. public static event EventHandler KeyPressed { - add => ControlEvents.EventManager.Control_KeyPressed.Add(value); - remove => ControlEvents.EventManager.Control_KeyPressed.Remove(value); + add => ControlEvents.EventManager.Legacy_Control_KeyPressed.Add(value); + remove => ControlEvents.EventManager.Legacy_Control_KeyPressed.Remove(value); } /// Raised when the player releases a keyboard key. public static event EventHandler KeyReleased { - add => ControlEvents.EventManager.Control_KeyReleased.Add(value); - remove => ControlEvents.EventManager.Control_KeyReleased.Remove(value); + add => ControlEvents.EventManager.Legacy_Control_KeyReleased.Add(value); + remove => ControlEvents.EventManager.Legacy_Control_KeyReleased.Remove(value); } /// Raised when the changes. That happens when the player moves the mouse, scrolls the mouse wheel, or presses/releases a button. @@ -48,29 +48,29 @@ namespace StardewModdingAPI.Events /// The player pressed a controller button. This event isn't raised for trigger buttons. public static event EventHandler ControllerButtonPressed { - add => ControlEvents.EventManager.Control_ControllerButtonPressed.Add(value); - remove => ControlEvents.EventManager.Control_ControllerButtonPressed.Remove(value); + add => ControlEvents.EventManager.Legacy_Control_ControllerButtonPressed.Add(value); + remove => ControlEvents.EventManager.Legacy_Control_ControllerButtonPressed.Remove(value); } /// The player released a controller button. This event isn't raised for trigger buttons. public static event EventHandler ControllerButtonReleased { - add => ControlEvents.EventManager.Control_ControllerButtonReleased.Add(value); - remove => ControlEvents.EventManager.Control_ControllerButtonReleased.Remove(value); + add => ControlEvents.EventManager.Legacy_Control_ControllerButtonReleased.Add(value); + remove => ControlEvents.EventManager.Legacy_Control_ControllerButtonReleased.Remove(value); } /// The player pressed a controller trigger button. public static event EventHandler ControllerTriggerPressed { - add => ControlEvents.EventManager.Control_ControllerTriggerPressed.Add(value); - remove => ControlEvents.EventManager.Control_ControllerTriggerPressed.Remove(value); + add => ControlEvents.EventManager.Legacy_Control_ControllerTriggerPressed.Add(value); + remove => ControlEvents.EventManager.Legacy_Control_ControllerTriggerPressed.Remove(value); } /// The player released a controller trigger button. public static event EventHandler ControllerTriggerReleased { - add => ControlEvents.EventManager.Control_ControllerTriggerReleased.Add(value); - remove => ControlEvents.EventManager.Control_ControllerTriggerReleased.Remove(value); + add => ControlEvents.EventManager.Legacy_Control_ControllerTriggerReleased.Add(value); + remove => ControlEvents.EventManager.Legacy_Control_ControllerTriggerReleased.Remove(value); } diff --git a/src/SMAPI/Events/EventArgsInput.cs b/src/SMAPI/Events/EventArgsInput.cs index d60f4017..0cafdba5 100644 --- a/src/SMAPI/Events/EventArgsInput.cs +++ b/src/SMAPI/Events/EventArgsInput.cs @@ -23,10 +23,10 @@ namespace StardewModdingAPI.Events public ICursorPosition Cursor { get; } /// Whether the input should trigger actions on the affected tile. - public bool IsActionButton { get; } + public bool IsActionButton => this.Button.IsActionButton(); /// Whether the input should use tools on the affected tile. - public bool IsUseToolButton { get; } + public bool IsUseToolButton => this.Button.IsUseToolButton(); /// Whether a mod has indicated the key was already handled. public bool IsSuppressed => this.SuppressButtons.Contains(this.Button); @@ -38,15 +38,11 @@ namespace StardewModdingAPI.Events /// Construct an instance. /// The button on the controller, keyboard, or mouse. /// The cursor position. - /// Whether the input should trigger actions on the affected tile. - /// Whether the input should use tools on the affected tile. /// The buttons to suppress. - public EventArgsInput(SButton button, ICursorPosition cursor, bool isActionButton, bool isUseToolButton, HashSet suppressButtons) + public EventArgsInput(SButton button, ICursorPosition cursor, HashSet suppressButtons) { this.Button = button; this.Cursor = cursor; - this.IsActionButton = isActionButton; - this.IsUseToolButton = isUseToolButton; this.SuppressButtons = suppressButtons; } diff --git a/src/SMAPI/Events/IInputEvents.cs b/src/SMAPI/Events/IInputEvents.cs new file mode 100644 index 00000000..92802fda --- /dev/null +++ b/src/SMAPI/Events/IInputEvents.cs @@ -0,0 +1,14 @@ +using System; + +namespace StardewModdingAPI.Events +{ + /// Events raised when the player provides input using a controller, keyboard, or mouse. + public interface IInputEvents + { + /// Raised when the player presses a button on the keyboard, controller, or mouse. + event EventHandler ButtonPressed; + + /// Raised when the player releases a button on the keyboard, controller, or mouse. + event EventHandler ButtonReleased; + } +} diff --git a/src/SMAPI/Events/IModEvents.cs b/src/SMAPI/Events/IModEvents.cs index 99e5523f..16ec6557 100644 --- a/src/SMAPI/Events/IModEvents.cs +++ b/src/SMAPI/Events/IModEvents.cs @@ -3,6 +3,9 @@ namespace StardewModdingAPI.Events /// Manages access to events raised by SMAPI. public interface IModEvents { + /// Events raised when the player provides input using a controller, keyboard, or mouse. + IInputEvents Input { get; } + /// Events raised when something changes in the world. IWorldEvents World { get; } } diff --git a/src/SMAPI/Events/InputButtonPressedEventArgs.cs b/src/SMAPI/Events/InputButtonPressedEventArgs.cs new file mode 100644 index 00000000..c8d55cd4 --- /dev/null +++ b/src/SMAPI/Events/InputButtonPressedEventArgs.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; + +namespace StardewModdingAPI.Events +{ + /// Event arguments when a button is pressed. + public class InputButtonPressedArgsInput : EventArgs + { + /********* + ** Properties + *********/ + /// The buttons to suppress. + private readonly HashSet SuppressButtons; + + + /********* + ** Accessors + *********/ + /// The button on the controller, keyboard, or mouse. + public SButton Button { get; } + + /// The current cursor position. + public ICursorPosition Cursor { get; } + + /// Whether a mod has indicated the key was already handled. + public bool IsSuppressed => this.SuppressButtons.Contains(this.Button); + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// The button on the controller, keyboard, or mouse. + /// The cursor position. + /// The buttons to suppress. + public InputButtonPressedArgsInput(SButton button, ICursorPosition cursor, HashSet suppressButtons) + { + this.Button = button; + this.Cursor = cursor; + this.SuppressButtons = suppressButtons; + } + + /// Prevent the game from handling the current button press. This doesn't prevent other mods from receiving the event. + public void SuppressButton() + { + this.SuppressButton(this.Button); + } + + /// Prevent the game from handling a button press. This doesn't prevent other mods from receiving the event. + /// The button to suppress. + public void SuppressButton(SButton button) + { + this.SuppressButtons.Add(button); + } + } +} diff --git a/src/SMAPI/Events/InputButtonReleasedEventArgs.cs b/src/SMAPI/Events/InputButtonReleasedEventArgs.cs new file mode 100644 index 00000000..863fab6a --- /dev/null +++ b/src/SMAPI/Events/InputButtonReleasedEventArgs.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; + +namespace StardewModdingAPI.Events +{ + /// Event arguments when a button is released. + public class InputButtonReleasedArgsInput : EventArgs + { + /********* + ** Properties + *********/ + /// The buttons to suppress. + private readonly HashSet SuppressButtons; + + + /********* + ** Accessors + *********/ + /// The button on the controller, keyboard, or mouse. + public SButton Button { get; } + + /// The current cursor position. + public ICursorPosition Cursor { get; } + + /// Whether a mod has indicated the key was already handled. + public bool IsSuppressed => this.SuppressButtons.Contains(this.Button); + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// The button on the controller, keyboard, or mouse. + /// The cursor position. + /// The buttons to suppress. + public InputButtonReleasedArgsInput(SButton button, ICursorPosition cursor, HashSet suppressButtons) + { + this.Button = button; + this.Cursor = cursor; + this.SuppressButtons = suppressButtons; + } + + /// Prevent the game from handling the current button press. This doesn't prevent other mods from receiving the event. + public void SuppressButton() + { + this.SuppressButton(this.Button); + } + + /// Prevent the game from handling a button press. This doesn't prevent other mods from receiving the event. + /// The button to suppress. + public void SuppressButton(SButton button) + { + this.SuppressButtons.Add(button); + } + } +} diff --git a/src/SMAPI/Events/InputEvents.cs b/src/SMAPI/Events/InputEvents.cs index 84d7ce5d..e62d6ee6 100644 --- a/src/SMAPI/Events/InputEvents.cs +++ b/src/SMAPI/Events/InputEvents.cs @@ -19,15 +19,15 @@ namespace StardewModdingAPI.Events /// Raised when the player presses a button on the keyboard, controller, or mouse. public static event EventHandler ButtonPressed { - add => InputEvents.EventManager.Input_ButtonPressed.Add(value); - remove => InputEvents.EventManager.Input_ButtonPressed.Remove(value); + add => InputEvents.EventManager.Legacy_Input_ButtonPressed.Add(value); + remove => InputEvents.EventManager.Legacy_Input_ButtonPressed.Remove(value); } /// Raised when the player releases a keyboard key on the keyboard, controller, or mouse. public static event EventHandler ButtonReleased { - add => InputEvents.EventManager.Input_ButtonReleased.Add(value); - remove => InputEvents.EventManager.Input_ButtonReleased.Remove(value); + add => InputEvents.EventManager.Legacy_Input_ButtonReleased.Add(value); + remove => InputEvents.EventManager.Legacy_Input_ButtonReleased.Remove(value); } diff --git a/src/SMAPI/Framework/Events/EventManager.cs b/src/SMAPI/Framework/Events/EventManager.cs index 100e4e43..62d9582e 100644 --- a/src/SMAPI/Framework/Events/EventManager.cs +++ b/src/SMAPI/Framework/Events/EventManager.cs @@ -32,6 +32,15 @@ namespace StardewModdingAPI.Framework.Events /// Raised after terrain features (like floors and trees) are added or removed in a location. public readonly ManagedEvent World_TerrainFeatureListChanged; + /**** + ** Input + ****/ + /// Raised when the player presses a button on the keyboard, controller, or mouse. + public readonly ManagedEvent Input_ButtonPressed; + + /// Raised when the player released a button on the keyboard, controller, or mouse. + public readonly ManagedEvent Input_ButtonReleased; + /********* ** Events (old) @@ -46,28 +55,28 @@ namespace StardewModdingAPI.Framework.Events ** ControlEvents ****/ /// Raised when the changes. That happens when the player presses or releases a key. - public readonly ManagedEvent Control_KeyboardChanged; + public readonly ManagedEvent Legacy_Control_KeyboardChanged; /// Raised when the player presses a keyboard key. - public readonly ManagedEvent Control_KeyPressed; + public readonly ManagedEvent Legacy_Control_KeyPressed; /// Raised when the player releases a keyboard key. - public readonly ManagedEvent Control_KeyReleased; + public readonly ManagedEvent Legacy_Control_KeyReleased; /// Raised when the changes. That happens when the player moves the mouse, scrolls the mouse wheel, or presses/releases a button. public readonly ManagedEvent Control_MouseChanged; /// The player pressed a controller button. This event isn't raised for trigger buttons. - public readonly ManagedEvent Control_ControllerButtonPressed; + public readonly ManagedEvent Legacy_Control_ControllerButtonPressed; /// The player released a controller button. This event isn't raised for trigger buttons. - public readonly ManagedEvent Control_ControllerButtonReleased; + public readonly ManagedEvent Legacy_Control_ControllerButtonReleased; /// The player pressed a controller trigger button. - public readonly ManagedEvent Control_ControllerTriggerPressed; + public readonly ManagedEvent Legacy_Control_ControllerTriggerPressed; /// The player released a controller trigger button. - public readonly ManagedEvent Control_ControllerTriggerReleased; + public readonly ManagedEvent Legacy_Control_ControllerTriggerReleased; /**** ** GameEvents @@ -124,10 +133,10 @@ namespace StardewModdingAPI.Framework.Events ** InputEvents ****/ /// Raised when the player presses a button on the keyboard, controller, or mouse. - public readonly ManagedEvent Input_ButtonPressed; + public readonly ManagedEvent Legacy_Input_ButtonPressed; /// Raised when the player releases a keyboard key on the keyboard, controller, or mouse. - public readonly ManagedEvent Input_ButtonReleased; + public readonly ManagedEvent Legacy_Input_ButtonReleased; /**** ** LocationEvents @@ -234,6 +243,9 @@ namespace StardewModdingAPI.Framework.Events ManagedEvent ManageEvent(string typeName, string eventName) => new ManagedEvent($"{typeName}.{eventName}", monitor, modRegistry); // init events (new) + this.Input_ButtonPressed = ManageEventOf(nameof(IModEvents.Input), nameof(IInputEvents.ButtonPressed)); + this.Input_ButtonReleased = ManageEventOf(nameof(IModEvents.Input), nameof(IInputEvents.ButtonReleased)); + this.World_BuildingListChanged = ManageEventOf(nameof(IModEvents.World), nameof(IWorldEvents.LocationListChanged)); this.World_LargeTerrainFeatureListChanged = ManageEventOf(nameof(IModEvents.World), nameof(IWorldEvents.LargeTerrainFeatureListChanged)); this.World_LocationListChanged = ManageEventOf(nameof(IModEvents.World), nameof(IWorldEvents.BuildingListChanged)); @@ -244,13 +256,13 @@ namespace StardewModdingAPI.Framework.Events // init events (old) this.Content_LocaleChanged = ManageEventOf>(nameof(ContentEvents), nameof(ContentEvents.AfterLocaleChanged)); - this.Control_ControllerButtonPressed = ManageEventOf(nameof(ControlEvents), nameof(ControlEvents.ControllerButtonPressed)); - this.Control_ControllerButtonReleased = ManageEventOf(nameof(ControlEvents), nameof(ControlEvents.ControllerButtonReleased)); - this.Control_ControllerTriggerPressed = ManageEventOf(nameof(ControlEvents), nameof(ControlEvents.ControllerTriggerPressed)); - this.Control_ControllerTriggerReleased = ManageEventOf(nameof(ControlEvents), nameof(ControlEvents.ControllerTriggerReleased)); - this.Control_KeyboardChanged = ManageEventOf(nameof(ControlEvents), nameof(ControlEvents.KeyboardChanged)); - this.Control_KeyPressed = ManageEventOf(nameof(ControlEvents), nameof(ControlEvents.KeyPressed)); - this.Control_KeyReleased = ManageEventOf(nameof(ControlEvents), nameof(ControlEvents.KeyReleased)); + this.Legacy_Control_ControllerButtonPressed = ManageEventOf(nameof(ControlEvents), nameof(ControlEvents.ControllerButtonPressed)); + this.Legacy_Control_ControllerButtonReleased = ManageEventOf(nameof(ControlEvents), nameof(ControlEvents.ControllerButtonReleased)); + this.Legacy_Control_ControllerTriggerPressed = ManageEventOf(nameof(ControlEvents), nameof(ControlEvents.ControllerTriggerPressed)); + this.Legacy_Control_ControllerTriggerReleased = ManageEventOf(nameof(ControlEvents), nameof(ControlEvents.ControllerTriggerReleased)); + this.Legacy_Control_KeyboardChanged = ManageEventOf(nameof(ControlEvents), nameof(ControlEvents.KeyboardChanged)); + this.Legacy_Control_KeyPressed = ManageEventOf(nameof(ControlEvents), nameof(ControlEvents.KeyPressed)); + this.Legacy_Control_KeyReleased = ManageEventOf(nameof(ControlEvents), nameof(ControlEvents.KeyReleased)); this.Control_MouseChanged = ManageEventOf(nameof(ControlEvents), nameof(ControlEvents.MouseChanged)); this.Game_FirstUpdateTick = ManageEvent(nameof(GameEvents), nameof(GameEvents.FirstUpdateTick)); @@ -270,8 +282,8 @@ namespace StardewModdingAPI.Framework.Events this.Graphics_OnPreRenderGuiEvent = ManageEvent(nameof(GraphicsEvents), nameof(GraphicsEvents.OnPreRenderGuiEvent)); this.Graphics_OnPostRenderGuiEvent = ManageEvent(nameof(GraphicsEvents), nameof(GraphicsEvents.OnPostRenderGuiEvent)); - this.Input_ButtonPressed = ManageEventOf(nameof(InputEvents), nameof(InputEvents.ButtonPressed)); - this.Input_ButtonReleased = ManageEventOf(nameof(InputEvents), nameof(InputEvents.ButtonReleased)); + this.Legacy_Input_ButtonPressed = ManageEventOf(nameof(InputEvents), nameof(InputEvents.ButtonPressed)); + this.Legacy_Input_ButtonReleased = ManageEventOf(nameof(InputEvents), nameof(InputEvents.ButtonReleased)); this.Legacy_Location_LocationsChanged = ManageEventOf(nameof(LocationEvents), nameof(LocationEvents.LocationsChanged)); this.Legacy_Location_BuildingsChanged = ManageEventOf(nameof(LocationEvents), nameof(LocationEvents.BuildingsChanged)); diff --git a/src/SMAPI/Framework/Events/ModEvents.cs b/src/SMAPI/Framework/Events/ModEvents.cs index cc4cf8d7..90853141 100644 --- a/src/SMAPI/Framework/Events/ModEvents.cs +++ b/src/SMAPI/Framework/Events/ModEvents.cs @@ -8,6 +8,9 @@ namespace StardewModdingAPI.Framework.Events /********* ** Accessors *********/ + /// Events raised when the player provides input using a controller, keyboard, or mouse. + public IInputEvents Input { get; } + /// Events raised when something changes in the world. public IWorldEvents World { get; } @@ -20,6 +23,7 @@ namespace StardewModdingAPI.Framework.Events /// The underlying event manager. public ModEvents(IModMetadata mod, EventManager eventManager) { + this.Input = new ModInputEvents(mod, eventManager); this.World = new ModWorldEvents(mod, eventManager); } } diff --git a/src/SMAPI/Framework/Events/ModInputEvents.cs b/src/SMAPI/Framework/Events/ModInputEvents.cs new file mode 100644 index 00000000..18baec16 --- /dev/null +++ b/src/SMAPI/Framework/Events/ModInputEvents.cs @@ -0,0 +1,36 @@ +using System; +using StardewModdingAPI.Events; + +namespace StardewModdingAPI.Framework.Events +{ + /// Events raised when the player provides input using a controller, keyboard, or mouse. + internal class ModInputEvents : ModEventsBase, IInputEvents + { + /********* + ** Accessors + *********/ + /// Raised when the player presses a button on the keyboard, controller, or mouse. + public event EventHandler ButtonPressed + { + add => this.EventManager.Input_ButtonPressed.Add(value); + remove => this.EventManager.Input_ButtonPressed.Remove(value); + } + + /// Raised when the player releases a button on the keyboard, controller, or mouse. + public event EventHandler ButtonReleased + { + add => this.EventManager.Input_ButtonReleased.Add(value); + remove => this.EventManager.Input_ButtonReleased.Remove(value); + } + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// The mod which uses this instance. + /// The underlying event manager. + internal ModInputEvents(IModMetadata mod, EventManager eventManager) + : base(mod, eventManager) { } + } +} diff --git a/src/SMAPI/Framework/SGame.cs b/src/SMAPI/Framework/SGame.cs index 1611861f..f87293c2 100644 --- a/src/SMAPI/Framework/SGame.cs +++ b/src/SMAPI/Framework/SGame.cs @@ -459,45 +459,47 @@ namespace StardewModdingAPI.Framework if (status == InputStatus.Pressed) { - this.Events.Input_ButtonPressed.Raise(new EventArgsInput(button, cursor, button.IsActionButton(), button.IsUseToolButton(), inputState.SuppressButtons)); + this.Events.Input_ButtonPressed.Raise(new InputButtonPressedArgsInput(button, cursor, inputState.SuppressButtons)); + this.Events.Legacy_Input_ButtonPressed.Raise(new EventArgsInput(button, cursor, inputState.SuppressButtons)); // legacy events if (button.TryGetKeyboard(out Keys key)) { if (key != Keys.None) - this.Events.Control_KeyPressed.Raise(new EventArgsKeyPressed(key)); + this.Events.Legacy_Control_KeyPressed.Raise(new EventArgsKeyPressed(key)); } 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.RealController.Triggers.Left : inputState.RealController.Triggers.Right)); + this.Events.Legacy_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)); + this.Events.Legacy_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(), inputState.SuppressButtons)); + this.Events.Input_ButtonReleased.Raise(new InputButtonReleasedArgsInput(button, cursor, inputState.SuppressButtons)); + this.Events.Legacy_Input_ButtonReleased.Raise(new EventArgsInput(button, cursor, inputState.SuppressButtons)); // legacy events if (button.TryGetKeyboard(out Keys key)) { if (key != Keys.None) - this.Events.Control_KeyReleased.Raise(new EventArgsKeyPressed(key)); + this.Events.Legacy_Control_KeyReleased.Raise(new EventArgsKeyPressed(key)); } 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.RealController.Triggers.Left : inputState.RealController.Triggers.Right)); + this.Events.Legacy_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)); + this.Events.Legacy_Control_ControllerButtonReleased.Raise(new EventArgsControllerButtonReleased(PlayerIndex.One, controllerButton)); } } } // raise legacy state-changed events if (inputState.RealKeyboard != previousInputState.RealKeyboard) - this.Events.Control_KeyboardChanged.Raise(new EventArgsKeyboardStateChanged(previousInputState.RealKeyboard, inputState.RealKeyboard)); + this.Events.Legacy_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)); } diff --git a/src/SMAPI/StardewModdingAPI.csproj b/src/SMAPI/StardewModdingAPI.csproj index 7b9629e2..1bdad1b5 100644 --- a/src/SMAPI/StardewModdingAPI.csproj +++ b/src/SMAPI/StardewModdingAPI.csproj @@ -85,7 +85,10 @@ Properties\GlobalAssemblyInfo.cs + + + @@ -107,6 +110,7 @@ + -- cgit