// --- KONFIGURASI AUDIO & DOM --- const bgMusic = document.getElementById("bgMusic"); const sfxClick = document.getElementById("sfxClick"); const sfxMatch = document.getElementById("sfxMatch"); const sfxWrong = document.getElementById("sfxWrong"); const sfxCountdown = document.getElementById("sfxCountdown"); const sfxWin = document.getElementById("sfxWin"); const sfxLose = document.getElementById("sfxLose"); const toggleBtn = document.getElementById("toggleMusic"); const overlay = document.getElementById("countdown-overlay"); let musicMuted = true; // AUTO-PLAY PADA INTERAKSI PERTAMA function initAudio() { if(musicMuted) { bgMusic.play().catch(() => {}); bgMusic.volume = 0.5; toggleBtn.textContent = "🔊"; musicMuted = false; document.removeEventListener('click', initAudio); } } document.addEventListener('click', initAudio); toggleBtn.onclick = (e) => { e.stopPropagation(); if (musicMuted) { bgMusic.play(); toggleBtn.textContent = "🔊"; } else { bgMusic.pause(); toggleBtn.textContent = "🔇"; } musicMuted = !musicMuted; }; function playSFX(audio) { if(!musicMuted) { audio.currentTime = 0; audio.play().catch(() => {}); } } // --- LOGIKA GAME HARD (10 Gambar = 20 Kartu) --- const images = [ "images/fruit1.png", "images/fruit2.png", "images/fruit3.png", "images/fruit4.png", "images/fruit5.png", "images/fruit6.png", "images/fruit7.png", "images/fruit8.png", "images/fruit9.png", "images/fruit10.png" ]; // Total 10 gambar = 20 kartu. // Kalau di CSS kolomnya 4, otomatis barisnya jadi 5. let cards = [...images, ...images]; let flipped = []; let timerStarted = false; let time = 60; // Waktu Hard: 120 Detik (2 Menit) let moves = 0; let score = 0; let countdown; let combo = 1; let lastMatchTime = 0; let pendingMatch = false; // [BARU] Fungsi Visual Combo function showComboPopup(targetCard, combo, bonus) { const popup = document.createElement("div"); popup.className = "combo-popup"; popup.innerHTML = `COMBO X${combo}
+${bonus} Bonus`; const rect = targetCard.getBoundingClientRect(); popup.style.left = rect.left + rect.width / 2 + "px"; popup.style.top = rect.top + "px"; popup.style.position = "fixed"; document.body.appendChild(popup); setTimeout(() => popup.remove(), 1500); } // [UPDATE] Shuffle Lebih Pintar (Biar kartu sama gak sebelahan) function shuffleDistant(cards, minDistance = 2) { let valid = false; let result; while (!valid) { result = [...cards].sort(() => Math.random() - 0.5); valid = true; for (let i = 0; i < result.length - 1; i++) { if (result[i] === result[i+1]) { // Cek tetangga valid = false; break; } } } return result; } function runCountdown(callback) { let count = 3; overlay.style.display = "flex"; overlay.innerHTML = `${count}`; playSFX(sfxCountdown); let cdTimer = setInterval(() => { count--; if (count > 0) { overlay.innerHTML = `${count}`; playSFX(sfxCountdown); } else if (count === 0) { overlay.innerHTML = `GO!`; } else { clearInterval(cdTimer); overlay.style.display = "none"; callback(); } }, 1000); } function startTimer() { timerStarted = true; countdown = setInterval(() => { document.getElementById("timer").textContent = --time; if (time <= 0) { clearInterval(countdown); showEndScreen(false); // Kalah } }, 1000); } function showEndScreen(isWin) { clearInterval(countdown); bgMusic.pause(); if(isWin) playSFX(sfxWin); else playSFX(sfxLose); document.getElementById("endTitle").textContent = isWin ? "🎉 Selamat!" : "⏰ Waktu Habis!"; document.getElementById("endMsg").textContent = isWin ? "Anda berhasil menyelesaikan permainan!" : "Coba lagi lain kali!"; let baseScore = score; let timeBonus = isWin ? time * 5 : 0; let moveBonus = isWin ? Math.max(0, 200 - moves * 10) : 0; let total = baseScore + timeBonus + moveBonus; document.getElementById("baseScoreEnd").textContent = baseScore; document.getElementById("timeBonusEnd").textContent = "+" + timeBonus; document.getElementById("moveBonusEnd").textContent = "+" + moveBonus; document.getElementById("totalScoreEnd").textContent = total; document.getElementById("endScreen").style.display = "flex"; // [BARU] FETCH KE DATABASE (HARD) if (isWin) { let formData = new FormData(); formData.append('score', total); formData.append('difficulty', 'Hard'); fetch('score.php', { method: 'POST', body: formData }) .then(response => response.text()) .then(result => console.log("Status Database: " + result)) .catch(error => console.error('Error:', error)); } } function flipCard(card) { if (!timerStarted) startTimer(); if (flipped.length === 2 || card.classList.contains("matched") || card.classList.contains("flipped")) return; playSFX(sfxClick); card.classList.add("flipped"); flipped.push(card); if (flipped.length === 2) { moves++; document.getElementById("moves").textContent = moves; let img1 = flipped[0].querySelector("img").src; let img2 = flipped[1].querySelector("img").src; if (img1 === img2) { playSFX(sfxMatch); const now = Date.now(); // LOGIKA COMBO & POPUP if (!pendingMatch) { pendingMatch = true; combo = 1; } else { if (now - lastMatchTime <= 3000) { // Waktu toleransi combo 3 detik combo++; let comboBonus = combo * 10; score += comboBonus; showComboPopup(flipped[0], combo, comboBonus); // MUNCULKAN POPUP } else { combo = 1; } } lastMatchTime = now; flipped.forEach(c => c.classList.add("matched")); score += 50; document.getElementById("score").textContent = score; time += 5; document.getElementById("timer").textContent = time; // Update tampilan waktu bonus flipped = []; if (document.querySelectorAll(".matched").length === cards.length) { setTimeout(() => showEndScreen(true), 600); } } else { playSFX(sfxWrong); setTimeout(() => { flipped.forEach(c => c.classList.remove("flipped")); flipped = []; }, 800); } } } function startGame() { document.getElementById("endScreen").style.display = "none"; const board = document.getElementById("game-board"); board.innerHTML = ""; runCountdown(() => { if(!musicMuted) bgMusic.play().catch(() => {}); // Pakai shuffleDistant agar kartu tidak menumpuk const shuffledCards = shuffleDistant([...cards]); shuffledCards.forEach(image => { const card = document.createElement("div"); card.className = "card"; card.innerHTML = `
?
`; card.onclick = () => flipCard(card); board.appendChild(card); }); time = 60; // Reset waktu jadi 120 detik moves = 0; score = 0; combo = 1; pendingMatch = false; flipped = []; timerStarted = false; document.getElementById("timer").textContent = time; document.getElementById("moves").textContent = moves; document.getElementById("score").textContent = score; }); } startGame();