|
|
@ -12,7 +12,7 @@ function bound(low, x, high) { |
|
|
|
return Math.max(low, Math.min(x, high)); |
|
|
|
} |
|
|
|
|
|
|
|
class Input { |
|
|
|
class SnesInput { |
|
|
|
constructor() { |
|
|
|
this.up = false; |
|
|
|
this.down = false; |
|
|
@ -26,72 +26,21 @@ class Input { |
|
|
|
this.r = false; |
|
|
|
this.select = false; |
|
|
|
this.start = false; |
|
|
|
|
|
|
|
this.keysPressed = {}; |
|
|
|
|
|
|
|
window.addEventListener('gamepadconnected', this.gamepadConnected); |
|
|
|
window.addEventListener('gamepaddisconnected', this.gamepadDisconnected); |
|
|
|
document.addEventListener('keydown', (e) => this.keyDown(e)); |
|
|
|
document.addEventListener('keyup', (e) => this.keyUp(e)); |
|
|
|
} |
|
|
|
|
|
|
|
keyDown(e) { |
|
|
|
this.keysPressed[e.key] = true; |
|
|
|
} |
|
|
|
|
|
|
|
keyUp(e) { |
|
|
|
this.keysPressed[e.key] = false; |
|
|
|
} |
|
|
|
|
|
|
|
update() { |
|
|
|
// Default ZSNES keybindings. See:
|
|
|
|
// http://zsnes-docs.sourceforge.net/html/readme.htm#default_keys
|
|
|
|
this.up = this.keysPressed['ArrowUp']; |
|
|
|
this.down = this.keysPressed['ArrowDown']; |
|
|
|
this.left = this.keysPressed['ArrowLeft']; |
|
|
|
this.right = this.keysPressed['ArrowRight']; |
|
|
|
this.start = this.keysPressed['Enter']; |
|
|
|
this.select = this.keysPressed['Shift']; |
|
|
|
this.a = this.keysPressed['x']; |
|
|
|
this.b = this.keysPressed['z']; |
|
|
|
this.x = this.keysPressed['s']; |
|
|
|
this.y = this.keysPressed['a']; |
|
|
|
this.l = this.keysPressed['d']; |
|
|
|
this.r = this.keysPressed['c']; |
|
|
|
|
|
|
|
const gamepad = navigator.getGamepads()[0]; |
|
|
|
if (gamepad == null || !gamepad.connected || gamepad.axes.length < 2 || |
|
|
|
gamepad.buttons.length < 12) { |
|
|
|
return; |
|
|
|
} |
|
|
|
// TODO: have a config screen instead of hard-coding the 8Bitdo SNES30 pad.
|
|
|
|
// TODO: handle connects / disconnects more correctly.
|
|
|
|
this.up |= gamepad.axes[1] < 0; |
|
|
|
this.down |= gamepad.axes[1] > 0; |
|
|
|
this.left |= gamepad.axes[0] < 0; |
|
|
|
this.right |= gamepad.axes[0] > 0; |
|
|
|
this.a |= gamepad.buttons[0].pressed; |
|
|
|
this.b |= gamepad.buttons[1].pressed; |
|
|
|
this.x |= gamepad.buttons[3].pressed; |
|
|
|
this.y |= gamepad.buttons[4].pressed; |
|
|
|
this.l |= gamepad.buttons[6].pressed; |
|
|
|
this.r |= gamepad.buttons[7].pressed; |
|
|
|
this.select |= gamepad.buttons[10].pressed; |
|
|
|
this.start |= gamepad.buttons[11].pressed; |
|
|
|
debug(this.toString()); |
|
|
|
} |
|
|
|
|
|
|
|
gamepadConnected(e) { |
|
|
|
debug('gamepad connected! :)'); |
|
|
|
console.log('gamepad connected @ index %d: %d buttons, %d axes\n[%s]', |
|
|
|
e.gamepad.index, e.gamepad.buttons.length, e.gamepad.axes.length, |
|
|
|
e.gamepad.id); |
|
|
|
} |
|
|
|
|
|
|
|
gamepadDisconnected(e) { |
|
|
|
debug('gamepad disconnected :('); |
|
|
|
console.log('gamepad disconnected @ index %d:\n[%s]', e.gamepad.index, |
|
|
|
e.gamepad.id); |
|
|
|
copyFrom(other) { |
|
|
|
this.up = other.up; |
|
|
|
this.down = other.down; |
|
|
|
this.left = other.left; |
|
|
|
this.right = other.right; |
|
|
|
this.a = other.a; |
|
|
|
this.b = other.b; |
|
|
|
this.x = other.x; |
|
|
|
this.y = other.y; |
|
|
|
this.l = other.l; |
|
|
|
this.r = other.r; |
|
|
|
this.select = other.select; |
|
|
|
this.start = other.start; |
|
|
|
} |
|
|
|
|
|
|
|
toString() { |
|
|
@ -143,6 +92,89 @@ class Input { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
class InputHandler { |
|
|
|
constructor() { |
|
|
|
this.up = false; |
|
|
|
this.down = false; |
|
|
|
this.left = false; |
|
|
|
this.right = false; |
|
|
|
this.a = false; |
|
|
|
this.b = false; |
|
|
|
this.x = false; |
|
|
|
this.y = false; |
|
|
|
this.l = false; |
|
|
|
this.r = false; |
|
|
|
this.select = false; |
|
|
|
this.start = false; |
|
|
|
|
|
|
|
this.keysPressed = {}; |
|
|
|
|
|
|
|
window.addEventListener('gamepadconnected', this.gamepadConnected); |
|
|
|
window.addEventListener('gamepaddisconnected', this.gamepadDisconnected); |
|
|
|
document.addEventListener('keydown', (e) => this.keyDown(e)); |
|
|
|
document.addEventListener('keyup', (e) => this.keyUp(e)); |
|
|
|
} |
|
|
|
|
|
|
|
keyDown(e) { |
|
|
|
this.keysPressed[e.key] = true; |
|
|
|
} |
|
|
|
|
|
|
|
keyUp(e) { |
|
|
|
this.keysPressed[e.key] = false; |
|
|
|
} |
|
|
|
|
|
|
|
update(input) { |
|
|
|
// Default ZSNES keybindings. See:
|
|
|
|
// http://zsnes-docs.sourceforge.net/html/readme.htm#default_keys
|
|
|
|
input.up = this.keysPressed['ArrowUp']; |
|
|
|
input.down = this.keysPressed['ArrowDown']; |
|
|
|
input.left = this.keysPressed['ArrowLeft']; |
|
|
|
input.right = this.keysPressed['ArrowRight']; |
|
|
|
input.start = this.keysPressed['Enter']; |
|
|
|
input.select = this.keysPressed['Shift']; |
|
|
|
input.a = this.keysPressed['x']; |
|
|
|
input.b = this.keysPressed['z']; |
|
|
|
input.x = this.keysPressed['s']; |
|
|
|
input.y = this.keysPressed['a']; |
|
|
|
input.l = this.keysPressed['d']; |
|
|
|
input.r = this.keysPressed['c']; |
|
|
|
|
|
|
|
const gamepad = navigator.getGamepads()[0]; |
|
|
|
if (gamepad == null || !gamepad.connected || gamepad.axes.length < 2 || |
|
|
|
gamepad.buttons.length < 12) { |
|
|
|
return; |
|
|
|
} |
|
|
|
// TODO: have a config screen instead of hard-coding the 8Bitdo SNES30 pad.
|
|
|
|
// TODO: handle connects / disconnects more correctly.
|
|
|
|
input.up |= gamepad.axes[1] < 0; |
|
|
|
input.down |= gamepad.axes[1] > 0; |
|
|
|
input.left |= gamepad.axes[0] < 0; |
|
|
|
input.right |= gamepad.axes[0] > 0; |
|
|
|
input.a |= gamepad.buttons[0].pressed; |
|
|
|
input.b |= gamepad.buttons[1].pressed; |
|
|
|
input.x |= gamepad.buttons[3].pressed; |
|
|
|
input.y |= gamepad.buttons[4].pressed; |
|
|
|
input.l |= gamepad.buttons[6].pressed; |
|
|
|
input.r |= gamepad.buttons[7].pressed; |
|
|
|
input.select |= gamepad.buttons[10].pressed; |
|
|
|
input.start |= gamepad.buttons[11].pressed; |
|
|
|
debug(input.toString()); |
|
|
|
} |
|
|
|
|
|
|
|
gamepadConnected(e) { |
|
|
|
debug('gamepad connected! :)'); |
|
|
|
console.log('gamepad connected @ index %d: %d buttons, %d axes\n[%s]', |
|
|
|
e.gamepad.index, e.gamepad.buttons.length, e.gamepad.axes.length, |
|
|
|
e.gamepad.id); |
|
|
|
} |
|
|
|
|
|
|
|
gamepadDisconnected(e) { |
|
|
|
debug('gamepad disconnected :('); |
|
|
|
console.log('gamepad disconnected @ index %d:\n[%s]', e.gamepad.index, |
|
|
|
e.gamepad.id); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
class Graphics { |
|
|
|
constructor(canvas) { |
|
|
|
this.canvas_ = canvas; |
|
|
@ -223,7 +255,9 @@ class World { |
|
|
|
constructor() { |
|
|
|
this.state_ = null; |
|
|
|
this.fpsCounter_ = new FpsCounter(); |
|
|
|
this.input_ = new Input(); |
|
|
|
this.inputHandler_ = new InputHandler(); |
|
|
|
this.input_ = new SnesInput(); |
|
|
|
this.lastInput_ = new SnesInput(); |
|
|
|
this.player_ = new Player(); |
|
|
|
|
|
|
|
// TODO: move rendering stuff to a separate object.
|
|
|
@ -235,9 +269,13 @@ class World { |
|
|
|
|
|
|
|
update(timestampMs) { |
|
|
|
this.fpsCounter_.update(timestampMs); |
|
|
|
const wasRPressed = this.input_.r; |
|
|
|
this.input_.update(); |
|
|
|
if (!wasRPressed && this.input_.r) { |
|
|
|
|
|
|
|
// We copy values to avoid allocating new SnesInput objects every frame.
|
|
|
|
// TODO: is this actually worth it?
|
|
|
|
this.lastInput_.copyFrom(this.input_); |
|
|
|
this.inputHandler_.update(this.input_); |
|
|
|
|
|
|
|
if (!this.lastInput_.r && this.input_.r) { |
|
|
|
this.player_.cycleSprite(); |
|
|
|
} |
|
|
|
if (this.input_.left) { |
|
|
|