using System;
using Chernobyl.Collections.Generic.Event;
using Chernobyl.Event;
using Chernobyl.Input.Controls;
using Chernobyl.Input.Controls.Axis;
using Chernobyl.Mathematics.Mechanics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
namespace Chernobyl.Input.Xna.Controls.Gamepad
{
///
/// An implementation of that represents an XNA
/// .
///
public class XnaGamepad : Control, IGamepad
{
///
/// Initializes a new instance of the class.
///
/// The instance that gives out services for use
/// by this type and takes services from this type for use by other
/// systems.
/// The player that controls this
/// instance.
/// The starting XNA state of this
/// instance.
public XnaGamepad(IEventCollection services, PlayerIndex playerIndex, GamePadState initialState)
{
_services = services;
PlayerIndex = playerIndex;
State = initialState;
}
///
/// The A button on the or null if it does
/// not exist.
///
/// Thrown if this
/// has been disconnected.
public IButtonControl A
{
get { return CheckedButtonCreation(ref _a, this, Capabilities.HasAButton, Buttons.A); }
}
///
/// The B button on the or null if it does
/// not exist.
///
/// Thrown if this
/// has been disconnected.
public IButtonControl B
{
get { return CheckedButtonCreation(ref _b, this, Capabilities.HasBButton, Buttons.B); }
}
///
/// The X button on the or null if it does
/// not exist.
///
/// Thrown if this
/// has been disconnected.
public IButtonControl C
{
get { return CheckedButtonCreation(ref _c, this, Capabilities.HasXButton, Buttons.X); }
}
///
/// The Y button on the or null if it does
/// not exist.
///
/// Thrown if this
/// has been disconnected.
public IButtonControl D
{
get { return CheckedButtonCreation(ref _d, this, Capabilities.HasYButton, Buttons.Y); }
}
///
/// The left bumper button on the or null if it
/// does not exist.
///
/// Thrown if this
/// has been disconnected.
public IButtonControl LeftShoulder1
{
get { return CheckedButtonCreation(ref _leftShoulder1, this, Capabilities.HasLeftShoulderButton, Buttons.LeftShoulder); }
}
///
/// The right bumper on the or null if it does
/// not exist.
///
/// Thrown if this
/// has been disconnected.
public IButtonControl RightShoulder1
{
get { return CheckedButtonCreation(ref _rightShoulder1, this, Capabilities.HasRightShoulderButton, Buttons.RightShoulder); }
}
///
/// The left thumb stick on the or null if it
/// does not exist.
///
/// Thrown if this
/// has been disconnected.
public IThumbstick LeftStick
{
get
{
return CheckedThumbstickCreation(ref _leftStick, Capabilities.HasLeftStickButton,
Buttons.LeftStick, Thumbstick.LeftRetriever);
}
}
///
/// The right thumb stick on the or null if it
/// does not exist.
///
public IThumbstick RightStick
{
get
{
return CheckedThumbstickCreation(ref _rightStick, Capabilities.HasRightStickButton,
Buttons.RightStick, Thumbstick.RightRetriever);
}
}
///
/// The back button on the or null if it does
/// not exist.
///
/// Thrown if this
/// has been disconnected.
public IButtonControl Select
{
get { return CheckedButtonCreation(ref _select, this, Capabilities.HasBackButton, Buttons.Back); }
}
///
/// The start button on the or null if it does
/// not exist.
///
/// Thrown if this
/// has been disconnected.
public IButtonControl Start
{
get { return CheckedButtonCreation(ref _start, this, Capabilities.HasStartButton, Buttons.Start); }
}
///
/// The big button on the or null if it does not
/// exist.
///
/// Thrown if this
/// has been disconnected.
public IButtonControl Main
{
get { return CheckedButtonCreation(ref _main, this, Capabilities.HasBigButton, Buttons.BigButton); }
}
///
/// The primary directional pad on the or null if
/// it does not exist.
///
/// Thrown if this
/// has been disconnected.
public IDpad Dpad
{
get
{
ThrowIfDisconnected();
if(_dpad == null)
{
_dpad = new DpadControl(this);
MakeParentChild(this, _dpad);
}
return _dpad;
}
}
///
/// The left trigger on the or null if it does
/// not exist. The left trigger has a maximum value of 1 and a minimum
/// value of 0.
///
/// Thrown if this
/// has been disconnected.
public IAxisControl LeftTrigger
{
get { return CheckedTriggerCreation(ref _leftTrigger, Capabilities.HasLeftTrigger, xnag => xnag.State.Triggers.Left); }
}
///
/// The left trigger on the or null if it does
/// not exist. The left trigger has a maximum value of 1 and a minimum
/// value of 0.
///
/// Thrown if this
/// has been disconnected.
public IAxisControl RightTrigger
{
get { return CheckedTriggerCreation(ref _rightTrigger, Capabilities.HasRightTrigger, xnag => xnag.State.Triggers.Right); }
}
///
/// The amount of vibration, between 0 and 1, in the left motor. 0 means
/// no vibration, 1 means maximum vibration.
///
/// Thrown when the value
/// set on this property is less than 0 or greater than 1.
/// Thrown if this
/// has been disconnected.
public float LeftVibration
{
get { return _leftVibration; }
set
{
if (value < 0)
throw new ArgumentOutOfRangeException("value", value,
"The value of the IGamepad.LeftVibration cannot be " +
"less than zero.");
if (value > 1)
throw new ArgumentOutOfRangeException("value", value,
"The value of the IGamepad.LeftVibration cannot be " +
"greater than one.");
_leftVibration = value;
GamePad.SetVibration(PlayerIndex, _leftVibration, RightVibration);
}
}
///
/// The amount of vibration, between 0 and 1, in the right motor. 0 means
/// no vibration, 1 means maximum vibration.
///
/// Thrown when the value
/// set on this property is less than 0 or greater than 1.
/// Thrown if this
/// has been disconnected.
public float RightVibration
{
get { return _rightVibration; }
set
{
if (value < 0)
throw new ArgumentOutOfRangeException("value", value,
"The value of the IGamepad.RightVibration cannot be " +
"less than zero.");
if(value > 1)
throw new ArgumentOutOfRangeException("value", value,
"The value of the IGamepad.RightVibration cannot be " +
"greater than one.");
_rightVibration = value;
GamePad.SetVibration(PlayerIndex, LeftVibration, _rightVibration);
}
}
///
/// The player that controls this instance.
///
public PlayerIndex PlayerIndex
{
get { return _playerIndex; }
private set
{
_playerIndex = value;
Capabilities = GamePad.GetCapabilities(_playerIndex);
}
}
///
/// The current XNA state of this instance.
///
/// Thrown if the value set on
/// this property is null.
public GamePadState State
{
get { return _state; }
set
{
_state = value;
_isConnected = _state.IsConnected;
if (_isConnected == false)
{
if (DisconnectedMethod != null)
DisconnectedMethod(this, EventArgs.Empty);
}
}
}
///
/// The control available from the XNA .
///
public GamePadCapabilities Capabilities { get; private set; }
///
/// An used to represent an XNA gamepad
/// button.
///
public class Button : ButtonControl
{
///
/// Initializes a new instance of the class.
///
/// The instance that gives out services for
/// use by this type and takes services from this type for use by
/// other systems.
/// The XNA instance
/// this belongs to.
/// The button that this instance refers to.
public Button(IEventCollection services, XnaGamepad gamepad, Buttons button)
: base(services)
{
_gamepad = gamepad;
_gamepad.Disconnected += OnGamepadDisconnected;
_button = button;
}
///
/// An event that is invoked when the gamepad has disconnected.
/// This method will attempt to raise the
/// event.
///
/// The instance that generated the event.
/// The instance
/// containing the event data.
void OnGamepadDisconnected(object sender, EventArgs e)
{
if (DisconnectedMethod != null)
DisconnectedMethod(this, e);
}
///
/// True if the button is down, false if otherwise.
///
/// Thrown if this
/// has been disconnected.
public override bool IsDown
{
get
{
_gamepad.ThrowIfDisconnected();
return _gamepad.State.IsButtonDown(_button);
}
protected set
{
throw new NotSupportedException("You cannot set the state " +
"of a XNA gamepad button.");
}
}
///
/// The XNA instance this
/// belongs to.
///
XnaGamepad _gamepad;
///
/// The button that this instance refers to.
///
Buttons _button;
}
///
/// Represents the XNA gamepads thumbsticks.
///
public class Thumbstick : Axis2DControl, IThumbstick
{
///
/// Initializes a new instance of the class.
///
/// The instance that gives out services for
/// use by this type and takes services from this type for use by
/// other systems.
/// The XNA instance
/// this belongs to.
/// The button that this instance refers to.
/// The method which is used to
/// retrieve the current
/// value of the thumbsticks.
public Thumbstick(IEventCollection services, XnaGamepad gamepad, Buttons button,
Func retrieveCurrentValue)
: base(services)
{
_services = services;
_gamepad = gamepad;
_gamepad.Disconnected += OnGamepadDisconnected;
_button = button;
Axis xControl = new Axis(_services, _gamepad, xnag => retrieveCurrentValue(xnag).X);
xControl.Sensitivity = 10.0f;
X = xControl;
Control.MakeParentChild(this, xControl);
Axis yControl = new Axis(_services, _gamepad, xnag => retrieveCurrentValue(xnag).Y);
yControl.Sensitivity = 10.0f;
Y = yControl;
Control.MakeParentChild(this, yControl);
}
///
/// Used to retrieve the
/// value of the left thumbstick.
///
/// The gamepad to retrieve the value from.
/// The value that was retrieved.
public static Microsoft.Xna.Framework.Vector2 LeftRetriever(XnaGamepad gamepad)
{
gamepad.ThrowIfDisconnected();
return gamepad.State.ThumbSticks.Left;
}
///
/// Used to retrieve the
/// value of the right thumbstick.
///
/// The gamepad to retrieve the value from.
/// The value that was retrieved.
public static Microsoft.Xna.Framework.Vector2 RightRetriever(XnaGamepad gamepad)
{
gamepad.ThrowIfDisconnected();
return gamepad.State.ThumbSticks.Right;
}
///
/// Checks to see if a thumbsticks or buttons state has been changed
/// and fires the necessary events if so. Also calls update on the
/// children.
///
/// The amount of time that has passed
/// since the last call to update.
public override void Update(TimeSpan deltaTime)
{
// update the previous state
bool previousState = WasDown;
WasDown = IsDown;
// check if the new button state has changed
if (IsDown != previousState)
{
// yes it has
EventArgs e = new EventArgs(this);
if (OnChange != null)
OnChange(this, EventArgs.Empty);
if (IsDown == true && OnDown != null)
OnDown(this, EventArgs.Empty);
else if (OnUp != null)
OnUp(this, e);
}
base.Update(deltaTime);
}
///
/// True if the button was down, false
/// if otherwise (set to the previous
/// value of "IsDown").
///
public bool WasDown { get; protected set; }
///
/// True if the button is down, false
/// if otherwise.
///
public virtual bool IsDown
{
get
{
_gamepad.ThrowIfDisconnected();
return _gamepad.State.IsButtonDown(_button);
}
}
///
/// An event that is raised when this button is pushed down or
/// turned on.
///
public event EventHandler OnDown;
///
/// An event that is raised when this button has stopped being
/// pushed down or is off.
///
public event EventHandler OnUp;
///
/// An event that is raised when this button's state has changed.
///
public event EventHandler OnChange;
///
/// An event that is invoked when the gamepad has disconnected.
/// This method will attempt to raise the
/// event.
///
/// The instance that generated the event.
/// The instance
/// containing the event data.
void OnGamepadDisconnected(object sender, EventArgs e)
{
if (DisconnectedMethod != null)
DisconnectedMethod(this, e);
}
///
/// The instance that gives out services for use by this type and
/// takes services from this type for use by other systems.
///
IEventCollection _services;
///
/// The XNA instance this instance belongs to.
///
XnaGamepad _gamepad;
///
/// The button that this instance refers to.
///
Buttons _button;
}
///
/// Used to represent one axis on an XNA .
///
public class Axis : AxisControl
{
///
/// Initializes a new instance of the class.
///
/// The instance that gives out services
/// for use by this type and takes services from this type for
/// use by other systems.
/// The XNA instance
/// this belongs to.
/// The method which is used
/// to retrieve the current value of the
/// thumbstick axis.
public Axis(IEventCollection services, XnaGamepad gamepad, Func retrieveCurrentValue)
: base(services)
{
_gamepad = gamepad;
_gamepad.Disconnected += OnGamepadDisconnected;
_retrieveCurrentValue = retrieveCurrentValue;
}
///
/// The amount of change, from the axis'
/// to its . Implementors should return
/// the raw change amount and not try to apply
/// to the returned value. The
/// class will apply the
/// value for you.
///
protected override float OverridableChangeAmount
{
get { return _retrieveCurrentValue(_gamepad); }
}
///
/// An event that is invoked when the gamepad has disconnected.
/// This method will attempt to raise the
/// event.
///
/// The instance that generated the event.
/// The instance
/// containing the event data.
void OnGamepadDisconnected(object sender, EventArgs e)
{
if (DisconnectedMethod != null)
DisconnectedMethod(this, e);
}
///
/// The XNA instance this instance
/// belongs to.
///
XnaGamepad _gamepad;
///
/// The method which is used to retrieve the current
/// value of the thumbstick axis.
///
Func _retrieveCurrentValue;
}
///
/// Used to represent the XNA instance's
/// .
///
public class DpadControl : Control, IDpad
{
///
/// Initializes a new instance of the class.
///
/// The gamepad.
public DpadControl(XnaGamepad gamepad)
{
_gamepad = gamepad;
_gamepad.Disconnected += OnGamepadDisconnected;
}
///
/// The top button on the or null if it does not
/// exist.
///
public IButtonControl Up
{
get { return _gamepad.CheckedButtonCreation(ref _up, this, _gamepad.Capabilities.HasDPadUpButton, Buttons.DPadUp); }
}
///
/// The right button on the or null if it does not
/// exist.
///
public IButtonControl Right
{
get { return _gamepad.CheckedButtonCreation(ref _right, this, _gamepad.Capabilities.HasDPadRightButton, Buttons.DPadRight); }
}
///
/// The bottom button on the or null if it does not
/// exist.
///
public IButtonControl Down
{
get { return _gamepad.CheckedButtonCreation(ref _down, this, _gamepad.Capabilities.HasDPadDownButton, Buttons.DPadDown); }
}
///
/// The left button on the or null if it does not
/// exist.
///
public IButtonControl Left
{
get { return _gamepad.CheckedButtonCreation(ref _left, this, _gamepad.Capabilities.HasDPadLeftButton, Buttons.DPadLeft); }
}
///
/// An event that is invoked when the gamepad has disconnected.
/// This method will attempt to raise the
/// event.
///
/// The instance that generated the event.
/// The instance
/// containing the event data.
void OnGamepadDisconnected(object sender, EventArgs e)
{
if (DisconnectedMethod != null)
DisconnectedMethod(this, e);
}
///
/// The XNA instance this instance belongs to.
///
XnaGamepad _gamepad;
///
/// The backing field to .
///
IButtonControl _up;
///
/// The backing field to .
///
IButtonControl _right;
///
/// The backing field to .
///
IButtonControl _down;
///
/// The backing field to .
///
IButtonControl _left;
}
///
/// Creates and configures a instance but only if
/// the is null and the
/// value is true. The created button or
/// null is then returned.
///
/// The that is to be checked
/// for existence before attempting to create one.
/// The that is the parent
/// to this control.
/// True if the getting
/// created is available on the XNA gamepad.
/// The XNA value of the to
/// create.
///
/// The instance that has been created or
/// null if was not null and
/// is true.
///
/// Thrown if this
/// has been disconnected.
IButtonControl CheckedButtonCreation(ref IButtonControl button, IControl parent, bool hasButton, Buttons xnaValue)
{
ThrowIfDisconnected();
if (button == null && hasButton == true)
{
button = new Button(_services, this, xnaValue);
MakeParentChild(parent, button);
}
return button;
}
///
/// Creates and configures a instance but only
/// if the is null and the
/// value is true. The created
/// thumbstick or null is then returned.
///
/// The that is to be
/// checked for existence before attempting to create one.
/// True if the
/// getting created is available on the XNA gamepad.
/// The XNA value of the
/// to create.
/// The method which is used to
/// retrieve the current
/// value of the thumbsticks.
/// The instance that has been created
/// or null if was not null and
/// is true.
/// Thrown if this
/// has been disconnected.
IThumbstick CheckedThumbstickCreation(ref IThumbstick thumbstick, bool hasThumbstick,
Buttons xnaValue, Func retrieveCurrentValue)
{
ThrowIfDisconnected();
if (thumbstick == null && hasThumbstick == true)
{
thumbstick = new Thumbstick(_services, this, xnaValue, retrieveCurrentValue);
MakeParentChild(this, thumbstick);
}
return thumbstick;
}
///
/// Creates and configures a trigger instance but only if the
/// is null and the
/// value is true. The created
/// thumbstick or null is then returned.
///
/// The gamepad trigger that is to be checked for
/// existence before attempting to create one.
/// True if the trigger getting created is
/// available on the XNA gamepad.
/// The method which is used to
/// retrieve the current value of the trigger.
/// The trigger instance that has been created or null if
/// was not null and
/// is true.
/// Thrown if this
/// has been disconnected.
IAxisControl CheckedTriggerCreation(ref IAxisControl trigger, bool hasThumbstick,
Func retrieveCurrentValue)
{
ThrowIfDisconnected();
if (trigger == null && hasThumbstick == true)
{
trigger = new Axis(_services, this, retrieveCurrentValue);
MakeParentChild(this, trigger);
}
return trigger;
}
///
/// Throws a if this
/// is disconnected.
///
/// Thrown if the controller is
/// disconnected.
void ThrowIfDisconnected()
{
if(_isConnected == false)
throw new DisconnectedException(this, "Unable to perform the " +
"requested action. The gamepad " +
"has been disconnected.");
}
///
/// The instance that gives out services for use
/// by this type and takes services from this type for use by other
/// systems.
///
IEventCollection _services;
///
/// True if the gamepad is disconnected, false if otherwise.
///
bool _isConnected;
///
/// The backing field to .
///
IButtonControl _a;
///
/// The backing field to .
///
IButtonControl _b;
///
/// The backing field to .
///
IButtonControl _c;
///
/// The backing field to .
///
IButtonControl _d;
///
/// The backing field to .
///
IButtonControl _leftShoulder1;
///
/// The backing field to .
///
IButtonControl _rightShoulder1;
///
/// The backing field to .
///
IThumbstick _leftStick;
///
/// The backing field to .
///
IThumbstick _rightStick;
///
/// The backing field to .
///
IButtonControl _select;
///
/// The backing field to .
///
IButtonControl _start;
///
/// The backing field to .
///
IButtonControl _main;
///
/// The backing field to .
///
IDpad _dpad;
///
/// The backing field to .
///
IAxisControl _leftTrigger;
///
/// The backing field to .
///
IAxisControl _rightTrigger;
///
/// The backing field to .
///
float _leftVibration;
///
/// The backing field to .
///
float _rightVibration;
///
/// The backing field to .
///
PlayerIndex _playerIndex;
///
/// The backing field to .
///
GamePadState _state;
}
}