//------------------------------------------------------------------------------
// Constructor scope
//------------------------------------------------------------------------------
/**
* Creates a new instance of the class.
*
* @constructor
*
* @param {Object} [options] Gamepad settings.
*
* @class
* @classdesc
*
* The Gamepads class represents a gamepad device handler, where each device is
* represented by a Gamepad object.
*/
rune.input.Gamepads = function(options) {
//--------------------------------------------------------------------------
// Protected properties
//--------------------------------------------------------------------------
/**
* List containing registered input devices.
*
* @type {Array}
* @protected
* @ignore
*/
this.m_devices;
/**
* List containing objects that represent each gamepad.
*
* @type {Array}
* @protected
* @ignore
*/
this.m_gamepads;
/**
* Gamepad settings.
*
* @type {rune.input.GamepadsOptions}
* @protected
* @ignore
*/
this.m_options = new rune.input.GamepadsOptions(options);
//--------------------------------------------------------------------------
// Constructor call
//--------------------------------------------------------------------------
/**
* Invokes secondary class constructor.
*/
this.m_construct();
}
//------------------------------------------------------------------------------
// Public getter and setter methods
//------------------------------------------------------------------------------
/**
* Whether the gamepads subsystem should be enabled (true) or not (false).
* When the subsystem is inactive, devices do not respond to input.
*
* @member {boolean} enable
* @memberof rune.input.Gamepads
* @instance
*/
Object.defineProperty(rune.input.Gamepads.prototype, "enable", {
/**
* @this rune.input.Gamepads
* @ignore
*/
get : function() {
return this.m_options.enable;
},
/**
* @this rune.input.Gamepads
* @ignore
*/
set : function(value) {
if (this.m_options.enable != value) {
this.m_options.enable = value;
if (this.m_options.enable == false) {
this.reset();
}
}
}
});
/**
* Returns the number of connected gamepads. This value is always represented
* by an integer between 0 and 4.
*
* @member {number} numGamepads
* @memberof rune.input.Gamepads
* @instance
* @readonly
*/
Object.defineProperty(rune.input.Gamepads.prototype, "numGamepads", {
/**
* @this rune.input.Gamepads
* @ignore
*/
get : function() {
var num = 0;
if (this.m_gamepads != null) {
for (var i = 0; i < this.m_gamepads.length; i++) {
if (this.m_gamepads[i].connected == true) {
num++;
}
}
}
return num;
}
});
/**
* Returns true if the direction of any of the connected gamepad's left analog
* stick is facing up. For gamepads that do not have analog sticks, this value
* is always false.
*
* @member {boolean} stickLeftUp
* @memberof rune.input.Gamepads
* @instance
* @readonly
*/
Object.defineProperty(rune.input.Gamepads.prototype, "stickLeftUp", {
/**
* @this rune.input.Gamepads
* @ignore
*/
get : function() {
return this.m_getPropOfGamepads("stickLeftUp");
}
});
/**
* Returns true if the direction of any of the connected gamepad's left analog
* stick was just facing up. For gamepads that do not have analog sticks, this
* value is always false.
*
* @member {boolean} stickLeftJustUp
* @memberof rune.input.Gamepads
* @instance
* @readonly
*/
Object.defineProperty(rune.input.Gamepads.prototype, "stickLeftJustUp", {
/**
* @this rune.input.Gamepads
* @ignore
*/
get : function() {
return this.m_getPropOfGamepads("stickLeftJustUp");
}
});
/**
* Returns true if the direction of any of the connected gamepad's left analog
* stick is facing down. For gamepads that do not have analog sticks, this value
* is always false.
*
* @member {boolean} stickLeftDown
* @memberof rune.input.Gamepads
* @instance
* @readonly
*/
Object.defineProperty(rune.input.Gamepads.prototype, "stickLeftDown", {
/**
* @this rune.input.Gamepads
* @ignore
*/
get : function() {
return this.m_getPropOfGamepads("stickLeftDown");
}
});
/**
* Returns true if the direction of any of the connected gamepad's left analog
* stick was just facing down. For gamepads that do not have analog sticks, this
* value is always false.
*
* @member {boolean} stickLeftJustDown
* @memberof rune.input.Gamepads
* @instance
* @readonly
*/
Object.defineProperty(rune.input.Gamepads.prototype, "stickLeftJustDown", {
/**
* @this rune.input.Gamepads
* @ignore
*/
get : function() {
return this.m_getPropOfGamepads("stickLeftJustDown");
}
});
/**
* Returns true if the direction of any of the connected gamepad's left analog
* stick is facing left. For gamepads that do not have analog sticks, this value
* is always false.
*
* @member {boolean} stickLeftLeft
* @memberof rune.input.Gamepads
* @instance
* @readonly
*/
Object.defineProperty(rune.input.Gamepads.prototype, "stickLeftLeft", {
/**
* @this rune.input.Gamepads
* @ignore
*/
get : function() {
return this.m_getPropOfGamepads("stickLeftLeft");
}
});
/**
* Returns true if the direction of any of the connected gamepad's left analog
* stick was just facing left. For gamepads that do not have analog sticks, this
* value is always false.
*
* @member {boolean} stickLeftJustLeft
* @memberof rune.input.Gamepads
* @instance
* @readonly
*/
Object.defineProperty(rune.input.Gamepads.prototype, "stickLeftJustLeft", {
/**
* @this rune.input.Gamepads
* @ignore
*/
get : function() {
return this.m_getPropOfGamepads("stickLeftJustLeft");
}
});
/**
* Returns true if the direction of any of the connected gamepad's left analog
* stick is facing right. For gamepads that do not have analog sticks, this
* value is always false.
*
* @member {boolean} stickLeftRight
* @memberof rune.input.Gamepads
* @instance
* @readonly
*/
Object.defineProperty(rune.input.Gamepads.prototype, "stickLeftRight", {
/**
* @this rune.input.Gamepads
* @ignore
*/
get : function() {
return this.m_getPropOfGamepads("stickLeftRight");
}
});
/**
* Returns true if the direction of any of the connected gamepad's left analog
* stick was just facing right. For gamepads that do not have analog sticks, this
* value is always false.
*
* @member {boolean} stickLeftJustRight
* @memberof rune.input.Gamepads
* @instance
* @readonly
*/
Object.defineProperty(rune.input.Gamepads.prototype, "stickLeftJustRight", {
/**
* @this rune.input.Gamepads
* @ignore
*/
get : function() {
return this.m_getPropOfGamepads("stickLeftJustRight");
}
});
/**
* Returns true if the direction of any of the connected gamepad's right analog
* stick is facing up. For gamepads that do not have analog sticks, this value
* is always false.
*
* @member {boolean} stickRightUP
* @memberof rune.input.Gamepads
* @instance
* @readonly
*/
Object.defineProperty(rune.input.Gamepads.prototype, "stickRightUP", {
/**
* @this rune.input.Gamepads
* @ignore
*/
get : function() {
return this.m_getPropOfGamepads("stickRightUp");
}
});
/**
* Returns true if the direction of any of the connected gamepad's right analog
* stick was just facing up. For gamepads that do not have analog sticks, this
* value is always false.
*
* @member {boolean} stickRightJustUp
* @memberof rune.input.Gamepads
* @instance
* @readonly
*/
Object.defineProperty(rune.input.Gamepads.prototype, "stickRightJustUp", {
/**
* @this rune.input.Gamepads
* @ignore
*/
get : function() {
return this.m_getPropOfGamepads("stickRightJustUp");
}
});
/**
* Returns true if the direction of any of the connected gamepad's right analog
* stick is facing down. For gamepads that do not have analog sticks, this value
* is always false.
*
* @member {boolean} stickRightDown
* @memberof rune.input.Gamepads
* @instance
* @readonly
*/
Object.defineProperty(rune.input.Gamepads.prototype, "stickRightDown", {
/**
* @this rune.input.Gamepads
* @ignore
*/
get : function() {
return this.m_getPropOfGamepads("stickRightDown");
}
});
/**
* Returns true if the direction of any of the connected gamepad's right analog
* stick was just facing down. For gamepads that do not have analog sticks, this
* value is always false.
*
* @member {boolean} stickRightJustDown
* @memberof rune.input.Gamepads
* @instance
* @readonly
*/
Object.defineProperty(rune.input.Gamepads.prototype, "stickRightJustDown", {
/**
* @this rune.input.Gamepads
* @ignore
*/
get : function() {
return this.m_getPropOfGamepads("stickRightJustDown");
}
});
/**
* Returns true if the direction of any of the connected gamepad's right analog
* stick is facing left. For gamepads that do not have analog sticks, this value
* is always false.
*
* @member {boolean} stickRightLeft
* @memberof rune.input.Gamepads
* @instance
* @readonly
*/
Object.defineProperty(rune.input.Gamepads.prototype, "stickRightLeft", {
/**
* @this rune.input.Gamepads
* @ignore
*/
get : function() {
return this.m_getPropOfGamepads("stickRightLeft");
}
});
/**
* Returns true if the direction of any of the connected gamepad's right analog
* stick was just facing left. For gamepads that do not have analog sticks, this
* value is always false.
*
* @member {boolean} stickRightJustLeft
* @memberof rune.input.Gamepads
* @instance
* @readonly
*/
Object.defineProperty(rune.input.Gamepads.prototype, "stickRightJustLeft", {
/**
* @this rune.input.Gamepads
* @ignore
*/
get : function() {
return this.m_getPropOfGamepads("stickRightJustLeft");
}
});
/**
* Returns true if the direction of any of the connected gamepad's right analog
* stick is facing right. For gamepads that do not have analog sticks, this
* value is always false.
*
* @member {boolean} stickRightRight
* @memberof rune.input.Gamepads
* @instance
* @readonly
*/
Object.defineProperty(rune.input.Gamepads.prototype, "stickRightRight", {
/**
* @this rune.input.Gamepads
* @ignore
*/
get : function() {
return this.m_getPropOfGamepads("stickRightRight");
}
});
/**
* Returns true if the direction of any of the connected gamepad's right analog
* stick was just facing right. For gamepads that do not have analog sticks,
* this value is always false.
*
* @member {boolean} stickRightJustRight
* @memberof rune.input.Gamepads
* @instance
* @readonly
*/
Object.defineProperty(rune.input.Gamepads.prototype, "stickRightJustRight", {
/**
* @this rune.input.Gamepads
* @ignore
*/
get : function() {
return this.m_getPropOfGamepads("stickRightJustRight");
}
});
//------------------------------------------------------------------------------
// Private getter and setter methods
//------------------------------------------------------------------------------
/**
* The number of connected input devices, note that this is not necessarily the
* same as the number of connected Gamepad objects.
*
* @member {number} m_numDevices
* @memberof rune.input.Gamepads
* @instance
* @readonly
* @private
*/
Object.defineProperty(rune.input.Gamepads.prototype, "m_numDevices", {
/**
* @this rune.input.Gamepads
* @ignore
*/
get : function() {
var num = 0;
for (var i = 0; i < this.m_devices.length; i++) {
if (this.m_devices[i] !== null) {
num++;
}
}
return num;
}
});
//------------------------------------------------------------------------------
// Public prototype methods (API)
//------------------------------------------------------------------------------
/**
* Request an instance of the Gamepad class. Each instance represents a
* connected device. Because the subsystem can handle up to four devices, an
* ID between 0 and 3 must be used. The method always returns a Gamepad object,
* even if there is no connected physical device for the requested ID. A
* Gamepad object that is not connected to a physical device can still be used
* via code, but all input is always false for that Gamepad object.
*
* @param {number} id ID of requested Gamepad object.
*
* @throws {RangeError} If the requested ID is outside the range of possible IDs.
*
* @returns {rune.input.Gamepad}
*/
rune.input.Gamepads.prototype.get = function(id) {
if (id < this.m_gamepads.length) {
return this.m_gamepads[id];
} throw new RangeError();
};
/**
* Check if any of the connected devices just pressed a specific button that
* is identified by its button ID.
*
* @param {number} button button Button ID.
*
* @returns {boolean}
*/
rune.input.Gamepads.prototype.justPressed = function(button) {
var i = this.m_gamepads.length;
while (i--) {
if (this.m_gamepads[i] != null) {
if (this.m_gamepads[i].justPressed(button) === true) {
return true;
};
};
};
return false;
};
/**
* Check if any of the connected devices released a specific button that is
* identified via their button ID.
*
* @param {number} button button Button ID.
*
* @returns {boolean}
*/
rune.input.Gamepads.prototype.justReleased = function(button) {
var i = this.m_gamepads.length;
while (i--) {
if (this.m_gamepads[i] != null) {
if (this.m_gamepads[i].justReleased(button) === true) {
return true;
};
};
};
return false;
};
/**
* Check if one of the connected devices presses a specific button which is
* identified via its button ID.
*
* @param {number} button Button ID.
*
* @returns {boolean}
*/
rune.input.Gamepads.prototype.pressed = function(button) {
var i = this.m_gamepads.length;
while (i--) {
if (this.m_gamepads[i] != null) {
if (this.m_gamepads[i].pressed(button) === true) {
return true;
};
};
};
return false;
};
/**
* Resets the input of all connected Gamepad objects.
*
* @returns {undefined}
*/
rune.input.Gamepads.prototype.reset = function() {
this.m_resetGamepads();
};
/**
* Triggers vibration on all connected gamepads.
*
* @param {number} [duration=250] - The duration of the vibration in milliseconds.
* @param {number} [delay=0] - The delay before the vibration starts, in milliseconds.
* @param {number} [weak=1.0] - The intensity of the weak (low-frequency) vibration, ranging from 0.0 to 1.0.
* @param {number} [strong=1.0] - The intensity of the strong (high-frequency) vibration, ranging from 0.0 to 1.0.
* @param {string} [type="dual-rumble"] - The type of vibration effect to use (e.g., "dual-rumble").
*
* @returns {undefined}
*/
rune.input.Gamepads.prototype.vibrate = function(duration, delay, weak, strong, type) {
var i = this.m_gamepads.length;
while (i--) {
if (this.m_gamepads[i] != null) {
this.m_gamepads[i].vibrate(
duration,
delay,
weak,
strong,
type
);
};
};
};
//------------------------------------------------------------------------------
// Public prototype methods (ENGINE)
//------------------------------------------------------------------------------
/**
* Destroys the subsystem and frees up allocated memory.
*
* @returns {undefined}
* @ignore
*/
rune.input.Gamepads.prototype.dispose = function() {
this.vibrate(0);
this.m_disposeGamepads();
this.m_disposeDevices();
};
/**
* Updates all connected Gamepad objects.
*
* @returns {undefined}
* @ignore
*/
rune.input.Gamepads.prototype.update = function() {
if (this.m_options.enable == true) {
this.m_updateDevices();
this.m_updateGamepads();
}
};
//------------------------------------------------------------------------------
// Protected prototype methods
//------------------------------------------------------------------------------
/**
* The class constructor.
*
* @returns {undefined}
* @protected
* @ignore
*/
rune.input.Gamepads.prototype.m_construct = function() {
this.m_constructDevices();
this.m_constructGamepads();
};
//------------------------------------------------------------------------------
// Private prototype methods
//------------------------------------------------------------------------------
/**
* Creates support for connected devices.
*
* @throws {Error} If there is no support for W3C Gamepad.
*
* @returns {undefined}
* @private
*/
rune.input.Gamepads.prototype.m_constructDevices = function() {
if (navigator && typeof navigator.getGamepads === "function") {
this.m_devices = window.navigator.getGamepads();
} else throw new Error("Gamepads not supported at runtime.");
};
/**
* Creates Gamepad objects to represent connected devices.
*
* @throws {Error} If Gamepad objects could not be created.
*
* @returns {undefined}
* @private
*/
rune.input.Gamepads.prototype.m_constructGamepads = function() {
this.m_disposeGamepads();
if (this.m_gamepads == null && this.m_devices != null) {
this.m_gamepads = [];
for (var i = 0; i < this.m_devices.length; i++) {
this.m_gamepads.push(
new rune.input.Gamepad()
);
}
} else throw new Error();
};
/**
* Updates the state of connected devices.
*
* @throws {Error} If the state could not be updated.
*
* @returns {undefined}
* @private
*/
rune.input.Gamepads.prototype.m_updateDevices = function() {
if (window.navigator != null) {
this.m_devices = window.navigator.getGamepads();
var a = this['m_numDevices'];
var b = this['numGamepads'];
if (a > b) this.m_onDeviceConnected();
else if (a < b) this.m_onDeviceDisconnected();
} else throw new Error();
};
/**
* Updates the states of Gamepad objects.
*
* @returns {undefined}
* @private
*/
rune.input.Gamepads.prototype.m_updateGamepads = function() {
if (this.m_gamepads == null) return;
for (var i = 0; i < this.m_gamepads.length; i++) {
if (this.m_gamepads[i].active === true) {
this.m_gamepads[i].update(
this.m_devices[i]
);
}
}
};
/**
* Destroy all Gamepad objects.
*
* @returns {undefined}
* @private
*/
rune.input.Gamepads.prototype.m_disposeGamepads = function() {
if (Array.isArray(this.m_gamepads)) {
while (this.m_gamepads.length > 0) {
this.m_gamepads[0].dispose();
this.m_gamepads.splice(0, 1);
}
}
this.m_gamepads = null;
};
/**
* Destroy connection to physical devices.
*
* @returns {undefined}
* @private
*/
rune.input.Gamepads.prototype.m_disposeDevices = function() {
this.m_devices = null;
};
/**
* Resets the state of all Gamepad objects.
*
* @returns {undefined}
* @private
*/
rune.input.Gamepads.prototype.m_resetGamepads = function() {
var i = this.m_gamepads.length;
while (i--) this.m_resetGamepad(this.m_gamepads[i]);
};
/**
* Resets the state of a Gamepad object.
*
* @param {rune.input.Gamepad} gamepad Gamepad to reset.
*
* @throws {TypeError} In case of incorrect data type.
*
* @returns {undefined}
* @private
*/
rune.input.Gamepads.prototype.m_resetGamepad = function(gamepad) {
if (gamepad instanceof rune.input.Gamepad) {
gamepad.reset();
} else throw new TypeError();
};
/**
* Reads a property of all Gamepad objects.
*
* @param {string} prop Property to read.
*
* @returns {boolean}
* @private
*/
rune.input.Gamepads.prototype.m_getPropOfGamepads = function(prop) {
var i = this.m_gamepads.length;
while (i--) {
if (this.m_gamepads[i] != null) {
if (this.m_gamepads[i][prop] === true) {
return true;
};
};
};
return false;
};
/**
* Callback for when gamepads are connected.
*
* @returns {undefined}
* @private
*/
rune.input.Gamepads.prototype.m_onDeviceConnected = function() {
this.m_options.exec("onConnect");
};
/**
* Callback for when gamepads are disconnected.
*
* @returns {undefined}
* @private
*/
rune.input.Gamepads.prototype.m_onDeviceDisconnected = function() {
this.m_options.exec("onDisconnect");
};