diff --git a/script.js b/script.js index fa46634..f8a7d56 100644 --- a/script.js +++ b/script.js @@ -1,72 +1,189 @@ -const game = document.getElementById("game"); -const movesText = document.getElementById("moves"); -const scoreText = document.getElementById("score"); +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 moves = 0; let score = 0; -let firstCard = null; -let lock = false; +let gameRunning = true; +let speed = 2; +let blockHeight = 35; +let initialWidth = 200; +let blocks = []; +let currentBlock = {}; +let direction = 1; +let hue = 0; -let icons = ["🍎", "🍌", "🍇", "🍓", "🍒", "🍑", "🍍", "🥝"]; // 8 pairs -let cards = [...icons, ...icons]; -cards.sort(() => Math.random() - 0.5); +function initGame() { + score = 0; + blocks = []; + speed = 3; + hue = Math.random() * 360; + + inGameScore.innerText = score; -cards.forEach(icon => { - const div = document.createElement("div"); - div.className = "card"; - div.dataset.icon = icon; - div.textContent = "?"; - game.appendChild(div); -}); - -game.querySelectorAll(".card").forEach(card => { - card.addEventListener("click", () => { - if(lock || card.classList.contains("open") || card.classList.contains("matched")) return; - - card.classList.add("open"); - card.textContent = card.dataset.icon; - - if(!firstCard){ - firstCard = card; - return; - } - - moves++; - movesText.textContent = moves; - lock = true; - - if(firstCard.dataset.icon === card.dataset.icon){ - score += 100; - scoreText.textContent = score; - - firstCard.classList.add("matched"); - card.classList.add("matched"); - - firstCard = null; - lock = false; - - saveScore(score); - } else { - score -= 20; - scoreText.textContent = score; - - setTimeout(() => { - firstCard.classList.remove("open"); - firstCard.textContent = "?"; - card.classList.remove("open"); - card.textContent = "?"; - - firstCard = null; - lock = false; - }, 700); - } + // Base block + blocks.push({ + x: (canvas.width - initialWidth) / 2, + y: canvas.height - 100, + width: initialWidth, + color: `hsl(${hue}, 100%, 50%)` }); -}); -function saveScore(score){ - fetch("save_score.php", { - method: "POST", - headers: { "Content-Type": "application/x-www-form-urlencoded" }, - body: "score=" + score + 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(); \ No newline at end of file