fix: 避免恢复默认被尚未完成的旧调配同步覆盖

This commit is contained in:
星闌 committed 2026-10-11 16:11:00 +08:00
1 parent bc50cac01f
commit 19c854c1f7
5 files changed
+204 -3

No files matched your search

+5
View File
@@ -768,6 +768,11 @@ leaker's own capsule / row (`ui/hud.js uniteRemaining`) and the leakers' team ro
## 5. Views (DESIGN §8.2 / §8.3) and deviations
The client's stored loadout and `m.private.loadout` / `ops` must agree after a successful sync. In
`public/js/ui/loadoutSync.js`, edits and `welcome` invalidate preparation still awaiting game data, so an older
payload cannot overwrite a newer reset on the session/seat or enter the next match (§16, §29.15). Request reply
ordering, debounce, close flush and retry/lock handling remain unchanged.
`m.public` (throttled ≤ 10/s, only sent when it changed) carries the DESIGN fields plus: `drawnDisabledBonds` (the 3+4
drawn set; `disabledBonds` = drawn ∪ the mode's static list), `hiddenBossId`, `bossRound`, `hiddenRound`, `spRound`,
`combatMode` (`'client'` | `'server'`), `fields[].progress { killed, total, done }` (teammates' progress UI), `paused`
+4
View File
@@ -106,6 +106,10 @@ Official rule (bwiki 盟约, 更新公告 5114): before a match the player canno
**Loadout wire & state:** the client keeps a per-browser loadout `{ [baseChessId]: { skill: index, module: uniEquipId | 'none' } }` (localStorage) and sends it with C2S `room.loadout { entries }` (validated: known visible chess, legal skill index for that chess's normal AND elite status, legal module id). The lobby stores it on the seat; at match start `Match` receives `seats[].loadout`; `PlayerState` keeps it immutable for the match; board pieces are resolved to `{chessId, skillIndex, moduleId}` when building `PlayerBattleInput.units[]` (fields `skillIndex`, `moduleId`; BattleSpec carries them). Bots use defaults. m.private exposes the effective loadout so the UI (shop cards, detail panel) shows the chosen skill/module.
Client sync preparation may await chess/backups data. A later edit (including restoring defaults) or `welcome`
invalidates the earlier preparation: its payload, missing-data result or failure cannot send or change sync status.
The current edit keeps the existing debounce/close flush, ordered requests and retry/lock handling.
**潜能 / 练度 (0.2.2, the owner's decision of 2026-10-08: 「调配干员里自己设置吧,默认满潜满加成」):** official — an operator fights at the player's own potential (调度手册 「卫戍协议中干员潜能由自身已持有干员潜能决定」; an unowned 特许 operator at potential 1) and an owned operator of the same name gets the 自持有 练度 bonus (activity_table `cultivateEffectList`: 未精英化 +0 %, 精英1 ATK / DEF +5 %, 精英2 ATK +10 % DEF / HP +5 %, 精英2 Lv.60 +10 % each — a multiplier of its own, never summed with the 直接乘算 bonuses). The 干员调配 screen sets both per operator (by charId: initial, elite and 自选 forms share them; default 潜能 6 and 精英2 Lv.60); they travel as `room.loadout.ops` (`{ [charId]: { potential, cultivate } }`, defaults dropped; shared/protocol.js `checkLoadoutOps`, strict like the entries), `seats[].ops` → `PlayerState.ops` (re-checked; bots none = the defaults), and every operator the player owns states them in its `PlayerBattleInput` entry (`potential`, `cultivate`; shared/potential.js `cultivationOf`) — never a 补位 stand-in (a not-owned NORMAL chess) or a prototype 自选 pick. The sim composes the def at the potential (data/*.json carry every potential, DATA.md §2.3; the summons' talents at the owner's) and multiplies ATK / DEF / max HP by the tier's `char_attribute_mul` (effects.json `aceffect_char_1…4`, SIM.md §12). The cards (局内数值, shop and detail cards, a teammate's unit from UnitInfo `potential` / `cultivate`) show the numbers at the settings.
**Sim:** `simdata` resolves a unit def for `(chessId, skillIndex, moduleId)` (cached): `skill` = the selected SkillRecord; stats = `statsBase` + module attr (elite) ; trait/talents = base with the selected module's overrides. Kit contract (backward compatible): a kit function `(bb, chess) => { skill?, skills?: { [skillId]: SkillSpec }, talents, trait }` receives `chess.skill` = the **selected** skill and `bb` = its blackboard; the loader uses `skills[selectedSkillId]` if present, else `skill` only when the selected skill is the chess's default, else the **generic** skill spec — talents/trait from the kit always apply. Every visible chess must get hand-authored specs for all of its selectable skills (tracked by a coverage test).
+14
View File
@@ -274,3 +274,17 @@ enemy: picking emitted a unit click with no definition, instead of the tile clic
engine-canvas regression retains the card/100 HP assertions and also covers another field refresh, live HP loss and
closing the card when the crate is destroyed. Device metadata supplied by the new entry still wins; changing fields
does not carry old devices across. Gameplay and allied targeting rules are unchanged.
### 29.15 Loadout sync preparation after a reset
`ui/loadoutSync.js` previously captured sanitised entries before awaiting backups, but assigned the request sequence
after that wait. With a saved non-default skill and potential, the loadout header's restore-all button remains
available while backups downloads (chess already ready, detail still hidden). A confirmed reset sends empty entries
and ops; the old preparation can then send the old skill with empty ops, leaving the browser showing zero changes
and synced while the session and next match use the old skill. Reproduced in production v0.2.4 using normal screen
controls in headless Chrome with a controlled backups HTTP download delay; no game-state or message injection.
Edits and welcomes now invalidate waiting preparation before it can send or report missing/failed data. Current
preparation, debounce/close flush, in-flight request ordering, deduplication and retry/lock semantics are retained.
`test/ui/loadout-sync-stale.test.js` covers reset, newer nonempty edits, debounce, stale failures, welcome and RATE
retry, with current missing-data and unrelated-screen-change controls. No gameplay rule or golden result changes.