排行榜改造+Boss挑战一命制

- 排行榜30秒自动刷新(进入页面已刷新,定时器仅home界面生效,刷新不闪屏)
- boss榜不再统计累计击坠:改为Boss挑战(rush)击败过的不同Boss数/总数(11),按mode=rush+win=1去重stage;rush局stage记录Boss索引
- Boss挑战一命制:startRushGo强制lives=1/maxLives=1,掉落与拾取加命均以maxLives为上限,中途无法再加命
- 排行榜文案:Boss挑战tab/Boss列头
This commit is contained in:
JOJO 2026-08-06 16:24:13 +08:00
parent 559e70fac5
commit 4e41ee2bef
4 changed files with 31 additions and 9 deletions

View File

@ -49,10 +49,10 @@
<div class="h3" style="color:var(--cyan);letter-spacing:3px">— 全服排行榜 TOP 100 —</div>
<div class="lb-tabs">
<button data-s="endless" class="on">无尽最高分</button>
<button data-s="boss">击坠Boss</button>
<button data-s="boss">Boss挑战</button>
<button data-s="codex">图鉴收集</button>
</div>
<div class="lb-row lb-head"><span>#</span><span>飞行员</span><span>击坠</span><span>图鉴</span><span>无尽最高</span></div>
<div class="lb-row lb-head"><span>#</span><span>飞行员</span><span>Boss</span><span>图鉴</span><span>无尽最高</span></div>
<div class="lb-list" id="lb-list"></div>
</div>
<div class="row" style="margin-bottom:2%">

View File

@ -1,6 +1,13 @@
"use strict";
/* ================= 飞行员大厅(主页):排行榜 + 图鉴展柜背景 ================= */
let LB_SORT = "endless";
let LB_T = 0; // 排行榜自动刷新定时器
/* 排行榜 30 秒自动刷新(进入页面时 enterHome 已刷新一次) */
function lbAutoRefresh() {
if (LB_T) return;
LB_T = setInterval(() => { if (UI === "home") buildLeaderboard(LB_SORT); }, 30000);
}
/* 图鉴已收集数(只统计合法 key */
function codexCount() {
@ -21,6 +28,7 @@ async function enterHome() {
/* status===0 网络抖动:沿用现有数据,不踢出 */
buildHomeStats();
buildLeaderboard(LB_SORT);
lbAutoRefresh();
}
function buildHomeStats() {
@ -42,17 +50,19 @@ document.querySelectorAll(".lb-tabs button").forEach((b) => {
async function buildLeaderboard(sort) {
const list = $("lb-list");
list.innerHTML = '<div class="lb-empty">载入中...</div>';
if (!list.children.length) list.innerHTML = '<div class="lb-empty">载入中...</div>'; // 首次/空态才显示载入,自动刷新不闪屏
const r = await NET.api("/api/leaderboard?sort=" + sort, "GET");
if (!r.ok) { list.innerHTML = '<div class="lb-empty">载入失败</div>'; return; }
const rows = r.data.rows || [];
list.innerHTML = "";
if (!rows.length) { list.innerHTML = '<div class="lb-empty">虚位以待</div>'; return; }
const meName = NET.user && NET.user.username;
const bossTotal = r.data.bossTotal || 11;
rows.forEach((row, i) => {
const d = document.createElement("div");
d.className = "lb-row" + (meName && row.username === meName ? " me" : "");
const cells = [String(i + 1), row.username, String(row.bossKills), row.codexN + "/" + r.data.codexTotal, String(row.endlessHigh)];
const bossCell = sort === "boss" ? row.bossKills + "/" + bossTotal : String(row.bossKills); // boss 榜显示“击败几个/总共几个”
const cells = [String(i + 1), row.username, bossCell, row.codexN + "/" + r.data.codexTotal, String(row.endlessHigh)];
for (const c of cells) {
const s = document.createElement("span");
s.textContent = c; // textContent 防注入

View File

@ -376,6 +376,8 @@ function startRushGo(i,build){
G.drones={};build.d.forEach(k=>G.drones[k]=2);G.droneLoadout=build.d.slice();
G.xpNeed=1e9; // 挑战模式无升级打断
G.rushIdx=i;G.rushT0=0;
G.stage=i; // 排行榜统计rush 局 stage 记录 Boss 索引(0~10)
G.lives=1;G.stat.maxLives=1; // BOSS 挑战一命制:掉落/拾取心均以 maxLives 为上限,中途无法再加命
UI="play";show("");acc=0;
MUS.start(true);
banner("BOSS 挑战 — "+BOSSES[i].name,"#ff5566",130);

View File

@ -194,15 +194,25 @@ function me(req, res, ctx) {
/* ---------- GET /api/leaderboard?sort=endless|boss|codex ---------- */
const LB_COLS = { endless: "endless_high", boss: "boss_kills", codex: "codexN" };
const BOSS_TOTAL = 11; // 与 js/enemies.js BOSSES 数量同步
function leaderboard(req, res, ctx, _body, query) {
const col = LB_COLS[(query && query.get("sort")) || ""] || "endless_high";
/* col 来自上方白名单映射,非用户输入直接拼接 */
const rows = db.prepare(`
const sort = (query && query.get("sort")) || "";
const bossSort = sort === "boss";
/* boss 榜:不统计累计击坠,改为 BOSS 挑战(rush)模式下击败过几个不同 Boss去重/ 总共 Boss 数 */
const rows = bossSort ? db.prepare(`
SELECT u.username AS username,
(SELECT COUNT(DISTINCT r.stage) FROM runs r
WHERE r.user_id=u.id AND r.mode='rush' AND r.win=1 AND r.stage BETWEEN 0 AND 10) AS bossKills,
s.endless_high AS endlessHigh,
(SELECT COUNT(*) FROM codex_unlocks c WHERE c.user_id=u.id) AS codexN
FROM users u JOIN user_stats s ON s.user_id=u.id
ORDER BY bossKills DESC, u.id ASC LIMIT 100`).all()
: db.prepare(`
SELECT u.username AS username, s.boss_kills AS bossKills, s.endless_high AS endlessHigh,
(SELECT COUNT(*) FROM codex_unlocks c WHERE c.user_id=u.id) AS codexN
FROM users u JOIN user_stats s ON s.user_id=u.id
ORDER BY ${col} DESC, u.id ASC LIMIT 100`).all();
return ctx.ok({ rows, codexTotal: CODEX_TOTAL, sort: col });
ORDER BY ${LB_COLS[sort] || "endless_high"} DESC, u.id ASC LIMIT 100`).all();
return ctx.ok({ rows, codexTotal: CODEX_TOTAL, bossTotal: BOSS_TOTAL, sort });
}
/* ---------- POST /api/run ----------