// leaderboard.js — Affichage du classement et soumission d'un résultat const API = window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1' ? 'http://localhost:8000' : `http://${window.location.hostname}:8000`; const CLASS_ICONS = { kael: '⚔️', seris: '🗡️', aldric: '🌿' }; // Leaderboard screen export async function showLeaderboard() { document.getElementById('home-screen').classList.add('hidden'); const screen = document.getElementById('leaderboard-screen'); screen.classList.remove('hidden'); await _loadAndRender(); } export function hideLeaderboard() { document.getElementById('leaderboard-screen').classList.add('hidden'); document.getElementById('home-screen').classList.remove('hidden'); } async function _loadAndRender() { const tbody = document.getElementById('lb-tbody'); tbody.innerHTML = 'Chargement...'; try { const res = await fetch(`${API}/leaderboard`); const rows = await res.json(); _renderTable(rows); } catch { tbody.innerHTML = 'Impossible de charger le classement.'; } } function _renderTable(rows) { const tbody = document.getElementById('lb-tbody'); if (!rows.length) { tbody.innerHTML = 'Aucune partie enregistrée.'; return; } tbody.innerHTML = rows.map((r, i) => { const time = _formatTime(r.time_seconds); const vic = r.victory ? '✓' : '✗'; const discords = [ r.p1_discord ? `@${r.p1_discord}` : (r.p1_username || ''), r.p2_discord ? `@${r.p2_discord}` : (r.p2_username || ''), r.p3_discord ? `@${r.p3_discord}` : (r.p3_username || ''), ].filter(Boolean).join(' · '); return ` #${i + 1} ${_esc(r.team_name)} ${r.score.toLocaleString()} ${time} ${vic} ${r.waves_completed}/3 ${_esc(discords)} `; }).join(''); } function _formatTime(s) { const m = Math.floor(s / 60); const sec = String(s % 60).padStart(2, '0'); return `${m}m${sec}s`; } function _esc(s) { return String(s ?? '').replace(/&/g, '&').replace(//g, '>'); } // End-game form export function showEndGameForm(data) { // data = { victory, score, timeSecs, wavesCompleted, players: [{username, class, souls, enemies_killed}] } const overlay = document.getElementById('endgame-overlay'); const title = document.getElementById('endgame-title'); const sub = document.getElementById('endgame-sub'); overlay.classList.remove('hidden'); title.textContent = data.victory ? 'VICTOIRE !' : 'DÉFAITE'; title.className = data.victory ? 'victory' : 'defeat'; sub.textContent = data.victory ? 'Le Soulgate est sauvegardé.' : 'Le Soulgate a été détruit.'; document.getElementById('endgame-score').textContent = `Score : ${data.score.toLocaleString()} pts`; document.getElementById('endgame-time').textContent = `Temps : ${_formatTime(data.timeSecs)}`; document.getElementById('endgame-waves').textContent = `Vagues : ${data.wavesCompleted}/3`; _buildDiscordForm(data); } function _buildDiscordForm(data) { const form = document.getElementById('endgame-form'); form.innerHTML = ''; // Defaite : pas de soumission au leaderboard, juste rejouer if (!data.victory) { const info = document.createElement('div'); info.className = 'lb-host-only'; info.textContent = "Défaite : le score n'est pas enregistré dans le leaderboard."; form.appendChild(info); const replayBtn = document.createElement('button'); replayBtn.className = 'mc-btn'; replayBtn.textContent = 'Rejouer'; replayBtn.style.marginTop = '8px'; replayBtn.addEventListener('click', () => location.reload()); form.appendChild(replayBtn); return; } // Seul le chef d'équipe peut soumettre le score → les autres voient un message + Rejouer if (!data.isHost) { const info = document.createElement('div'); info.className = 'lb-host-only'; info.textContent = "Seul le chef d'équipe peut enregistrer le score dans le leaderboard."; form.appendChild(info); const replayBtn = document.createElement('button'); replayBtn.className = 'mc-btn'; replayBtn.textContent = 'Rejouer'; replayBtn.style.marginTop = '8px'; replayBtn.addEventListener('click', () => location.reload()); form.appendChild(replayBtn); return; } // Champ nom d'équipe const teamRow = document.createElement('div'); teamRow.className = 'lb-field'; teamRow.innerHTML = ``; form.appendChild(teamRow); // Un champ Discord par joueur data.players.forEach((p, i) => { const icon = CLASS_ICONS[p.class] ?? ''; const row = document.createElement('div'); row.className = 'lb-field'; row.innerHTML = ` `; form.appendChild(row); }); // Bouton soumettre const submitBtn = document.createElement('button'); submitBtn.id = 'lb-submit-btn'; submitBtn.className = 'mc-btn mc-btn-gold'; submitBtn.textContent = 'Enregistrer dans le leaderboard'; form.appendChild(submitBtn); // Bouton rejouer const replayBtn = document.createElement('button'); replayBtn.className = 'mc-btn'; replayBtn.textContent = 'Rejouer'; replayBtn.style.marginTop = '8px'; replayBtn.addEventListener('click', () => location.reload()); form.appendChild(replayBtn); // Vérification Discord à la saisie form.querySelectorAll('.lb-discord-input').forEach(input => { let _debounce; input.addEventListener('input', () => { clearTimeout(_debounce); _debounce = setTimeout(() => _checkDiscord(input), 500); }); }); submitBtn.addEventListener('click', () => _submitForm(data, form, submitBtn)); } async function _checkDiscord(input) { const tag = input.value.trim().replace(/^@/, ''); const errEl = input.parentElement.querySelector('.lb-discord-error'); if (!tag) { errEl.textContent = ''; input.classList.remove('taken'); return; } try { const res = await fetch(`${API}/leaderboard/check-discord/${encodeURIComponent(tag)}`); const json = await res.json(); if (json.taken) { errEl.textContent = '❌ Ce Discord est déjà dans le leaderboard — choisis-en un autre.'; input.classList.add('taken'); } else { errEl.textContent = '✓'; input.classList.remove('taken'); } } catch { errEl.textContent = ''; } } async function _submitForm(data, form, btn) { // Bloquer si un Discord est déjà pris if (form.querySelector('.lb-discord-input.taken')) { alert('Un ou plusieurs Discord sont déjà utilisés dans le leaderboard.'); return; } const teamName = document.getElementById('lb-team-name').value.trim(); if (!teamName) { alert("Entre un nom d'équipe."); return; } const discordInputs = [...form.querySelectorAll('.lb-discord-input')]; const players = data.players; const payload = { team_name: teamName, score: data.score, time_seconds: data.timeSecs, waves_completed: data.wavesCompleted, victory: data.victory, lobby_code: data.lobbyCode ?? '', // enforcement host-only côté serveur submitter_id: data.submitterId ?? '', p1_username: players[0]?.username ?? '', p1_class: players[0]?.class ?? '', p1_discord: (discordInputs[0]?.value ?? '').trim().replace(/^@/, ''), p2_username: players[1]?.username ?? '', p2_class: players[1]?.class ?? '', p2_discord: (discordInputs[1]?.value ?? '').trim().replace(/^@/, ''), p3_username: players[2]?.username ?? '', p3_class: players[2]?.class ?? '', p3_discord: (discordInputs[2]?.value ?? '').trim().replace(/^@/, ''), }; btn.disabled = true; btn.textContent = 'Envoi...'; try { const res = await fetch(`${API}/leaderboard/submit`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), }); const json = await res.json(); if (!res.ok) { alert(json.detail ?? 'Erreur lors de l\'envoi.'); btn.disabled = false; btn.textContent = 'Enregistrer dans le leaderboard'; return; } btn.textContent = `✓ Enregistré ! Vous êtes #${json.rank} au classement.`; btn.style.background = '#2a7'; } catch { alert('Erreur réseau — impossible de contacter le serveur.'); btn.disabled = false; btn.textContent = 'Enregistrer dans le leaderboard'; } }