61 lines
4.3 KiB
JavaScript
61 lines
4.3 KiB
JavaScript
// Gamepad input.
|
|
// ---------- Gamepad ----------
|
|
const PAD = { prev: [], active: false, menuT: 0 };
|
|
function padPressed(gp, b) { const v = gp.buttons[b] && gp.buttons[b].pressed; const was = PAD.prev[b]; return v && !was; }
|
|
function pollPad() {
|
|
const gps = navigator.getGamepads ? navigator.getGamepads() : [];
|
|
let gp = null; for (const g of gps) if (g && g.connected) { gp = g; break; }
|
|
if (!gp) return { mx: 0, my: 0 };
|
|
const dz = SET.deadzone;
|
|
const ax = v => Math.abs(v) < dz ? 0 : (v - Math.sign(v) * dz) / (1 - dz);
|
|
const lx = ax(gp.axes[0] || 0), ly = ax(gp.axes[1] || 0), rx = ax(gp.axes[2] || 0), ry = ax(gp.axes[3] || 0);
|
|
const pressedAny = gp.buttons.some(b => b && b.pressed) || lx || ly || rx || ry;
|
|
if (pressedAny) GAME.usingPad = true;
|
|
const modal = topModal();
|
|
if (modal || !GAME.running) {
|
|
const now = performance.now();
|
|
const up = padPressed(gp, 12) || (ly < -0.6 && now > PAD.menuT), down = padPressed(gp, 13) || (ly > 0.6 && now > PAD.menuT);
|
|
const left = padPressed(gp, 14) || (lx < -0.6 && now > PAD.menuT), right = padPressed(gp, 15) || (lx > 0.6 && now > PAD.menuT);
|
|
if (up || down || left || right) PAD.menuT = now + 220;
|
|
const root = modal ? $(modal) : $('title');
|
|
const focusables = [...root.querySelectorAll('button:not([disabled]), select, input')].filter(e => e.offsetParent !== null);
|
|
const cur = document.activeElement;
|
|
let idx = focusables.indexOf(cur);
|
|
if (up || down) { idx = idx < 0 ? 0 : (idx + (down ? 1 : -1) + focusables.length) % focusables.length; focusables[idx] && focusables[idx].focus(); }
|
|
if ((left || right) && cur) {
|
|
if (cur.type === 'range') { const st = parseFloat(cur.step) || 1; cur.value = Math.max(+cur.min, Math.min(+cur.max, parseFloat(cur.value) + (right ? st : -st))); cur.dispatchEvent(new Event('input')); }
|
|
else if (cur.tagName === 'SELECT') { cur.selectedIndex = Math.max(0, Math.min(cur.options.length - 1, cur.selectedIndex + (right ? 1 : -1))); cur.dispatchEvent(new Event('change')); }
|
|
else if (cur.closest('.tabs')) { const tb = [...cur.parentNode.children]; const k = tb.indexOf(cur); const n = tb[(k + (right ? 1 : -1) + tb.length) % tb.length]; n.click(); setTimeout(() => { const t2 = root.querySelector('.tabs .on'); t2 && t2.focus(); }, 0); }
|
|
}
|
|
if (padPressed(gp, 0) && cur && focusables.includes(cur)) cur.click();
|
|
if (padPressed(gp, 1) && modal) closeModal(modal);
|
|
if (padPressed(gp, 9) && modal === 'm-pause') closeModal(modal);
|
|
PAD.prev = gp.buttons.map(b => b && b.pressed);
|
|
return { mx: 0, my: 0 };
|
|
}
|
|
const mag = Math.hypot(rx, ry);
|
|
const reachNow = toolId() === 'drill' ? drillReach(GAME.drillW) : REACH;
|
|
if (mag > 0.05) { GAME.padAim.x = rx / Math.max(1, mag) * reachNow * SET.aimSpeed * Math.min(1, mag); GAME.padAim.y = ry / Math.max(1, mag) * reachNow * SET.aimSpeed * Math.min(1, mag); }
|
|
if (padPressed(gp, 4)) selectTool((GAME.tool + TOOLS.length - 1) % TOOLS.length);
|
|
if (padPressed(gp, 5)) selectTool((GAME.tool + 1) % TOOLS.length);
|
|
if (padPressed(gp, 14)) cycleItem(-1);
|
|
if (padPressed(gp, 15)) cycleItem(1);
|
|
if (padPressed(gp, 12)) setBrush(1);
|
|
if (padPressed(gp, 13)) setBrush(-1);
|
|
if (padPressed(gp, 0)) doInteract();
|
|
if (padPressed(gp, 2)) rotate();
|
|
if (padPressed(gp, 3)) toggleLens();
|
|
if (padPressed(gp, 1)) { if (GAME.selDrag) { GAME.selDrag = null; hudDirty = true; } else if (GAME.drag) GAME.drag = null; else if (GAME.paste) { GAME.paste = false; hudDirty = true; } else if (toolId() === 'vacuum' && GAME.blow.size) { GAME.blow.clear(); lastBagKey = ''; hudDirty = true; playSfx('ui'); } }
|
|
if (padPressed(gp, 10)) bot.lamp = !bot.lamp;
|
|
if (padPressed(gp, 11)) { SET.zoom = SET.zoom >= 10 ? 2 : SET.zoom + 2; saveSettings(); resizeRender(); R.snapNext = true; }
|
|
if (padPressed(gp, 8)) openNotebook();
|
|
if (padPressed(gp, 9)) openPause();
|
|
const rt = gp.buttons[7] && (gp.buttons[7].pressed || gp.buttons[7].value > 0.4), lt = gp.buttons[6] && (gp.buttons[6].pressed || gp.buttons[6].value > 0.4);
|
|
if (GAME.usingPad) {
|
|
if (rt && !GAME.lmb) { GAME.lmb = true; primaryPress(false); } else if (!rt && GAME.lmb) { primaryRelease(false); GAME.lmb = false; }
|
|
if (lt && !GAME.rmb) { GAME.rmb = true; secondaryPress(); } else if (!lt && GAME.rmb) { GAME.rmb = false; endRmbRecord(); }
|
|
}
|
|
PAD.prev = gp.buttons.map(b => b && b.pressed);
|
|
return { mx: lx, my: ly };
|
|
}
|