nose-pluto/src/js/gamepad.ts
2025-09-29 21:18:39 -07:00

64 lines
1.6 KiB
TypeScript

////
// Support for gamepads.
// Maps incoming gamepad button presses to keyboard buttons.
//
const BUTTONS = [
"A", "B", "X", "Y",
"LB", "RB", "LT", "RT",
"Select", "Start",
"LStick", "RStick",
"DPad Up", "DPad Down", "DPad Left", "DPad Right"
]
const BUTTONS_TO_KEYS: Record<number, string> = {
0: "z", // A → Z
1: "x", // B → X
2: "c", // X → C
3: "v", // Y → V
12: "ArrowUp",
13: "ArrowDown",
14: "ArrowLeft",
15: "ArrowRight",
9: "Enter", // Start
8: "Escape" // Select
}
const activePads = new Set<number>()
const prevState: Record<number, boolean[]> = {}
export function initGamepad() {
window.addEventListener("gamepadconnected", e => {
console.log("Gamepad connected:", e.gamepad)
activePads.add(e.gamepad.index)
requestAnimationFrame(handleGamepad)
})
window.addEventListener("gamepaddisconnected", e => {
console.log("Gamepad disconnected:", e.gamepad)
activePads.delete(e.gamepad.index)
})
}
function handleGamepad() {
const pads = navigator.getGamepads()
for (const gp of pads) {
if (!gp) continue
if (!prevState[gp.index]) prevState[gp.index] = gp.buttons.map(() => false)
gp.buttons.forEach((btn, i) => {
const was = prevState[gp.index]![i]
const is = btn.pressed
if (was !== is) {
const key = BUTTONS_TO_KEYS[i]
if (key) {
const type = is ? "keydown" : "keyup"
window.dispatchEvent(new KeyboardEvent(type, { code: key, key }))
}
prevState[gp.index]![i] = is
}
})
}
requestAnimationFrame(handleGamepad)
}