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; } }