feedback7: §27.51 — two more scratch references replaced by plain wording

This commit is contained in:
sganggs committed 2026-10-08 22:49:06 +08:00
1 parent 115d4d66aa
commit 925f2d836d
1 file changed
+2 -2
+2 -2
View File
@@ -1155,10 +1155,10 @@ players alive when the fight starts; no change.)
- Tests: `test/ui/spectator-roster.e2e.test.js` (3, opt-in `SP_E2E=1` on the mock: no capsule without a spectator; the host's capsule count, roster rows, offline mark, ✕ → the request, the roster / count following the room.state, the last seat takes the capsule and an open roster with it; a non-host sees the roster without ✕, 关闭 / Esc close it), `test/match/lobby-integration.test.js` (+1, the real Match over sockets: the host removes a spectator WHILE the match runs — `room.closed {kicked}`, the other spectator and the host get the new `room.state` with `inMatch`, a second removal / a player / a stranger id → BAD_TARGET, a spectator removing → NOT_HOST, no frame at all reaches the removed socket while the match goes on, the other spectator keeps getting it, the freed seat is taken again — the host-removal test the owner asked for; the server already did all of this). Docs: docs/PLAYING.md §8, docs/design/network.md. - Tests: `test/ui/spectator-roster.e2e.test.js` (3, opt-in `SP_E2E=1` on the mock: no capsule without a spectator; the host's capsule count, roster rows, offline mark, ✕ → the request, the roster / count following the room.state, the last seat takes the capsule and an open roster with it; a non-host sees the roster without ✕, 关闭 / Esc close it), `test/match/lobby-integration.test.js` (+1, the real Match over sockets: the host removes a spectator WHILE the match runs — `room.closed {kicked}`, the other spectator and the host get the new `room.state` with `inMatch`, a second removal / a player / a stranger id → BAD_TARGET, a spectator removing → NOT_HOST, no frame at all reaches the removed socket while the match goes on, the other spectator keeps getting it, the freed seat is taken again — the host-removal test the owner asked for; the server already did all of this). Docs: docs/PLAYING.md §8, docs/design/network.md.
### 27.51 The top-bar capsule counts like the official: resolved = killed + leaked, split / summoned / boss-summoned enemies in neither number (PR #308 by @xcdoge — the capsule half of PR #157; the owner's review of #157 split the bounty / kill-credit half off) — `sim/battle/spawns.js` (`inTotal`), `sim/battle/deploy.js` (`killedInTotal` / `leakedInTotal`), `sim/Battle.js` (`resolved`), `sim/battle/events.js`, `sim/battle/lifecycle.js`, `sim/battle/players.js`, `sim/units.js`, `sim/snapshot.js`, `sim/spec.js` (`battleProgress`, `compactResult`), `match/fields.js` (`timelineSample`, `validateClientResult`), `match/match/{clientCombat,reports,views,unitePhase}.js`, `shared/protocol.js`, `battle/{runner,observe}.js`, `ui/{hud,combatHud}.js`, `ui/gameLogic/format.js` ### 27.51 The top-bar capsule counts like the official: resolved = killed + leaked, split / summoned / boss-summoned enemies in neither number (PR #308 by @xcdoge — the capsule half of PR #157; the owner's review of #157 split the bounty / kill-credit half off) — `sim/battle/spawns.js` (`inTotal`), `sim/battle/deploy.js` (`killedInTotal` / `leakedInTotal`), `sim/Battle.js` (`resolved`), `sim/battle/events.js`, `sim/battle/lifecycle.js`, `sim/battle/players.js`, `sim/units.js`, `sim/snapshot.js`, `sim/spec.js` (`battleProgress`, `compactResult`), `match/fields.js` (`timelineSample`, `validateClientResult`), `match/match/{clientCombat,reports,views,unitePhase}.js`, `shared/protocol.js`, `battle/{runner,observe}.js`, `ui/{hud,combatHud}.js`, `ui/gameLogic/format.js`
- **Seen**: the capsule in the top bar showed `killed / total`: the numerator counted only knock-outs (a leaked enemy was no step), and `total` was raised by every counted enemy the battle spawned at run time (`spawnEnemy`: `if (e.counted && !opts._precounted) this.total++`) — a 磨砻's two 木制瑞印, a summoner's seeds, a boss's 余音 / 妖怪 — so after a split the denominator grew and a round with a summoning leader never filled its capsule. Reproduced on the parent (`w7-uiback-notes/repro-308.mjs`, the snapshot's killed / total): the official example reads 0/3 → 0/3 (a leak is no step) → 1/3 → 2/5 (the splitter's death raised the denominator by its two children); 4 磨砻 killed read 4/12 once their 8 children had spawned, and still 4/12 after the children leaked. - **Seen**: the capsule in the top bar showed `killed / total`: the numerator counted only knock-outs (a leaked enemy was no step), and `total` was raised by every counted enemy the battle spawned at run time (`spawnEnemy`: `if (e.counted && !opts._precounted) this.total++`) — a 磨砻's two 木制瑞印, a summoner's seeds, a boss's 余音 / 妖怪 — so after a split the denominator grew and a round with a summoning leader never filled its capsule. Reproduced on the parent (a repro script, the snapshot's killed / total): the official example reads 0/3 → 0/3 (a leak is no step) → 1/3 → 2/5 (the splitter's death raised the denominator by its two children); 4 磨砻 killed read 4/12 once their 8 children had spawned, and still 4/12 after the children leaked.
- **Official**: the player report relayed in PR #157 / #308 (first-hand, 2026-10-05, the same reading the PR's author re-stated on 2026-10-07): the capsule is 「已解决 / 本回合敌人数」 — 开局 0/3 → 漏一个 1/3 → 打死一个 2/3 → 打死会分裂的 3/3 —, and a split child, a summon (a boss's branch summons, tentacles, shells included) and a part are in neither number while each of them still costs its own LP when it leaks (4 磨砻 killed, their 8 children all leaked: the capsule reads 4/4 and the round charges 8 LP). Neither the data tables nor PRTS (卫戍协议 帮助 / 作战界面, searched) state the counting rule; the research screenshots (docs/research/06 / 09) only show `n/m`. The numbers the PR keeps untouched are the ones the owner has already settled: `counted` (LP charge `min(lpCapPerRound, counted leaks)`, 完美作战, the kill statistics `killed`, the bounties) is exactly 0.2.1's. - **Official**: the player report relayed in PR #157 / #308 (first-hand, 2026-10-05, the same reading the PR's author re-stated on 2026-10-07): the capsule is 「已解决 / 本回合敌人数」 — 开局 0/3 → 漏一个 1/3 → 打死一个 2/3 → 打死会分裂的 3/3 —, and a split child, a summon (a boss's branch summons, tentacles, shells included) and a part are in neither number while each of them still costs its own LP when it leaks (4 磨砻 killed, their 8 children all leaked: the capsule reads 4/4 and the round charges 8 LP). Neither the data tables nor PRTS (卫戍协议 帮助 / 作战界面, searched) state the counting rule; the research screenshots (docs/research/06 / 09) only show `n/m`. The numbers the PR keeps untouched are the ones the owner has already settled: `counted` (LP charge `min(lpCapPerRound, counted leaks)`, 完美作战, the kill statistics `killed`, the bounties) is exactly 0.2.1's.
- **Now**: two counter sets that never cross. `counted` is as before. `inTotal` is the capsule's own set — set on a spawn the battle's spec scheduled (`_queueSpawn`, only when it counts: `countInTotal: false`, `tag` boss / part and data `notCountInTotal` stay out), false for everything a runtime `spawnEnemy` makes (an explicit `inTotal: true` is the content back door); a form change / revive (`setForm`) is the same unit and keeps its flag. `Battle.total` counts the `inTotal` enemies, `killedInTotal` / `leakedInTotal` (battle and per player) count those knocked down / leaked, `resolved = min(total, killedInTotal + leakedInTotal)`. `resolved` rides `b.snap`, `BattleResult` (battle and per player), `b.progress` (a new OPTIONAL field of the C→S frame), `m.public.fields[].progress` (null, never 0, while unknown; a bot / server-run field reads the battle's own counter through the headless timeline sample `[gt, killed, total, resolved(, left)]` — `left` moved to index 4, `unitePhase.js` follows) and the teammates' rows. The HUD reads `resolved ?? killed`, so a server that does not send it shows 0.2.1's numbers. `killed` may now exceed `total` (4 磨砻 killed plus one child reads killed 5, total 4, capsule 4/4): `compactResult` no longer clamps it, the result validation bounds `killed` and the counted-leak count by the spec's content-spawn allowance (`4·spawns + 100`) instead of by `total`, `isPerPlayer` drops `killed ≤ total`. A 联防 field's live counter (`uniteLeft`, the ×N tag) is unchanged; because a spawn no longer moves `total`, the browser runner's change mark for re-reading it (`uniteLeftMark`) adds the length of the enemy list. Removed from the PR: the design-doc line `cf9b19d (…)` in docs/design/network.md — it was a commit subject pasted OVER the `S→C b.end` bullet (the PR's diff deletes that bullet), so the bullet is back. - **Now**: two counter sets that never cross. `counted` is as before. `inTotal` is the capsule's own set — set on a spawn the battle's spec scheduled (`_queueSpawn`, only when it counts: `countInTotal: false`, `tag` boss / part and data `notCountInTotal` stay out), false for everything a runtime `spawnEnemy` makes (an explicit `inTotal: true` is the content back door); a form change / revive (`setForm`) is the same unit and keeps its flag. `Battle.total` counts the `inTotal` enemies, `killedInTotal` / `leakedInTotal` (battle and per player) count those knocked down / leaked, `resolved = min(total, killedInTotal + leakedInTotal)`. `resolved` rides `b.snap`, `BattleResult` (battle and per player), `b.progress` (a new OPTIONAL field of the C→S frame), `m.public.fields[].progress` (null, never 0, while unknown; a bot / server-run field reads the battle's own counter through the headless timeline sample `[gt, killed, total, resolved(, left)]` — `left` moved to index 4, `unitePhase.js` follows) and the teammates' rows. The HUD reads `resolved ?? killed`, so a server that does not send it shows 0.2.1's numbers. `killed` may now exceed `total` (4 磨砻 killed plus one child reads killed 5, total 4, capsule 4/4): `compactResult` no longer clamps it, the result validation bounds `killed` and the counted-leak count by the spec's content-spawn allowance (`4·spawns + 100`) instead of by `total`, `isPerPlayer` drops `killed ≤ total`. A 联防 field's live counter (`uniteLeft`, the ×N tag) is unchanged; because a spawn no longer moves `total`, the browser runner's change mark for re-reading it (`uniteLeftMark`) adds the length of the enemy list. Removed from the PR: the design-doc line `cf9b19d (…)` in docs/design/network.md — it was a commit subject pasted OVER the `S→C b.end` bullet (the PR's diff deletes that bullet), so the bullet is back.
- **Follow-up (the review of this branch, 2026-10-08)**: the per-player counters split an enemy that crosses the midline — `total` and a knock-out go to the spawn owner, a leak to the player whose half it reached (`_recordLeak`) — so in a two-half field (the 联防 lane col 18 → col 2, the boss pair's crossing routes) an enemy that entered one helper's half and leaked on the other's left 0 and 0 in the players' own `min(total, killedInTotal + leakedInTotal)` while the field read 1/1. The finished field's `m.public` progress, which summed those clamps (`validateClientResult` and `_fieldProgress`), then dropped the capsule from 1/1 to 0/1 for the teammates. Reproduced with the real sim (`w7-uiback-notes/repro-midline.mjs`: the unite walk leak, the boss-pair walk; and a 3-human match with four escaped enemies and unarmed helpers — the authority's capsule 4/4, `m.public` 0/4). Now `validateClientResult` keeps the result's field-level `resolved` (the client's `compactResult` already carried `Battle.resolved`; clamped to the field's `total`) and `_fieldProgress` publishes it, for client-run and server-run fields alike (`Battle.result()` has the same field-level number); the players' own numbers are only the fallback of a result with none (an older client, a hand-made result) and with none at all the value stays `null`. The per-player `resolved` of the result is as the PR defined it (clamped to the player's own `total`); no sim change. - **Follow-up (the review of this branch, 2026-10-08)**: the per-player counters split an enemy that crosses the midline — `total` and a knock-out go to the spawn owner, a leak to the player whose half it reached (`_recordLeak`) — so in a two-half field (the 联防 lane col 18 → col 2, the boss pair's crossing routes) an enemy that entered one helper's half and leaked on the other's left 0 and 0 in the players' own `min(total, killedInTotal + leakedInTotal)` while the field read 1/1. The finished field's `m.public` progress, which summed those clamps (`validateClientResult` and `_fieldProgress`), then dropped the capsule from 1/1 to 0/1 for the teammates. Reproduced with the real sim (a repro script: the unite walk leak, the boss-pair walk; and a 3-human match with four escaped enemies and unarmed helpers — the authority's capsule 4/4, `m.public` 0/4). Now `validateClientResult` keeps the result's field-level `resolved` (the client's `compactResult` already carried `Battle.resolved`; clamped to the field's `total`) and `_fieldProgress` publishes it, for client-run and server-run fields alike (`Battle.result()` has the same field-level number); the players' own numbers are only the fallback of a result with none (an older client, a hand-made result) and with none at all the value stays `null`. The per-player `resolved` of the result is as the PR defined it (clamped to the player's own `total`); no sim change.
- [ASSUMED]: that a boss's / summoner's runtime spawns follow the same reading as a split child (the report names the splits and the boss summons together); that a 变身 / 重生 of the same unit stays one enemy. - [ASSUMED]: that a boss's / summoner's runtime spawns follow the same reading as a split child (the report names the splits and the boss summons together); that a 变身 / 重生 of the same unit stays one enemy.
- Golden: only `total`, `players.p1.total` and the snapshot hashes `snaps` (a snapshot carries `total`) move, in exactly the scenarios whose battle spawns an enemy at run time; every battle outcome (killed, kills, leaks, coins, units, enemies, hooks, events, time, rng draws) is bit-identical — roster 23 of 49 (roster-002 003 007 008 009 010 011 012 013 015 017 018 020 023 024 025 026 027 037 045 047 048 049), bonds 2 (bond-maniShip-low, bond-soloShip-high), fields 11 (boss-boss_1-pair / -solo, boss-boss_3-pair / -solo, boss-boss_5-pair / -solo, boss-boss_6-solo, hidden-boss_8-pair / -solo, hidden-boss_10-pair / -solo), diy 11 (diy-036 040 056 062 068 073 083 091 095 114 115), matches 12 of 18 (solo-FUNNY-1, solo-NORMAL-1, solo-HARD-1, solo-ABYSS-1, solo-ABYSS-2, coop2-NORMAL-3, coop2-HARD-4, coop3-ABYSS-5, coop4-ABYSS-7, coop2-NORMAL-8-serverrun, solo-HARD-9-boosted, coop2-NORMAL-70-diy: 36 player-round rows, the `total` column only — a round that read 53 / 53 now reads 53 / 16 —; the end results, LP, shop and bot RNG draws are identical), standins unchanged. The follow-up above moves nothing (it reads a number the result already carries). - Golden: only `total`, `players.p1.total` and the snapshot hashes `snaps` (a snapshot carries `total`) move, in exactly the scenarios whose battle spawns an enemy at run time; every battle outcome (killed, kills, leaks, coins, units, enemies, hooks, events, time, rng draws) is bit-identical — roster 23 of 49 (roster-002 003 007 008 009 010 011 012 013 015 017 018 020 023 024 025 026 027 037 045 047 048 049), bonds 2 (bond-maniShip-low, bond-soloShip-high), fields 11 (boss-boss_1-pair / -solo, boss-boss_3-pair / -solo, boss-boss_5-pair / -solo, boss-boss_6-solo, hidden-boss_8-pair / -solo, hidden-boss_10-pair / -solo), diy 11 (diy-036 040 056 062 068 073 083 091 095 114 115), matches 12 of 18 (solo-FUNNY-1, solo-NORMAL-1, solo-HARD-1, solo-ABYSS-1, solo-ABYSS-2, coop2-NORMAL-3, coop2-HARD-4, coop3-ABYSS-5, coop4-ABYSS-7, coop2-NORMAL-8-serverrun, solo-HARD-9-boosted, coop2-NORMAL-70-diy: 36 player-round rows, the `total` column only — a round that read 53 / 53 now reads 53 / 16 —; the end results, LP, shop and bot RNG draws are identical), standins unchanged. The follow-up above moves nothing (it reads a number the result already carries).
- Tests: `test/sim/hud-capsule-resolved.test.js` (5: the official 0/3 → 3/3 sequence; 4 磨砻 + their 8 leaked children — 4/4, 8 counted leaks, charge min(10, 8), 完美作战 broken; a killed / leaked child moves nothing, `killed` +1; 枯朽萃聚使徒's seeds; a real boss round — 假想敌:管's 余音 summons — fills to resolved === total; `inTotal` flags), `test/match/hud-capsule-resolved.test.js` (10: a bot field reads the battle's own counter, a leak-only bot field, `resolved` null vs a reported 0, a finished field's reported number, the 联防 change mark; and the five of the follow-up, all failing before it — a 3-human match whose 联防 field runs the real sim with unarmed helpers: four escaped enemies cross the midline, the authority's capsule 4/4 and `m.public` 4/4 not 0/4; the boss pair's crossing route; a server-run 联防 field and its timeline; `validateClientResult`'s field-level rule — clamp, junk, fallback, absent; `_fieldProgress`'s precedence incl. a synthetic result), and the PR's updates of `clientCombat` / `observe` / `playtest6-matchflow` / `fuzz` / `fidelity` / `gameLogic` / `docs-consistency` / the battle harness invariants. Docs: docs/SIM.md §1.3 / §9, docs/design/network.md (顶栏胶囊), docs/PLAYING.md. - Tests: `test/sim/hud-capsule-resolved.test.js` (5: the official 0/3 → 3/3 sequence; 4 磨砻 + their 8 leaked children — 4/4, 8 counted leaks, charge min(10, 8), 完美作战 broken; a killed / leaked child moves nothing, `killed` +1; 枯朽萃聚使徒's seeds; a real boss round — 假想敌:管's 余音 summons — fills to resolved === total; `inTotal` flags), `test/match/hud-capsule-resolved.test.js` (10: a bot field reads the battle's own counter, a leak-only bot field, `resolved` null vs a reported 0, a finished field's reported number, the 联防 change mark; and the five of the follow-up, all failing before it — a 3-human match whose 联防 field runs the real sim with unarmed helpers: four escaped enemies cross the midline, the authority's capsule 4/4 and `m.public` 4/4 not 0/4; the boss pair's crossing route; a server-run 联防 field and its timeline; `validateClientResult`'s field-level rule — clamp, junk, fallback, absent; `_fieldProgress`'s precedence incl. a synthetic result), and the PR's updates of `clientCombat` / `observe` / `playtest6-matchflow` / `fuzz` / `fidelity` / `gameLogic` / `docs-consistency` / the battle harness invariants. Docs: docs/SIM.md §1.3 / §9, docs/design/network.md (顶栏胶囊), docs/PLAYING.md.