diff options
author | Jesse Plamondon-Willard <github@jplamondonw.com> | 2018-04-22 19:59:03 -0400 |
---|---|---|
committer | Jesse Plamondon-Willard <github@jplamondonw.com> | 2018-04-22 19:59:03 -0400 |
commit | 5e7eaf9f75d72d8cbb338c35b43f2974440b3456 (patch) | |
tree | 4cc95e003f4434941e22d2588b9dea11dfae9469 /src/SMAPI/Framework/Input | |
parent | 902814d308289f169750a615ae573edc348893d3 (diff) | |
download | SMAPI-5e7eaf9f75d72d8cbb338c35b43f2974440b3456.tar.gz SMAPI-5e7eaf9f75d72d8cbb338c35b43f2974440b3456.tar.bz2 SMAPI-5e7eaf9f75d72d8cbb338c35b43f2974440b3456.zip |
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.
Diffstat (limited to 'src/SMAPI/Framework/Input')
-rw-r--r-- | src/SMAPI/Framework/Input/GamePadStateBuilder.cs | 153 | ||||
-rw-r--r-- | src/SMAPI/Framework/Input/InputState.cs | 78 |
2 files changed, 217 insertions, 14 deletions
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 +{ + /// <summary>An abstraction for manipulating controller state.</summary> + internal class GamePadStateBuilder + { + /********* + ** Properties + *********/ + /// <summary>The current button states.</summary> + private readonly IDictionary<SButton, ButtonState> ButtonStates; + + /// <summary>The left trigger value.</summary> + private float LeftTrigger; + + /// <summary>The right trigger value.</summary> + private float RightTrigger; + + /// <summary>The left thumbstick position.</summary> + private Vector2 LeftStickPos; + + /// <summary>The left thumbstick position.</summary> + private Vector2 RightStickPos; + + + /********* + ** Public methods + *********/ + /// <summary>Construct an instance.</summary> + /// <param name="state">The initial controller state.</param> + public GamePadStateBuilder(GamePadState state) + { + this.ButtonStates = new Dictionary<SButton, ButtonState> + { + [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; + } + + /// <summary>Mark all matching buttons unpressed.</summary> + /// <param name="buttons">The buttons.</param> + public void SuppressButtons(IEnumerable<SButton> buttons) + { + foreach (SButton button in buttons) + this.SuppressButton(button); + } + + /// <summary>Mark a button unpressed.</summary> + /// <param name="button">The button.</param> + 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; + } + } + + /// <summary>Construct an equivalent gamepad state.</summary> + public GamePadState ToGamePadState() + { + return new GamePadState( + leftThumbStick: this.LeftStickPos, + rightThumbStick: this.RightStickPos, + leftTrigger: this.LeftTrigger, + rightTrigger: this.RightTrigger, + buttons: this.GetPressedButtons().ToArray() + ); + } + + /********* + ** Private methods + *********/ + /// <summary>Get all pressed buttons.</summary> + private IEnumerable<Buttons> 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 @@ -12,6 +12,16 @@ namespace StardewModdingAPI.Framework.Input /********* ** Accessors *********/ + /// <summary>The maximum amount of direction to ignore for the left thumbstick.</summary> + private const float LeftThumbstickDeadZone = 0.2f; + + /// <summary>The maximum amount of direction to ignore for the right thumbstick.</summary> + private const float RightThumbstickDeadZone = 0f; + + + /********* + ** Accessors + *********/ /// <summary>The underlying controller state.</summary> public GamePadState ControllerState { get; } @@ -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<SButton, InputStatus> prev in previousState.ActiveButtons) @@ -109,7 +119,8 @@ namespace StardewModdingAPI.Framework.Input /// <param name="keyboard">The keyboard state.</param> /// <param name="mouse">The mouse state.</param> /// <param name="controller">The controller state.</param> - private static IEnumerable<SButton> GetPressedButtons(KeyboardState keyboard, MouseState mouse, GamePadState controller) + /// <remarks>Thumbstick direction logic derived from <see cref="ButtonCollection"/>.</remarks> + private IEnumerable<SButton> 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; + } } } + + /// <summary>Get whether the right thumbstick should be considered outside the dead zone.</summary> + /// <param name="direction">The right thumbstick value.</param> + private bool IsRightThumbstickOutsideDeadZone(Vector2 direction) + { + return direction.Length() > 0.9f; + } } } |