kelompok06-2048/Leaderboard.js
Michelle Aquilera 7019608338 Leaderboard js
2025-11-24 19:52:51 +07:00

243 lines
7.9 KiB
JavaScript

// ========== 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);
}
// ========== LEADERBOARD DATA MANAGEMENT ==========
// Get leaderboard data from localStorage
function getLeaderboardData() {
const data = localStorage.getItem('leaderboard2048');
return data ? JSON.parse(data) : [];
}
// Save leaderboard data to localStorage
function saveLeaderboardData(data) {
localStorage.setItem('leaderboard2048', JSON.stringify(data));
}
// Get current logged-in user
function getCurrentUser() {
return localStorage.getItem('currentUser') || null;
}
// Add or update score for a player
function addScore(playerName, score) {
let leaderboard = getLeaderboardData();
// Find if player already exists
const existingIndex = leaderboard.findIndex(p => p.name === playerName);
if (existingIndex >= 0) {
// Update only if new score is higher
if (score > leaderboard[existingIndex].score) {
leaderboard[existingIndex].score = score;
leaderboard[existingIndex].level = Math.floor(score / 100);
leaderboard[existingIndex].date = new Date().toISOString();
}
} else {
// Add new player
leaderboard.push({
name: playerName,
score: score,
level: Math.floor(score / 100),
date: new Date().toISOString()
});
}
saveLeaderboardData(leaderboard);
}
// ========== RENDER LEADERBOARD ==========
function renderLeaderboard() {
let leaderboardData = getLeaderboardData();
const currentUser = getCurrentUser();
const list = document.getElementById('leaderboardList');
const emptyState = document.getElementById('emptyState');
// Sort by score (highest first)
leaderboardData.sort((a, b) => b.score - a.score);
// Update stats
const totalPlayers = leaderboardData.length;
const topScore = leaderboardData.length > 0 ? leaderboardData[0].score : 0;
document.getElementById('totalPlayers').textContent = totalPlayers;
document.getElementById('topScore').textContent = topScore.toLocaleString();
// Find current user's rank
let userRank = '--';
if (currentUser) {
const userIndex = leaderboardData.findIndex(p => p.name === currentUser);
if (userIndex >= 0) {
userRank = userIndex + 1;
}
}
document.getElementById('yourRank').textContent = userRank;
// Check if there's data
if (leaderboardData.length === 0) {
list.style.display = 'none';
if (emptyState) emptyState.style.display = 'block';
return;
}
list.style.display = 'flex';
if (emptyState) emptyState.style.display = 'none';
// Render top 10 players
const top10 = leaderboardData.slice(0, 10);
list.innerHTML = top10.map((player, index) => {
const rank = index + 1;
let rankClass = 'rank-other';
if (rank === 1) rankClass = 'rank-1';
else if (rank === 2) rankClass = 'rank-2';
else if (rank === 3) rankClass = 'rank-3';
const isCurrentUser = currentUser && player.name === currentUser;
const yourRankClass = isCurrentUser ? 'your-rank' : '';
return `
<li class="leaderboard-item ${rankClass} ${yourRankClass}">
<div class="rank-badge">${rank}</div>
<div class="player-info">
<div class="player-name">${escapeHtml(player.name)}</div>
<div class="player-level">Level ${player.level || Math.floor(player.score / 100)}</div>
</div>
<div class="player-score">
<div class="score-value">${player.score.toLocaleString()}</div>
<div class="score-label">Points</div>
</div>
</li>
`;
}).join('');
}
// Escape HTML to prevent XSS
function escapeHtml(text) {
const map = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#039;'
};
return text.replace(/[&<>"']/g, m => map[m]);
}
// ========== SAMPLE DATA (FOR TESTING) ==========
function initSampleData() {
const existingData = getLeaderboardData();
// Only add sample data if leaderboard is empty
if (existingData.length === 0) {
const sampleData = [
{ name: "CyberKing", score: 9850, level: 98, date: new Date().toISOString() },
{ name: "NeonMaster", score: 8200, level: 82, date: new Date().toISOString() },
{ name: "PixelHunter", score: 6950, level: 69, date: new Date().toISOString() },
{ name: "StarGazer", score: 5420, level: 54, date: new Date().toISOString() },
{ name: "CodeNinja", score: 4890, level: 48, date: new Date().toISOString() },
{ name: "ByteWarrior", score: 4320, level: 43, date: new Date().toISOString() },
{ name: "DataDragon", score: 3850, level: 38, date: new Date().toISOString() },
{ name: "SyntaxSage", score: 3120, level: 31, date: new Date().toISOString() },
{ name: "LogicLord", score: 2780, level: 27, date: new Date().toISOString() },
{ name: "BugSlayer", score: 2340, level: 23, date: new Date().toISOString() }
];
saveLeaderboardData(sampleData);
console.log('Sample data initialized');
}
}
// ========== CLEAR LEADERBOARD (FOR TESTING) ==========
function clearLeaderboard() {
if (confirm('Are you sure you want to clear all leaderboard data?')) {
localStorage.removeItem('leaderboard2048');
renderLeaderboard();
console.log('Leaderboard cleared');
}
}
// ========== REFRESH LEADERBOARD ==========
function refreshLeaderboard() {
renderLeaderboard();
console.log('Leaderboard refreshed');
}
// ========== INITIALIZE ON PAGE LOAD ==========
window.addEventListener('DOMContentLoaded', function() {
renderLeaderboard();
console.log('Leaderboard initialized');
console.log('Available functions:');
console.log('- addScore(name, score) - Add/update a score');
console.log('- clearLeaderboard() - Clear all data');
console.log('- refreshLeaderboard() - Refresh display');
});
// ========== EXPOSE FUNCTIONS TO WINDOW (FOR EXTERNAL USE) ==========
window.leaderboard = {
addScore: addScore,
getLeaderboardData: getLeaderboardData,
clearLeaderboard: clearLeaderboard,
refreshLeaderboard: refreshLeaderboard,
initSampleData: initSampleData
};