Leaderboard
This commit is contained in:
parent
b7db68222c
commit
76f0967456
57
Animation_Leaderboard.js
Normal file
57
Animation_Leaderboard.js
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
// Particles Animation
|
||||||
|
const particlesContainer = document.getElementById('particles');
|
||||||
|
|
||||||
|
function createParticle() {
|
||||||
|
const particle = document.createElement('div');
|
||||||
|
particle.className = 'particle';
|
||||||
|
|
||||||
|
particle.style.left = Math.random() * 100 + '%';
|
||||||
|
particle.style.top = Math.random() * 100 + '%';
|
||||||
|
|
||||||
|
const size = 3 + Math.random() * 4;
|
||||||
|
particle.style.width = size + 'px';
|
||||||
|
particle.style.height = size + 'px';
|
||||||
|
|
||||||
|
const duration = 3 + Math.random() * 5;
|
||||||
|
const delay = Math.random() * 2;
|
||||||
|
const moveX = (Math.random() - 0.5) * 200;
|
||||||
|
|
||||||
|
const animationName = `float-${Date.now()}-${Math.random()}`;
|
||||||
|
const keyframes = `
|
||||||
|
@keyframes ${animationName} {
|
||||||
|
0% {
|
||||||
|
transform: translateY(0) translateX(0);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
10% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
90% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translateY(-100vh) translateX(${moveX}px);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const style = document.createElement('style');
|
||||||
|
style.innerHTML = keyframes;
|
||||||
|
document.head.appendChild(style);
|
||||||
|
|
||||||
|
particle.style.animation = `${animationName} ${duration}s ${delay}s ease-in-out forwards`;
|
||||||
|
|
||||||
|
particlesContainer.appendChild(particle);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
particle.remove();
|
||||||
|
style.remove();
|
||||||
|
}, (duration + delay) * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
setInterval(createParticle, 300);
|
||||||
|
|
||||||
|
for (let i = 0; i < 25; i++) {
|
||||||
|
setTimeout(createParticle, i * 100);
|
||||||
|
}
|
||||||
164
Leaderboard.html
164
Leaderboard.html
@ -19,167 +19,11 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<h1>Leaderboard</h1>
|
<h1>Leaderboard</h1>
|
||||||
|
|
||||||
<div class="stats-container">
|
<ul class="leaderboard-list" id="leaderboardList"></ul>
|
||||||
<div class="stat-box">
|
|
||||||
<div class="stat-label">Players</div>
|
|
||||||
<div class="stat-value" id="totalPlayers">10</div>
|
|
||||||
</div>
|
|
||||||
<div class="stat-box">
|
|
||||||
<div class="stat-label">Your Rank</div>
|
|
||||||
<div class="stat-value" id="yourRank">5</div>
|
|
||||||
</div>
|
|
||||||
<div class="stat-box">
|
|
||||||
<div class="stat-label">High Score</div>
|
|
||||||
<div class="stat-value" id="highScore">9,850</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ul class="leaderboard-list" id="leaderboardList">
|
|
||||||
<li class="leaderboard-item rank-1">
|
|
||||||
<div class="rank-badge">1</div>
|
|
||||||
<div class="player-info">
|
|
||||||
<div class="player-name">CyberKing</div>
|
|
||||||
</div>
|
|
||||||
<div class="player-score">
|
|
||||||
<div class="score-value">9,850</div>
|
|
||||||
<div class="score-label">Points</div>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li class="leaderboard-item rank-2">
|
|
||||||
<div class="rank-badge">2</div>
|
|
||||||
<div class="player-info">
|
|
||||||
<div class="player-name">NeonMaster</div>
|
|
||||||
</div>
|
|
||||||
<div class="player-score">
|
|
||||||
<div class="score-value">8,200</div>
|
|
||||||
<div class="score-label">Points</div>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li class="leaderboard-item rank-3">
|
|
||||||
<div class="rank-badge">3</div>
|
|
||||||
<div class="player-info">
|
|
||||||
<div class="player-name">PixelHunter</div>
|
|
||||||
</div>
|
|
||||||
<div class="player-score">
|
|
||||||
<div class="score-value">6,950</div>
|
|
||||||
<div class="score-label">Points</div>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li class="leaderboard-item rank-other">
|
|
||||||
<div class="rank-badge">4</div>
|
|
||||||
<div class="player-info">
|
|
||||||
<div class="player-name">StarGazer</div>
|
|
||||||
</div>
|
|
||||||
<div class="player-score">
|
|
||||||
<div class="score-value">5,420</div>
|
|
||||||
<div class="score-label">Points</div>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li class="leaderboard-item rank-other">
|
|
||||||
<div class="rank-badge">5</div>
|
|
||||||
<div class="player-info">
|
|
||||||
<div class="player-name">You</div>
|
|
||||||
</div>
|
|
||||||
<div class="player-score">
|
|
||||||
<div class="score-value">4,890</div>
|
|
||||||
<div class="score-label">Points</div>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li class="leaderboard-item rank-other">
|
|
||||||
<div class="rank-badge">6</div>
|
|
||||||
<div class="player-info">
|
|
||||||
<div class="player-name">ByteWarrior</div>
|
|
||||||
</div>
|
|
||||||
<div class="player-score">
|
|
||||||
<div class="score-value">4,320</div>
|
|
||||||
<div class="score-label">Points</div>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li class="leaderboard-item rank-other">
|
|
||||||
<div class="rank-badge">7</div>
|
|
||||||
<div class="player-info">
|
|
||||||
<div class="player-name">DataDragon</div>
|
|
||||||
</div>
|
|
||||||
<div class="player-score">
|
|
||||||
<div class="score-value">3,850</div>
|
|
||||||
<div class="score-label">Points</div>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li class="leaderboard-item rank-other">
|
|
||||||
<div class="rank-badge">8</div>
|
|
||||||
<div class="player-info">
|
|
||||||
<div class="player-name">SyntaxSage</div>
|
|
||||||
</div>
|
|
||||||
<div class="player-score">
|
|
||||||
<div class="score-value">3,120</div>
|
|
||||||
<div class="score-label">Points</div>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li class="leaderboard-item rank-other">
|
|
||||||
<div class="rank-badge">9</div>
|
|
||||||
<div class="player-info">
|
|
||||||
<div class="player-name">LogicLord</div>
|
|
||||||
</div>
|
|
||||||
<div class="player-score">
|
|
||||||
<div class="score-value">2,780</div>
|
|
||||||
<div class="score-label">Points</div>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li class="leaderboard-item rank-other">
|
|
||||||
<div class="rank-badge">10</div>
|
|
||||||
<div class="player-info">
|
|
||||||
<div class="player-name">BugSlayer</div>
|
|
||||||
</div>
|
|
||||||
<div class="player-score">
|
|
||||||
<div class="score-value">2,340</div>
|
|
||||||
<div class="score-label">Points</div>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<div class="your-rank-container">
|
|
||||||
<div class="your-rank-item">
|
|
||||||
<div
|
|
||||||
class="rank-badge"
|
|
||||||
style="
|
|
||||||
background: linear-gradient(135deg, #00ff88, #00eaff);
|
|
||||||
color: #0c001a;
|
|
||||||
box-shadow: 0 0 20px rgba(0, 255, 136, 0.8);
|
|
||||||
"
|
|
||||||
>
|
|
||||||
5
|
|
||||||
</div>
|
|
||||||
<div class="player-info">
|
|
||||||
<div
|
|
||||||
class="player-name"
|
|
||||||
style="
|
|
||||||
color: #00ff88;
|
|
||||||
filter: drop-shadow(0 0 8px rgba(0, 255, 136, 0.6));
|
|
||||||
"
|
|
||||||
>
|
|
||||||
You
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="player-score"
|
|
||||||
style="color: #00ff88; text-shadow: 0 0 15px rgba(0, 255, 136, 0.8)"
|
|
||||||
>
|
|
||||||
<div class="score-value" style="font-size: 24px">4,890</div>
|
|
||||||
<div class="score-label">Points</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="Leaderboard.js"></script>
|
<script src="Leaderboard.js"></script>
|
||||||
|
<script src="Animation_Leaderboard.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
107
Leaderboard.js
107
Leaderboard.js
@ -1,57 +1,62 @@
|
|||||||
// Particles Animation
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
const particlesContainer = document.getElementById('particles');
|
loadLeaderboard();
|
||||||
|
});
|
||||||
|
|
||||||
function createParticle() {
|
function loadLeaderboard() {
|
||||||
const particle = document.createElement('div');
|
fetch('Leaderboard.php')
|
||||||
particle.className = 'particle';
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
particle.style.left = Math.random() * 100 + '%';
|
if (data.status === "success") {
|
||||||
particle.style.top = Math.random() * 100 + '%';
|
renderLeaderboard(data.leaderboard);
|
||||||
|
|
||||||
const size = 3 + Math.random() * 4;
|
|
||||||
particle.style.width = size + 'px';
|
|
||||||
particle.style.height = size + 'px';
|
|
||||||
|
|
||||||
const duration = 3 + Math.random() * 5;
|
|
||||||
const delay = Math.random() * 2;
|
|
||||||
const moveX = (Math.random() - 0.5) * 200;
|
|
||||||
|
|
||||||
const animationName = `float-${Date.now()}-${Math.random()}`;
|
|
||||||
const keyframes = `
|
|
||||||
@keyframes ${animationName} {
|
|
||||||
0% {
|
|
||||||
transform: translateY(0) translateX(0);
|
|
||||||
opacity: 0;
|
|
||||||
}
|
}
|
||||||
10% {
|
})
|
||||||
opacity: 1;
|
.catch(error => console.error("Error loading leaderboard:", error));
|
||||||
}
|
|
||||||
90% {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
transform: translateY(-100vh) translateX(${moveX}px);
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const style = document.createElement('style');
|
|
||||||
style.innerHTML = keyframes;
|
|
||||||
document.head.appendChild(style);
|
|
||||||
|
|
||||||
particle.style.animation = `${animationName} ${duration}s ${delay}s ease-in-out forwards`;
|
|
||||||
|
|
||||||
particlesContainer.appendChild(particle);
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
particle.remove();
|
|
||||||
style.remove();
|
|
||||||
}, (duration + delay) * 1000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setInterval(createParticle, 300);
|
function renderLeaderboard(players) {
|
||||||
|
const listContainer = document.getElementById('leaderboardList');
|
||||||
|
if (!listContainer) return; // Safety check
|
||||||
|
|
||||||
for (let i = 0; i < 25; i++) {
|
listContainer.innerHTML = ""; // Hapus data dummy statis
|
||||||
setTimeout(createParticle, i * 100);
|
|
||||||
|
players.forEach((player, index) => {
|
||||||
|
const rank = index + 1;
|
||||||
|
let rankClass = 'rank-other';
|
||||||
|
|
||||||
|
// Tentukan styling berdasarkan ranking
|
||||||
|
if (rank === 1) rankClass = 'rank-1';
|
||||||
|
else if (rank === 2) rankClass = 'rank-2';
|
||||||
|
else if (rank === 3) rankClass = 'rank-3';
|
||||||
|
|
||||||
|
// Format angka skor dengan koma (contoh: 1,000)
|
||||||
|
// Fallback jika score tidak valid
|
||||||
|
const scoreVal = parseInt(player.score) || 0;
|
||||||
|
const formattedScore = new Intl.NumberFormat().format(scoreVal);
|
||||||
|
|
||||||
|
const itemHtml = `
|
||||||
|
<li class="leaderboard-item ${rankClass}">
|
||||||
|
<div class="rank-badge">${rank}</div>
|
||||||
|
<div class="player-info">
|
||||||
|
<div class="player-name">${escapeHtml(player.username)}</div>
|
||||||
|
</div>
|
||||||
|
<div class="player-score">
|
||||||
|
<div class="score-value">${formattedScore}</div>
|
||||||
|
<div class="score-label">Points</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
`;
|
||||||
|
|
||||||
|
listContainer.insertAdjacentHTML('beforeend', itemHtml);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mencegah XSS attack (keamanan tambahan)
|
||||||
|
function escapeHtml(text) {
|
||||||
|
if (!text) return text;
|
||||||
|
return text
|
||||||
|
.replace(/&/g, "&")
|
||||||
|
.replace(/</g, "<")
|
||||||
|
.replace(/>/g, ">")
|
||||||
|
.replace(/"/g, """)
|
||||||
|
.replace(/'/g, "'");
|
||||||
}
|
}
|
||||||
57
Score.php
57
Score.php
@ -1,15 +1,16 @@
|
|||||||
<?php
|
<?php
|
||||||
session_start();
|
session_start();
|
||||||
header('Content-Type: application/json');
|
header('Content-Type: application/json');
|
||||||
include 'Connection.php';
|
require 'Connection.php'; // Gunakan require agar stop jika file tidak ada
|
||||||
|
|
||||||
// Pastikan user sudah login
|
// 1. Pastikan user login & user_id tersedia
|
||||||
if (!isset($_SESSION['username'])) {
|
if (!isset($_SESSION['username']) || !isset($_SESSION['user_id'])) {
|
||||||
echo json_encode(["status" => "error", "message" => "Belum login"]);
|
echo json_encode(["status" => "error", "message" => "Belum login atau sesi tidak valid"]);
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
$username = $_SESSION['username'];
|
$username = $_SESSION['username'];
|
||||||
|
$user_id = $_SESSION['user_id']; // AMBIL ID DARI SESSION
|
||||||
$score = intval($_POST['score'] ?? 0);
|
$score = intval($_POST['score'] ?? 0);
|
||||||
|
|
||||||
// Validasi score
|
// Validasi score
|
||||||
@ -18,62 +19,54 @@ if ($score <= 0) {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validasi score maksimal (opsional, sesuaikan dengan game Anda)
|
// Cek apakah user sudah punya record di leaderboard
|
||||||
if ($score > 999999) {
|
$checkStmt = $conn->prepare("SELECT score FROM leaderboard WHERE user_id = ?");
|
||||||
echo json_encode(["status" => "error", "message" => "Skor terlalu tinggi"]);
|
$checkStmt->bind_param("i", $user_id); // Cek berdasarkan ID, lebih akurat daripada username
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cek apakah user sudah punya record
|
|
||||||
$checkStmt = $conn->prepare("SELECT score FROM leaderboard WHERE username = ?");
|
|
||||||
$checkStmt->bind_param("s", $username);
|
|
||||||
$checkStmt->execute();
|
$checkStmt->execute();
|
||||||
$result = $checkStmt->get_result();
|
$result = $checkStmt->get_result();
|
||||||
|
|
||||||
if ($result->num_rows > 0) {
|
if ($result->num_rows > 0) {
|
||||||
// User sudah ada, cek apakah score baru lebih tinggi
|
// --- UPDATE ---
|
||||||
$row = $result->fetch_assoc();
|
$row = $result->fetch_assoc();
|
||||||
$oldScore = $row['score'];
|
$oldScore = $row['score'];
|
||||||
|
|
||||||
if ($score > $oldScore) {
|
if ($score > $oldScore) {
|
||||||
// Update dengan score yang lebih tinggi
|
// Update score berdasarkan user_id
|
||||||
$updateStmt = $conn->prepare("UPDATE leaderboard SET score = ? WHERE username = ?");
|
$updateStmt = $conn->prepare("UPDATE leaderboard SET score = ?, username = ? WHERE user_id = ?");
|
||||||
$updateStmt->bind_param("is", $score, $username);
|
// Kita update username juga untuk jaga-jaga kalau user ganti nama
|
||||||
|
$updateStmt->bind_param("isi", $score, $username, $user_id);
|
||||||
|
|
||||||
if ($updateStmt->execute()) {
|
if ($updateStmt->execute()) {
|
||||||
echo json_encode([
|
echo json_encode([
|
||||||
"status" => "success",
|
"status" => "success",
|
||||||
"message" => "Skor berhasil diperbarui!",
|
"message" => "High Score baru tercatat!",
|
||||||
"newHighScore" => true,
|
"newHighScore" => true
|
||||||
"score" => $score
|
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
echo json_encode(["status" => "error", "message" => "Gagal memperbarui skor"]);
|
echo json_encode(["status" => "error", "message" => "Gagal update database"]);
|
||||||
}
|
}
|
||||||
$updateStmt->close();
|
$updateStmt->close();
|
||||||
} else {
|
} else {
|
||||||
// Score baru lebih rendah, tidak perlu update
|
|
||||||
echo json_encode([
|
echo json_encode([
|
||||||
"status" => "success",
|
"status" => "success",
|
||||||
"message" => "Skor tidak diperbarui (skor lama lebih tinggi)",
|
"message" => "Skor lebih rendah dari rekor sebelumnya.",
|
||||||
"newHighScore" => false,
|
"newHighScore" => false
|
||||||
"currentHighScore" => $oldScore
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// User belum ada, insert baru
|
// --- INSERT BARU ---
|
||||||
$insertStmt = $conn->prepare("INSERT INTO leaderboard (username, score) VALUES (?, ?)");
|
// Masukkan user_id, username, dan score
|
||||||
$insertStmt->bind_param("si", $username, $score);
|
$insertStmt = $conn->prepare("INSERT INTO leaderboard (user_id, username, score) VALUES (?, ?, ?)");
|
||||||
|
$insertStmt->bind_param("isi", $user_id, $username, $score);
|
||||||
|
|
||||||
if ($insertStmt->execute()) {
|
if ($insertStmt->execute()) {
|
||||||
echo json_encode([
|
echo json_encode([
|
||||||
"status" => "success",
|
"status" => "success",
|
||||||
"message" => "Skor berhasil disimpan!",
|
"message" => "Skor pertama berhasil disimpan!",
|
||||||
"newHighScore" => true,
|
"newHighScore" => true
|
||||||
"score" => $score
|
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
echo json_encode(["status" => "error", "message" => "Gagal menyimpan skor"]);
|
echo json_encode(["status" => "error", "message" => "Gagal insert database"]);
|
||||||
}
|
}
|
||||||
$insertStmt->close();
|
$insertStmt->close();
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user