using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
namespace StardewModdingAPI.Framework.Input
{
/// Manages controller state.
internal class GamePadStateBuilder : IInputStateBuilder
{
/*********
** Fields
*********/
/// The maximum direction to ignore for the left thumbstick.
private const float LeftThumbstickDeadZone = 0.2f;
/// The maximum direction to ignore for the right thumbstick.
private const float RightThumbstickDeadZone = 0.9f;
/// The underlying controller state.
private GamePadState? State;
/// The current button states.
private 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;
/*********
** Accessors
*********/
/// Whether the gamepad is currently connected.
public bool IsConnected { get; private set; }
/*********
** Public methods
*********/
/// Construct an instance.
/// The initial state, or null to get the latest state.
public GamePadStateBuilder(GamePadState? state = null)
{
this.Reset(state);
}
/// Reset the tracked state.
/// The state from which to reset, or null to get the latest state.
public GamePadStateBuilder Reset(GamePadState? state = null)
{
this.State = state ??= GamePad.GetState(PlayerIndex.One);
this.IsConnected = state.Value.IsConnected;
if (!this.IsConnected)
return this;
GamePadDPad pad = state.Value.DPad;
GamePadButtons buttons = state.Value.Buttons;
GamePadTriggers triggers = state.Value.Triggers;
GamePadThumbSticks sticks = state.Value.ThumbSticks;
this.ButtonStates = new Dictionary
{
[SButton.DPadUp] = pad.Up,
[SButton.DPadDown] = pad.Down,
[SButton.DPadLeft] = pad.Left,
[SButton.DPadRight] = pad.Right,
[SButton.ControllerA] = buttons.A,
[SButton.ControllerB] = buttons.B,
[SButton.ControllerX] = buttons.X,
[SButton.ControllerY] = buttons.Y,
[SButton.LeftStick] = buttons.LeftStick,
[SButton.RightStick] = buttons.RightStick,
[SButton.LeftShoulder] = buttons.LeftShoulder,
[SButton.RightShoulder] = buttons.RightShoulder,
[SButton.ControllerBack] = buttons.Back,
[SButton.ControllerStart] = buttons.Start,
[SButton.BigButton] = buttons.BigButton
};
this.LeftTrigger = triggers.Left;
this.RightTrigger = triggers.Right;
this.LeftStickPos = sticks.Left;
this.RightStickPos = sticks.Right;
return this;
}
/// Override the states for a set of buttons.
/// The button state overrides.
public GamePadStateBuilder OverrideButtons(IDictionary overrides)
{
if (!this.IsConnected)
return this;
foreach (var pair in overrides)
{
bool changed = true;
bool isDown = pair.Value.IsDown();
switch (pair.Key)
{
// left thumbstick
case SButton.LeftThumbstickUp:
this.LeftStickPos.Y = isDown ? 1 : 0;
break;
case SButton.LeftThumbstickDown:
this.LeftStickPos.Y = isDown ? 1 : 0;
break;
case SButton.LeftThumbstickLeft:
this.LeftStickPos.X = isDown ? 1 : 0;
break;
case SButton.LeftThumbstickRight:
this.LeftStickPos.X = isDown ? 1 : 0;
break;
// right thumbstick
case SButton.RightThumbstickUp:
this.RightStickPos.Y = isDown ? 1 : 0;
break;
case SButton.RightThumbstickDown:
this.RightStickPos.Y = isDown ? 1 : 0;
break;
case SButton.RightThumbstickLeft:
this.RightStickPos.X = isDown ? 1 : 0;
break;
case SButton.RightThumbstickRight:
this.RightStickPos.X = isDown ? 1 : 0;
break;
// triggers
case SButton.LeftTrigger:
this.LeftTrigger = isDown ? 1 : 0;
break;
case SButton.RightTrigger:
this.RightTrigger = isDown ? 1 : 0;
break;
// buttons
default:
if (this.ButtonStates.ContainsKey(pair.Key))
this.ButtonStates[pair.Key] = isDown ? ButtonState.Pressed : ButtonState.Released;
else
changed = false;
break;
}
if (changed)
this.State = null;
}
return this;
}
/// Get the currently pressed buttons.
public IEnumerable GetPressedButtons()
{
if (!this.IsConnected)
yield break;
// buttons
foreach (var pair in this.ButtonStates)
{
if (pair.Value == ButtonState.Pressed && pair.Key.TryGetController(out Buttons button))
yield return button.ToSButton();
}
// triggers
if (this.LeftTrigger > 0.2f)
yield return SButton.LeftTrigger;
if (this.RightTrigger > 0.2f)
yield return SButton.RightTrigger;
// left thumbstick direction
if (this.LeftStickPos.Y > GamePadStateBuilder.LeftThumbstickDeadZone)
yield return SButton.LeftThumbstickUp;
if (this.LeftStickPos.Y < -GamePadStateBuilder.LeftThumbstickDeadZone)
yield return SButton.LeftThumbstickDown;
if (this.LeftStickPos.X > GamePadStateBuilder.LeftThumbstickDeadZone)
yield return SButton.LeftThumbstickRight;
if (this.LeftStickPos.X < -GamePadStateBuilder.LeftThumbstickDeadZone)
yield return SButton.LeftThumbstickLeft;
// right thumbstick direction
if (this.RightStickPos.Length() > GamePadStateBuilder.RightThumbstickDeadZone)
{
if (this.RightStickPos.Y > 0)
yield return SButton.RightThumbstickUp;
if (this.RightStickPos.Y < 0)
yield return SButton.RightThumbstickDown;
if (this.RightStickPos.X > 0)
yield return SButton.RightThumbstickRight;
if (this.RightStickPos.X < 0)
yield return SButton.RightThumbstickLeft;
}
}
/// Get the equivalent state.
public GamePadState GetState()
{
this.State ??= new GamePadState(
leftThumbStick: this.LeftStickPos,
rightThumbStick: this.RightStickPos,
leftTrigger: this.LeftTrigger,
rightTrigger: this.RightTrigger,
buttons: this.GetButtonBitmask() // MonoGame requires one bitmask here; don't specify multiple values
);
return this.State.Value;
}
/*********
** Private methods
*********/
/// Get a bitmask representing the pressed buttons.
private Buttons GetButtonBitmask()
{
Buttons flag = 0;
foreach (var pair in this.ButtonStates)
{
if (pair.Value == ButtonState.Pressed && pair.Key.TryGetController(out Buttons button))
flag |= button;
}
return flag;
}
}
}