test
This commit is contained in:
parent
19d3eb26cd
commit
6957a5f9d9
@ -217,8 +217,6 @@ body {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.play-again { background: #b700ff; color: white; }
|
.play-again { background: #b700ff; color: white; }
|
||||||
.leaderboard { background: gold; }
|
.leaderboard { background: gold; }
|
||||||
.back-menu { background: #444; color: white; }
|
.back-menu { background: #444; color: white; }
|
||||||
@ -281,45 +279,6 @@ body {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// === SIMPAN SKOR KE LEADERBOARD ===
|
|
||||||
function saveScoreToLeaderboard(totalScore) {
|
|
||||||
let leaderboard = JSON.parse(localStorage.getItem("leaderboard")) || [];
|
|
||||||
|
|
||||||
let record = {
|
|
||||||
player: localStorage.getItem("username") || "Guest",
|
|
||||||
score: totalScore,
|
|
||||||
date: new Date().toLocaleString()
|
|
||||||
};
|
|
||||||
|
|
||||||
leaderboard.push(record);
|
|
||||||
leaderboard.sort((a, b) => b.score - a.score);
|
|
||||||
leaderboard = leaderboard.slice(0, 10); // max 10 skor terbaik
|
|
||||||
|
|
||||||
localStorage.setItem("leaderboard", JSON.stringify(leaderboard));
|
|
||||||
}
|
|
||||||
|
|
||||||
// === GANTI BAGIAN showEndScreen() DENGAN TAMBAHAN SAVE ===
|
|
||||||
function showEndScreen() {
|
|
||||||
clearInterval(countdown);
|
|
||||||
let baseScore = score;
|
|
||||||
let timeBonus = time * 5;
|
|
||||||
let moveBonus = Math.max(0, 200 - moves * 10);
|
|
||||||
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";
|
|
||||||
|
|
||||||
saveScoreToLeaderboard(total); // ⬅ SIMPAN SKOR
|
|
||||||
}
|
|
||||||
|
|
||||||
// === KLIK TOMBOL LEADERBOARD KE HALAMAN LEADERBOARD ===
|
|
||||||
document.querySelector(".leaderboard").addEventListener("click", () => {
|
|
||||||
window.location.href = "leaderboard.html";
|
|
||||||
});
|
|
||||||
|
|
||||||
const images = [
|
const images = [
|
||||||
"images/fruit1.png",
|
"images/fruit1.png",
|
||||||
"images/fruit2.png",
|
"images/fruit2.png",
|
||||||
|
|||||||
@ -19,7 +19,7 @@ body {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TOP UI */
|
|
||||||
.topbar {
|
.topbar {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
@ -56,7 +56,7 @@ body {
|
|||||||
box-shadow: 0 3px 6px rgba(0,0,0,0.2), inset 0 0 6px rgba(255,255,255,0.6);
|
box-shadow: 0 3px 6px rgba(0,0,0,0.2), inset 0 0 6px rgba(255,255,255,0.6);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* === 5×4 GRID === */
|
|
||||||
.gameboard {
|
.gameboard {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: grid;
|
display: grid;
|
||||||
@ -67,7 +67,7 @@ body {
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* === CARDS === */
|
|
||||||
.card {
|
.card {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@ -84,7 +84,7 @@ body {
|
|||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Face */
|
|
||||||
.front, .back {
|
.front, .back {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -116,7 +116,7 @@ body {
|
|||||||
filter: drop-shadow(0 3px 4px rgba(0,0,0,0.45));
|
filter: drop-shadow(0 3px 4px rgba(0,0,0,0.45));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Hover & Flip */
|
|
||||||
.card:not(.flipped):hover .front {
|
.card:not(.flipped):hover .front {
|
||||||
transform: scale(1.05);
|
transform: scale(1.05);
|
||||||
transition: 0.25s;
|
transition: 0.25s;
|
||||||
@ -124,14 +124,14 @@ body {
|
|||||||
|
|
||||||
.card.flipped .inner { transform: rotateY(180deg); }
|
.card.flipped .inner { transform: rotateY(180deg); }
|
||||||
|
|
||||||
/* Match Shine */
|
|
||||||
.card.matched .front,
|
.card.matched .front,
|
||||||
.card.matched .back {
|
.card.matched .back {
|
||||||
border-color: #7affd6;
|
border-color: #7affd6;
|
||||||
box-shadow: 0 0 15px #7affd6, 0 0 30px rgba(122,255,214,0.8);
|
box-shadow: 0 0 15px #7affd6, 0 0 30px rgba(122,255,214,0.8);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* === COMBO POPUP === */
|
|
||||||
.combo-popup {
|
.combo-popup {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
padding: 12px 20px;
|
padding: 12px 20px;
|
||||||
@ -200,16 +200,6 @@ body {
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.gameboard {
|
|
||||||
flex: 1;
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(4, 1fr);
|
|
||||||
gap: 16px;
|
|
||||||
padding: 20px 50px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
place-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.play-again { background: #b700ff; color: white; }
|
.play-again { background: #b700ff; color: white; }
|
||||||
.leaderboard { background: gold; }
|
.leaderboard { background: gold; }
|
||||||
.back-menu { background: #444; color: white; }
|
.back-menu { background: #444; color: white; }
|
||||||
@ -232,7 +222,6 @@ body {
|
|||||||
<div class="end-box">
|
<div class="end-box">
|
||||||
<div class="end-title">🎉 Selamat!</div>
|
<div class="end-title">🎉 Selamat!</div>
|
||||||
<p>Anda berhasil menyelesaikan permainan!</p>
|
<p>Anda berhasil menyelesaikan permainan!</p>
|
||||||
|
|
||||||
<div class="score-row"><span>Skor Base:</span> <span id="baseScoreEnd">0</span></div>
|
<div class="score-row"><span>Skor Base:</span> <span id="baseScoreEnd">0</span></div>
|
||||||
<div class="score-row"><span>Time Bonus:</span> <span id="timeBonusEnd">0</span></div>
|
<div class="score-row"><span>Time Bonus:</span> <span id="timeBonusEnd">0</span></div>
|
||||||
<div class="score-row"><span>Move Bonus:</span> <span id="moveBonusEnd">0</span></div>
|
<div class="score-row"><span>Move Bonus:</span> <span id="moveBonusEnd">0</span></div>
|
||||||
@ -251,7 +240,7 @@ body {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
/* === 10 GAMBAR (20 kartu) === */
|
|
||||||
const images = [
|
const images = [
|
||||||
"images/fruit1.png",
|
"images/fruit1.png",
|
||||||
"images/fruit2.png",
|
"images/fruit2.png",
|
||||||
@ -272,7 +261,6 @@ let time = 60;
|
|||||||
let moves = 0;
|
let moves = 0;
|
||||||
let score = 0;
|
let score = 0;
|
||||||
let countdown;
|
let countdown;
|
||||||
|
|
||||||
let combo = 1;
|
let combo = 1;
|
||||||
let pendingMatch = false;
|
let pendingMatch = false;
|
||||||
let lastMatchTime = 0;
|
let lastMatchTime = 0;
|
||||||
@ -292,7 +280,6 @@ function showComboPopup(targetCard, combo, bonus) {
|
|||||||
setTimeout(() => popup.remove(), 1500);
|
setTimeout(() => popup.remove(), 1500);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function shuffleDistant(cards, minDistance = 4) {
|
function shuffleDistant(cards, minDistance = 4) {
|
||||||
let valid = false;
|
let valid = false;
|
||||||
let result;
|
let result;
|
||||||
|
|||||||
@ -19,7 +19,6 @@ body {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TOP UI */
|
|
||||||
.topbar {
|
.topbar {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
@ -56,7 +55,6 @@ body {
|
|||||||
box-shadow: 0 3px 6px rgba(0,0,0,0.2), inset 0 0 6px rgba(255,255,255,0.6);
|
box-shadow: 0 3px 6px rgba(0,0,0,0.2), inset 0 0 6px rgba(255,255,255,0.6);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* === 4×4 GRID === */
|
|
||||||
.gameboard {
|
.gameboard {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: grid;
|
display: grid;
|
||||||
@ -67,7 +65,6 @@ body {
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* === CARDS === */
|
|
||||||
.card {
|
.card {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@ -84,7 +81,6 @@ body {
|
|||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Face */
|
|
||||||
.front, .back {
|
.front, .back {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -116,7 +112,6 @@ body {
|
|||||||
filter: drop-shadow(0 3px 4px rgba(0,0,0,0.45));
|
filter: drop-shadow(0 3px 4px rgba(0,0,0,0.45));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Hover & Flip */
|
|
||||||
.card:not(.flipped):hover .front {
|
.card:not(.flipped):hover .front {
|
||||||
transform: scale(1.05);
|
transform: scale(1.05);
|
||||||
transition: 0.25s;
|
transition: 0.25s;
|
||||||
@ -124,14 +119,12 @@ body {
|
|||||||
|
|
||||||
.card.flipped .inner { transform: rotateY(180deg); }
|
.card.flipped .inner { transform: rotateY(180deg); }
|
||||||
|
|
||||||
/* Match Shine */
|
|
||||||
.card.matched .front,
|
.card.matched .front,
|
||||||
.card.matched .back {
|
.card.matched .back {
|
||||||
border-color: #7affd6;
|
border-color: #7affd6;
|
||||||
box-shadow: 0 0 15px #7affd6, 0 0 30px rgba(122,255,214,0.8);
|
box-shadow: 0 0 15px #7affd6, 0 0 30px rgba(122,255,214,0.8);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* === COMBO POPUP === */
|
|
||||||
.combo-popup {
|
.combo-popup {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
padding: 12px 20px;
|
padding: 12px 20px;
|
||||||
@ -248,7 +241,6 @@ body {
|
|||||||
<div class="end-box">
|
<div class="end-box">
|
||||||
<div class="end-title">🎉 Selamat!</div>
|
<div class="end-title">🎉 Selamat!</div>
|
||||||
<p>Anda berhasil menyelesaikan permainan!</p>
|
<p>Anda berhasil menyelesaikan permainan!</p>
|
||||||
|
|
||||||
<div class="score-row"><span>Skor Base:</span> <span id="baseScoreEnd">0</span></div>
|
<div class="score-row"><span>Skor Base:</span> <span id="baseScoreEnd">0</span></div>
|
||||||
<div class="score-row"><span>Time Bonus:</span> <span id="timeBonusEnd">0</span></div>
|
<div class="score-row"><span>Time Bonus:</span> <span id="timeBonusEnd">0</span></div>
|
||||||
<div class="score-row"><span>Move Bonus:</span> <span id="moveBonusEnd">0</span></div>
|
<div class="score-row"><span>Move Bonus:</span> <span id="moveBonusEnd">0</span></div>
|
||||||
@ -267,7 +259,7 @@ body {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
/* === 8 GAMBAR (16 kartu) === */
|
|
||||||
const images = [
|
const images = [
|
||||||
"images/fruit1.png",
|
"images/fruit1.png",
|
||||||
"images/fruit2.png",
|
"images/fruit2.png",
|
||||||
@ -286,7 +278,6 @@ let time = 60;
|
|||||||
let moves = 0;
|
let moves = 0;
|
||||||
let score = 0;
|
let score = 0;
|
||||||
let countdown;
|
let countdown;
|
||||||
|
|
||||||
let combo = 1;
|
let combo = 1;
|
||||||
let pendingMatch = false;
|
let pendingMatch = false;
|
||||||
let lastMatchTime = 0;
|
let lastMatchTime = 0;
|
||||||
@ -306,7 +297,6 @@ function showComboPopup(targetCard, combo, bonus) {
|
|||||||
setTimeout(() => popup.remove(), 1500);
|
setTimeout(() => popup.remove(), 1500);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function shuffleDistant(cards, minDistance = 4) {
|
function shuffleDistant(cards, minDistance = 4) {
|
||||||
let valid = false;
|
let valid = false;
|
||||||
let result;
|
let result;
|
||||||
|
|||||||
109
leaderboard.css
109
leaderboard.css
@ -1,109 +0,0 @@
|
|||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
font-family: Poppins, Arial, sans-serif;
|
|
||||||
height: 100vh;
|
|
||||||
overflow: hidden;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
background: linear-gradient(135deg, #ff9ed1, #d784ff);
|
|
||||||
}
|
|
||||||
/* animasi fruit */
|
|
||||||
.fruit {
|
|
||||||
position: absolute;
|
|
||||||
width: 95px;
|
|
||||||
opacity: 0.85;
|
|
||||||
animation: float 7s infinite ease-in-out;
|
|
||||||
pointer-events: none;
|
|
||||||
filter: drop-shadow(0 4px 6px rgba(0,0,0,0.12));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Posisi lebih seimbang & tidak menutupi teks */
|
|
||||||
.f1 { top: 6%; left: 12%; animation-delay: .3s; }
|
|
||||||
.f2 { top: 12%; right: 18%; animation-delay: 1.2s; }
|
|
||||||
.f3 { top: 48%; right: 8%; animation-delay: .7s; }
|
|
||||||
.f4 { top: 72%; left: 20%; animation-delay: 1.6s; }
|
|
||||||
.f5 { top: 28%; left: 60%; animation-delay: .5s; }
|
|
||||||
.f6 { top: 68%; right: 22%; animation-delay: 1s; }
|
|
||||||
.f7 { top: 40%; left: 10%; animation-delay: .9s; }
|
|
||||||
.f8 { top: 82%; left: 70%; animation-delay: 1.4s; }
|
|
||||||
.f9 { top: 18%; right: 46%; animation-delay: .6s; }
|
|
||||||
.f10 { top: 80%; right: 10%; animation-delay: 1.8s; }
|
|
||||||
|
|
||||||
/* Smooth floating animation */
|
|
||||||
@keyframes float {
|
|
||||||
0% { transform: translateY(0) rotate(0deg); }
|
|
||||||
50% { transform: translateY(-18px) rotate(6deg); }
|
|
||||||
100% { transform: translateY(0) rotate(0deg); }
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes drift {
|
|
||||||
0% { transform: translateX(0); }
|
|
||||||
50% { transform: translateX(22px); }
|
|
||||||
100% { transform: translateX(0); }
|
|
||||||
}
|
|
||||||
|
|
||||||
.fruit {
|
|
||||||
width: clamp(70px, 7vw, 105px);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* leaderboard */
|
|
||||||
table {
|
|
||||||
width: 100%;
|
|
||||||
border-collapse: collapse;
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
.rank-up {
|
|
||||||
animation: rankUp 0.6s ease;
|
|
||||||
background: #d2ffdf !important; /* hijau */
|
|
||||||
}
|
|
||||||
|
|
||||||
.rank-down {
|
|
||||||
animation: rankDown 0.6s ease;
|
|
||||||
background: #ffd2d2 !important; /* merah */
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes rankUp {
|
|
||||||
from { transform: translateY(10px); opacity: 0.4; }
|
|
||||||
to { transform: translateY(0); opacity: 1; }
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes rankDown {
|
|
||||||
from { transform: translateY(-10px); opacity: 0.4; }
|
|
||||||
to { transform: translateY(0); opacity: 1; }
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes rankFlashUp {
|
|
||||||
0% { background: rgba(0,255,0,0.7); }
|
|
||||||
100% { background: rgba(0,255,0,0.2); }
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes rankFlashDown {
|
|
||||||
0% { background: rgba(255,0,0,0.7); }
|
|
||||||
100% { background: rgba(255,0,0,0.2); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Difficulty Tabs */
|
|
||||||
.difficulty-tabs {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
gap: 10px;
|
|
||||||
margin-bottom: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.difficulty-tabs button {
|
|
||||||
padding: 8px 16px;
|
|
||||||
border: none;
|
|
||||||
border-radius: 12px;
|
|
||||||
font-size: 16px;
|
|
||||||
cursor: pointer;
|
|
||||||
background: #d2b6ff;
|
|
||||||
font-weight: 600;
|
|
||||||
transition: 0.25s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.difficulty-tabs button.active {
|
|
||||||
background: #8e00ff;
|
|
||||||
color: white;
|
|
||||||
transform: scale(1.1);
|
|
||||||
}
|
|
||||||
@ -1,36 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Leaderboard – Memory Card</title>
|
|
||||||
<link rel="stylesheet" href="leaderboard.css" />
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<div id="leaderboardPopup" class="leaderboard-popup">
|
|
||||||
<div class="leaderboard-box">
|
|
||||||
<h2>🏆 Leaderboard</h2>
|
|
||||||
<div class="difficulty-tabs">
|
|
||||||
<button onclick="changeDifficulty('easy')" id="tab-easy">Easy</button>
|
|
||||||
<button onclick="changeDifficulty('medium')" id="tab-medium">Medium</button>
|
|
||||||
<button onclick="changeDifficulty('hard')" id="tab-hard">Hard</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Rank</th>
|
|
||||||
<th>Nama</th>
|
|
||||||
<th>Score</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody id="leaderboard-body"></tbody>
|
|
||||||
</table>
|
|
||||||
<button class="close-leaderboard" onclick="closeLeaderboard()">✖ Tutup</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script src="leaderboard.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
108
leaderboard.js
108
leaderboard.js
@ -1,108 +0,0 @@
|
|||||||
const leaderboardFile = "leaderboard.json";
|
|
||||||
|
|
||||||
// 🔹 Buka popup leaderboard
|
|
||||||
function openLeaderboard() {
|
|
||||||
document.getElementById("leaderboardPopup").style.display = "flex";
|
|
||||||
loadLeaderboard();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 🔹 Tutup popup leaderboard
|
|
||||||
function closeLeaderboard() {
|
|
||||||
document.getElementById("leaderboardPopup").style.display = "none";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 🔹 Muat leaderboard dari JSON
|
|
||||||
async function loadLeaderboard() {
|
|
||||||
const difficulty = localStorage.getItem("difficulty") || "easy";
|
|
||||||
|
|
||||||
try {
|
|
||||||
const res = await fetch(leaderboardFile + "?v=" + Date.now());
|
|
||||||
const data = await res.json();
|
|
||||||
|
|
||||||
// Ambil list sesuai difficulty
|
|
||||||
let list = data[difficulty] || [];
|
|
||||||
|
|
||||||
// Urutkan score dari paling tinggi
|
|
||||||
list.sort((a, b) => b.score - a.score);
|
|
||||||
|
|
||||||
const tbody = document.getElementById("leaderboard-body");
|
|
||||||
tbody.innerHTML = "";
|
|
||||||
|
|
||||||
newOrder.length = 0;
|
|
||||||
list.forEach((p, i) => {
|
|
||||||
newOrder.push(p.name);
|
|
||||||
|
|
||||||
let movementClass = "";
|
|
||||||
if (previousOrder.length > 0) {
|
|
||||||
const oldIndex = previousOrder.indexOf(p.name);
|
|
||||||
if (oldIndex !== -1) {
|
|
||||||
if (oldIndex > i) movementClass = "rank-up";
|
|
||||||
if (oldIndex < i) movementClass = "rank-down";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tbody.innerHTML += `
|
|
||||||
<tr class="${movementClass}">
|
|
||||||
<td class="rank">#${i + 1}</td>
|
|
||||||
<td>${p.name}</td>
|
|
||||||
<td>${p.score}</td>
|
|
||||||
</tr>`;
|
|
||||||
});
|
|
||||||
|
|
||||||
highlightTab(difficulty);
|
|
||||||
|
|
||||||
// Simpan posisi lama → jadi perbandingan load berikutnya
|
|
||||||
localStorage.setItem("prev_ranking", JSON.stringify(newOrder));
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
document.getElementById("leaderboard-body").innerHTML =
|
|
||||||
`<tr><td colspan="3">⚠ Gagal memuat leaderboard</td></tr>`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 🔹 Menyimpan skor ke leaderboard.json
|
|
||||||
async function saveScore(name, score, difficulty) {
|
|
||||||
const res = await fetch(leaderboardFile);
|
|
||||||
let data = await res.json();
|
|
||||||
let board = data[difficulty];
|
|
||||||
|
|
||||||
const player = board.find(e => e.name.toLowerCase() === name.toLowerCase());
|
|
||||||
|
|
||||||
if (player) {
|
|
||||||
if (score > player.score) player.score = score;
|
|
||||||
} else {
|
|
||||||
board.push({ name, score });
|
|
||||||
}
|
|
||||||
|
|
||||||
await fetch(leaderboardFile, {
|
|
||||||
method: "POST",
|
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
body: JSON.stringify(data, null, 2),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const difficulty = localStorage.getItem("difficulty") || "easy";
|
|
||||||
localStorage.setItem("playerName", username);
|
|
||||||
|
|
||||||
let previousOrder = JSON.parse(localStorage.getItem("prev_ranking") || "[]");
|
|
||||||
let newOrder = [];
|
|
||||||
|
|
||||||
function highlightTab(diff) {
|
|
||||||
document.querySelectorAll(".difficulty-tabs button").forEach(btn => btn.classList.remove("active"));
|
|
||||||
document.getElementById("tab-" + diff).classList.add("active");
|
|
||||||
}
|
|
||||||
|
|
||||||
function changeDifficulty(diff) {
|
|
||||||
localStorage.setItem("difficulty", diff);
|
|
||||||
previousOrder = JSON.parse(localStorage.getItem("prev_ranking")) || [];
|
|
||||||
loadLeaderboard();
|
|
||||||
highlightTab(diff);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"easy": [],
|
|
||||||
"medium": [],
|
|
||||||
"hard": []
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user