starraid/star-raid/js/home.js

123 lines
4.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use strict";
/* ================= 飞行员大厅(主页):排行榜 + 图鉴展柜背景 ================= */
let LB_SORT = "endless";
let HOME_SHOW = null;
/* 图鉴已收集数(只统计合法 key */
function codexCount() {
let n = 0;
for (const k in SAVE.codex)
if (SAVE.codex[k] && (WEAPONS[k] || SUBS[k] || DRONES[k] || PASSIVES[k])) n++;
return n;
}
async function enterHome() {
G = null;
UI = "home";
rollHomeShow();
show("o-home");
/* 刷新账号数据stats/codex再渲染面板与排行榜 */
const r = await NET.api("/api/me", "GET");
if (r.ok && r.data.user) NET._apply(r.data);
else if (r.ok) { NET.user = null; UI = "auth"; show("o-auth"); return; } // 会话失效
/* status===0 网络抖动:沿用现有数据,不踢出 */
buildHomeStats();
buildLeaderboard(LB_SORT);
}
function buildHomeStats() {
const s = NET.stats || { bossKills: 0, endlessHigh: 0 };
const nm = NET.user ? NET.user.username : "--";
$("home-stats").textContent =
`${nm} · 击坠Boss ${s.bossKills} · 图鉴 ${codexCount()}/${NET.codexTotal} · 无尽最高 ${s.endlessHigh}`;
}
/* ---------- 排行榜 ---------- */
document.querySelectorAll(".lb-tabs button").forEach((b) => {
b.onclick = () => {
LB_SORT = b.dataset.s;
document.querySelectorAll(".lb-tabs button").forEach((x) => x.classList.toggle("on", x === b));
SFX.pick();
buildLeaderboard(LB_SORT);
};
});
async function buildLeaderboard(sort) {
const list = $("lb-list");
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;
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)];
for (const c of cells) {
const s = document.createElement("span");
s.textContent = c; // textContent 防注入
d.appendChild(s);
}
list.appendChild(d);
});
}
/* ---------- 大厅背景:随机军械展柜 + 随机刷怪巡游 ---------- */
const HOME_MOB_TYPES = ["scout", "weaver", "kami", "splitter", "mine", "gunship", "sniper"];
function rollHomeShow() {
HOME_SHOW = {
m: shuffle(Object.keys(WEAPONS)).slice(0, 3),
s: shuffle(Object.keys(SUBS)).slice(0, 3),
d: shuffle(Object.keys(DRONES)).slice(0, 2),
t: 0, spawnT: 20, mobs: [],
};
}
/* 由 render() 在 UI==="home" 时每帧调用dt=1 */
function drawHomeShowcase(dt) {
if (!HOME_SHOW || HOME_SHOW.t > 1800) rollHomeShow(); // 30 秒重抽一轮
const hs = HOME_SHOW;
hs.t += dt;
/* 展柜3主武+3副武+2僚机两排漂浮 */
const items = [
...hs.m.map((k) => ({ d: WEAPONS[k] })),
...hs.s.map((k) => ({ d: SUBS[k] })),
...hs.d.map((k) => ({ d: DRONES[k] })),
];
ctx.textAlign = "center";
items.forEach((it, i) => {
const col = i % 4, row = (i / 4) | 0;
const x = W / 2 + (col - 1.5) * 56;
const y = 118 + row * 52 + Math.sin(hs.t * 0.03 + i * 1.7) * 2.5;
const img = it.d.icon ? ICONS[it.d.icon] : SPR[it.d.spr];
if (img) {
ctx.globalAlpha = 0.85;
ctx.drawImage(img, x - img.width * 1.5, y - img.height * 1.5, img.width * 3, img.height * 3);
ctx.globalAlpha = 1;
}
ctx.font = "6px 'Press Start 2P',monospace";
ctx.fillStyle = "#7f8cb8";
ctx.fillText(it.d.name, x, y + 22);
});
/* 随机刷怪:从顶部飘入,正弦摆动 */
hs.spawnT -= dt;
if (hs.spawnT <= 0 && hs.mobs.length < 8) {
hs.spawnT = rndi(50, 110);
const ty = pick(HOME_MOB_TYPES), elite = Math.random() < 0.2;
const spr = SPR[(elite ? "e_" : "") + ty] || SPR[ty] || SPR.scout;
hs.mobs.push({ x: rnd(20, W - 20), y: -16, vy: rnd(0.25, 0.6), ph: rnd(0, TAU), amp: rnd(6, 26), spr, sc: elite ? 1.8 : 1.3, t: 0 });
}
for (const m of hs.mobs) {
m.t += dt;
m.y += m.vy * dt;
const mx = m.x + Math.sin(m.t * 0.02 + m.ph) * m.amp;
ctx.globalAlpha = 0.8;
ctx.drawImage(m.spr, mx - (m.spr.width * m.sc) / 2, m.y - (m.spr.height * m.sc) / 2, m.spr.width * m.sc, m.spr.height * m.sc);
ctx.globalAlpha = 1;
}
hs.mobs = hs.mobs.filter((m) => m.y < H + 24);
}