117 lines
3.5 KiB
JavaScript
117 lines
3.5 KiB
JavaScript
/* ==========================================
|
|
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 }); |