/* ------------------------ 6. INPUT HANDLER ------------------------ */ let inputLocked = false; function handleKey(e) { if (isMoving || inputLocked) return; // ⛔ BLOK kalau sedang buka sound panel let moved = false; const k = e.key; if (k === "ArrowLeft" || k === "a" || k === "A") { e.preventDefault(); moved = moveLeft(); } else if (k === "ArrowRight" || k === "d" || k === "D") { e.preventDefault(); moved = moveRight(); } else if (k === "ArrowUp" || k === "w" || k === "W") { e.preventDefault(); moved = moveUp(); } else if (k === "ArrowDown" || k === "s" || k === "S") { e.preventDefault(); moved = moveDown(); } if (moved) { isMoving = true; setTimeout(() => { const added = addNewTile(); if (!added || !canMove()) { setTimeout(() => showGameOver(), 300); } isMoving = false; }, 100); } else { // Shake effect on invalid move const b = document.getElementById("board"); if (b) { b.classList.add("shake"); setTimeout(()=>b.classList.remove("shake"), 400); } } } /* Touch Swipe */ let touchStartX = 0; let touchStartY = 0; document.addEventListener("touchstart", function (e) { if (inputLocked) return; // ⛔ jangan catat swipe const t = e.touches[0]; touchStartX = t.clientX; touchStartY = t.clientY; }, { passive: true }); document.addEventListener("touchend", function (e) { if (isMoving || inputLocked) return; // ⛔ swipe diblok saat sound panel aktif const t = e.changedTouches[0]; const dx = t.clientX - touchStartX; const dy = t.clientY - touchStartY; let moved = false; if (Math.abs(dx) > Math.abs(dy) && Math.abs(dx) > 30) { if (dx > 0) moved = moveRight(); else moved = moveLeft(); } else if (Math.abs(dy) > 30) { if (dy > 0) moved = moveDown(); else moved = moveUp(); } if (moved) { isMoving = true; setTimeout(() => { const added = addNewTile(); if (!added || !canMove()) { setTimeout(() => showGameOver(), 300); } isMoving = false; }, 100); } }, { passive: true });