Files

183 lines
6.1 KiB
JavaScript

// Leaderboard & High Score Registry System
window.LeaderboardSystem = class LeaderboardSystem {
constructor() {
this.selectedMode = 'survival'; // default tab
// High score records per mode
this.records = {
adventure: [
{ name: 'Безумный Дэйв', score: 10, detail: 'Уровень 10 (Пройдено)', date: '2026-09-01' },
{ name: 'Сосед Боб', score: 8, detail: 'Уровень 8', date: '2026-09-10' },
{ name: 'Пенни', score: 5, detail: 'Уровень 5', date: '2026-09-12' }
],
survival: [
{ name: 'Безумный Дэйв', score: 32, detail: '32 Флага', date: '2026-09-02' },
{ name: 'Зомби Йети', score: 18, detail: '18 Флагов', date: '2026-09-05' },
{ name: 'Пенни', score: 12, detail: '12 Флагов', date: '2026-09-11' }
],
roguelike: [
{ name: 'Безумный Дэйв', score: 14, detail: '14 Этажей (6 Реликвий)', date: '2026-09-03' },
{ name: 'Сосед Боб', score: 9, detail: '9 Этажей', date: '2026-09-08' },
{ name: 'Пенни', score: 6, detail: '6 Этажей', date: '2026-09-14' }
],
izombie: [
{ name: 'Доктор Зомбосс', score: 50, detail: '50 Мозгов съедено', date: '2026-09-04' },
{ name: 'Футболист Зомби', score: 25, detail: '25 Мозгов', date: '2026-09-09' },
{ name: 'Обычный Зомби', score: 15, detail: '15 Мозгов', date: '2026-09-13' }
],
bowling: [
{ name: 'Безумный Дэйв', score: 6800, detail: '6800 очков (Страйк x8)', date: '2026-09-06' },
{ name: 'Пенни', score: 4500, detail: '4500 очков', date: '2026-09-07' },
{ name: 'Сосед Боб', score: 3200, detail: '3200 очков', date: '2026-09-15' }
],
boss: [
{ name: 'Безумный Дэйв', score: 1, detail: 'Побежден за 2 мин 15 сек', date: '2026-09-15' }
],
bossrush: [
{ name: 'Безумный Дэйв', score: 3, detail: 'Boss Rush завершен! (3 волны)', date: '2026-09-16' },
{ name: 'Сосед Боб', score: 2, detail: '2 волны', date: '2026-09-16' }
]
};
this.loadFromAuth();
this.fetchAllLeaderboards();
}
loadFromAuth() {
if (window.PvZAuth) {
const uData = window.PvZAuth.getUserData();
if (uData.leaderboards) {
this.records = { ...this.records, ...uData.leaderboards };
}
}
}
saveToAuth() {
if (window.PvZAuth && !window.PvZAuth.isGuest()) {
window.PvZAuth.saveUserData({
leaderboards: this.records
});
}
}
async fetchAllLeaderboards() {
try {
const resp = await fetch('/api/leaderboard');
if (resp.ok) {
const json = await resp.json();
if (json && json.success && json.records) {
for (const [mode, rows] of Object.entries(json.records)) {
if (Array.isArray(rows) && rows.length > 0) {
this.mergeServerRecords(mode, rows);
}
}
if (window._pvzEngine && typeof window._pvzEngine.renderLeaderboardUI === 'function') {
window._pvzEngine.renderLeaderboardUI(this.selectedMode);
}
}
}
} catch (_) {}
}
async fetchLeaderboard(mode) {
try {
const resp = await fetch(`/api/leaderboard?mode=${encodeURIComponent(mode)}`);
if (resp.ok) {
const json = await resp.json();
if (json && json.success && Array.isArray(json.records)) {
this.mergeServerRecords(mode, json.records);
if (window._pvzEngine && typeof window._pvzEngine.renderLeaderboardUI === 'function') {
window._pvzEngine.renderLeaderboardUI(mode);
}
}
}
} catch (_) {}
}
mergeServerRecords(mode, serverRows) {
if (!this.records[mode]) {
this.records[mode] = [];
}
const combined = [...this.records[mode]];
for (const sr of serverRows) {
const existing = combined.find(r => r.name === sr.name);
if (existing) {
if (sr.score > existing.score) {
existing.score = sr.score;
existing.detail = sr.detail;
existing.date = sr.date;
}
} else {
combined.push({
name: sr.name,
score: sr.score,
detail: sr.detail,
date: sr.date
});
}
}
combined.sort((a, b) => b.score - a.score);
this.records[mode] = combined.slice(0, 10);
}
submitScore(mode, score, detail = '') {
if (!this.records[mode]) {
this.records[mode] = [];
}
let playerName = 'Игрок';
let token = null;
if (window.PvZAuth) {
playerName = window.PvZAuth.getCurrentUser();
if (window.PvZAuth.isGuest()) playerName = 'Гость';
token = window.PvZAuth.getToken();
}
const today = new Date().toISOString().slice(0, 10);
// Check if player already on this board
const existing = this.records[mode].find(r => r.name === playerName);
if (existing) {
if (score > existing.score) {
existing.score = score;
existing.detail = detail || `${score} очков`;
existing.date = today;
}
} else {
this.records[mode].push({
name: playerName,
score,
detail: detail || `${score} очков`,
date: today
});
}
// Sort descending
this.records[mode].sort((a, b) => b.score - a.score);
// Keep top 10
this.records[mode] = this.records[mode].slice(0, 10);
this.saveToAuth();
// Submit to server leaderboard asynchronously
try {
const headers = { 'Content-Type': 'application/json' };
if (token) headers['Authorization'] = `Bearer ${token}`;
fetch('/api/leaderboard', {
method: 'POST',
headers,
body: JSON.stringify({
mode,
score,
detail: detail || `${score} очков`,
name: playerName,
token
})
}).then(r => r.json()).catch(() => {});
} catch (_) {}
}
getEntries(mode) {
return this.records[mode] || [];
}
};