const canvas = document.getElementById('gameCanvas'); const ctx = canvas.getContext('2d'); const uiLayer = document.getElementById('ui-layer'); const finalScoreSpan = document.getElementById('final-score'); const inGameScore = document.getElementById('ingame-score'); const highScoreDisplay = document.getElementById('high-score-display'); // Setup UI Flexbox uiLayer.style.display = 'none'; let score = 0; let gameRunning = true; let speed = 2; let blockHeight = 35; let initialWidth = 200; let blocks = []; let currentBlock = {}; let direction = 1; let hue = 0; function initGame() { score = 0; blocks = []; speed = 3; hue = Math.random() * 360; inGameScore.innerText = score; // Base block blocks.push({ x: (canvas.width - initialWidth) / 2, y: canvas.height - 100, width: initialWidth, color: `hsl(${hue}, 100%, 50%)` }); spawnBlock(); // Perubahan: Jangan panggil draw() di sini jika ini reset, // biarkan resetGame yang mengontrol loop if(gameRunning) draw(); } function spawnBlock() { const prevBlock = blocks[blocks.length - 1]; hue += 10; currentBlock = { x: 0, y: prevBlock.y - blockHeight, width: prevBlock.width, color: `hsl(${hue}, 100%, 50%)` }; if (currentBlock.y < 150) { blocks.forEach(b => b.y += blockHeight); currentBlock.y += blockHeight; } } function drawBackground() { ctx.strokeStyle = 'rgba(255, 255, 255, 0.05)'; ctx.lineWidth = 1; for (let i = 0; i < canvas.width; i += 40) { ctx.beginPath(); ctx.moveTo(i, 0); ctx.lineTo(i, canvas.height); ctx.stroke(); } for (let i = 0; i < canvas.height; i += 40) { ctx.beginPath(); ctx.moveTo(0, i); ctx.lineTo(canvas.width, i); ctx.stroke(); } } function draw() { if (!gameRunning) return; ctx.clearRect(0, 0, canvas.width, canvas.height); drawBackground(); currentBlock.x += speed * direction; if (currentBlock.x + currentBlock.width > canvas.width || currentBlock.x < 0) { direction *= -1; } ctx.shadowBlur = 20; blocks.forEach(b => { ctx.fillStyle = b.color; ctx.shadowColor = b.color; ctx.fillRect(b.x, b.y, b.width, blockHeight); ctx.fillStyle = 'rgba(255,255,255,0.2)'; ctx.fillRect(b.x, b.y, b.width, 5); }); ctx.fillStyle = currentBlock.color; ctx.shadowColor = currentBlock.color; ctx.fillRect(currentBlock.x, currentBlock.y, currentBlock.width, blockHeight); ctx.fillStyle = 'rgba(255,255,255,0.4)'; ctx.fillRect(currentBlock.x, currentBlock.y, currentBlock.width, 5); ctx.shadowBlur = 0; requestAnimationFrame(draw); } function placeBlock() { if (!gameRunning) return; const prevBlock = blocks[blocks.length - 1]; let overlap = currentBlock.width - Math.abs(currentBlock.x - prevBlock.x); if (overlap <= 0) { gameOver(); return; } if (currentBlock.x < prevBlock.x) { currentBlock.width = overlap; currentBlock.x = prevBlock.x; } else { currentBlock.width = overlap; } blocks.push(currentBlock); score++; inGameScore.innerText = score; speed += 0.15; spawnBlock(); } function gameOver() { gameRunning = false; uiLayer.style.display = 'flex'; finalScoreSpan.innerText = score; saveScore(score); } // --- PERBAIKAN UTAMA ADA DI SINI --- function resetGame() { uiLayer.style.display = 'none'; // Matikan gameRunning sementara agar klik tombol tidak terbaca sebagai 'placeBlock' gameRunning = false; initGame(); // Beri jeda 300ms. Setelah tombol dilepas, baru game aktif. setTimeout(() => { gameRunning = true; draw(); // Mulai loop animasi }, 300); } // Input Handler yang lebih aman document.body.onkeydown = (e) => { if (e.code === 'Space') { if (gameRunning) { placeBlock(); } else if (uiLayer.style.display === 'flex') { // Cegah spasi merestart terlalu cepat resetGame(); } } }; document.body.onclick = (e) => { // PENTING: Jika yang diklik adalah tombol atau bagian dari UI Layer, JANGAN jalankan game if (e.target.closest('#ui-layer') || e.target.closest('.btn')) return; if (gameRunning) placeBlock(); }; function saveScore(newScore) { const formData = new FormData(); formData.append('score', newScore); fetch('save_score.php', { method: 'POST', body: formData }) .then(response => response.json()) .then(data => fetchHighScore()); } function fetchHighScore() { fetch('save_score.php?action=get') .then(response => response.json()) .then(data => { highScoreDisplay.innerText = data.high_score || "0"; }); } // Start pertama kali initGame(); // Paksa draw pertama kali gameRunning = true; draw();