Files
deepgrain-foundry/src/ui/input.js
T

93 lines
5.7 KiB
JavaScript

// Keyboard and mouse listeners.
// ---------- Input ----------
const canvas = $('game');
function isTyping(e) { const t = e.target; return t && (t.tagName === 'INPUT' || t.tagName === 'TEXTAREA' || t.tagName === 'SELECT'); }
window.addEventListener('keydown', e => {
if ((e.key === 'Control' || e.key === 'Meta') && !GAME.ctrl) { GAME.ctrl = true; lastBagKey = ''; hudDirty = true; }
if (isTyping(e)) { if (e.key === 'Escape') e.target.blur(); return; }
const k = e.key.length === 1 ? e.key.toLowerCase() : e.key;
if (GAME.running && k === 'm' && e.shiftKey && !e.repeat) { SET.muted = !SET.muted; saveSettings(); auVolumes(); toast(SET.muted ? 'Sound muted.' : 'Sound on.'); return; }
if (anyModal()) {
if (k === 'Escape') { e.preventDefault(); const t = topModal(); closeModal(t); }
else if (k === 'm' && !e.repeat && topModal() === 'm-map') closeModal('m-map');
else if (k === 'n' && !$('m-notebook').hidden) closeModal('m-notebook');
return;
}
if (!GAME.running) return;
if (e.ctrlKey || e.metaKey) {
if (k === 's') { e.preventDefault(); saveWorld('main'); }
else if (k === 'z' && !e.shiftKey) { e.preventDefault(); doUndo(); }
return;
}
keys.add(k);
if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', ' ', 'Tab'].includes(k)) e.preventDefault();
if (e.repeat) return;
GAME.usingPad = false;
switch (k) {
case '`': e.preventDefault(); toggleDevConsole(); break;
case '1': case '2': case '3': case '4': selectTool(+k - 1); break;
case 'c': case 'x': hudDirty = true; break;
case 'v': startPaste(); break;
case 'q': if (e.shiftKey && toolId() === 'build') cycleBuildCat(-1); else cycleItem(-1); break;
case 'e': if (e.shiftKey && toolId() === 'build') cycleBuildCat(1); else cycleItem(1); break;
case 'r': rotate(); break;
case 't': if (toolId() === 'build' && piece().code === BP_WALL) turnSlope(); else if (toolId() === 'build' && piece().filter) pickFilterUnderCursor(); break;
case 'f': doInteract(); break;
case 'g': GAME.replace = true; hudDirty = true; break;
case 'h': toggleLens(); break;
case 'i': GAME.infoOverlay = !GAME.infoOverlay; setFlowCounting(GAME.infoOverlay); playSfx('ui'); toast(GAME.infoOverlay ? 'Info overlay on: what every filter does, which way belts and screw conveyors run, and how many grains a second each line moves (orange: backing up). I hides it.' : 'Info overlay off.'); break;
case 'l': bot.lamp = !bot.lamp; playSfx('ui'); break;
case 'n': openNotebook(); break;
case 'b': openNotebook('Blueprints'); break;
case 'm': openMap(); break;
case '[': setBrush(-1); break;
case ']': setBrush(1); break;
case 'Delete': if (ghostBlocks.size || ghostMachines.length || ghostMolds.length) { clearAllGhosts(); toast('Blueprint ghosts cleared.'); } break;
case 'Escape': if (!$('dev-console').hidden) closeDevConsole(); else if (GAME.selDrag || GAME.paste) stopSelectAndPaste(); else openPause(); break;
case 'Tab': $('bagpanel').hidden = !$('bagpanel').hidden; break;
}
});
window.addEventListener('keyup', e => { const k = e.key.length === 1 ? e.key.toLowerCase() : e.key; keys.delete(k); if ((k === 'Control' || k === 'Meta') && GAME.ctrl) { GAME.ctrl = false; lastBagKey = ''; hudDirty = true; } if (k === 'g') { GAME.replace = false; hudDirty = true; } if (k === 'c' || k === 'x') hudDirty = true; });
window.addEventListener('blur', () => { keys.clear(); if (GAME.ctrl) { GAME.ctrl = false; lastBagKey = ''; hudDirty = true; } GAME.lmb = GAME.rmb = false; GAME.replace = false; GAME.selDrag = null; endRmbRecord(); });
canvas.addEventListener('mousemove', e => { GAME.mouse.x = e.clientX; GAME.mouse.y = e.clientY; GAME.mouse.in = true; GAME.usingPad = false; });
canvas.addEventListener('mouseleave', () => { GAME.mouse.in = false; });
canvas.addEventListener('contextmenu', e => e.preventDefault());
canvas.addEventListener('mousedown', e => {
if (!GAME.running || GAME.paused) return;
auInit();
GAME.usingPad = false;
if (e.button === 0) { GAME.lmb = true; primaryPress(e.shiftKey && !steering()); }
else if (e.button === 2) { GAME.rmb = true; secondaryPress(e.shiftKey && !steering()); }
else if (e.button === 1) { e.preventDefault(); pickUnderCursor(); }
});
window.addEventListener('mouseup', e => {
if (e.button === 0) { if (GAME.lmb) primaryRelease(modShift()); GAME.lmb = false; }
else if (e.button === 2) { GAME.rmb = false; endRmbRecord(); }
});
canvas.addEventListener('wheel', e => {
e.preventDefault();
if (!GAME.running || GAME.paused) return;
const d = e.deltaY > 0 ? -1 : 1;
if (e.shiftKey || e.ctrlKey) setBrush(d);
else { SET.zoom = Math.max(2, Math.min(12, SET.zoom + d)); saveSettings(); resizeRender(); R.snapNext = true; }
}, { passive: false });
// Ctrl + wheel would zoom the browser page (Ctrl shows the full bag list): scroll the list under the mouse instead
window.addEventListener('wheel', e => {
if (!e.ctrlKey || e.target === canvas) return;
e.preventDefault();
for (let n = e.target; n && n !== document.body; n = n.parentElement) {
const oy = getComputedStyle(n).overflowY;
if ((oy === 'auto' || oy === 'scroll') && n.scrollHeight > n.clientHeight) { n.scrollTop += e.deltaY * (e.deltaMode === 1 ? 16 : e.deltaMode === 2 ? n.clientHeight : 1); break; }
}
}, { passive: false });
window.addEventListener('resize', () => { if (R.canvas) resizeRender(); });
document.addEventListener('visibilitychange', () => {
if (document.hidden && GAME.running) saveWorld('auto', true);
if (!AU.ctx) return;
if (document.hidden) { if (SET.muteUnfocused) AU.ctx.suspend(); }
else AU.ctx.resume();
});
// Ctrl+W closes the tab and the page cannot stop it:
// while a world is open, ask the browser to confirm leaving
window.addEventListener('beforeunload', e => { if (GAME.running) { e.preventDefault(); e.returnValue = ''; } });