feedback5 R2: Match.js → server/match/match/*.js, a pure move — the 167 class members (166 methods and the soloUntimed getter), verbatim with their comments, go to 16 per-concern method containers (platform, infra, messaging, views, watch, intents, pause, phases, spDraft, prep, combat, clientCombat, reports, unitePhase, bossRounds, settle) that Match.js installs on Match.prototype in a fixed order with class-method descriptors (a name defined twice throws); the constants the modules share go to match/common.js (FLOW_TICKER_PRIORITY, DELAYS, BAND_TURN_SECONDS re-exported by Match.js; OK, fail, BOSS_CLOCK_MS), GAME_TYPES to platform.js, the boss-round budgets / rates to bossRounds.js; Match.js keeps the header (+ a code-layout paragraph), the constructor-only constants and the class with its constructor. Section-header comments are replaced by the module headers. docs-consistency reads the method modules (matchText) and pins layerGainRoom in match/settle.js
This commit is contained in:
1 parent
8ab6d021da
commit
1ca4936f0a
19 files changed
+3105
-2923
No files matched your search
@@ -0,0 +1,72 @@
|
||||
// server/match/match/pause.js — Match methods: the solo pause (g.pause, DESIGN §14 "Solo pause") — freeze, resume
|
||||
// (every clock and deadline shifted by the pause), the drop at the battle phase's end, and the field clocks' "now".
|
||||
// Installed on Match.prototype by server/match/Match.js (a method container: never instantiated; `this` is the match).
|
||||
|
||||
import { PHASE, ERR } from '../../../shared/constants.js';
|
||||
import { BOSS_CLOCK_MS, OK, fail } from './common.js';
|
||||
|
||||
export class MatchPause {
|
||||
/**
|
||||
* Solo pause (official PauseUp / ResumeUp; DESIGN §14 "Solo pause"): g.pause { on } freezes the running battle — the
|
||||
* field clock (b.start `elapsed`, the boss budgets and overtime), the result deadline / server release timers, the
|
||||
* boss clock, the HUD `deadline` / `overtimeAt` (shifted by the pause on resume) and the server-run pacers
|
||||
* (FieldRunner / HeadlessPacer skip their intervals) — and the browser's runner stops its local clock while
|
||||
* `m.public.paused` is true. Solo matches only (co-op battles never pause: WRONG_PHASE) and only while a battle runs;
|
||||
* `{ on: false }` is always accepted. A disconnect / leave, or the battle phase ending, resumes.
|
||||
*/
|
||||
setPause(ps, on) {
|
||||
void ps;
|
||||
if (!this.isSolo) return fail(ERR.WRONG_PHASE, 'co-op battles never pause');
|
||||
if (!on) { this._resume(); return OK; }
|
||||
if (this.paused) return OK;
|
||||
const battlePhase = this.phase === PHASE.COMBAT || this.phase === PHASE.FINAL_ASSAULT || this.phase === PHASE.HIDDEN_CORE;
|
||||
if (!battlePhase || this._finalEnding || !this.fields.some((f) => f.live && !f.done)) return fail(ERR.WRONG_PHASE, 'no battle running');
|
||||
this.paused = true;
|
||||
this._pausedAt = this.sched.now();
|
||||
for (const f of this.fields) {
|
||||
if (!f.cc) continue;
|
||||
if (f.deadlineTimer) { this.cancel(f.deadlineTimer); f.deadlineTimer = null; f.rearmDeadline = true; }
|
||||
if (f.doneTimer) { this.cancel(f.doneTimer); f.doneTimer = null; f.rearmRelease = true; }
|
||||
}
|
||||
if (this._bossClock) { this.cancel(this._bossClock); this._bossClock = null; }
|
||||
this.markPublic();
|
||||
return OK;
|
||||
}
|
||||
|
||||
/** End a solo pause: every clock and deadline moves on by the paused time (no-op when not paused). */
|
||||
_resume() {
|
||||
if (!this.paused) return;
|
||||
const d = Math.max(0, this.sched.now() - this._pausedAt);
|
||||
this.paused = false;
|
||||
this._pausedAt = 0;
|
||||
this.pausedMs += d;
|
||||
if (this.deadline) this.deadline += d;
|
||||
if (this.overtimeAt) this.overtimeAt += d;
|
||||
if (this._bossStartAt != null) this._bossStartAt += d;
|
||||
for (const f of this.fields) {
|
||||
if (!f.cc || f.done) continue;
|
||||
f.startAt += d;
|
||||
f.lastProgressAt += d;
|
||||
if (f.rearmDeadline && f.mode === 'client') this._armDeadline(f);
|
||||
if (f.rearmRelease && f.mode === 'server') this._armRelease(f);
|
||||
f.rearmDeadline = false;
|
||||
f.rearmRelease = false;
|
||||
}
|
||||
if (this._bossClockOn && !this._bossClock && (this.phase === PHASE.FINAL_ASSAULT || this.phase === PHASE.HIDDEN_CORE)) {
|
||||
this._bossClock = this.later(BOSS_CLOCK_MS, () => this._bossClockTick());
|
||||
}
|
||||
this.markPublic();
|
||||
}
|
||||
|
||||
/** The battle phase is over: drop the pause without shifting anything (its timers are gone). */
|
||||
_clearPause() {
|
||||
if (!this.paused) return;
|
||||
this.pausedMs += Math.max(0, this.sched.now() - this._pausedAt);
|
||||
this.paused = false;
|
||||
this._pausedAt = 0;
|
||||
this.markPublic();
|
||||
}
|
||||
|
||||
/** The field clocks' "now": frozen at the pause instant while paused. */
|
||||
_clockNow() { return this.paused ? this._pausedAt : this.sched.now(); }
|
||||
}
|
||||
Reference in new issue
Block a user