Object.assign(pc, function () {
/**
* @constructor
* @name pc.Controller
* @classdesc A general input handler which handles both mouse and keyboard input assigned to named actions.
* This allows you to define input handlers separately to defining keyboard/mouse configurations.
* @description Create a new instance of a Controller.
* @param {Element} [element] Element to attach Controller to.
* @param {Object} [options] Optional arguments.
* @param {pc.Keyboard} [options.keyboard] A Keyboard object to use.
* @param {pc.Mouse} [options.mouse] A Mouse object to use.
* @param {pc.GamePads} [options.gamepads] A Gamepads object to use.
* @example
* var c = new pc.Controller(document)
*
* // Register the "fire" action and assign it to both the Enter key and the Spacebar.
* c.registerKeys("fire", [pc.KEY_ENTER, pc.KEY_SPACE]);
*/
var Controller = function (element, options) {
options = options || {};
this._keyboard = options.keyboard || null;
this._mouse = options.mouse || null;
this._gamepads = options.gamepads || null;
this._element = null;
this._actions = {};
this._axes = {};
this._axesValues = {};
if (element) {
this.attach(element);
}
};
/**
* @function
* @name pc.Controller#attach
* @description Attach Controller to a Element, this is required before you can monitor for key/mouse inputs.
* @param {Element} element The element to attach mouse and keyboard event handler too
*/
Controller.prototype.attach = function (element) {
this._element = element;
if (this._keyboard) {
this._keyboard.attach(element);
}
if (this._mouse) {
this._mouse.attach(element);
}
};
/**
* @function
* @name pc.Controller#detach
* @description Detach Controller from an Element, this should be done before the Controller is destroyed
*/
Controller.prototype.detach = function () {
if (this._keyboard) {
this._keyboard.detach();
}
if (this._mouse) {
this._mouse.detach();
}
this._element = null;
};
/**
* @function
* @name pc.Controller#disableContextMenu
* @description Disable the context menu usually activated with the right mouse button.
*/
Controller.prototype.disableContextMenu = function () {
if (!this._mouse) {
this._enableMouse();
}
this._mouse.disableContextMenu();
};
/**
* @function
* @name pc.Controller#enableContextMenu
* @description Enable the context menu usually activated with the right mouse button. This is enabled by default.
*/
Controller.prototype.enableContextMenu = function () {
if (!this._mouse) {
this._enableMouse();
}
this._mouse.enableContextMenu();
};
/**
* @function
* @name pc.Controller#update
* @description Update the Keyboard and Mouse handlers
* @param {Object} dt The time since the last frame
*/
Controller.prototype.update = function (dt) {
if (this._keyboard) {
this._keyboard.update(dt);
}
if (this._mouse) {
this._mouse.update(dt);
}
if (this._gamepads) {
this._gamepads.update(dt);
}
// clear axes values
this._axesValues = {};
for (var key in this._axes) {
this._axesValues[key] = [];
}
};
/**
* @function
* @name pc.Controller#registerKeys
* @description Create or update a action which is enabled when the supplied keys are pressed.
* @param {String} action The name of the action
* @param {Number[]} keys A list of keycodes
*/
Controller.prototype.registerKeys = function (action, keys) {
if (!this._keyboard) {
this._enableKeyboard();
}
if (this._actions[action]) {
throw new Error(pc.string.format("Action: {0} already registered", action));
}
if (keys === undefined) {
throw new Error('Invalid button');
}
// convert to an array
if (!keys.length) {
keys = [keys];
}
if (this._actions[action]) {
this._actions[action].push({
type: pc.ACTION_KEYBOARD,
keys: keys
});
} else {
this._actions[action] = [{
type: pc.ACTION_KEYBOARD,
keys: keys
}];
}
};
/**
* @function
* @name pc.Controller#registerMouse
* @description Create or update an action which is enabled when the supplied mouse button is pressed
* @param {String} action The name of the action
* @param {Number} button The mouse button
*/
Controller.prototype.registerMouse = function (action, button) {
if (!this._mouse) {
this._enableMouse();
}
if (button === undefined) {
throw new Error('Invalid button');
}
// Mouse actions are stored as negative numbers to prevent clashing with keycodes.
if (this._actions[action]) {
this._actions[action].push({
type: pc.ACTION_MOUSE,
button: button
});
} else {
this._actions[action] = [{
type: pc.ACTION_MOUSE,
button: -button
}];
}
};
/**
* @function
* @name pc.Controller#registerPadButton
* @description Create or update an action which is enabled when the gamepad button is pressed
* @param {String} action The name of the action
* @param {Number} pad The index of the pad to register (use pc.PAD_1, etc)
* @param {Number} button The pad button
*/
Controller.prototype.registerPadButton = function (action, pad, button) {
if (button === undefined) {
throw new Error('Invalid button');
}
// Mouse actions are stored as negative numbers to prevent clashing with keycodes.
if (this._actions[action]) {
this._actions[action].push({
type: pc.ACTION_GAMEPAD,
button: button,
pad: pad
});
} else {
this._actions[action] = [{
type: pc.ACTION_GAMEPAD,
button: button,
pad: pad
}];
}
};
/**
* @function
* @name pc.Controller#registerAxis
* @param {Object} [options] Optional options object.
* @param {Object} [options.pad] The index of the game pad to register for (use pc.PAD_1, etc)
*/
Controller.prototype.registerAxis = function (options) {
var name = options.name;
if (!this._axes[name]) {
this._axes[name] = [];
}
var i = this._axes[name].push(name);
//
options = options || {};
options.pad = options.pad || pc.PAD_1;
var bind = function (controller, source, value, key) {
switch (source) {
case 'mousex':
controller._mouse.on(pc.EVENT_MOUSEMOVE, function (e) {
controller._axesValues[name][i] = e.dx / 10;
});
break;
case 'mousey':
controller._mouse.on(pc.EVENT_MOUSEMOVE, function (e) {
controller._axesValues[name][i] = e.dy / 10;
});
break;
case 'key':
controller._axes[name].push(function () {
return controller._keyboard.isPressed(key) ? value : 0;
});
break;
case 'padrx':
controller._axes[name].push(function () {
return controller._gamepads.getAxis(options.pad, pc.PAD_R_STICK_X);
});
break;
case 'padry':
controller._axes[name].push(function () {
return controller._gamepads.getAxis(options.pad, pc.PAD_R_STICK_Y);
});
break;
case 'padlx':
controller._axes[name].push(function () {
return controller._gamepads.getAxis(options.pad, pc.PAD_L_STICK_X);
});
break;
case 'padly':
controller._axes[name].push(function () {
return controller._gamepads.getAxis(options.pad, pc.PAD_L_STICK_Y);
});
break;
default:
throw new Error('Unknown axis');
}
};
bind(this, options.positive, 1, options.positiveKey);
if (options.negativeKey || options.negative !== options.positive) {
bind(this, options.negative, -1, options.negativeKey);
}
};
/**
* @function
* @name pc.Controller#isPressed
* @description Returns true if the current action is enabled.
* @param {String} actionName The name of the action.
* @returns {Boolean} True if the action is enabled.
*/
Controller.prototype.isPressed = function (actionName) {
if (!this._actions[actionName]) {
return false;
}
var action;
var index = 0;
var length = this._actions[actionName].length;
for (index = 0; index < length; ++index) {
action = this._actions[actionName][index];
switch (action.type) {
case pc.ACTION_KEYBOARD:
if (this._keyboard) {
var i, len = action.keys.length;
for (i = 0; i < len; i++) {
if (this._keyboard.isPressed(action.keys[i])) {
return true;
}
}
}
break;
case pc.ACTION_MOUSE:
if (this._mouse && this._mouse.isPressed(action.button)) {
return true;
}
break;
case pc.ACTION_GAMEPAD:
if (this._gamepads && this._gamepads.isPressed(action.pad, action.button)) {
return true;
}
break;
}
}
return false;
};
/**
* @function
* @name pc.Controller#wasPressed
* @description Returns true if the action was enabled this since the last update.
* @param {String} actionName The name of the action.
* @returns {Boolean} True if the action was enabled this since the last update.
*/
Controller.prototype.wasPressed = function (actionName) {
if (!this._actions[actionName]) {
return false;
}
var index = 0;
var length = this._actions[actionName].length;
for (index = 0; index < length; ++index) {
var action = this._actions[actionName][index];
switch (action.type) {
case pc.ACTION_KEYBOARD:
if (this._keyboard) {
var i, len = action.keys.length;
for (i = 0; i < len; i++) {
if (this._keyboard.wasPressed(action.keys[i])) {
return true;
}
}
}
break;
case pc.ACTION_MOUSE:
if (this._mouse && this._mouse.wasPressed(action.button)) {
return true;
}
break;
case pc.ACTION_GAMEPAD:
if (this._gamepads && this._gamepads.wasPressed(action.pad, action.button)) {
return true;
}
break;
}
}
return false;
};
Controller.prototype.getAxis = function (name) {
var value = 0;
if (this._axes[name]) {
var i, len = this._axes[name].length;
for (i = 0; i < len; i++) {
if (pc.type(this._axes[name][i]) === 'function') {
var v = this._axes[name][i]();
if (Math.abs(v) > Math.abs(value)) {
value = v;
}
} else if (this._axesValues[name]) {
if (Math.abs(this._axesValues[name][i]) > Math.abs(value)) {
value = this._axesValues[name][i];
}
}
}
}
return value;
};
Controller.prototype._enableMouse = function () {
this._mouse = new pc.Mouse();
if (!this._element) {
throw new Error("Controller must be attached to an Element");
}
this._mouse.attach(this._element);
};
Controller.prototype._enableKeyboard = function () {
this._keyboard = new pc.Keyboard();
if (!this._element) {
throw new Error("Controller must be attached to an Element");
}
this._keyboard.attach(this._element);
};
return {
Controller: Controller
};
}());