diff --git a/docs/history/0.2.0.md b/docs/history/0.2.0.md index b84ed00d..b3e697c1 100644 --- a/docs/history/0.2.0.md +++ b/docs/history/0.2.0.md @@ -9,12 +9,14 @@ later merges of feedback4 brought the rest of 0.1.4 in. The workstreams, each on after review with its golden scenarios named: R1–R6 (refactor, tooling), W5A (golden results), W5B / W5D / W5G / K1–K3 / W5N (补位), W5H / W5K / O1–O24 (自选编队 and its operator kits), W5I / W5S (languages, phases 1 and 2), W5C / W5E / W5F / W5J / W5L (fidelity, wave 1), WE1 / WE2 (the follow-ups the kit waves reported), W5M, W5R and the feedback4 -merges (the 0.1.4 ports), W5V (the owner's items of 2026-10-06), W5U (packaging), W5T (the docs) and W5Y (round 18: -three GitHub issues and three community PRs). Each rule was -checked against the official data and PRTS; anything no source backs is marked [ASSUMED]. +merges (the 0.1.4 ports), W5V (the owner's items of 2026-10-06), W5U (packaging), W5T (the docs), W5Y (round 18: +three GitHub issues and three community PRs), W5AA and CB2 / CB4 / CB5 (the community reports of 2026-10-06), W5AC +(rebindable shortcuts) and W5AD (language packs). Each rule was checked against the official data and PRTS; anything no +source backs is marked [ASSUMED]. The normative sections were rewritten with the streams: §0 (the DIY slots), §2 (the layout), §5.5 (the enemy damage -frame, the block-count target rule), §5.6 (the trigger rules), §8.1 / §8.3 (`welcome.diyKitted`, `standIns`, `diy`, -`diyBanned`), §16 (干员持有, 自选编队). Where each is handled: refactor and tooling → §25.1; Chinese and English → +frame, the block-count target rule, 自制投石机's 索敌不受阻挡影响), §5.6 (the trigger rules), §8.1 / §8.3 +(`welcome.diyKitted`, `standIns`, `diy`, `diyBanned`), §10 (the rebindable shortcuts), §16 (干员持有, 自选编队). Where each +is handled: refactor and tooling → §25.1; Chinese and English → §25.2; 补位 → §25.3; 自选编队 and the 71 operator kits → §25.4; trigger rules (the larger-range rule, AUTO skills) → §25.5; fidelity fixes of wave 1 (GitHub #41, #49, #82, #96 / #137, #107, #116; 伺夜's 狼群; Touch) → §25.6; WE1, sim rules (GitHub #181, #187 / #170, follow-ups) → §25.7; WE2, data / match / UI (follow-ups, GitHub #148) → §25.8; the @@ -25,17 +27,19 @@ settled (泥岩's 闭锁; 忍冬, 深靛, 阿) → §25.11; release packaging an with no Spine model) → §25.12; round 18 — 拟态物质 at the copy cap (GitHub #207), 华法琳's 7 / 14 (GitHub #175, PR #192), a merged elite 拉普兰德 as a new one (GitHub #169), the leader pool per player alive (PR #209), the flying lift and two model quirks (PR #211) → §25.13; the community reports of 2026-10-06 (the 孤岛风云 prisoners drawn freed, 忍冬's trigger, a -bounty burned to death by the 活性源石, 枯朽之种 paying a bounty) → §25.14; the second batch of community reports of -2026-10-06, stream CB5 (match flow and UI: a module's 特性追加 line after the class trait, the new shop slot filled at the -upgrade, an eliminated player following a player through every round, the boss field and its leader in a watcher's boss -prep, the pair together in the boss prep with the official green frames; PRs #189 / #191 reused, not merged; the -battle-history and hotkey requests left to the owner) → §25.19; the community reports of 2026-10-06, stream CB2 (healers, summons, timers: 刺玫's S2 heals, 琳琅诗怀雅 healing herself and her 破财消灾 cost after a -redeploy, 赫默's drone at full SP, the countdown summons' 无敌 / 禁疗 / timer bar, stacked alchemy zones; the chain heal, -DoT damage, 伺夜's 狼影 and the 吟游者 trait answered) → §25.16; the community reports of 2026-10-06, stream CB4 (enemies, bosses, waves, movement: the 突袭 landing on the 临时整备区 row and the other half of the -联防 map, a silenced 萨卡兹枯朽战士's death pollution, 萨卡兹穿刺手's speed after a stun and its first hit, 自制投石机 blocked on the 排气格栅; the briefing types' -7–9 waves, 新硎's 断刃, a freed prisoner in 联防 and 鸭爵's two swaps in one wave already answered; the moving leaders' routes -checked; 野鬃, 锏's pulls, 昆图斯's footprint and the leaders' attack rate not reproduced) → §25.18; the rebindable shortcuts -→ §25.15; language packs, the first type of content packs (a language is a file in the language folder) → §25.16. +bounty burned to death by the 活性源石, 枯朽之种 paying a bounty) → §25.14; the rebindable shortcuts → §25.15; the +second batch of community reports of 2026-10-06, one section per stream: CB2, healers, summons, timers (刺玫's S2 heals, +琳琅诗怀雅 healing herself and her 破财消灾 cost after a redeploy, 赫默's drone at full SP, the countdown summons' 无敌 / +禁疗 / timer bar, stacked alchemy zones; the chain heal, DoT damage, 伺夜's 狼影 and the 吟游者 trait answered) → §25.16; +CB4, enemies, bosses, waves, movement (the 突袭 landing on the 临时整备区 row and the other half of the 联防 map, a +silenced 萨卡兹枯朽战士's death pollution, 萨卡兹穿刺手's speed after a stun and its first hit, 自制投石机 blocked on the +排气格栅; the briefing types' 7–9 waves, 新硎's 断刃, a freed prisoner in 联防 and 鸭爵's two swaps in one wave already +answered; the moving leaders' routes checked; 野鬃, 锏's pulls, 昆图斯's footprint and the leaders' attack rate not +reproduced) → §25.18; CB5, match flow and UI (a module's 特性追加 line after the class trait, the new shop slot filled at +the upgrade, an eliminated player following a player through every round, the boss field and its leader in a watcher's +boss prep, the pair together in the boss prep with the official green frames; PRs #189 / #191 reused, not merged; the +battle-history and hotkey requests left to the owner) → §25.19; language packs, the first type of content packs (a +language is a file in the language folder) → §25.20. Decisions of the owner: 2026-10-04 — the server stays on plain `node:http` + `ws`, no web framework (§25.1); 2026-10-05 — the 补位 plan (§25.3); the 自选编队 rules, the collab operators out of the pool, ISW-A refused (§25.4); @@ -48,8 +52,8 @@ a melee 突袭 member lands where it can block (§25.11); 华法琳's granted tr lock (§25.13.2); a newly merged elite 拉普兰德 counts as a new 拉普兰德 (§25.13.3); the leader pool = bloodPoint × the players alive at the fight's start, adopting PR #209 and replacing 「保持固定血量」 (§25.13.4); flying units at 1.3 tiles and the two model quirks of PR #211 (§25.13.5); the bounty pays whenever its enemy dies (relayed with the community -reports, §25.14.3); 2026-10-07 — a language is a file in the language folder, and the language pack is the first type -of a general content-pack mechanism (§25.16). +reports, §25.14.3); 2026-10-07 — the in-match shortcuts rebindable in 设置 (§25.15); a language is a file in the +language folder, and the language pack is the first type of a general content-pack mechanism (§25.20). ### 25.1 Refactor and tooling: the big files split, golden results, lint, types, import boundaries (refactor plan; owner's decision of 2026-10-04: no web framework) — `server/http/`, `server/match/match/`, `server/match/player/`, `server/sim/battle/`, `content/enemies/`, `kits/ops/`, `public/js/ui/gameLogic/`, `render/fx/`, `render/app/`, `screens/game/`, `tools/golden.mjs`, `tools/check-imports.mjs`, `eslint.config.js`, `jsconfig.json` @@ -1104,120 +1108,6 @@ fixed (W5AA's commits name every golden scenario moved); two were already fixed keyboard only in the mock match — Q then does nothing, W retreats, the choice survives a reload; phone width). Docs: docs/PLAYING.md §11, README 操作, DESIGN §10 (design/client.md). -### 25.19 Community reports of 2026-10-06, stream CB5: match flow and UI — `ui/loadoutModel.js traitLines`, `player/economy.js _openLevelSlots`, `match/watch.js _followScout`, `match/views.js prepFieldMeta / bossMateView`, `render/app.js syncMates`, `ui/gameLogic/camera.js teamFrameIds` - -CB5 took the items about the match flow and the screens (16.2, 19, 51, 55, 56 of the list; 22 and 48 are features the -owner has not decided — what each would take is at the end). Each was reproduced first (a script, or the real server in -headless Chrome for the screens), checked against the official data, PRTS and the official tutorial pages, then fixed in -one commit with its tests; two open community PRs were reused without merging them (§25.19.6). - -#### 25.19.1 「干员调配界面很多干员的某个模组只有额外特性没有基础特性,不清楚是文本错误还是实际效果也有错」 (item 16.2): text only — the module's added line now follows the class trait — `ui/loadoutModel.js traitLines / fullTraitText`, `ui/detailPanel.js traitText`, `screens/loadout.js ModuleInfo`, `screens/diy.js ModuleRow` - -- **Seen**: 114 of the 164 modules the 干员调配 screen offers (128 of all 184 elite module records) showed under 特性 only - the module's extra line — 隐现's MAR-X read 「攻击空中单位时攻击力提升至110%」 without 「优先攻击空中单位」; 局内数值, the - in-match detail card and the 自选 module row did the same. -- **Official**: a module's trait candidate either rewrites the class trait (`overrideDescripton`) or adds a line after it - (`additionalDescription`); PRTS flags the latter 「特性追加」 on every such module (e.g. 伊芙利特 BLA-X / BLA-Δ, 玛恩纳 - LIB-X, 莫斯提马 SPC-X / SPC-Y). -- **Cause**: the data keeps both lines (`traitOverride.desc` = the class trait or its rewrite, `moduleDesc` = the added - line); the screens showed `moduleDesc` instead of `desc` (since 0.1.2: the review of GitHub #64 in §22 picked the - module line over a cloned record's class trait). The battle was right: for every added-line module the unit keeps the class - trait — the same trait text, targeting, attack shape and trait blackboard as 不装备 (圣约送葬人 REA-Y also rewrites it). -- **Now**: `traitLines` / `fullTraitText`: the class trait (or the rewrite), then the added line; the module card shows - them as 特性 and 特性追加 (en "Added Trait"). -- Golden: unchanged (no sim change). Tests: `test/ui/feedback5-module-trait.test.js` (6; the text cases fail on the - parent, the data and sim pins pass there), `test/ui/loadout-stats.test.js` updated; SP_E2E `loadout.e2e` 9 / 9. - Docs: docs/DATA.md (`trait`, `traitOverride`). - -#### 25.19.2 「升级商店获得新的商店位时用新卡补上,而不是空着」 (item 19): the upgrade opens the new slot with a new card — `player/economy.js _openLevelSlots`, `audit.js` - -- **Seen**: a level-up from 1 to 2 (险境: 3 → 4 operator slots) or 3 to 4 (4 → 5; 标准 5 → 6) showed no new card until the - next refresh or round start (the shop kept its rolled layout; META listed it as an assumption). -- **Official**: the tutorial's 休整期 page (local guide art `autochess_home_4`): 「升级:消耗资金升级调度中心。升级后将出现更多 - 的商品栏位、可调度干员以及新装备」; PRTS 帮助 「消耗资金可以为调度中心升级,从而增加刷新栏位」. -- **Now**: `levelUp` → `_openLevelSlots`: the new level's extra slots are appended to the operator slots at once (the - item slot stays after them), each drawn at the new level; the cards shown stay in place, a bought card stays sold. - `audit.js` checks the count and the kept cards (`matchrun --check`: no violation). -- [ASSUMED]: the new card follows the freeze toggle, as a manual refresh's cards do. -- Golden: matches — all 18 scenarios (every bot levels its shop and now sees, and draws the shop RNG for, the new card at - once); the battle families unchanged. Tests: `test/match/feedback5-shop-levelslots.test.js` (3, all fail on the - parent). Docs: docs/META.md §3 / §7, docs/PLAYING.md. - -#### 25.19.3 「死亡观战时每一回合结束都会被转回自己家」 (item 56): an eliminated player follows a player through every phase reset — `match/watch.js watchPref / _watchTargetOf / _followScout`, `battle/observe.js followedScout` - -- **Seen** (real server, headless Chrome): every round start cleared an eliminated player's watch — in 回合开始 / 机变 / - 休整期 it saw its own empty board, each battle started on the first field; a spectator seat asked for the first player - itself. -- **Official**: research 09 §3.1 — after elimination the client keeps observing (self_dead_dialog KEEP_WATCH, the - server's DeadAutoObDn { obIndex, state, preparation }, FindFirstAvailObTarget). -- **Now**: `Match.watchPref` = the player of the viewer's last manual `g.watch` (a prep scout, a single-player field, or - — new, optional — `g.watch { playerId }` naming the row tapped on a shared 联防 / boss pair field); `_watchTargetOf` = that - player while still in, else the first player still in (seat order, AI seats included). The round start pushes its prep - board (`_followScout`, kept through 机变 and 休整期), both combat modes and the boss fields start on its field, a resync or - a spectator joining in prep gets the board, a followed player who quits hands over at once. The screen adopts the pushed - board like a 前往查看 tap (`followedScout`; PR #189's stale-field fix) and keeps it through the prep phases; 返回自己 asks - the server for nothing (the viewer has no board). -- Golden: unchanged. Tests: `test/match/feedback5-watch-follow.test.js` (9; 8 fail on the parent's server), - `test/match/spectator.test.js` updated. Docs: design/network.md, docs/PLAYING.md. - -#### 25.19.4 「死亡后观战视角看不到场上的关底boss」 (item 55): a watcher of a boss round's prep sees the boss field and its leader — `match/views.js prepFieldMeta`, `screens/game.js` - -- **Seen** (headless Chrome, the guest eliminated at the boss round): in the Final Assault fight the watched boss field - showed the leader (enemy_9013_acstmk at 10,3); in the boss round's prep the guest saw its own empty normal board (camera - 'prep', no leader) and a scout of a teammate was the normal board too — while the players prepare on their half of the - boss field with the leader standing there (§23.18, community report #12). -- **Now**: in a boss round's prep the scout is built on the boss field — `kind: 'boss'`, `BOSS_RECT`, the scouted player's - pieces through `bossFieldPlacement` (rows − 7, the right half mirrored with RIGHT ↔ LEFT), `side`, the leader's spawn in - `nextEnemies` — and framed by the boss-field prep camera of that side, which shows the leader. With §25.19.3 the - eliminated player gets it at the round start; the same run after the fix: field 'boss', camera bossPrep, leader shown. -- Golden: unchanged. Tests: `test/match/feedback5-boss-prep-scout.test.js` (2, fail on the parent); SP_E2E - `test/ui/feedback5-boss-pair.e2e.test.js` (item 55 case). Docs: design/network.md, design/client.md §15. - -#### 25.19.5 「还原原版最终boss战准备阶段:像战斗中那样把会成一组的两个玩家放在一起…同组两个玩家屏幕左边的头像也像原版那样用绿框框起来」 (item 51): the pair together in the boss prep, the official green frames — `match/views.js bossMateView`, `match/messaging.js markPrivate`, `render/app.js syncMates`, `ui/teamPanel.js`, `ui/gameLogic/camera.js teamFrameIds` - -- **Seen**: the own half was already the official one — the right-hand player on the right half with the - right_boss_*_camera_param camera, its layout mirrored col c → 20 − c with RIGHT ↔ LEFT, so its operators face the leader - as before (research 09 §1.2, §15) — but the partner's half stood empty and nothing framed the pair. -- **Official**: every client knows every board (ChangePositionDn { boardStatus }, research 09); the team list frames the - co-op group (the owner's recall; the client's ui/battle `bg_team_border`, a white 100×100 frame with its glow, and the - autochess green of `icon_self`, #4ed8af). -- **Now**: in a boss round's prep `m.private.bossMate { playerId, side, units }` carries the partner's board on its half, - placed as the battle will (bossFieldPlacement); a partner's change marks the player's m.private too (`markPrivate`), so - it follows live; the own prep view draws those units read-only ('m:' views, never picked or dragged) and lights the - whole boss field; a scout of either player shows both halves (`mate`). The team panel frames the pair's two avatars - (`teamFrameIds`: the players still in by seat, two by two, as `prepCamera`; the boss field in the fight) from the round - start through the fight, with the official sprite tinted green (a plain ring without the local art); a lone player, an - eliminated player and a spectator seat get none; 联防 is not framed (not asked). -- [ASSUMED]: the partner's bench is not shown (its board only); the frame's exact official geometry (one frame per avatar). -- Golden: unchanged. Tests: `test/match/feedback5-boss-pair-prep.test.js` (4, fail on the parent), SP_E2E - `test/ui/feedback5-boss-pair.e2e.test.js` (both halves, the mirrored right half, the frames in prep and fight). - Docs: design/client.md §10 / §15, design/network.md, docs/PLAYING.md. - -#### 25.19.6 Overlap with the open community PRs #189 and #191 (not merged; their authors' fixes were pending) - -- PR #189 (watch preference, @2321Robin) — §25.19.3 builds the same chain (a manual watch is the preference; round start, - both combat modes, the boss fields and the resync follow it; the stale-field fix of the adopted scout), on the 0.2.0 - method containers. Differences: the fallback is the first player still in with AI seats included (a lone human with AI - teammates had nobody to follow — the report's case), and a shared field records the `playerId` the row tap sends (the - review's point 2) instead of nothing. -- PR #191 (same-field frames, issue #190) — §25.19.5 frames the pair as the owner asked: green, the official sprite, both - avatars (self included), the boss rounds only; the pairing is read from m.public as the prep camera already does, so no - `bossPairing` field; #191's 联防 frames and the phase banner naming the partner are not built. If the owner still wants - them, they can go on top of this. - -#### 25.19.7 Not built — what the two feature requests would take (the owner has not decided) - -- **22 战绩 / 分队胜率**: the result is already complete (`m.result`: per-player stats, lineup, band, bonds, titles, rounds - passed, victory — `server/match/results.js`). With no accounts the simplest is per browser: append a compact record at - `m.result` to localStorage (capped, e.g. the last 200; only matches of ≥ 2 rounds, as asked), a 战绩 screen in the lobby - (list, win rate per strategy / core bond / mode and difficulty, 导出 / 导入 like the loadout), tests and i18n — no - server change. Server-side history needs persistence and an identity the remake does not have. Decisions: browser-only - or not; what 「分队」 groups by (strategy, core bond, lineup); the sample cap. -- **48 自定义快捷键**: the keys are fixed in `ui/gameLogic/shortcuts.js shortcutFor` (R 刷新, F 冻结, D 升级, Q 撤退, X 出售, - Space 准备 / 暂停, Esc). It would take a 快捷键 section in 设置 (capture a key per action, conflicts refused, 恢复默认), - a sanitised map in the settings (`sanitizeSettings`), `shortcutFor` reading it, every `` hint and tooltip reading it - too, i18n and tests. Decision: whether to allow it at all (the official PC client's keys are fixed as far as the data - shows: `pc_key_holder` hints). ### 25.16 Community reports of 2026-10-06, stream CB2: healers, summons, timers — `kits/ops/chess_char_1_06-vendla.js`, `chess_char_3_04-swire2.js`, `chess_char_2_02-silent.js`, `content/tokens.js COUNTDOWN_SUMMONS / startCountdown`, `sim/snapshot.js unitTuple`, `render/fx/zones.js zoneAlpha` The owner's second collection of community reports (2026-10-06/07) was split over five streams; CB2 took the healers, @@ -1313,6 +1203,7 @@ the evidence. trait is 生命回复速度, which 禁疗 and 无法被友方治疗 do not stop (§25.6.8): 狼群 440 → 532 and 斥罪 1480 → 1618 HP in 3 s under 魔王 (pinned by `test/content/feedback5-regen.test.js`); 孤立 units stay out (PRTS 孤立). The countdown summons cannot be refilled since §25.16.5. + ### 25.18 Community reports of 2026-10-06, stream CB4: enemies, bosses, waves, movement — `sim/battle/players.js onOwnBoard`, `content/bonds/addon/battle.js raidTile`, `content/enemies/archetypes.js kitPolluted`, `content/enemies/special.js kitRush`, `targeting.js canTargetAlly` The owner's second collection of community reports (2026-10-06/07) was split over five streams; CB4 took the items about @@ -1507,7 +1398,122 @@ briefing types' share of the waves). 卢西恩 attacks only its blocker and blinks past it. A leader and a situation from the reporter would show what differs. - Golden: unchanged. Pinned: `test/sim/feedback5-cb4-pins.test.js` (the four interval leaders at ≥ max − 1 attacks). -### 25.16 Language packs: a language is a file in the language folder, the first type of content packs (W5AD; the owner's decisions of 2026-10-07) — `shared/packs.js`, `shared/i18nPacks.js`, `shared/i18n.js`, `shared/i18nData.js`, `server/packs.js`, `server/http/static.js`, `public/js/ui/lang.js`, `public/js/data.js`, `tools/i18n.mjs`, `tools/packs.mjs`, `tools/build-i18n.mjs`, `tools/package.mjs`, docs/I18N.md §5, docs/PACKS.md +### 25.19 Community reports of 2026-10-06, stream CB5: match flow and UI — `ui/loadoutModel.js traitLines`, `player/economy.js _openLevelSlots`, `match/watch.js _followScout`, `match/views.js prepFieldMeta / bossMateView`, `render/app.js syncMates`, `ui/gameLogic/camera.js teamFrameIds` + +CB5 took the items about the match flow and the screens (16.2, 19, 51, 55, 56 of the list; 22 and 48 are features the +owner has not decided — what each would take is at the end). Each was reproduced first (a script, or the real server in +headless Chrome for the screens), checked against the official data, PRTS and the official tutorial pages, then fixed in +one commit with its tests; two open community PRs were reused without merging them (§25.19.6). + +#### 25.19.1 「干员调配界面很多干员的某个模组只有额外特性没有基础特性,不清楚是文本错误还是实际效果也有错」 (item 16.2): text only — the module's added line now follows the class trait — `ui/loadoutModel.js traitLines / fullTraitText`, `ui/detailPanel.js traitText`, `screens/loadout.js ModuleInfo`, `screens/diy.js ModuleRow` + +- **Seen**: 114 of the 164 modules the 干员调配 screen offers (128 of all 184 elite module records) showed under 特性 only + the module's extra line — 隐现's MAR-X read 「攻击空中单位时攻击力提升至110%」 without 「优先攻击空中单位」; 局内数值, the + in-match detail card and the 自选 module row did the same. +- **Official**: a module's trait candidate either rewrites the class trait (`overrideDescripton`) or adds a line after it + (`additionalDescription`); PRTS flags the latter 「特性追加」 on every such module (e.g. 伊芙利特 BLA-X / BLA-Δ, 玛恩纳 + LIB-X, 莫斯提马 SPC-X / SPC-Y). +- **Cause**: the data keeps both lines (`traitOverride.desc` = the class trait or its rewrite, `moduleDesc` = the added + line); the screens showed `moduleDesc` instead of `desc` (since 0.1.2: the review of GitHub #64 in §22 picked the + module line over a cloned record's class trait). The battle was right: for every added-line module the unit keeps the class + trait — the same trait text, targeting, attack shape and trait blackboard as 不装备 (圣约送葬人 REA-Y also rewrites it). +- **Now**: `traitLines` / `fullTraitText`: the class trait (or the rewrite), then the added line; the module card shows + them as 特性 and 特性追加 (en "Added Trait"). +- Golden: unchanged (no sim change). Tests: `test/ui/feedback5-module-trait.test.js` (6; the text cases fail on the + parent, the data and sim pins pass there), `test/ui/loadout-stats.test.js` updated; SP_E2E `loadout.e2e` 9 / 9. + Docs: docs/DATA.md (`trait`, `traitOverride`). + +#### 25.19.2 「升级商店获得新的商店位时用新卡补上,而不是空着」 (item 19): the upgrade opens the new slot with a new card — `player/economy.js _openLevelSlots`, `audit.js` + +- **Seen**: a level-up from 1 to 2 (险境: 3 → 4 operator slots) or 3 to 4 (4 → 5; 标准 5 → 6) showed no new card until the + next refresh or round start (the shop kept its rolled layout; META listed it as an assumption). +- **Official**: the tutorial's 休整期 page (local guide art `autochess_home_4`): 「升级:消耗资金升级调度中心。升级后将出现更多 + 的商品栏位、可调度干员以及新装备」; PRTS 帮助 「消耗资金可以为调度中心升级,从而增加刷新栏位」. +- **Now**: `levelUp` → `_openLevelSlots`: the new level's extra slots are appended to the operator slots at once (the + item slot stays after them), each drawn at the new level; the cards shown stay in place, a bought card stays sold. + `audit.js` checks the count and the kept cards (`matchrun --check`: no violation). +- [ASSUMED]: the new card follows the freeze toggle, as a manual refresh's cards do. +- Golden: matches — all 18 scenarios (every bot levels its shop and now sees, and draws the shop RNG for, the new card at + once); the battle families unchanged. Tests: `test/match/feedback5-shop-levelslots.test.js` (3, all fail on the + parent). Docs: docs/META.md §3 / §7, docs/PLAYING.md. + +#### 25.19.3 「死亡观战时每一回合结束都会被转回自己家」 (item 56): an eliminated player follows a player through every phase reset — `match/watch.js watchPref / _watchTargetOf / _followScout`, `battle/observe.js followedScout` + +- **Seen** (real server, headless Chrome): every round start cleared an eliminated player's watch — in 回合开始 / 机变 / + 休整期 it saw its own empty board, each battle started on the first field; a spectator seat asked for the first player + itself. +- **Official**: research 09 §3.1 — after elimination the client keeps observing (self_dead_dialog KEEP_WATCH, the + server's DeadAutoObDn { obIndex, state, preparation }, FindFirstAvailObTarget). +- **Now**: `Match.watchPref` = the player of the viewer's last manual `g.watch` (a prep scout, a single-player field, or + — new, optional — `g.watch { playerId }` naming the row tapped on a shared 联防 / boss pair field); `_watchTargetOf` = that + player while still in, else the first player still in (seat order, AI seats included). The round start pushes its prep + board (`_followScout`, kept through 机变 and 休整期), both combat modes and the boss fields start on its field, a resync or + a spectator joining in prep gets the board, a followed player who quits hands over at once. The screen adopts the pushed + board like a 前往查看 tap (`followedScout`; PR #189's stale-field fix) and keeps it through the prep phases; 返回自己 asks + the server for nothing (the viewer has no board). +- Golden: unchanged. Tests: `test/match/feedback5-watch-follow.test.js` (9; 8 fail on the parent's server), + `test/match/spectator.test.js` updated. Docs: design/network.md, docs/PLAYING.md. + +#### 25.19.4 「死亡后观战视角看不到场上的关底boss」 (item 55): a watcher of a boss round's prep sees the boss field and its leader — `match/views.js prepFieldMeta`, `screens/game.js` + +- **Seen** (headless Chrome, the guest eliminated at the boss round): in the Final Assault fight the watched boss field + showed the leader (enemy_9013_acstmk at 10,3); in the boss round's prep the guest saw its own empty normal board (camera + 'prep', no leader) and a scout of a teammate was the normal board too — while the players prepare on their half of the + boss field with the leader standing there (§23.18, community report #12). +- **Now**: in a boss round's prep the scout is built on the boss field — `kind: 'boss'`, `BOSS_RECT`, the scouted player's + pieces through `bossFieldPlacement` (rows − 7, the right half mirrored with RIGHT ↔ LEFT), `side`, the leader's spawn in + `nextEnemies` — and framed by the boss-field prep camera of that side, which shows the leader. With §25.19.3 the + eliminated player gets it at the round start; the same run after the fix: field 'boss', camera bossPrep, leader shown. +- Golden: unchanged. Tests: `test/match/feedback5-boss-prep-scout.test.js` (2, fail on the parent); SP_E2E + `test/ui/feedback5-boss-pair.e2e.test.js` (item 55 case). Docs: design/network.md, design/client.md §15. + +#### 25.19.5 「还原原版最终boss战准备阶段:像战斗中那样把会成一组的两个玩家放在一起…同组两个玩家屏幕左边的头像也像原版那样用绿框框起来」 (item 51): the pair together in the boss prep, the official green frames — `match/views.js bossMateView`, `match/messaging.js markPrivate`, `render/app.js syncMates`, `ui/teamPanel.js`, `ui/gameLogic/camera.js teamFrameIds` + +- **Seen**: the own half was already the official one — the right-hand player on the right half with the + right_boss_*_camera_param camera, its layout mirrored col c → 20 − c with RIGHT ↔ LEFT, so its operators face the leader + as before (research 09 §1.2, §15) — but the partner's half stood empty and nothing framed the pair. +- **Official**: every client knows every board (ChangePositionDn { boardStatus }, research 09); the team list frames the + co-op group (the owner's recall; the client's ui/battle `bg_team_border`, a white 100×100 frame with its glow, and the + autochess green of `icon_self`, #4ed8af). +- **Now**: in a boss round's prep `m.private.bossMate { playerId, side, units }` carries the partner's board on its half, + placed as the battle will (bossFieldPlacement); a partner's change marks the player's m.private too (`markPrivate`), so + it follows live; the own prep view draws those units read-only ('m:' views, never picked or dragged) and lights the + whole boss field; a scout of either player shows both halves (`mate`). The team panel frames the pair's two avatars + (`teamFrameIds`: the players still in by seat, two by two, as `prepCamera`; the boss field in the fight) from the round + start through the fight, with the official sprite tinted green (a plain ring without the local art); a lone player, an + eliminated player and a spectator seat get none; 联防 is not framed (not asked). +- [ASSUMED]: the partner's bench is not shown (its board only); the frame's exact official geometry (one frame per avatar). +- Golden: unchanged. Tests: `test/match/feedback5-boss-pair-prep.test.js` (4, fail on the parent), SP_E2E + `test/ui/feedback5-boss-pair.e2e.test.js` (both halves, the mirrored right half, the frames in prep and fight). + Docs: design/client.md §10 / §15, design/network.md, docs/PLAYING.md. + +#### 25.19.6 Overlap with the open community PRs #189 and #191 (not merged; their authors' fixes were pending) + +- PR #189 (watch preference, @2321Robin) — §25.19.3 builds the same chain (a manual watch is the preference; round start, + both combat modes, the boss fields and the resync follow it; the stale-field fix of the adopted scout), on the 0.2.0 + method containers. Differences: the fallback is the first player still in with AI seats included (a lone human with AI + teammates had nobody to follow — the report's case), and a shared field records the `playerId` the row tap sends (the + review's point 2) instead of nothing. +- PR #191 (same-field frames, issue #190) — §25.19.5 frames the pair as the owner asked: green, the official sprite, both + avatars (self included), the boss rounds only; the pairing is read from m.public as the prep camera already does, so no + `bossPairing` field; #191's 联防 frames and the phase banner naming the partner are not built. If the owner still wants + them, they can go on top of this. + +#### 25.19.7 Not built — what the two feature requests would take (the owner has not decided) + +- **22 战绩 / 分队胜率**: the result is already complete (`m.result`: per-player stats, lineup, band, bonds, titles, rounds + passed, victory — `server/match/results.js`). With no accounts the simplest is per browser: append a compact record at + `m.result` to localStorage (capped, e.g. the last 200; only matches of ≥ 2 rounds, as asked), a 战绩 screen in the lobby + (list, win rate per strategy / core bond / mode and difficulty, 导出 / 导入 like the loadout), tests and i18n — no + server change. Server-side history needs persistence and an identity the remake does not have. Decisions: browser-only + or not; what 「分队」 groups by (strategy, core bond, lineup); the sample cap. +- **48 自定义快捷键**: the keys are fixed in `ui/gameLogic/shortcuts.js shortcutFor` (R 刷新, F 冻结, D 升级, Q 撤退, X 出售, + Space 准备 / 暂停, Esc). It would take a 快捷键 section in 设置 (capture a key per action, conflicts refused, 恢复默认), + a sanitised map in the settings (`sanitizeSettings`), `shortcutFor` reading it, every `` hint and tooltip reading it + too, i18n and tests. Decision: whether to allow it at all (the official PC client's keys are fixed as far as the data + shows: `pc_key_holder` hints). + +### 25.20 Language packs: a language is a file in the language folder, the first type of content packs (W5AD; the owner's decisions of 2026-10-07) — `shared/packs.js`, `shared/i18nPacks.js`, `shared/i18n.js`, `shared/i18nData.js`, `server/packs.js`, `server/http/static.js`, `public/js/ui/lang.js`, `public/js/data.js`, `tools/i18n.mjs`, `tools/packs.mjs`, `tools/build-i18n.mjs`, `tools/package.mjs`, docs/I18N.md §5, docs/PACKS.md - **Seen**: after §25.2 English was wired in by name — the language list `['zh', 'en']`, the switch's labels, English checks in the title screen, `fmtNum`, the tab title, a CSS rule, the overlay URL pattern — so another language meant