import EventEmitter from "../../common/events/EventEmitter.js";
import { fixed } from "../../common/math/Utils.js";
/**
* @see https://w3c.github.io/gamepad/#dfn-standard-gamepad
*/
class GamePad extends EventEmitter {
constructor(gamepad) {
super();
this.index = gamepad.index;
this.id = gamepad.id;
this.buttons = {};
this.sticks = {};
this.deadzones = [];
this.default_deadzone = 0.2;
/**
* threshold at which a stick triggers a single directional event
*/
this.axeThreshold = 0.5;
this.directions = {
"_all": { "up": false, "down": false, "left": false, "right": false }
};
}
/**
* Set the axis deadzone(s)
* @param {Number} dz 0-1
* @param {Number} a optional specify the axis
*/
setDeadzone(dz, a = null) {
this.default_deadzone = dz;
if (s !== null) {
this.deadzones[a] = dz;
} else {
for (let a = 0; a < this.deadzones.length; a++) {
this.deadzones[a] = dz;
}
}
}
update(gamepad) {
if (gamepad.buttons) {
for (let b = 0; b < gamepad.buttons.length; b++) {
const btn = gamepad.buttons[b];
if (typeof this.buttons[b] !== "undefined" && this.buttons[b] !== btn.value) {
this.emit("button", {
button: b,
pressed: btn.pressed,
value: btn.value,
gamepad: this
});
}
this.buttons[b] = btn.value;
if (b === 12) this.updateDirection("button" + b, "up", btn.pressed);
if (b === 13) this.updateDirection("button" + b, "down", btn.pressed);
if (b === 14) this.updateDirection("button" + b, "left", btn.pressed);
if (b === 15) this.updateDirection("button" + b, "right", btn.pressed);
}
}
if (gamepad.axes) {
if (gamepad.axes.length % 2 !== 0) {
console.warn("Connected gamepad does not have an even amount of axes");
}
for (let s = 0; s < gamepad.axes.length; s += 2) {
if (typeof this.deadzones[s] === "undefined") {
this.deadzones[s] = this.default_deadzone;
}
let x = fixed(gamepad.axes[s], 4);
let y = fixed(gamepad.axes[s + 1], 4);
if (this.deadzones[s] > 0) {
if (Math.abs(x) < this.deadzones[s]) {
x = 0;
}
if (Math.abs(y) < this.deadzones[s]) {
y = 0;
}
//x is no longer a value between 0 and 1, its deadzone - 1, normalize it back to 0 - 1
x = ((Math.abs(x) - this.deadzones[s]) * Math.sign(x)) / (1 - this.deadzones[s]);
y = ((Math.abs(y) - this.deadzones[s]) * Math.sign(y)) / (1 - this.deadzones[s]);
}
if (typeof this.sticks[s] === "undefined") {
this.sticks[s] = { x, y };
} else {
if (x !== this.sticks[s].x || y !== this.sticks[s].y) {
this.emit("stick", {
stick: s,
x: x,
y: y,
gamepad: this
});
}
this.sticks[s].x = x;
this.sticks[s].y = y;
}
this.updateDirection("stick" + s, "left", x <= -this.axeThreshold);
this.updateDirection("stick" + s, "right", x >= this.axeThreshold);
this.updateDirection("stick" + s, "up", y <= -this.axeThreshold);
this.updateDirection("stick" + s, "down", y >= this.axeThreshold);
}
}
}
updateDirection(src, dir, val) {
if (!this.directions[src]) {
this.directions[src] = { "up": false, "down": false, "left": false, "right": false };
}
if (this.directions[src][dir] !== val) {
this.directions[src][dir] = val;
let direction_active = false;
if (val) {
direction_active = true;
} else {
for (const s in this.directions) {
if (s === "_all") continue;
let source_directions = this.directions[s];
if (source_directions[dir]) {
direction_active = true;
break;
}
}
}
if (this.directions["_all"][dir] !== direction_active) {
this.emit(dir, {
src,
pressed: direction_active
});
this.directions["_all"][dir] = direction_active
}
}
}
}
export default GamePad;