diff --git a/public/js/screens/game.js b/public/js/screens/game.js index 47a1d602..3225f684 100644 --- a/public/js/screens/game.js +++ b/public/js/screens/game.js @@ -65,8 +65,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from '../../vendor/hooks.module.js'; import { PHASE, GEO } from '../../../shared/constants.js'; -import { fxForm } from '../../../shared/protocol.js'; -import { html, Spinner, PhaseBanner, Icon, Button, MicroLabel, confirmDialog, closeAllDialogs, useTicker } from '../ui/components.js'; +import { html, Spinner, PhaseBanner, Icon, Button, confirmDialog, closeAllDialogs, useTicker } from '../ui/components.js'; import { useGameData, GIcon } from '../ui/gameComponents.js'; import { useFieldView } from '../ui/fieldHost.js'; import { TopBar, liveLp, ownLeaks, uniteRemaining, tempInfo, tempReadyReason } from '../ui/hud.js'; @@ -103,24 +102,17 @@ import { BriefingScreen } from './briefing.js'; import { BandDraftScreen } from './bandDraft.js'; import { ResultScreen } from './result.js'; import { net } from '../net.js'; -import { store, useStore, shallowEqual, serverNow, emptyMatch, isSpectating } from '../store.js'; +import { useStore, shallowEqual, serverNow, isSpectating } from '../store.js'; import { battleRunner } from '../battle/runner.js'; import { isClientCombat, observeTarget, teammateProgress, cameraLayers, layerCamera, sidesOf, resumedWatch } from '../battle/observe.js'; import { screenStrip, playerBonds, playerLayer, detailBondOwner, toggleBond, popupView } from '../ui/watchBonds.js'; -import { data, localAsset, getMode } from '../data.js'; +import { data, getMode } from '../data.js'; import { audio } from '../audio.js'; import { useDocClass, FullscreenButton } from '../ui/device.js'; - -const cx = (...p) => p.flat().filter(Boolean).join(' '); -const HUD_HZ_MS = 200; -/** Range tiles of the selected unit (its own highlight group: the wheel's 'facing' group may be up at the same time). */ -const SEL_RANGE = Object.freeze({ group: 'selRange', color: 0xff9c33, fill: 0.3, line: 0.95 }); -/** The tile an armed merge-completing card's elite will take (its own group; gold like the promotion cue, render/fx.js). */ -const MERGE_HL = Object.freeze({ group: 'mergeTile', color: 0xffd45a, fill: 0.34, line: 1 }); -const STATE_EV = new Set(['spawn', 'die', 'deploy', 'status', 'skill']); -/** Event tuples replayed when a field is entered late: the state-bearing kinds and the fx that change an enemy's model - * form (shared/protocol.js fxForm — the field meta's UnitInfo `form` predates them). */ -const keepEarly = (e) => Array.isArray(e) && (STATE_EV.has(e[0]) || fxForm(e) !== undefined); +// MatchEnded, PausedOverlay, the highlight styles and keepEarly live in ./game/*.js. +import { HUD_HZ_MS, MERGE_HL, SEL_RANGE, cx } from './game/marks.js'; +import { keepEarly } from './game/early.js'; +import { MatchEnded, PausedOverlay } from './game/overlays.js'; /** Router for the in-match screens. */ export function GameScreen() { @@ -147,36 +139,6 @@ export function GameScreen() { ${ended && !hasResult && mode !== 'result' ? html`<${MatchEnded} />` : null}`; } -/** The room went back to its lobby without a result (match aborted): offer the way back. */ -function MatchEnded() { - return html``; -} - -/** Solo pause (m.public.paused): the field dims under the 暂停中 plate; 继续作战 resumes (g.pause off). */ -function PausedOverlay({ canResume, busy, onResume, onExit }) { - const plate = localAsset('ui/battle', 'matte_pause'); - return html``; -} - // ---- match screen ---------------------------------------------------------------------------------------- function MatchScreen() { @@ -1325,4 +1287,3 @@ function MatchScreen() { <${ExitModal} open=${exitOpen} onClose=${() => setExitOpen(false)} solo=${solo} /> `; } - diff --git a/public/js/screens/game/early.js b/public/js/screens/game/early.js new file mode 100644 index 00000000..62f3c282 --- /dev/null +++ b/public/js/screens/game/early.js @@ -0,0 +1,9 @@ +// public/js/screens/game/early.js — which events are replayed when a field is entered late. + +import { fxForm } from '../../../../shared/protocol.js'; + +export const STATE_EV = new Set(['spawn', 'die', 'deploy', 'status', 'skill']); + +/** Event tuples replayed when a field is entered late: the state-bearing kinds and the fx that change an enemy's model + * form (shared/protocol.js fxForm — the field meta's UnitInfo `form` predates them). */ +export const keepEarly = (e) => Array.isArray(e) && (STATE_EV.has(e[0]) || fxForm(e) !== undefined); diff --git a/public/js/screens/game/marks.js b/public/js/screens/game/marks.js new file mode 100644 index 00000000..1f11b3c8 --- /dev/null +++ b/public/js/screens/game/marks.js @@ -0,0 +1,11 @@ +// public/js/screens/game/marks.js — class-name join and the field highlight styles the match screen passes to the view. + +export const cx = (...p) => p.flat().filter(Boolean).join(' '); + +export const HUD_HZ_MS = 200; + +/** Range tiles of the selected unit (its own highlight group: the wheel's 'facing' group may be up at the same time). */ +export const SEL_RANGE = Object.freeze({ group: 'selRange', color: 0xff9c33, fill: 0.3, line: 0.95 }); + +/** The tile an armed merge-completing card's elite will take (its own group; gold like the promotion cue, render/fx.js). */ +export const MERGE_HL = Object.freeze({ group: 'mergeTile', color: 0xffd45a, fill: 0.34, line: 1 }); diff --git a/public/js/screens/game/overlays.js b/public/js/screens/game/overlays.js new file mode 100644 index 00000000..4ab312a4 --- /dev/null +++ b/public/js/screens/game/overlays.js @@ -0,0 +1,35 @@ +// public/js/screens/game/overlays.js — the match-ended plate and the solo pause plate. + +import { Button, MicroLabel, html } from '../../ui/components.js'; +import { emptyMatch, store } from '../../store.js'; +import { localAsset } from '../../data.js'; + +/** The room went back to its lobby without a result (match aborted): offer the way back. */ +export function MatchEnded() { + return html``; +} + +/** Solo pause (m.public.paused): the field dims under the 暂停中 plate; 继续作战 resumes (g.pause off). */ +export function PausedOverlay({ canResume, busy, onResume, onExit }) { + const plate = localAsset('ui/battle', 'matte_pause'); + return html``; +} diff --git a/test/match/feedback1-runner-forms.test.js b/test/match/feedback1-runner-forms.test.js index 4c61b64b..4d6336c8 100644 --- a/test/match/feedback1-runner-forms.test.js +++ b/test/match/feedback1-runner-forms.test.js @@ -234,7 +234,8 @@ test('a hidden-tab backlog still beyond HELD_MAX after compaction: the first fra test('screens/game.js buffers the form fx with the state-bearing events it replays when a field is entered late', () => { const src = readFileSync(path.join(ROOT, 'public/js/screens/game.js'), 'utf8'); - assert.match(src, /const keepEarly = \(e\) => Array\.isArray\(e\) && \(STATE_EV\.has\(e\[0\]\) \|\| fxForm\(e\) !== undefined\);/); + const early = readFileSync(path.join(ROOT, 'public/js/screens/game/early.js'), 'utf8'); + assert.match(early, /const keepEarly = \(e\) => Array\.isArray\(e\) && \(STATE_EV\.has\(e\[0\]\) \|\| fxForm\(e\) !== undefined\);/); assert.match(src, /for \(const e of msg\.ev\) if \(keepEarly\(e\)\) buf\.push\(e\);/); // a new m.field for the field on screen buffers its frames until the enter effect re-enters it (enterBattle resets) assert.match(src, /if \(msg\.fieldId === lastFieldRef\.current\) reentryRef\.current = msg\.fieldId;/); diff --git a/test/ui/playtest6-elite.test.js b/test/ui/playtest6-elite.test.js index edf384c6..95afc3d6 100644 --- a/test/ui/playtest6-elite.test.js +++ b/test/ui/playtest6-elite.test.js @@ -171,6 +171,6 @@ describe('shop bar: the merge tag and the armed card', () => { assert.match(src, /onArm=\$\{setArmedCard\}/, 'ShopBar reports the armed card'); assert.match(src, /mergeTarget\(priv, armedCard\.id, gd\.chess\)/, 'the target comes from gameLogic.mergeTarget'); assert.match(src, /view\.highlightTiles\(mergeAt \? \[\[mergeAt\.row, mergeAt\.col\]\] : \[\], MERGE_HL\)/, 'its own highlight group (cleared with [])'); - assert.match(src, /const MERGE_HL = Object\.freeze\(\{ group: 'mergeTile'/); + assert.match(read('public/js/screens/game/marks.js'), /const MERGE_HL = Object\.freeze\(\{ group: 'mergeTile'/); }); });