ui/phase-27: skip phantom frames during play + freeze final layout
Two more KNNTS041 viewer fixes: 1. Phantom-frame fast-forward. `buildFrames` now flags every frame whose shot landed on an already-empty defender group as `phantom: true`. During play the BattleViewer effect detects a phantom frame and chains a 0 ms timer to the next non-phantom, so streaks of phantoms (the ~30 frames between shots 224 and 255, and the 401..414 stretch) collapse from "the player just mots the timeline" into a single visual tick. Step controls and the scrubber can still land on a phantom deliberately for protocol inspection. 2. Final-frame layout freeze. `displayFrame` derives from the raw `frames[i]` and, on the very last frame when `activeRaceIds` shrinks vs the penultimate frame (the killing blow eliminates a race), substitutes the penultimate's `remaining` and `activeRaceIds` while keeping the current `shotIndex` and `lastAction`. The result: the surviving cluster no longer reflows onto the planet ring on the very last shot — the user sees the killing line + defender flash rendered against the picture they saw a moment earlier. Tests: `phantom-destroy clamp` case extended with `frame.phantom` flag assertions across the protocol; 644 Vitest cases stay green, 4 Playwright `battle-viewer` cases stay green. Docs: `ui/docs/battle-viewer-ux.md` documents the fast-forward behaviour and the final-frame freeze. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -135,14 +135,31 @@ stay drawn so the user can study the current shot.
|
||||
## Phantom destroys
|
||||
|
||||
Legacy emitters (the `dg` engine format that feeds the synthetic-
|
||||
report path) occasionally log more `Destroyed` lines against a
|
||||
ship-group bucket than the bucket's initial population — the
|
||||
emitter keeps recording hits past the moment the group emptied.
|
||||
`buildFrames` clamps each per-group remaining count at zero and
|
||||
only decrements race totals on a real shrink, so a race stays on
|
||||
the scene until its actual ships are gone. The phantom shots still
|
||||
draw a line during the frame they belong to; only the running
|
||||
counters are protected.
|
||||
report path) occasionally log more `Destroyed` (and `Shields`)
|
||||
lines against a ship-group bucket than the bucket's initial
|
||||
population — the emitter keeps recording hits past the moment a
|
||||
group emptied. `buildFrames` marks every such frame as
|
||||
`phantom: true` and skips the race-total decrement so the race
|
||||
stays on the scene until its actual ships are gone.
|
||||
|
||||
During play the BattleViewer fast-forwards through streaks of
|
||||
phantom frames via a 0 ms timer so the user never sees a silent
|
||||
gap (KNNTS041 had ~30 phantom frames between shots 224 and 255
|
||||
right after the last `Nails:pup` died). Step controls and the
|
||||
scrubber can still land on a phantom frame deliberately — useful
|
||||
when inspecting the protocol entry that the engine emitted into
|
||||
the void.
|
||||
|
||||
## Final-frame freeze
|
||||
|
||||
When the last protocol action eliminates a race, the surviving
|
||||
side would otherwise reflow alone to the planet ring at the very
|
||||
last shot — visually jarring and uninformative. `displayFrame`
|
||||
freezes the layout-determining state (`remaining` and
|
||||
`activeRaceIds`) at the penultimate frame's values while keeping
|
||||
the final frame's `shotIndex` and `lastAction`, so the killing
|
||||
shot still renders as a line + flash against the picture the user
|
||||
saw a moment earlier.
|
||||
|
||||
## Header + layout
|
||||
|
||||
|
||||
Reference in New Issue
Block a user