public/js/ui/gameLogic.js becomes a re-export barrel. The bodies move verbatim into public/js/ui/gameLogic/*.js (shared, phases, camera, watch, bonds, shop, placement, draft, enemies, format, shortcuts, settings, result, loadout, panel). The barrel re-exports the same 96 names in the same order. isObj and int gain export on shared.js so sibling modules can import them; they stay off the barrel. No importer changes.
59 lines
3.1 KiB
JavaScript
59 lines
3.1 KiB
JavaScript
// ui/gameLogic/result.js — end-of-match result normalisation. Re-exported from ../gameLogic.js.
|
|
|
|
import { int, isObj, sortedPlayers } from './shared.js';
|
|
|
|
|
|
// ---- result -----------------------------------------------------------------------------------------------------------
|
|
|
|
/**
|
|
* Normalise an m.result payload (free-form; see screens/result.js header for the expected shape).
|
|
* @param {any} res
|
|
* @param {any} pub last m.public (fallbacks)
|
|
*/
|
|
export function normalizeResult(res, pub) {
|
|
const r = isObj(res) ? res : {};
|
|
const pubPlayers = new Map(sortedPlayers(pub).map((p) => [p.playerId, p]));
|
|
// From the 最终攻势 on, every survivor's LP is one merged pool (m.public teamLp) and leaks / overtime only drain
|
|
// that pool: a survivor's remaining LP is the pool, not its own LP from before the Final Assault.
|
|
const teamLp = Number.isFinite(r.teamLp) ? r.teamLp : Number.isFinite(pub?.teamLp) ? pub.teamLp : null;
|
|
const players = (Array.isArray(r.players) ? r.players : []).filter(isObj).map((p) => {
|
|
const pp = pubPlayers.get(p.playerId) || {};
|
|
const title = typeof p.title === 'string' ? { id: p.title } : isObj(p.title) ? p.title : null;
|
|
const alive = p.alive ?? pp.alive ?? true;
|
|
const ownLp = Number.isFinite(p.lp) ? p.lp : (Number.isFinite(pp.lp) ? pp.lp : null);
|
|
return {
|
|
playerId: p.playerId,
|
|
seat: int(p.seat, int(pp.seat, 0)),
|
|
name: p.name || pp.name || '博士',
|
|
isBot: !!(p.isBot ?? pp.isBot),
|
|
alive,
|
|
lp: teamLp != null ? (alive === false ? 0 : Math.max(0, teamLp)) : ownLp,
|
|
lpShared: teamLp != null,
|
|
bandId: p.bandId ?? pp.bandId ?? null,
|
|
roundsPassed: Number.isFinite(p.roundsPassed) ? p.roundsPassed : (Number.isFinite(r.roundsPassed) ? r.roundsPassed : 0),
|
|
title,
|
|
lineup: (Array.isArray(p.lineup) ? p.lineup : Array.isArray(p.board) ? p.board : []).filter(isObj).slice(0, 12),
|
|
bonds: (Array.isArray(p.bonds) ? p.bonds : Array.isArray(pp.bonds) ? pp.bonds : []).filter(isObj),
|
|
stats: isObj(p.stats) ? p.stats : {},
|
|
trophies: Number.isFinite(p.trophies) ? p.trophies : 0,
|
|
reward: Number.isFinite(p.reward) ? p.reward : 0,
|
|
left: !!p.left,
|
|
};
|
|
}).sort((a, b) => a.seat - b.seat);
|
|
return {
|
|
victory: !!r.victory,
|
|
roundsPassed: Number.isFinite(r.roundsPassed) ? r.roundsPassed : Math.max(0, ...players.map((p) => p.roundsPassed), 0),
|
|
lastRound: Number.isFinite(r.lastRound) ? r.lastRound : (Number.isFinite(pub?.lastRound) ? pub.lastRound : 14),
|
|
hiddenCleared: !!r.hiddenCleared,
|
|
// the Hidden Core (R15) was fought: only then does the result show its boss medal (m.result.hiddenReached;
|
|
// older payloads without it: a clear implies it was reached)
|
|
hiddenReached: typeof r.hiddenReached === 'boolean' ? (r.hiddenReached || !!r.hiddenCleared) : !!r.hiddenCleared,
|
|
difficulty: r.difficulty || pub?.difficulty || null,
|
|
modeId: r.modeId || pub?.modeId || null,
|
|
bossId: r.bossId ?? pub?.bossId ?? null,
|
|
hiddenBossId: r.hiddenBossId ?? pub?.hiddenBossId ?? null,
|
|
durationMs: Number.isFinite(r.durationMs) ? r.durationMs : null,
|
|
players,
|
|
};
|
|
}
|