feedback5: merge R19C — GitHub #235: the official 「作战结束」 result box (全员无伤! / 生命值减少 −N) and the three battle-over sounds at settlement, PR #112 by @Convey123 ported onto the 0.2.0 layout (test/ui/gameLogic.test.js: both streams' tests kept); golden unchanged
This commit is contained in:
commit
93a45f733f
21 files changed
+705
-14
No files matched your search
@@ -6,7 +6,8 @@
|
||||
// with the DOM HUD layered on top: top bar, bond strip + popup, team panel (click = g.watch), effects,
|
||||
// shop bar (prep), merge-reward and 机变 overlays, detail panel, enemy / match-info drawer, combat HUD
|
||||
// (DP, "作战结束,等待队友完成作战" + teammates' progress, observing pill, 联防 / 最终攻势 ‹ › camera halves),
|
||||
// phase banners, ticker, emote wheel + bubbles,
|
||||
// phase banners, the round's result box at SETTLE (官方「作战结束」 + 「全员无伤!」 / 「生命值减少 −N」 and the BATTLEOVER sound;
|
||||
// GitHub #235, PR #112), ticker, emote wheel + bubbles,
|
||||
// settings, exit flow. Drag & drop from the view: pieceDrop on a board tile (also the piece's own tile) opens the
|
||||
// direction wheel (ui/facingWheel.js, research 09 §1.2) and its release sends g.move {uid, to, dir} (g.art {…, dir}
|
||||
// for 画卷); pieceDrop on a bench slot → g.move; items → g.equip (confirm when replacing) / g.art. Equipment
|
||||
@@ -65,7 +66,7 @@
|
||||
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from '../../vendor/hooks.module.js';
|
||||
import { PHASE, GEO } from '../../../shared/constants.js';
|
||||
import { html, Spinner, PhaseBanner, Icon, Button, confirmDialog, closeAllDialogs, useTicker } from '../ui/components.js';
|
||||
import { html, Spinner, PhaseBanner, ResultDialog, 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';
|
||||
@@ -93,6 +94,7 @@ import { pauseAvailable, isPaused, frozenNow } from '../ui/matchStatus.js';
|
||||
import { pieceTile } from '../render/drag.js';
|
||||
import {
|
||||
phaseMode, phaseBanner, isCombatPhase, showDeadPill, isBossPhase, placementContext, canPlace, boardTargets, dropIntent,
|
||||
battleOverSfx, uniteResultBox, battleResultBox,
|
||||
snapHud, activeBubbles, shortcutFor, shortcutBlocked, closesOnFieldPress, phaseTotalSeconds, homeFieldId, ownFieldId, normalizeSp, sortedPlayers,
|
||||
terrainInfo,
|
||||
countdownState, shopBlockReason, stageOverrides, effectiveStage, watchTarget, dropFailureReason,
|
||||
@@ -179,6 +181,7 @@ function MatchScreen() {
|
||||
const [holdSeq, setHoldSeq] = useState(0); // bumped when a held piece is released (re-apply the prep state)
|
||||
const [hud, setHud] = useState(null);
|
||||
const [banner, setBanner] = useState(null);
|
||||
const [resultBox, setResultBox] = useState(null); // the round's result box (ResultDialog), shown at SETTLE
|
||||
const [readyBusy, setReadyBusy] = useState(false);
|
||||
const [spBusy, setSpBusy] = useState(null);
|
||||
const [layer, setLayer] = useState('ALL'); // 联防 / 最终攻势 camera: 'L' | 'ALL' | 'R'
|
||||
@@ -233,6 +236,23 @@ function MatchScreen() {
|
||||
uniteLeft: leaker ? uniteRemaining(localLeft, meP?.uniteLeft) : null,
|
||||
});
|
||||
lpBaseRef.current = liveLpNow.base;
|
||||
// what this round's battle cost me, kept while it runs for the result box and the 战斗结束 sound at SETTLE (GitHub
|
||||
// #235, PR #112 by @Convey123): by then the settlement has landed (Match.flush sends m.private before the SETTLE
|
||||
// m.public) and the live count reads 0. A per-round MAXIMUM — leaks only grow within a round, and a frame that lands
|
||||
// after the settlement must not wipe a real count — and only for a player in the round (a spectator seat or an
|
||||
// eliminated player fights nothing and gets no box, ui/gameLogic/phases.js battleResultBox). `unite`: a 联防 ran.
|
||||
const roundLossRef = useRef(null);
|
||||
if (isCombatPhase(phase) && alive) {
|
||||
const prev = roundLossRef.current;
|
||||
const same = !!prev && prev.round === pub?.round;
|
||||
const leaks = ownLeaks(localLeaks, meP?.pendingLp);
|
||||
roundLossRef.current = {
|
||||
round: pub?.round,
|
||||
leaks: same ? Math.max(prev.leaks, leaks) : leaks,
|
||||
cap: gd.config?.lpCapPerRound,
|
||||
unite: phase === PHASE.UNITE || (same && prev.unite),
|
||||
};
|
||||
}
|
||||
|
||||
// latest values for event handlers bound once
|
||||
const live = useRef({});
|
||||
@@ -572,6 +592,7 @@ function MatchScreen() {
|
||||
// phase changes: banners, sounds, resets
|
||||
const phaseKey = `${phase}:${pub?.round}`;
|
||||
const prevPhase = useRef(null);
|
||||
const resultSeq = useRef(0); // re-keys the result box (ResultDialog) at every SETTLE
|
||||
useEffect(() => {
|
||||
const prev = prevPhase.current;
|
||||
prevPhase.current = phase;
|
||||
@@ -585,6 +606,19 @@ function MatchScreen() {
|
||||
else if (phase === PHASE.FINAL_ASSAULT) audio.sfx(solo ? 'bossRoundSingle' : 'bossRoundTeam');
|
||||
else if (phase === PHASE.HIDDEN_CORE) audio.sfx('bossRoundSecret');
|
||||
else if (phase === PHASE.SP_DRAFT) audio.sfx('draft');
|
||||
else if (phase === PHASE.SETTLE) {
|
||||
// the official 战斗结束 sound and result box (GitHub #235, PR #112 by @Convey123; ui/gameLogic/phases.js): the LP this
|
||||
// round cost ME — after a 联防 the authority's own charge (m.public.uniteResult.losses: a leaker pays for the
|
||||
// survivors, not for its own battle's leaks), else the own battle's as kept while it ran (roundLossRef)
|
||||
const kept = roundLossRef.current;
|
||||
roundLossRef.current = null;
|
||||
const cost = kept && kept.round === pub?.round ? kept : null;
|
||||
const sfx = battleOverSfx(cost, pub?.uniteResult?.losses?.[myId]);
|
||||
if (sfx) audio.sfx(sfx);
|
||||
const box = uniteResultBox(pub?.uniteResult, myId) || battleResultBox(cost);
|
||||
resultSeq.current += 1;
|
||||
if (box) setResultBox({ ...box, key: `result:${pub?.round}:${resultSeq.current}` });
|
||||
}
|
||||
// the server resets every watcher to its own field on phase changes — but an eliminated player / spectator seat
|
||||
// keeps the prep board it follows from 回合开始 through 机变 and 休整期 (Match._followScout re-points it only at the
|
||||
// round start): resetting here would fly the camera home and back at every prep phase (item 56)
|
||||
@@ -1373,6 +1407,9 @@ function MatchScreen() {
|
||||
${banner ? html`<${PhaseBanner} key=${banner.key} mode="overlay" title=${banner.title} sub=${banner.sub} micro=${banner.micro}
|
||||
tone=${banner.tone} duration=${banner.duration || 1500} onDone=${() => setBanner(null)} />` : null}
|
||||
|
||||
${resultBox ? html`<${ResultDialog} key=${resultBox.key} title=${resultBox.title} sub=${resultBox.sub} micro=${resultBox.micro}
|
||||
tone=${resultBox.tone} duration=${resultBox.duration} onDone=${() => setResultBox(null)} />` : null}
|
||||
|
||||
${facing && view ? html`<${FacingWheel} key=${`${facing.uid}:${facing.row},${facing.col}`} view=${view} row=${facing.row} col=${facing.col}
|
||||
grid=${facing.grid} name=${facing.name} onPreview=${previewFacing} onCommit=${commitFacing} onCancel=${cancelFacing} />` : null}
|
||||
|
||||
|
||||
Reference in new issue
Block a user