Add top-5 leaderboard UI — was fully wired server-side, no way to view it
topSolutions()/api.php?action=top existed since the first PHP deploy but
nothing ever called it. Add 🏆 HUD button + T key, small modal listing
rank/name/move count/date, closes via button or Esc. User-supplied names
go through escapeHtml() before landing in innerHTML.
This commit is contained in:
@@ -27,6 +27,20 @@
|
||||
#solve-cancel.solve-found { background: #7CFF6B; color: #0a0a15; }
|
||||
#solve-cancel.solve-found:hover { background: #9dff8f; }
|
||||
|
||||
#top-btn { cursor: pointer; }
|
||||
#top-btn:hover { background: rgba(233,69,96,0.25) !important; }
|
||||
#top-overlay { position: fixed; inset: 0; background: rgba(10,10,21,0.75); display: flex; justify-content: center; align-items: center; z-index: 10; }
|
||||
#top-box { background: #1a1a2e; border: 2px solid #e94560; border-radius: 12px; padding: 20px 28px; text-align: center; box-shadow: 0 0 30px rgba(233,69,96,0.5); min-width: 280px; }
|
||||
#top-box p { color: #e94560; font-size: 16px; font-weight: bold; margin-bottom: 12px; }
|
||||
#top-list { text-align: left; margin-bottom: 16px; }
|
||||
#top-list .row { display: flex; justify-content: space-between; gap: 16px; padding: 5px 4px; color: #ccc; border-bottom: 1px solid rgba(233,69,96,0.15); }
|
||||
#top-list .rank { color: #f0c27a; font-weight: bold; width: 1.5em; }
|
||||
#top-list .name { flex: 1; color: #fff; }
|
||||
#top-list .count { color: #48cae4; }
|
||||
#top-list .empty { color: #777; padding: 8px 4px; }
|
||||
#top-close { background: #e94560; color: #fff; border: none; border-radius: 8px; padding: 8px 20px; font: inherit; font-weight: bold; cursor: pointer; }
|
||||
#top-close:hover { background: #ff6b8a; }
|
||||
|
||||
/* ============ ЭКРАНЫ (меню/старт/финал/настройки) ============ */
|
||||
.screen { display: flex; flex-direction: column; align-items: center; gap: 16px; color: #e94560; text-align: center; padding: 20px; }
|
||||
.screen.hidden { display: none; }
|
||||
@@ -126,6 +140,7 @@
|
||||
<span id="level-name">Уровень 1</span>
|
||||
<span id="box-count">📦 0/0</span>
|
||||
<span id="moves-count">👣 0</span>
|
||||
<span id="top-btn" title="Топ-5 уровня">🏆</span>
|
||||
<span id="menu-btn" title="В меню">🏠</span>
|
||||
</div>
|
||||
<div id="controls">
|
||||
@@ -135,6 +150,7 @@
|
||||
<kbd>R</kbd> — рестарт
|
||||
<kbd>F</kbd> — автопоиск (<kbd>Esc</kbd> — отмена)
|
||||
<kbd>G</kbd> — лучшее сохранённое решение
|
||||
<kbd>T</kbd> — топ-5 уровня
|
||||
<kbd>M</kbd> — в меню
|
||||
</div>
|
||||
</div>
|
||||
@@ -147,6 +163,14 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="top-overlay" class="hidden">
|
||||
<div id="top-box">
|
||||
<p id="top-title">🏆 Топ-5</p>
|
||||
<div id="top-list"></div>
|
||||
<button id="top-close">Закрыть</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Вставь ВЕСЬ дамп между тегами -->
|
||||
<script type="text/plain" id="levels-data">
|
||||
*************************************
|
||||
@@ -2076,6 +2100,9 @@ window.addEventListener('keydown', e => {
|
||||
const key = e.key.toLowerCase();
|
||||
|
||||
if (key === 'escape' && (solveState || pendingSolution)) { cancelAutoSolve(); e.preventDefault(); return; }
|
||||
if (key === 'escape' && !document.getElementById('top-overlay').classList.contains('hidden')) {
|
||||
document.getElementById('top-overlay').classList.add('hidden'); e.preventDefault(); return;
|
||||
}
|
||||
if (solveState || pendingSolution) { e.preventDefault(); return; } // поиск идёт или ждём клика по найденному — левел не трогаем
|
||||
|
||||
// Системные клавиши — обрабатываем сразу
|
||||
@@ -2085,6 +2112,7 @@ window.addEventListener('keydown', e => {
|
||||
if (key === 'f') { startAutoSolve(); e.preventDefault(); return; }
|
||||
if (key === 'g') { playBestSolution(); e.preventDefault(); return; }
|
||||
if (key === 'm') { backToMenu(); e.preventDefault(); return; }
|
||||
if (key === 't') { showTop(); e.preventDefault(); return; }
|
||||
|
||||
// Стрелки и WASD
|
||||
if (['arrowup','arrowdown','arrowleft','arrowright','w','a','s','d'].includes(key)) {
|
||||
@@ -2165,6 +2193,29 @@ function backToMenu() {
|
||||
}
|
||||
document.getElementById('menu-btn').addEventListener('click', backToMenu);
|
||||
|
||||
async function showTop() {
|
||||
const rows = await topSolutions(LEVELS[currentLevel].name);
|
||||
const list = document.getElementById('top-list');
|
||||
document.getElementById('top-title').textContent = `🏆 Топ-5 — ${LEVELS[currentLevel].name}`;
|
||||
if (!rows || !rows.length) {
|
||||
list.innerHTML = '<div class="empty">Пока никто не прошёл этот уровень</div>';
|
||||
} else {
|
||||
list.innerHTML = rows.map((r, i) => {
|
||||
const date = new Date(r.created_at * 1000).toLocaleDateString('ru-RU');
|
||||
const icon = r.source === 'solver' ? '🤖' : '🧑';
|
||||
return `<div class="row"><span class="rank">${i+1}.</span><span class="name">${icon} ${escapeHtml(r.name)}</span><span class="count">${r.move_count} ход.</span><span class="count">${date}</span></div>`;
|
||||
}).join('');
|
||||
}
|
||||
document.getElementById('top-overlay').classList.remove('hidden');
|
||||
}
|
||||
function escapeHtml(s) {
|
||||
const d = document.createElement('div');
|
||||
d.textContent = s;
|
||||
return d.innerHTML;
|
||||
}
|
||||
document.getElementById('top-btn').addEventListener('click', showTop);
|
||||
document.getElementById('top-close').addEventListener('click', () => document.getElementById('top-overlay').classList.add('hidden'));
|
||||
|
||||
document.getElementById('settings-save').addEventListener('click', async () => {
|
||||
const name = document.getElementById('settings-name-input').value.trim();
|
||||
if (!name) return;
|
||||
|
||||
Reference in New Issue
Block a user