/* ========================================== 2048 CONTROLS ========================================== fungsi utama: 1. handleKey() - mendeteksi keyboard (Arrow/WASD) 2. Touch Events - meneteksi swipe mobile 3. inputLocked - prevent konflik saat panel volume buka ========================================== */ // flag untuk blokir input game saat setting panel aktif let inputLocked = false; /* ========================================== KEYBOARD INPUT HANDLER ========================================== */ function handleKey(e) { // Blokir input kalau: // 1. Tile sedang bergerak (isMoving = true) // 2. Panel volume sedang dibuka (inputLocked = true) if (isMoving || inputLocked) return; let moved = false; const k = e.key; // Deteksi 4 arah: Arrow keys atau WASD if (k === "ArrowLeft" || k === "a" || k === "A") { e.preventDefault(); // Cegah scroll halaman 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(); } // Kalau tile berhasil bergerak if (moved) { isMoving = true; // Lock input biar nggak spam // Delay 100ms untuk animasi selesai setTimeout(() => { const added = addNewTile(); // Spawn tile baru // Cek game over: board penuh ATAU nggak bisa move if (!added || !canMove()) { setTimeout(() => showGameOver(), 300); } isMoving = false; // Unlock input }, 100); } else { //Invalid move - Kasih feedback shake const b = document.getElementById("board"); if (b) { b.classList.add("shake"); setTimeout(()=>b.classList.remove("shake"), 400); } } } /* ========================================== TOUCH/SWIPE INPUT HANDLER (Mobile) ========================================== */ // Simpan posisi saat awal sentuhan let touchStartX = 0; let touchStartY = 0; // Event 1: Catat posisi awal saat jari menyentuh layar document.addEventListener("touchstart", function (e) { if (inputLocked) return; // Jangan catat swipe kalau panel volume aktif const t = e.touches[0]; touchStartX = t.clientX; // Posisi X awal touchStartY = t.clientY; // Posisi Y awal }, { passive: true }); // Passive = performa lebih smooth // Event 2: Deteksi arah swipe saat jari diangkat document.addEventListener("touchend", function (e) { if (isMoving || inputLocked) return; // cek isMoving DAN inputLocked const t = e.changedTouches[0]; // Hitung selisih posisi (delta) const dx = t.clientX - touchStartX; //Horizontal movement const dy = t.clientY - touchStartY; //Vertical movement let moved = false; // Tentukan arah swipe berdasarkan delta terbesar if (Math.abs(dx) > Math.abs(dy) && Math.abs(dx) > 30) { // Swipe HORIZONTAL (kiri/kanan) // Minimal 30px biar nggak terlalu sensitif if (dx > 0) moved = moveRight(); // swipe kanan else moved = moveLeft(); // swipe kiri } else if (Math.abs(dy) > 30) { // Swipe VERTICAL (atas/bawah) if (dy > 0) moved = moveDown(); // swipe bawah else moved = moveUp(); // swipe atas } // Logic sama seperti keyboard handler if (moved) { isMoving = true; setTimeout(() => { const added = addNewTile(); if (!added || !canMove()) { setTimeout(() => showGameOver(), 300); } isMoving = false; }, 100); } }, { passive: true });