// 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] || []; } };