diff --git a/2048.css b/2048.css
index 84e3768..cc04407 100644
--- a/2048.css
+++ b/2048.css
@@ -1047,14 +1047,14 @@ h1 {
}
}
-/* Best Score Display - ORANGE gradient */
-.best-score-display {
+/* High Score Display - ORANGE gradient */
+.high-score-display {
margin-top: 28px;
padding-top: 28px;
border-top: 2px solid rgba(255, 140, 0, 0.25);
}
-.best-score-label {
+.high-score-label {
font-size: 11px;
text-transform: uppercase;
color: rgba(255, 160, 50, 0.7);
@@ -1063,7 +1063,7 @@ h1 {
font-weight: 700;
}
-.best-score-value {
+.high-score-value {
font-size: clamp(34px, 5vw, 42px);
font-weight: 900;
background: linear-gradient(135deg, #ff8c00 0%, #ffa500 50%, #ffb347 100%);
diff --git a/2048.html b/2048.html
index 8617af5..73b338c 100644
--- a/2048.html
+++ b/2048.html
@@ -210,7 +210,7 @@
@@ -325,14 +325,14 @@
New High Score
-
+
-
High Score
-
0
+
High Score
+
0
diff --git a/2048.js b/2048.js
index 9adc49f..4d85301 100644
--- a/2048.js
+++ b/2048.js
@@ -1,11 +1,18 @@
-
-
/* ------------------------
State & Variables
------------------------ */
let board = [];
let currentScore = 0;
-let bestScore = parseInt(localStorage.getItem('bestScore2048')) || 0;
+
+// 1. Ambil username dari sessionStorage (sesuai sistem login kamu)
+const currentUser = sessionStorage.getItem("loggedInUser") || "guest";
+
+// 2. Buat nama kunci unik, misal: "highScore2048_budi"
+const storageKey = 'highScore2048_' + currentUser;
+
+// 3. Ambil skor milik user tersebut saja
+
+let highScore = parseInt(localStorage.getItem(storageKey)) || 0;
let lastMoveDir = null;
let isMoving = false;
let mergesInCurrentMove = 0;
@@ -71,7 +78,7 @@ function checkAndShowTutorial() {
DOM Ready
------------------------ */
document.addEventListener("DOMContentLoaded", () => {
- updateBestScoreDisplay();
+ updateHighScoreDisplay();
setupBoard();
addNewTile();
addNewTile();
@@ -223,17 +230,18 @@ function updateScoreDisplay() {
scoreEl.textContent = currentScore;
}
- if (currentScore > bestScore) {
- bestScore = currentScore;
- localStorage.setItem('bestScore2048', bestScore);
- updateBestScoreDisplay();
- }
+ if (currentScore > highScore) {
+ highScore = currentScore;
+ // Gunakan storageKey yang sudah kita buat di atas (dinamis sesuai user)
+ localStorage.setItem(storageKey, highScore);
+ updateHighScoreDisplay();
}
+ }
-function updateBestScoreDisplay() {
- const bestScoreEl = document.getElementById('best-score');
- if (bestScoreEl) {
- bestScoreEl.textContent = bestScore;
+function updateHighScoreDisplay() {
+ const highScoreEl = document.getElementById('high-score');
+ if (highScoreEl) {
+ highScoreEl.textContent = highScore;
}
}
@@ -601,7 +609,7 @@ function showGameOver() {
console.error("Fungsi saveScore tidak ditemukan! Pastikan Score_Request.js sudah diload.");
}
- const isNewHighScore = finalScore >= bestScore && finalScore > 0;
+ const isNewHighScore = finalScore >= highScore && finalScore > 0;
const finalScoreEl = document.getElementById('final-score');
if (finalScoreEl) {
@@ -609,16 +617,16 @@ function showGameOver() {
}
const newHighScoreBadge = document.getElementById('new-high-score-badge');
- const bestScoreDisplay = document.getElementById('best-score-display');
+ const highScoreDisplay = document.getElementById('high-score-display');
if (isNewHighScore) {
if (newHighScoreBadge) newHighScoreBadge.style.display = 'inline-block';
- if (bestScoreDisplay) bestScoreDisplay.style.display = 'none';
+ if (highScoreDisplay) highScoreDisplay.style.display = 'none';
} else {
if (newHighScoreBadge) newHighScoreBadge.style.display = 'none';
- if (bestScoreDisplay) bestScoreDisplay.style.display = 'block';
- const modalBestScore = document.getElementById('modal-best-score');
- if (modalBestScore) modalBestScore.textContent = bestScore;
+ if (highScoreDisplay) highScoreDisplay.style.display = 'block';
+ const modalHighScore = document.getElementById('modal-high-score');
+ if (modalHighScore) modalHighScore.textContent = highScore;
}
const gameOverOverlay = document.getElementById('game-over-overlay');
diff --git a/Leaderboard.css b/Leaderboard.css
index 0b14c99..aedb23e 100644
--- a/Leaderboard.css
+++ b/Leaderboard.css
@@ -198,7 +198,9 @@ h1::before {
box-shadow: 0 0 15px rgba(0, 234, 255, 0.8);
}
-/* Leaderboard Item */
+/* --- LEADERBOARD ITEMS & COLORS (HARMONISASI) --- */
+
+/* Base Item Style */
.leaderboard-item {
display: flex;
align-items: center;
@@ -206,7 +208,7 @@ h1::before {
padding: 18px 25px;
border-radius: 14px;
background: rgba(30, 0, 50, 0.5);
- border: 1px solid rgba(0, 255, 255, 0.2);
+ border: 1px solid rgba(255, 255, 255, 0.1);
transition: all 0.3s ease;
position: relative;
}
@@ -217,57 +219,105 @@ h1::before {
box-shadow: 0 0 20px rgba(0, 255, 255, 0.3);
}
-/* Rank Styles */
-.leaderboard-item.rank-1 {
- background: linear-gradient(135deg, rgba(0, 234, 255, 0.25), rgba(0, 255, 136, 0.2));
- border: 2px solid rgba(0, 234, 255, 0.6);
- box-shadow:
- 0 0 25px rgba(0, 234, 255, 0.4),
- inset 0 0 25px rgba(0, 234, 255, 0.15);
-}
-
-.leaderboard-item.rank-2 {
- background: linear-gradient(135deg, rgba(255, 0, 255, 0.2), rgba(204, 0, 255, 0.15));
- border: 2px solid rgba(255, 0, 255, 0.5);
- box-shadow: 0 0 18px rgba(255, 0, 255, 0.3);
-}
-
-.leaderboard-item.rank-3 {
- background: linear-gradient(135deg, rgba(138, 43, 226, 0.25), rgba(75, 0, 130, 0.2));
- border: 2px solid rgba(138, 43, 226, 0.5);
- box-shadow: 0 0 15px rgba(138, 43, 226, 0.3);
-}
-
-/* Rank Badge */
-.rank-badge {
- width: 45px;
- height: 45px;
- border-radius: 50%;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 17px;
- font-weight: bold;
- flex-shrink: 0;
+/* === RANK 1: CYAN NEON (Juara) === */
+.leaderboard-item.rank-1,
+.your-rank-container.rank-1 .your-rank-item {
+ background: linear-gradient(90deg, rgba(0, 234, 255, 0.25) 0%, rgba(0, 234, 255, 0.05) 100%);
+ border: 2px solid #00eaff;
+ box-shadow: 0 0 15px rgba(0, 234, 255, 0.3);
}
.rank-1 .rank-badge {
- background: linear-gradient(135deg, #00eaff, #00ff88);
- color: #0c001a;
- box-shadow: 0 0 20px rgba(0, 234, 255, 0.8);
+ background: #00eaff;
+ color: #000;
+ box-shadow: 0 0 15px #00eaff;
animation: pulseBadge 2s ease-in-out infinite;
}
-.rank-2 .rank-badge {
- background: linear-gradient(135deg, #ff00ff, #cc00ff);
+.rank-1 .player-name {
+ /* 1. Matikan efek gradient aneh pada teks */
+ background: none;
+ -webkit-text-fill-color: initial;
+ background-clip: border-box;
+
+ /* 2. Samakan ukuran font dengan yang lain (sebelumnya 22px jadi 20px) */
+ font-size: 20px;
+
+ /* 3. Warna Putih Solid + Sedikit Glow Cyan biar tetap spesial tapi rapi */
color: #ffffff;
- box-shadow: 0 0 18px rgba(255, 0, 255, 0.7);
+ text-shadow: 0 0 10px rgba(0, 234, 255, 0.8);
+
+ /* Hapus filter drop-shadow berlebih */
+ filter: none;
+}
+
+/* FIX: Warna Score Rank 1 jadi Putih Solid (Anti Silau) */
+.rank-1 .score-value {
+ /* 1. Ukuran disamakan dengan yang lain */
+ font-size: 22px !important;
+
+ /* 2. Warna Teks Putih Solid */
+ background: none !important;
+ -webkit-text-fill-color: initial !important;
+ color: #ffffff !important;
+
+ /* 3. GLOWING: Ganti bayangan hitam jadi cahaya Cyan */
+ text-shadow: 0 0 10px #00eaff, 0 0 20px rgba(0, 234, 255, 0.4) !important;
+
+ /* Reset filter */
+ filter: none !important;
+}
+
+/* === RANK 2: MAGENTA NEON (Runner Up) === */
+.leaderboard-item.rank-2,
+.your-rank-container.rank-2 .your-rank-item {
+ background: linear-gradient(90deg, rgba(255, 0, 255, 0.25) 0%, rgba(255, 0, 255, 0.05) 100%);
+ border: 2px solid #ff00ff;
+ box-shadow: 0 0 15px rgba(255, 0, 255, 0.3);
+}
+
+.rank-2 .rank-badge {
+ background: #ff00ff;
+ color: #fff;
+ box-shadow: 0 0 15px #ff00ff;
+}
+
+.rank-2 .score-value {
+ color: #fff;
+ text-shadow: 0 0 10px #ff00ff;
+}
+
+.rank-2 .player-score {
+ color: #ff00ff;
+}
+
+/* === RANK 3: VIOLET NEON (Third) === */
+.leaderboard-item.rank-3,
+.your-rank-container.rank-3 .your-rank-item {
+ background: linear-gradient(90deg, rgba(138, 43, 226, 0.25) 0%, rgba(138, 43, 226, 0.05) 100%);
+ border: 2px solid #8a2be2;
+ box-shadow: 0 0 15px rgba(138, 43, 226, 0.3);
}
.rank-3 .rank-badge {
- background: linear-gradient(135deg, #8a2be2, #4b0082);
- color: #ffffff;
- box-shadow: 0 0 15px rgba(138, 43, 226, 0.6);
+ background: #8a2be2;
+ color: #fff;
+ box-shadow: 0 0 15px #8a2be2;
+}
+
+.rank-3 .score-value {
+ color: #fff;
+ text-shadow: 0 0 10px #8a2be2;
+}
+
+.rank-3 .player-score {
+ color: #a855f7;
+}
+
+/* === RANK OTHER === */
+.leaderboard-item.rank-other {
+ background: rgba(255, 255, 255, 0.05);
+ border: 1px solid rgba(255, 255, 255, 0.1);
}
.rank-other .rank-badge {
@@ -276,6 +326,7 @@ h1::before {
border: 1px solid rgba(0, 255, 255, 0.3);
}
+/* Animation */
@keyframes pulseBadge {
0%, 100% {
transform: scale(1);
@@ -287,7 +338,7 @@ h1::before {
}
}
-/* Player Info */
+/* Player Info Common */
.player-info {
flex: 1;
display: flex;
@@ -305,22 +356,11 @@ h1::before {
text-overflow: ellipsis;
}
-.rank-1 .player-name {
- background: linear-gradient(90deg, #00eaff, #00ff88);
- -webkit-background-clip: text;
- -webkit-text-fill-color: transparent;
- background-clip: text;
- text-shadow: none;
- filter: drop-shadow(0 0 6px rgba(0, 234, 255, 0.5));
- font-size: 22px;
-}
-
-/* Score */
+/* Score Common */
.player-score {
font-weight: 700;
font-size: 18px;
color: #00eaff;
- text-shadow: 0 0 12px rgba(0, 234, 255, 0.7);
display: flex;
flex-direction: column;
align-items: flex-end;
@@ -332,25 +372,6 @@ h1::before {
font-size: 22px;
}
-.rank-1 .score-value {
- font-size: 25px;
- background: linear-gradient(90deg, #00eaff, #ff00ff);
- -webkit-background-clip: text;
- -webkit-text-fill-color: transparent;
- background-clip: text;
- filter: drop-shadow(0 0 10px rgba(0, 234, 255, 0.8));
-}
-
-.rank-2 .player-score {
- color: #ff00ff;
- text-shadow: 0 0 12px rgba(255, 0, 255, 0.7);
-}
-
-.rank-3 .player-score {
- color: #a855f7;
- text-shadow: 0 0 12px rgba(138, 43, 226, 0.6);
-}
-
.score-label {
font-size: 11px;
text-transform: uppercase;
@@ -358,15 +379,26 @@ h1::before {
color: rgba(200, 200, 255, 0.5);
}
-/* Your Rank Fixed Section */
+/* Badge Size Common */
+.rank-badge {
+ width: 45px;
+ height: 45px;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 17px;
+ font-weight: bold;
+ flex-shrink: 0;
+}
+
+/* Your Rank Section Structure */
.your-rank-container {
- background: rgba(20, 0, 40, 0.9);
- border-radius: 14px;
- padding: 15px 25px;
- border: 2px solid rgba(0, 255, 136, 0.6);
- box-shadow:
- 0 0 25px rgba(0, 255, 136, 0.4),
- inset 0 0 20px rgba(0, 255, 136, 0.15);
+ background: transparent;
+ border: none;
+ box-shadow: none;
+ padding: 0;
+ margin-top: 20px;
}
.your-rank-item {
@@ -375,15 +407,34 @@ h1::before {
gap: 20px;
padding: 18px 25px;
border-radius: 14px;
- background: linear-gradient(135deg, rgba(0, 255, 136, 0.15), rgba(0, 234, 255, 0.1));
+ /* Default style if not top 3 */
+ background: linear-gradient(135deg, rgba(0, 255, 136, 0.15), rgba(0, 0, 0, 0.5));
border: 2px solid rgba(0, 255, 136, 0.5);
box-shadow: 0 0 20px rgba(0, 255, 136, 0.3);
}
-/* Responsive */
+/* --- RESPONSIVE FIX (MODE LAPTOP/LAYAR KECIL) --- */
+
+@media screen and (max-width: 1440px) {
+ /* Mencegah container nabrak tombol back */
+ .container {
+ width: 88%;
+ padding: 30px 50px;
+ max-height: 90vh;
+ }
+
+ .btn-back {
+ top: 20px;
+ left: 20px;
+ width: 45px;
+ height: 45px;
+ }
+}
+
@media (max-width: 768px) {
.container {
padding: 35px 30px;
+ width: 95%;
}
h1 {
@@ -394,18 +445,6 @@ h1::before {
grid-template-columns: repeat(3, 1fr);
gap: 10px;
}
-
- .btn-back {
- width: 45px;
- height: 45px;
- top: 20px;
- left: 20px;
- }
-
- .btn-back svg {
- width: 20px;
- height: 20px;
- }
}
@media (max-width: 480px) {
diff --git a/Leaderboard.html b/Leaderboard.html
index a88f02f..933c91d 100644
--- a/Leaderboard.html
+++ b/Leaderboard.html
@@ -21,6 +21,8 @@
+
+
diff --git a/Leaderboard.js b/Leaderboard.js
index 8329105..8932575 100644
--- a/Leaderboard.js
+++ b/Leaderboard.js
@@ -7,12 +7,53 @@ function loadLeaderboard() {
.then(response => response.json())
.then(data => {
if (data.status === "success") {
+ // Render Top 10
renderLeaderboard(data.leaderboard);
+
+ // Render Ranking Saya (User Rank)
+ if (data.user_rank) {
+ renderUserRank(data.user_rank);
+ }
}
})
.catch(error => console.error("Error loading leaderboard:", error));
}
+// ... fungsi renderLeaderboard tetap sama ...
+
+// TAMBAHKAN FUNGSI INI DI BAWAH
+function renderUserRank(user) {
+ const container = document.getElementById('userRankContainer');
+ if (!container) return;
+
+ // Format angka skor
+ const formattedScore = new Intl.NumberFormat().format(user.score);
+
+ // HTML structure sesuai CSS .your-rank-container
+ const html = `
+
+
+
+
+
+ ${user.rank}
+
+
+
${escapeHtml(user.username)}
+
+
+
${formattedScore}
+
Points
+
+
+
+ `;
+
+ container.innerHTML = html;
+}
+
+// ... fungsi escapeHtml tetap sama ...
+
function renderLeaderboard(players) {
const listContainer = document.getElementById('leaderboardList');
if (!listContainer) return; // Safety check
diff --git a/Leaderboard.php b/Leaderboard.php
index 6af6f98..689876a 100644
--- a/Leaderboard.php
+++ b/Leaderboard.php
@@ -1,23 +1,76 @@
"error",
+ "leaderboard" => [],
+ "user_rank" => null
+];
+
+// ---------------------------------------------------------
+// 1. Ambil Top 10 Global (DIPERBAIKI)
+// Ditambahkan "user_id ASC" agar urutannya PASTI (Konsisten)
+// Jika skor sama, user dengan ID lebih kecil (daftar duluan) akan di atas
+// ---------------------------------------------------------
+$query = "SELECT username, score FROM leaderboard ORDER BY score DESC, user_id ASC LIMIT 10";
$result = $conn->query($query);
-$leaderboard = [];
-
if ($result && $result->num_rows > 0) {
while ($row = $result->fetch_assoc()) {
- $leaderboard[] = $row;
+ $response['leaderboard'][] = $row;
}
}
-// Kirim data ke frontend dalam bentuk JSON
-echo json_encode([
- "status" => "success",
- "leaderboard" => $leaderboard
-]);
+// ---------------------------------------------------------
+// 2. Ambil Ranking User Login (DIPERBAIKI)
+// ---------------------------------------------------------
+if (isset($_SESSION['user_id'])) {
+ $my_id = $_SESSION['user_id'];
+
+ // Ambil score user saat ini
+ $scoreQuery = $conn->prepare("SELECT username, score FROM leaderboard WHERE user_id = ?");
+ $scoreQuery->bind_param("i", $my_id);
+ $scoreQuery->execute();
+ $scoreResult = $scoreQuery->get_result();
+
+ if ($scoreRow = $scoreResult->fetch_assoc()) {
+ $myScore = $scoreRow['score'];
+ $myUsername = $scoreRow['username'];
+
+ // --- LOGIKA BARU ---
+ // Hitung ranking dengan Tie-Breaker.
+ // Rank = Jumlah orang yang skornya LEBIH BESAR
+ // DITAMBAH Jumlah orang yang skornya SAMA tapi ID-nya LEBIH KECIL (dia di atas kita)
+
+ $rankQuery = $conn->prepare("
+ SELECT COUNT(*) as rank_above
+ FROM leaderboard
+ WHERE score > ?
+ OR (score = ? AND user_id < ?)
+ ");
+
+ // Kita bind 3 parameter: score, score, id
+ $rankQuery->bind_param("iii", $myScore, $myScore, $my_id);
+
+ $rankQuery->execute();
+ $rankResult = $rankQuery->get_result();
+ $rankRow = $rankResult->fetch_assoc();
+
+ // Rank adalah jumlah orang di atas kita + 1
+ $myRank = $rankRow['rank_above'] + 1;
+
+ $response['user_rank'] = [
+ "username" => $myUsername,
+ "score" => $myScore,
+ "rank" => $myRank
+ ];
+ }
+}
+
+$response['status'] = "success";
+echo json_encode($response);
$conn->close();
?>
\ No newline at end of file
diff --git a/Login.php b/Login.php
index b206227..32313dd 100644
--- a/Login.php
+++ b/Login.php
@@ -1,12 +1,11 @@
false, "message" => "Username dan password wajib diisi"]);
- exit;
-}
+// ... (Validasi input kosong tetap sama) ...
-$stmt = $conn->prepare("SELECT password FROM users WHERE username = ?");
+// 🔴 PERBAIKAN 1: Tambahkan 'id' di dalam SELECT
+$stmt = $conn->prepare("SELECT id, password FROM users WHERE username = ?");
$stmt->bind_param("s", $username);
$stmt->execute();
$stmt->store_result();
@@ -37,11 +33,15 @@ if ($stmt->num_rows === 0) {
exit;
}
-$stmt->bind_result($hashedPassword);
+// 🔴 PERBAIKAN 2: Bind result untuk menangkap 'id' dan 'password'
+$stmt->bind_result($userId, $hashedPassword);
$stmt->fetch();
if (password_verify($password, $hashedPassword)) {
+ // 🔴 PERBAIKAN 3: Simpan 'user_id' ke dalam SESSION
+ $_SESSION['user_id'] = $userId;
$_SESSION['username'] = $username;
+
echo json_encode([
"success" => true,
"message" => "Login berhasil",
diff --git a/Register.html b/Register.html
index f315141..dca1da9 100644
--- a/Register.html
+++ b/Register.html
@@ -62,6 +62,7 @@
+