Merge branch 'main' of https://git-eng.ukwms.ac.id/2526-web/kelompok06-2048
This commit is contained in:
commit
a4a74357b9
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);
|
||||
}
|
||||
160
Leaderboard.html
160
Leaderboard.html
@ -19,167 +19,11 @@
|
||||
<div class="container">
|
||||
<h1>Leaderboard</h1>
|
||||
|
||||
<div class="stats-container">
|
||||
<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"></ul>
|
||||
|
||||
<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>
|
||||
|
||||
<script src="Leaderboard.js"></script>
|
||||
<script src="Animation_Leaderboard.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
107
Leaderboard.js
107
Leaderboard.js
@ -1,57 +1,62 @@
|
||||
// Particles Animation
|
||||
const particlesContainer = document.getElementById('particles');
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
loadLeaderboard();
|
||||
});
|
||||
|
||||
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;
|
||||
function loadLeaderboard() {
|
||||
fetch('Leaderboard.php')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.status === "success") {
|
||||
renderLeaderboard(data.leaderboard);
|
||||
}
|
||||
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);
|
||||
})
|
||||
.catch(error => console.error("Error loading leaderboard:", error));
|
||||
}
|
||||
|
||||
setInterval(createParticle, 300);
|
||||
function renderLeaderboard(players) {
|
||||
const listContainer = document.getElementById('leaderboardList');
|
||||
if (!listContainer) return; // Safety check
|
||||
|
||||
for (let i = 0; i < 25; i++) {
|
||||
setTimeout(createParticle, i * 100);
|
||||
listContainer.innerHTML = ""; // Hapus data dummy statis
|
||||
|
||||
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
|
||||
session_start();
|
||||
header('Content-Type: application/json');
|
||||
include 'Connection.php';
|
||||
require 'Connection.php'; // Gunakan require agar stop jika file tidak ada
|
||||
|
||||
// Pastikan user sudah login
|
||||
if (!isset($_SESSION['username'])) {
|
||||
echo json_encode(["status" => "error", "message" => "Belum login"]);
|
||||
// 1. Pastikan user login & user_id tersedia
|
||||
if (!isset($_SESSION['username']) || !isset($_SESSION['user_id'])) {
|
||||
echo json_encode(["status" => "error", "message" => "Belum login atau sesi tidak valid"]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$username = $_SESSION['username'];
|
||||
$user_id = $_SESSION['user_id']; // AMBIL ID DARI SESSION
|
||||
$score = intval($_POST['score'] ?? 0);
|
||||
|
||||
// Validasi score
|
||||
@ -18,62 +19,54 @@ if ($score <= 0) {
|
||||
exit;
|
||||
}
|
||||
|
||||
// Validasi score maksimal (opsional, sesuaikan dengan game Anda)
|
||||
if ($score > 999999) {
|
||||
echo json_encode(["status" => "error", "message" => "Skor terlalu tinggi"]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Cek apakah user sudah punya record
|
||||
$checkStmt = $conn->prepare("SELECT score FROM leaderboard WHERE username = ?");
|
||||
$checkStmt->bind_param("s", $username);
|
||||
// Cek apakah user sudah punya record di leaderboard
|
||||
$checkStmt = $conn->prepare("SELECT score FROM leaderboard WHERE user_id = ?");
|
||||
$checkStmt->bind_param("i", $user_id); // Cek berdasarkan ID, lebih akurat daripada username
|
||||
$checkStmt->execute();
|
||||
$result = $checkStmt->get_result();
|
||||
|
||||
if ($result->num_rows > 0) {
|
||||
// User sudah ada, cek apakah score baru lebih tinggi
|
||||
// --- UPDATE ---
|
||||
$row = $result->fetch_assoc();
|
||||
$oldScore = $row['score'];
|
||||
|
||||
if ($score > $oldScore) {
|
||||
// Update dengan score yang lebih tinggi
|
||||
$updateStmt = $conn->prepare("UPDATE leaderboard SET score = ? WHERE username = ?");
|
||||
$updateStmt->bind_param("is", $score, $username);
|
||||
// Update score berdasarkan user_id
|
||||
$updateStmt = $conn->prepare("UPDATE leaderboard SET score = ?, username = ? WHERE user_id = ?");
|
||||
// Kita update username juga untuk jaga-jaga kalau user ganti nama
|
||||
$updateStmt->bind_param("isi", $score, $username, $user_id);
|
||||
|
||||
if ($updateStmt->execute()) {
|
||||
echo json_encode([
|
||||
"status" => "success",
|
||||
"message" => "Skor berhasil diperbarui!",
|
||||
"newHighScore" => true,
|
||||
"score" => $score
|
||||
"message" => "High Score baru tercatat!",
|
||||
"newHighScore" => true
|
||||
]);
|
||||
} else {
|
||||
echo json_encode(["status" => "error", "message" => "Gagal memperbarui skor"]);
|
||||
echo json_encode(["status" => "error", "message" => "Gagal update database"]);
|
||||
}
|
||||
$updateStmt->close();
|
||||
} else {
|
||||
// Score baru lebih rendah, tidak perlu update
|
||||
echo json_encode([
|
||||
"status" => "success",
|
||||
"message" => "Skor tidak diperbarui (skor lama lebih tinggi)",
|
||||
"newHighScore" => false,
|
||||
"currentHighScore" => $oldScore
|
||||
"message" => "Skor lebih rendah dari rekor sebelumnya.",
|
||||
"newHighScore" => false
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
// User belum ada, insert baru
|
||||
$insertStmt = $conn->prepare("INSERT INTO leaderboard (username, score) VALUES (?, ?)");
|
||||
$insertStmt->bind_param("si", $username, $score);
|
||||
// --- INSERT BARU ---
|
||||
// Masukkan user_id, username, dan score
|
||||
$insertStmt = $conn->prepare("INSERT INTO leaderboard (user_id, username, score) VALUES (?, ?, ?)");
|
||||
$insertStmt->bind_param("isi", $user_id, $username, $score);
|
||||
|
||||
if ($insertStmt->execute()) {
|
||||
echo json_encode([
|
||||
"status" => "success",
|
||||
"message" => "Skor berhasil disimpan!",
|
||||
"newHighScore" => true,
|
||||
"score" => $score
|
||||
"message" => "Skor pertama berhasil disimpan!",
|
||||
"newHighScore" => true
|
||||
]);
|
||||
} else {
|
||||
echo json_encode(["status" => "error", "message" => "Gagal menyimpan skor"]);
|
||||
echo json_encode(["status" => "error", "message" => "Gagal insert database"]);
|
||||
}
|
||||
$insertStmt->close();
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user