cloud-game/web/js/input/joystick.js
88hcsif 42514fa09a Swap A and B for PS3 controller
Add PS3 controller mapping for Safari and Firefox
2020-06-28 23:35:06 +01:00

278 lines
8.6 KiB
JavaScript
Vendored

/**
* Joystick controls.
*
* cross == a <--> a
* circle == b <--> b
* square == x <--> start
* triangle == y <--> select
* share <--> load
* option <--> save
* L2 == LT <--> full
* R2 == RT <--> quit
* dpad <--> up down left right
* axis 0, 1 <--> second dpad
*
* change full to help (temporary)
*
* @version 1
*/
const joystick = (() => {
const deadZone = 0.1;
let joystickMap;
let joystickState = {};
let joystickAxes = [];
let joystickIdx;
let joystickTimer = null;
let dpadMode = true;
function onDpadToggle(checked) {
if (dpadMode === checked) {
return //error?
}
if (dpadMode) {
dpadMode = false;
// reset dpad keys pressed before moving to analog stick mode
checkJoystickAxisState(KEY.LEFT, false);
checkJoystickAxisState(KEY.RIGHT, false);
checkJoystickAxisState(KEY.UP, false);
checkJoystickAxisState(KEY.DOWN, false);
} else {
dpadMode = true;
// reset analog stick axes before moving to dpad mode
joystickAxes.forEach(function (value, index) {
checkJoystickAxis(index, 0);
});
}
}
// check state for each axis -> dpad
function checkJoystickAxisState(name, state) {
if (joystickState[name] !== state) {
joystickState[name] = state;
event.pub(state === true ? KEY_PRESSED : KEY_RELEASED, {key: name});
}
}
function checkJoystickAxis(axis, value) {
if (-deadZone < value && value < deadZone) value = 0;
if (joystickAxes[axis] !== value) {
joystickAxes[axis] = value;
event.pub(AXIS_CHANGED, {id: axis, value: value});
}
}
// loop timer for checking joystick state
function checkJoystickState() {
let gamepad = navigator.getGamepads()[joystickIdx];
if (gamepad) {
if (dpadMode) {
// axis -> dpad
let corX = gamepad.axes[0]; // -1 -> 1, left -> right
let corY = gamepad.axes[1]; // -1 -> 1, up -> down
checkJoystickAxisState(KEY.LEFT, corX <= -0.5);
checkJoystickAxisState(KEY.RIGHT, corX >= 0.5);
checkJoystickAxisState(KEY.UP, corY <= -0.5);
checkJoystickAxisState(KEY.DOWN, corY >= 0.5);
} else {
gamepad.axes.forEach(function (value, index) {
checkJoystickAxis(index, value);
});
}
// normal button map
Object.keys(joystickMap).forEach(function (btnIdx) {
const buttonState = gamepad.buttons[btnIdx];
const isPressed = navigator.webkitGetGamepads ? buttonState === 1 :
buttonState.value > 0 || buttonState.pressed === true;
if (joystickState[btnIdx] !== isPressed) {
joystickState[btnIdx] = isPressed;
event.pub(isPressed === true ? KEY_PRESSED : KEY_RELEASED, {key: joystickMap[btnIdx]});
}
});
}
}
// we only capture the last plugged joystick
const onGamepadConnected = (e) => {
let gamepad = e.gamepad;
log.info(`Gamepad connected at index ${gamepad.index}: ${gamepad.id}. ${gamepad.buttons.length} buttons, ${gamepad.axes.length} axes.`);
joystickIdx = gamepad.index;
// Ref: https://github.com/giongto35/cloud-game/issues/14
// get mapping first (default KeyMap2)
let os = env.getOs();
let browser = env.getBrowser();
if (os === 'android') {
// default of android is KeyMap1
joystickMap = {
2: KEY.A,
0: KEY.B,
3: KEY.START,
4: KEY.SELECT,
10: KEY.LOAD,
11: KEY.SAVE,
8: KEY.HELP,
9: KEY.QUIT,
12: KEY.UP,
13: KEY.DOWN,
14: KEY.LEFT,
15: KEY.RIGHT
};
} else {
// default of other OS is KeyMap2
joystickMap = {
0: KEY.A,
1: KEY.B,
2: KEY.START,
3: KEY.SELECT,
8: KEY.LOAD,
9: KEY.SAVE,
6: KEY.HELP,
7: KEY.QUIT,
12: KEY.UP,
13: KEY.DOWN,
14: KEY.LEFT,
15: KEY.RIGHT
};
}
if (os === 'android' && (browser === 'firefox' || browser === 'uc')) { //KeyMap2
joystickMap = {
0: KEY.A,
1: KEY.B,
2: KEY.START,
3: KEY.SELECT,
8: KEY.LOAD,
9: KEY.SAVE,
6: KEY.HELP,
7: KEY.QUIT,
12: KEY.UP,
13: KEY.DOWN,
14: KEY.LEFT,
15: KEY.RIGHT
};
}
if (os === 'win' && browser === 'firefox') { //KeyMap3
joystickMap = {
1: KEY.A,
2: KEY.B,
0: KEY.START,
3: KEY.SELECT,
8: KEY.LOAD,
9: KEY.SAVE,
6: KEY.HELP,
7: KEY.QUIT
};
}
if (os === 'mac' && browser === 'safari') { //KeyMap4
joystickMap = {
1: KEY.A,
2: KEY.B,
0: KEY.START,
3: KEY.SELECT,
8: KEY.LOAD,
9: KEY.SAVE,
6: KEY.HELP,
7: KEY.QUIT,
14: KEY.UP,
15: KEY.DOWN,
16: KEY.LEFT,
17: KEY.RIGHT
};
}
if (os === 'mac' && browser === 'firefox') { //KeyMap5
joystickMap = {
1: KEY.A,
2: KEY.B,
0: KEY.START,
3: KEY.SELECT,
8: KEY.LOAD,
9: KEY.SAVE,
6: KEY.HELP,
7: KEY.QUIT,
14: KEY.UP,
15: KEY.DOWN,
16: KEY.LEFT,
17: KEY.RIGHT
};
}
// https://bugs.chromium.org/p/chromium/issues/detail?id=1076272
if (gamepad.id.includes('PLAYSTATION(R)3')) {
if (browser === 'chrome') {
joystickMap = {
1: KEY.A,
0: KEY.B,
2: KEY.Y,
3: KEY.X,
4: KEY.L,
5: KEY.R,
8: KEY.SELECT,
9: KEY.START,
10: KEY.DTOGGLE,
11: KEY.R3,
};
} else {
joystickMap = {
13: KEY.A,
14: KEY.B,
12: KEY.X,
15: KEY.Y,
3: KEY.START,
0: KEY.SELECT,
4: KEY.UP,
6: KEY.DOWN,
7: KEY.LEFT,
5: KEY.RIGHT,
10: KEY.L,
11: KEY.R,
8: KEY.L2,
9: KEY.R2,
1: KEY.DTOGGLE,
2: KEY.R3,
};
}
}
// reset state
joystickState = {[KEY.LEFT]: false, [KEY.RIGHT]: false, [KEY.UP]: false, [KEY.DOWN]: false};
Object.keys(joystickMap).forEach(function (btnIdx) {
joystickState[btnIdx] = false;
});
joystickAxes = new Array(gamepad.axes.length).fill(0);
// looper, too intense?
if (joystickTimer !== null) {
clearInterval(joystickTimer);
}
joystickTimer = setInterval(checkJoystickState, 10); // miliseconds per hit
event.pub(GAMEPAD_CONNECTED);
};
event.sub(DPAD_TOGGLE, (data) => onDpadToggle(data.checked));
return {
init: () => {
// we only capture the last plugged joystick
window.addEventListener('gamepadconnected', onGamepadConnected);
// disconnected event is triggered
window.addEventListener('gamepaddisconnected', (event) => {
clearInterval(joystickTimer);
log.info(`Gamepad disconnected at index ${event.gamepad.index}`);
event.pub(GAMEPAD_DISCONNECTED);
});
log.info('[input] joystick has been initialized');
}
}
})(event, env, KEY, navigator, window);