Files
ark-stronghold-protocol/docs/history/0.2.0.md
T

126 KiB
Raw Blame History

DESIGN §25 — 0.2.0

Part of DESIGN.md (the index; section numbers are global).

25. 0.2.0 — the maintainability refactor, two languages, 补位, 自选编队, fidelity fixes, the 0.1.4 ports, release packaging

0.2.0 was built on feedback5, branched from feedback4 at 583899d (0.1.3, the golden-result harness and part of 0.1.4); later merges of feedback4 brought the rest of 0.1.4 in. The workstreams, each on its own fb5-* branch, each merged 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]. 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 → §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 0.1.4 ports → §25.9 (the last two, W4D's sleep rule and W4C's board tap → §25.9.1 / §25.9.2); the settled decisions, what stays [ASSUMED] and the questions left to the owner → §25.10. The closing additions: the owner's decisions of 2026-10-06 (望's stock, skill triggers through summon areas, the 突袭 landing, GitHub #148) and the items the sources settled (泥岩's 闭锁; 忍冬, 深靛, 阿) → §25.11; release packaging and the asset manifest (the 自选 voices, the summons 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.

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); Chinese by default with an English switch, official English game texts, PR #70's translations reused with credit (§25.2); the larger-range trigger rule on the basic strategy, the SEARCH row and 深巡 S2, every 自选 重装 cast with an enemy in range, 纯烬艾雅法拉 S1 at full SP, 伺夜 S1 only with his pack on the field (§25.5); 2026-10-06 — a chess the player does not own shows as its stand-in everywhere (the owner's recall of the official mode, §25.3); the enemy damage frame kept (§25.7.2); 望's placed 棋子 count in her stock, skills that act through summons cast on an enemy in a summon's area, a melee 突袭 member lands where it can block (§25.11); 华法琳's granted trait at the data's 7 / 14, replacing the 12 / 24 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).

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

  • Why: the files every fix touched had grown to thousands of lines (Match.js 3237, enemies.js 3162, Battle.js 2465, fx.js 2259, render/app.js 2046, gameLogic.js 1913, PlayerState.js 1673, the six kit tiers 864–3362 each), and the new operator kits needed one file per operator. Official: none — nothing a player sees changes (refactor plan rule 1).
  • Safety net first (W5A, merged on feedback4 before the refactor; test/golden/README.md): tools/golden.mjs runs a seeded corpus through the production BattleSpec path and Match with a VirtualScheduler and reduces every scenario to a digest — families roster (every chess record × skill × module, every stage and enemy kind), bonds (every bond at 1 and 999 layers), fields (every leader, 联防 with 1 and 2 helpers), matches (bot matches to the end), and since 0.2.0 standins (§25.3) and diy (§25.4). npm test checks the fast subset, GOLDEN_FULL=1 / npm run golden everything; a refactor never changes the files, a gameplay change regenerates them in the same commit and names every scenario that moved (the rule every 0.2.0 stream followed).
  • Now (R1–R5, each a pure move listed in .git-blame-ignore-revs, then a docs commit; golden unchanged after each):
    1. R1: server/index.js (786 lines → 110) keeps startServer() and the same 15 exports; the code is in server/http/ (config, websocket, static, media, files, buildTag, routes, common, boot). Still plain node:http + ws (the owner's decision of 2026-10-04: no Hono); response header order, the /healthz keys, the banner and the /data.js stand-in text are byte for byte the same (a scripted fingerprint of 51 requests matched).
    2. R2: Match.js (3237 → 383) and PlayerState.js (1673 → 239) keep the class, the constructor and the interface header; their 167 / 87 members moved verbatim into 16 / 9 per-concern method containers (server/match/match/, server/match/player/ — W5K added diy.js later; shared constants in common.js), installed on the prototype in a fixed order — a name defined twice throws.
    3. R3: Battle.js (2465 → 189) the same way into server/sim/battle/ (131 methods, 14 containers + util.js); content/enemies.js (3162 → 321) into content/enemies/ (helpers, archetypes, one file per special type, leaders) with the KITS order kept.
    4. R4: the 129 kits of kits/tier1..6.js became kits/ops/<chessId>-<codename>.js (the code name = the charId without char_<n>_; the chess id is the unique key — 锡人, 耶拉, 华法琳 are two chess each), helpers shared by a tier in kits/shared/tierN.js, the registry kits/index.js (a guarded dynamic import per file: a broken file only drops its own kit to the generic one); kits/README.md is the kit guide with the fidelity checklist. The tier*.js re-export shims were removed in WE2 (§25.8.8).
    5. R5: the client — ui/gameLogic.js (1913 → 47) and render/fx.js (2259 → 45) became barrels over ui/gameLogic/ (18 modules) and render/fx/ (15); the field view's helpers moved to render/app/ and the match screen's standalone parts to screens/game/ (same exports).
  • Tooling (R6): npm run lint — a correctness-only ESLint (eslint.config.js; no formatter, warnings allowed); npm run typecheck — tsc --checkJs over a first slice (jsconfig.json: shared/ but protocol.js / bandBonds.js, sim/rng.js, dir.js, constants.js, the typedefs of types/core.js; types/README.md says how to widen it); npm run check:imports — tools/check-imports.mjs: server/sim/ (served to browsers) imports neither the match, the lobby, the entry, the net layer, public/ nor a Node builtin, server/match/ neither public/ nor the net layer; the one listed exception is sim/nodeData.js, the Node-only loader (test/check-imports.test.js pins the list); tools/package.mjs (+ --lite) builds the release zips from a runtime allowlist (W5U, §25.12). CI gained a tooling job (lint, imports, typecheck; once, not per matrix entry).
  • Measured: a match now loads about 340 small script modules; over HTTP/1.1 at 100 ms latency a cold load took 13.4 s instead of 9.8 s, so docs/DEPLOY.md recommends HTTP/2 at the reverse proxy (the browser sim warms up during the band draft, so play is not delayed).
  • Tests: test/content/kits_layout.test.js (every ops/ file listed once, named after its chess id and charId, one registry key, no Node-only import, no clock or Math.random), test/check-imports.test.js, test/version.test.js (reads the banner / /healthz from their new files), test/docs-consistency.test.js (reads the classes as their façade plus method modules); GOLDEN_FULL=1 node --test test/golden.test.js after every refactor commit.
  • Docs: docs/ARCHITECTURE.md (the map of the code after the split) and CONTRIBUTING.md (setup, tests, the fidelity rules, conventions) are new in 0.2.0; §2 lists every file.

25.2 Chinese by default, an English switch (W5I, phase 1; W5S, phase 2; PR #70 by @YuriRestia; the owner's decision of 2026-10-05) — shared/i18n.js, shared/i18nData.js, tools/build-i18n.mjs, tools/i18n.mjs, public/i18n/en.json, data/i18n/en.json, public/js/ui/lang.js (sentText), docs/I18N.md

  • Seen: English-speaking players asked for an English interface. PR #70 (@YuriRestia) translated by replacing the strings in place across every file, logic included; the owner did not merge it (reply on PR #70, 2026-10-05) and asked for real i18n instead.
  • Official: the English game texts exist in the official EN client data (the EN tables carry act2autochess with the zh_CN ids); the YoStar repository the plan first named is archived and has no 盟约 season.
  • Decision: the owner's decision of 2026-10-05 — Chinese stays the default (an English browser still opens in Chinese), English is a switch; game texts from the official EN data; PR #70's translations reused with credit; no i18n framework.
  • Now (docs/I18N.md is the reference): UI strings go through t('中文') — gettext style, the Chinese text is the message id, so an untranslated string shows Chinese, never a key ({name} parameters, one plural form, tc() for context, N_() marks module-level tables); their English is public/i18n/en.json, seeded from PR #70's translations. Game texts (operators, skills, talents, modules, traits, enemies, bonds, items, effects, strategies, 特质, 机变 cards, modes, stages, tokens) come from an overlay, data/i18n/en.json, which tools/build-i18n.mjs builds by walking the zh_CN and EN tables side by side (exact, template and composite matches; fallbacks: the remake's own strings, PR #70's, the UI table) and public/js/data.js applies: 97.6 % of 22 183 texts with the 自选 data (the CN-only operators and modules are what is missing). Server toasts and ticker lines travel as msgid + params (wireMessage; text keeps older clients working) and each client renders its own language. The switch: the title screen (top right) and 设置, ?lang=en once, kept in sp.pref.lang, re-rendered in place. Phase 1 converted the title screen, lobby, room, settings, the app shell and every server message; tools/i18n.mjs (extract / check / codemod / seed) finds what is left.
  • Phase 2 (W5S, 2026-10-06; docs/I18N.md §4): of the 1 128 Chinese literals extract still listed, 1 056 go through t(), N_() or the new tParts() (a sentence with markup or a component inside: the translation split at its placeholders, vnode params kept) — the screens (band draft, briefing, the game screen and its overlays, 干员调配 / 干员持有, the result), the HUD, shop bar, detail card, bond strip and popup, 机变 overlay, enemy drawer, team panel, guide, emotes, components and the client models, the phase banners, refusal reasons and formats of ui/gameLogic (fmtNum writes K / M / B in English, 万 / 亿 in Chinese), the canvas, 2D-fallback and facing-wheel labels and the msgid tables of shared/constants.js; the other 72 are marked as no player text and skipped by the audit (error details and logger lines, the invariant reports, the bilingual HTTP error pages, the AI seats' names, the dev STUB match, data keys). extract now reports 0 and check 1 002 msgids, every one translated (public/i18n/en.json 1 015 entries; the 64 fragments the sweep replaced dropped). Game texts the server copies into the match views in Chinese — a 机变 card, an effect entry, the draft's name, a reward offer's label, the result title — show the localized record when the sent text is that record's own (data.lookupRaw, ui/lang.js sentText; names through tName); a text the server reworded (the multi-round 悬赏's two-battle text, choices.js bountyText) stays as sent. A language switch in the middle of a screen re-reads the game texts (useGameData(), the 干员调配 / 干员持有 rosters, the strategy list and the open detail card list data.locale() among their memo dependencies). What stays Chinese in English: player names (the AI seats' too), that reworded 悬赏 text, the game texts the official EN client lacks (some 自选 operators, newer modules) and the 玩法说明 pictures (their titles translated). The 虫动 emote set follows the official EN name (Slug Style). The Chinese output did not change: a render of every converted file in Chinese equals the code before the sweep, and the few rewrites it cannot follow were checked by hand.
  • [ASSUMED]: the English of the remake's own strings (排气格栅 "Exhaust Grille" as PR #70 has it, the 联防 map names, the server's own templates, the 126 msgids phase 2 added).
  • Golden: unchanged. Tests: test/i18n.test.js (since phase 2 it scans the whole client, shared and the server messages: no Chinese literal left unwrapped, every msgid translated — extract 0 and check --strict as a test; floors 1 000 catalog entries, 1 350 msgid uses), test/i18n-data.test.js (coverage floors), test/i18n-server.test.js (also the sent data texts), test/ui/i18n.e2e.test.js (SP_E2E=1).

25.3 补位: an operator the player does not own fights as its official stand-in (W5B, W5D, W5G, K1–K3, W5N; the approved plan, the owner's decision of 2026-10-05; the owner's recall of 2026-10-06) — tools/build-data.mjs buildBackups, data/backups.json, shared/standIn.js, sim/simdata.js getStandIn, content/index.js kitOf, kits/ops/standin-*.js, shared/protocol.js room.ownership, screens/ownership.js, ui/gameLogic/standIn.js

  • Seen: the official mode fills an operator the player does not own with a stand-in; the remake always fielded the real operator, and the data build dropped the official backup fields.

  • Official: act2autochess — 133 chess: 74 PRESET (特许, always available), 55 NORMAL (each naming a backup: charId, template, skill, module, potential), 4 DIY; PRTS 卫戍协议 — a missing operator is filled by a prototype of its class with a fixed skill, and the bonds and 特质 stay the chess's. The 17 stand-ins: the eight 4★ 预备干员 and nine elite prototypes (郁金香, Sharp, Mechanist, Stormeye, Pith, Touch, Raidian, Misery, 领主·Sharp).

  • Decision: the approved 补位 plan (2026-10-05): only the 55 NORMAL chess can be marked not owned, everything is owned by default, no borrowing from friends, bots own everything; the 重装 stand-ins (预备干员-重装, Mechanist) cast with an enemy in range, like §21.29. The owner's first-hand recall of 2026-10-06: the shop, the hand, the board and the result screen show the STAND-IN — this replaced the first build's cards that kept the chess with a 「替补:X」 badge.

  • Now: data (W5B): every chess.json record keeps backup; data/backups.json units holds the 17 stand-ins in every form (DATA.md §18). Battle (W5D): getChess(id, { standIn: true }) = the chess's ids, bonds and tier with the stand-in's body (def.standInFor); its kit is the stand-in's own (kits/ops/standin-<codename>.js, STANDIN_KIT_FILES), never the replaced operator's; without one, the generic kit plus genericTalents — the eight 预备干员 get their talents, 一击即退 for 10 s from every deployment and 冲锋号令 +12 DP; the browser battle loads data/backups.json too. Match (W5G): C→S room.ownership { notOwned } (the 55 droppable chess only; fixed for the match); battleInput marks standIn: true in every field — the rules read the chess (price, bonds, 特质, merges, pools), the body is the stand-in's (placement by its own position, range, no summons). Client: the 干员持有 tab of 干员调配 (export / import, sp.pref.ownership); since W5N the stand-in is drawn everywhere — shop and reward cards, hand, 临时整备区, board, detail card, bond popups, result lineup, bench scouting, tickers — with a small ice 「替补」 mark and the chess's tier, price and bonds. It speaks with its own voice (the §21.30 port; a 预备干员 stays silent).

  • Kits (K1–K3; every skill on every form, the numbers from the composed record, the data's trigger):

    stand-in kit (kits/ops/) stands in for notable [ASSUMED]
    Sharp standin-acguad.js 忍冬, 银灰, 百炼嘉维尔, 隐德来希, 归溟幽灵鲨, 佩佩 —
    领主·Sharp standin-sharp2.js 水月, 玛恩纳, 维娜·维多利亚, 妮芙 陷阵勇气 counts targetable enemies; LOR-X's 流失 follows a shielded hit
    Misery standin-acspec.js 缄默德克萨斯, 山, 新约能天使, 锏 四维分离 counts her own tile and air units; S3's drag area is its x-4; S1 / S2 deploy-timed duration skills (WE1)
    Stormeye standin-acnipe.js 空弦, 莫斯提马, 百炼嘉维尔, 妮芙, 蕾缪安, 异客 风雨欲来's delay = no attack for 2 s, counted from deployment
    Pith standin-accast.js 烛煌, 焰影苇草, 迷迭香, 荒芜拉普兰德 two Piths' 授我所授 / S2 never add up
    Raidian standin-acsupo.js 灵知, 夕, 安洁莉娜, 浊心斯卡蒂, 仇白 诱引 rolls at an attack's first damage instance; new applyStatus option stackAs
    郁金香 standin-acpion.js 瑕光, 焰尾, 凛御银灰, 圣聆初雪, 缪尔赛思, 耀骑士临光 the 集成战略 备注 applies; S3's 8 slashes at the clip's OnAttack events
    Mechanist standin-acfend.js 星熊, 号角, 余 S3 pulses from 1 s after the cast
    Touch standin-acmedc.js 塞雷娅, 塑心, 纯烬艾雅法拉 shares the 外勤医疗 strategy's Touch code (§25.6.6); S2's 所有医疗干员 = her own player's
  • [ASSUMED]: a stand-in is placed by its own position (melee tiles for a melee stand-in); without accounts any of the 55 may be dropped, also one the player owns; AI 托管 values a dropped chess by the chess record; the detail card names 「X的替补」 in place of the English name; the 「替补」 mark's look.

  • Golden: new family standins (10 battles: every NORMAL record, normal and elite, fielded as its stand-in) and the match coop2-NORMAL-14-standins (a human seat on AI 托管 owning none of 7 NORMAL chess); nothing else moved.

  • Tests: test/backups.test.js, test/content/standin.test.js (the plumbing and the eight 预备干员), test/content/standin_*.test.js (the nine kits), test/match/standin-ownership.test.js, test/lobby-ownership.test.js, test/ui/standin-ui.test.js, test/golden-standins.test.js, test/ui/standin.e2e.test.js (SP_E2E=1).

25.4 自选编队: the four DIY slots field the player's own 6★, and 71 operator kits (W5H, W5K, O1–O24; the owner's decisions of 2026-10-05) — data/backups.json diy, shared/diy.js, sim/simdata.js getDiy, server/match/player/diy.js, shared/protocol.js room.diy, screens/diy.js, kits/index.js OPERATOR_KIT_FILES, kits/ops/op-*.js, kits/shared/summoner.js

  • Seen: the four DIY chess were removed from the shared pool (§0). A first attempt (PR #71) had put them in the shared pool, one chess id meaning a different operator per player — the shape to avoid.
  • Official: act2autochess diyChessDict / shopLevelDisplayDataDict — two slots at tier 5 (from 调度中心 level 5), two at tier 6 (level 6), price 4, sold for 1; PRTS 盟约 下半 — the player's own 6★ or a 6★ prototype, no 特质, the core bonds its factions meet (else 协防); forms: normal = E2 Lv1 with skills at rank 4, elite = E2 Lv60 rank 7 with its module at stage 1 (tier 5) or 3 (tier 6).
  • Decision: the owner's decisions of 2026-10-05 — two plus two slots per player; an owned 6★ with a kit, any of its three skills, any elite module; a prototype keeps the skill and module of its 补位 rows; a per-player shop; levels follow the chess forms; the six collab 6★ are out (copyright), and so is 焰狐龙梓兰 (MH05, a Monster Hunter 联动寻访 operator the team list does not name: DIY_EXCLUDED_NUMBER_PREFIXES) — 71 owned picks; the 集成战略-only ISW-A modules are refused (WE2 adds SO-A / SO-B / RA-A, §25.8).
  • Now: data (W5H): data/backups.json units adds the 71 owned picks in the three slot forms, tokens their 38 summons, diy the slots, the prototypes (15 at tier 5, 9 at tier 6), their locked loadouts, ownedPool and the derived bonds (DATA.md §18); shared/diy.js (diyPool, validateDiyPicks, diyRecordOf); the sim fields a pick (getDiy; kit KITS[charId]). In play (W5K): C→S room.diy { picks } (checkDiyPicks; welcome.diyKitted lists the operators with a kit); the player's data view makes a slotted DIY slot its operator everywhere — bonds, placement, summons, AI 托管 — with no 特质; a per-player stock never enters the shared pool and the player's own shop draws it from the slot's 调度中心 level; a piece whose every bond is off this match leaves the shop (m.private.diyBanned, shown since WE2); a placed operator's summons go to the hand (official counts since WE2, §25.8). Client: the 自选编队 tab of 干员调配 (two tier-5 and two tier-6 slots, the picker with skill and module, export / import, sp.pref.diy) and a mint 「自选」 badge on cards and pieces.
  • The kits: 推进之王 first (W5H, the worked example of kits/README.md "How to add an operator (自选)"), then O1–O24 in three waves — every skill at ranks 4 and 7, every talent, every module at stages 1 and 3, the range while a skill runs, triggers, targeting, damage typing, summons and statuses, per the kit checklist; potential 0 for every pick [ASSUMED: no account]. Engine additions, each additive (only the diy golden family moved): palsyTrigger (O3, ai.js: a 麻痹 stack interrupting an enemy attack), the hpDamage hook after the shields (O5, damage.js), target priority heaviest and the boomerang hooks boomerangCaught / boomerangOnward (O7), shields limited to damage types (shieldType, O8 / O16) and a profile's skipEnemy (O8), enemy shots carrying isProjectile and priority elite (O11), a healer profile's healThrough (O16), the 缚地 status groundbind (O20), the enemyAttackStart hook, the noNewBlock flag and holdsUndying reading an undying buff (O22), ally attack targets (Battle.setAllyTarget, O23), the air-block mod blockRadiusScale (O24); kits/shared/summoner.js (O21) holds the 召唤师 deck of 麦哲伦 / 令 / 电弧. Match: a placeable summon's hand count reads the active module's variant (O19: 望's TRP-X 7). Client: visuals for the 13 fx kinds the new kits emit (render/fx/kinds.js); the picks' art comes with the asset plan (tools/fetch-assets.mjs --add-only; their battle voices and the summons with no Spine model: §25.12).
干员 (summons) kit (kits/ops/) stream notable [ASSUMED] (and later changes)
推进之王 op-siege.js W5H S2 跃空锤 reaches ground enemies only (no source says air); SOL-Y's extra SP goes to a random deployed other 【先锋】
陈 op-chen.js O4 S3's ten slashes = the first ten of the clip's eleven OnAttack events; S3 refused with no ground enemy in its 技能范围
艾丽妮 op-irene.js O4 S3's first bombardment 0.3 s after the opening strike, an empty one skipped; ISW-A refused in 自选 (W5K)
赫拉格 op-helage.js O4 运筹帷幄 is the 生命回复速度 attribute (no PRTS 备注); the 武者 heal per damage instance (WE1)
闪灵 op-shining.js O8 S2's DEF bonus ends with her last barrier on that unit; PHY-X heals ×1.15 at or below 50 % (WE1)
夜莺 (幻影) op-cgbird.js O8 a held 幻影 returns after 20 s for 5 DP whether or not 夜莺 stands; 3 hand pieces (WE2)
嵯峨 op-saga.js O8 清明 is a 生命回复速度 (hpRegenRatio) buff; SOL-Y stage 3 takes the blackboard's 6.5 % (the text says 6 %)
艾雅法拉 op-amgoat.js O2 S3's lava has no splash; 乱火 maxima 15 / 15; S3 now casts by the larger-range rule (「攻击范围增大」)
刻俄柏 op-cerber.js O2 剥壳's ramp restarts per deployment
霍尔海雅 op-heyak.js O2 S3's whirlwinds hit air units, nearest first; one not yet released drops when she leaves; ISW-A refused
早露 op-poca.js O7 S3's strikes are her attacks, the first one tick after the cast; SIE-Y's distance bonus is linear
提丰 op-typhon.js O7 S3's arrows are skill damage, not attacks (no SIE-X / SIE-Y bonus)
娜仁图亚 op-narant.js O7 S1 (切换) stays on until she leaves; S3's boomerangs fly together and its blast is no attack
逻各斯 op-logos.js O3 语汇演化 rolls once per attacked target and may draw the attacked enemy
真言 op-mantra.js O3 S2's first shot keeps the engine's bolt speed 11 (备注 10); only operators starting a skill grant 麻痹
维伊 op-veen.js O3 战争技艺 restarts its 5 s with every stack; her energy store is now the engine's 秘术师 rule (WE1)
左乐 op-zuole.js O5 S3's 7 slashes resolve at the cast and reach air units
重岳 op-chyue.js O5 S2's second segment always fires; 止戈 rolls only on hits that leave the target alive
煌 op-huang.js O5 S3's cuts deal 100 % ATK once a second (no blackboard key); the card keeps her 1-1 (the game shows 3-6)
黑 op-shwaz.js O6 正前方 = her straight line ahead, her own tile included; S3 DEFAULT (「攻击范围改为」 is a range change)
鸿雪 (打字机) op-bgsnow.js O6 the 打字机 returns only while 鸿雪 stands and keeps its 25 s when she falls
伊芙利特 op-ifrit.js O1 BLA-Δ's extra 元素伤害 also follows 灼地 ticks (_targetFamilyMask 31); 灼地's 2 % 流失 as stated
谬因 (中继器) op-aphris.js O1 a huge enemy counts on every tile it covers; the 中继器's 无敌 / 孤立 / 禁疗 are kit flags; S2 area trigger (WE2)
林 op-lin.js O1 the 琉璃璧 sits in the buff order (an earlier shield absorbs first); its burst comes before a knock-out
年 op-nian.js O14 PRO-Y counts a teammate's 重装 on a shared field; 铁御 skips summons
斩业星熊 op-hsgma2.js O14 AST-X's extra damage with or without a running skill; 我执 on the engine's 1-HP floor; normal attack physical (WE2)
珊比 op-thumpy.js O14 the conveyor moves 0.533 tile/s (20 frames read per game second); S2 / S3 cast by the area rule
森蚺 op-zumama.js O15 a blockee turning 无敌 + 无法选中 loses the S2 stun after one pulse; RA-A refused in 自选 (WE2)
斥罪 op-judge.js O15 her barrier is spent oldest first (the client's priority −1000 would put it last); the thorns cannot be dodged
黍 op-shu.js O15 the 【播种标记】 and the teleport end with S3; S3's TRY_SEARCH_ALLY_SKILL is now resolved in the data (WE2)
帕拉斯 op-pallas.js O9 休谟斯's own 精力充沛 is not merged with hers; S2 casts by the larger-range rule (「向前延伸」)
止颂 op-lessng.js O9 S3 casts nothing under 晕眩 / 冻结 / 沉睡; S2's strike spacing is not modelled
贝洛内 (牵绊) op-demetr.js O9 several 贝洛内 on one enemy share one 【手段】; every move is instant
W (此面向敌) op-cqbw.js O12 no cap on mines; 此面向敌 cannot be targeted; her knock-out withdraws the mines
菲亚梅塔 op-phenxi.js O12 S1 follows the current client (no air targets, no range growth, trigger DEFAULT — PRTS 备注 of 2026-08-01)
维什戴尔 (魂灵之影) op-wisdel.js O12 aftershocks 0.3 s apart; S3's last shell loses the +ATK (the engine's ammo rule)
棘刺 op-thorns.js O10 the spikes carry the LOR-X addition; his poison keeps ticking after he left
赤刃明霄陈 op-chen3.js O10 the first slash at the cast; a hiding target counts as gone; the 【移动】 lands at once
司霆惊蛰 op-leizi2.js O10 currents outlive the skill, bounce off the field edge, use screen directions; S1 by the larger-range rule
薇薇安娜 op-vvana.js O11 enemy content projectiles carry no isProjectile (melee for 散华); S3 area trigger (WE2)
怒潮凛冬 op-headb2.js O11 the hammers are skill damage, lost while she is stunned; 高台 = the HIGH tiles of the field
赫德雷 op-hodrer.js O11 S3's 流失 may knock him out; attack riders land even on a dodged hit
假日威龙陈 op-chen2.js O13 the slime shot's selector range is her attack range at the attack; it outlives her
莱伊 (沙地兽) op-ray.js O13 the 沙地兽 scouts 25 s, attacks nothing, returns after its redeploy time for 3 DP while she stands
阿斯卡纶 op-ascln.js O13 高台 = the level's HIGHLAND tiles (the forbidden rim too); separate 命中率 cuts roll apart
涤火杰西卡 (机动盾牌) op-jesca2.js O16 a 机动盾牌 not next to her gets no turn, mark or aura
机械师 (结构性原理) op-mcnist.js O16 the S3 charge lands free of DP; _ignoreTriggerOnce read as nothing
凯尔希 (Mon3tr) op-kalts.js O16 S2 / S3 are heal skills on DEFAULT (no injured ally, no cast); PHY-X Lv3's half-HP blast once per deployment
琴柳 op-sleach.js O17 精神感召 saves DP only on a redeploy (the battle-start deployment is free here)
可露希尔 (指挥中心) op-closur.js O17 部署费用下限 has no counterpart (costs never drop below 0); her own tile counts beside the 指挥中心
澄闪 op-gdglow.js O17 her own attack stops in every skill (all drones out); S2 fires at full SP
阿 op-haak.js O18 S2 / S3 keep the data's DEFAULT: with no ally in range the cast does nothing (settled by the data and the skill prefabs, §25.11.5)
傀影 (镜中虚影) op-phatom.js O18 S3's push and status land on a dodged hit too; the twin is readied at his redeploy (WE1)
弑君者 op-crosly.js O18 S2's blast lands at once; any action-stopping state or 缴械 cancels it
老鲤 op-lmlee.js O19 周围八格 = the 3 × 3 with his tile, flyers counted
温蒂 (工程蓄水炮) op-weedy.js O19 S2 reaches air units; the cannon's forced skill fires once per deployment (data DEFAULT, re-checked in WE2)
望 (棋子, 跟子) op-wang.js O19 跟子 are kit records, no unit; placed 棋子 take and return her stock, so S1 / S2 keep cycling (the owner's decision of 2026-10-06, §25.11.1); 棋子 on any tile (WE2)
多萝西 (共振装置) op-doroth.js O20 a used-up piece returns 5 s after the LAST trap deployment; the hand holds at most 9
予愿安洁莉娜 (“一会儿见!”) op-aglna2.js O20 flight 2 tiles/s, landing clips not modelled; the marker is no hand card (WE2); new status 缚地
黑键 (旧日残影) op-ebnhlz.js O20 his energies land with the main bolt; S2 at full SP with a free tile (WE2)
麦哲伦 (龙腾) op-mgllan.js O21 龙腾.F makes no attack (ATK 0); 3 summon pieces, 4 with SUM-Y (WE2); S1 casts on an enemy in a drone's range (§25.11.2)
令 (清平 / 逍遥 / 弦惊) op-ling.js O21 her summons strike during S2 even when stunned; 3 pieces (WE2); S3 casts on a ground enemy in a summon's x-5 (§25.11.2)
电弧 (戴乌 / 赛柯 / 桑特拉) op-radian.js O21 桑特拉's link is its own attack with its own ATK; SO-A / SO-B refused in 自选 (WE2); S2 / S3 cast on an enemy in a summon's range (§25.11.2)
淬羽赫默 (夜灯) op-slent2.js O22 丰润羽翼 as 生命回复速度 rests on the client template (kept, §25.11.5; owner question)
遥 op-haruka.js O22 her bubbles outlast her; the T2 heal uses the ATK of the grant (kept, §25.11.5; owner question)
酒神 (本能的召唤, 迷狂牢笼) op-phatm2.js O22 本能的召唤 lures at most 4 in all; its redeploys cost no DP; the 迷狂牢笼 blocks no sleeper (§25.9.1)
白铁 (支援装置, 铁钳号) op-ironmn.js O23 the 铁钳号 is charged only by attacks that select it; 2 devices in hand (WE2)
娜斯提 (支援装置) op-nasti.js O23 the built 高台's operator bonus (m4–m6_bonus) never applies
死芒 (悲叹的仆役) op-necras.js O23 the servant consumed is the earliest made; S3's strikes are skill damage; S2's sleepers leave her servants' block (§25.9.1)
凯尔希·思衡托 (战术锚点) op-kalts2.js O24 S2's shot flies at the engine's orb speed 10 (official 12); 战术锚点 placement (WE2)
Mon3tr (重构体) op-monstr.js O24 the 路标形态 marker is not placed; 重构体 placement (WE2); the chain heal jumps in the 3×3 (WE1)
  • [ASSUMED] (besides the table): the per-player stock, 8 at tier 5 and 5 at tier 6 (no table gives it); bots field no 自选 piece; 信标 sends no 自选 piece to a teammate; a prototype in a tier-5 and a tier-6 slot counts as two bond members; the reward offers' temporary refresh draws the player's 自选 pieces from the slot's level on.
  • Golden: new family diy (138 battles: every kitted owned operator in each form × module × skill at tiers 5 and 6, then the prototypes) and the match coop2-NORMAL-70-diy (a human seat on AI 托管 slotting 推进之王 and prototypes); each kit wave added its own scenarios, no other family moved.
  • Tests: test/backups.test.js, test/diy.test.js, test/content/diy.test.js, test/content/kits_layout.test.js, test/content/op_*.test.js (71: one per kit, every form × module × skill), test/match/diy-shop.test.js, test/match/diy-bonds.test.js, test/lobby-diy.test.js, test/ui/diy-ui.test.js, test/golden-diy.test.js, test/assets-diy.test.js, test/ui/diy.e2e.test.js (SP_E2E=1).

25.5 Trigger rules: a larger running range casts as soon as an enemy is inside it; AUTO skills that need no target fire at full SP (W5C, W5F, the kit waves; the owner's decisions of 2026-10-05) — tools/build-data.mjs resolveTrigger / ACTIVE_RANGE_OVER / ATTACK_RANGE_CHANGE / UNIT_TRIGGER_CORRECTIONS / TRIGGER_ALLY_RULES, sim/skills.js setTrigger, §5.6

  • Seen (community report): a skill that enlarges the attack range waited for an enemy inside the normal range, as the official basic strategy does (§20.2); the 0.2.0 audit after GitHub #124 found 35 AUTO skills among the 112 visible chess that never fired alone with full SP and no enemy.
  • Official: PRTS 卫戍协议/帮助 技能策略 — DEFAULT casts on an enemy in the initial range; SEARCH on an enemy in the initial range; AUTO skills take no strategy row, so DEFAULT waits for the unit's own attack.
  • Decision and now (the normative text is §5.6):
    1. ACTIVE_RANGE — the owner's deliberate deviation of 2026-10-05, like §21.29: a MANUAL skill whose running range strictly contains the operator's own checks the DEFAULT condition on that running range every tick, no attack needed (rawRule keeps the official row). First on the basic strategy (W5C: 33 skills / 66 records, e.g. 野鬃 S2, 银灰 S3, 莫斯提马 S3), then the SEARCH row and 深巡 S2 (W5F: 薄绿 S1, 蜜蜡 S1, 卡涅利安 S3, 玛恩纳 S2, 安洁莉娜 S3, 深巡 S2 — 39 skills / 78 records). The wording of a range change was widened as the kit reports found misses: 「攻击范围增大」 (艾雅法拉 S3), 「攻击范围改为」 (黑 S3: DEFAULT, not a 技能范围), 「攻击范围向前延伸」 / 「攻击范围朝…扩大」 (帕拉斯 S2, 司霆惊蛰 S1); UNIT_TRIGGER_CORRECTIONS follows a live-client change the text misses (菲亚梅塔 S1 is DEFAULT since the 2026-08-01 client, PRTS 备注: no range growth, no air targets). A range the data cannot express is set by the kit: 珊比 S2 / S3 (her tile plus the glue / conveyor area), 谬因 S2 (the beam path, addTriggerRange), 薇薇安娜 S3 (3-2 from her second cast, the new skill.setTrigger).
    2. 重装 of data/backups.json — the owner's decisions of 2026-10-05: the 重装 stand-ins and every 自选 重装 (森蚺, 涤火杰西卡, 斩业星熊, 年, 黍, 斥罪, 机械师, 珊比; 48 skill forms) cast with an enemy in range — DEFAULT, or ACTIVE_RANGE when the running range is larger — instead of the class row's TAKE_DAMAGE.
    3. AUTO skills that need no target fire at full SP (SP_FULL; kits/README.md checklist item 5): 浊心斯卡蒂 S2 and 伺夜 S2 (W5C, after GitHub #124's 引星棘刺 S1), 纯烬艾雅法拉 S1 (the owner's decision of 2026-10-05, W5F), and in the 自选 kits every AUTO skill that acts on the operator itself, its allies or its summons (e.g. 谬因 S1, 维伊 S1, 莱伊 S2, 澄闪 S2, 多萝西's three). Skills that need a target keep waiting (a "next attack / next heal" skill, attack buffs). 伺夜 S1 / S2 cast only while his pack is on the field (PRTS 备注; the owner's decision of 2026-10-05 for S1, W5F; the 战术点形态, §25.6.5 and §25.7.9), 黑键 S2 only with a free deployable tile in range (§25.8.4).
    4. Data aliases: 黍 S3's TRY_SEARCH_ALLY_SKILL resolves to SKILL_RANGE with allies on its x-2 (TRIGGER_ALLY_RULES, §25.8.4).
    5. Through summons — the owner's decision of 2026-10-06, the larger-range rule read through the summons' areas: 麦哲伦 S1, 令 S3 and 电弧 S2 / S3 also cast on an enemy in a standing summon's area the skill acts through (kits/shared/summoner.js summonTriggerArea, §25.11.2).
  • Kept on their rows: 烛煌 S2 / S3 (their ranges do not contain her 3-1), TAKE_DAMAGE 重装 skills of the chess pool but the §21.29 six, CUSTOM_RANGE skills, AUTO skills that need a target, SEARCH skills whose range does not grow.
  • Golden: each step names its scenarios in its commit (the SEARCH / 深巡 step: roster-001 / 002 / 016 / 026 / 027 / 041 / 042 / 044, bond-egirShip-low, bond-suntShip-high, boss-boss_2-pair, boss-boss_6-pair and the bot matches fielding them; 纯烬艾雅法拉 S1: roster-038; 伺夜 S1: roster-028; the range-wording fixes: only diy).
  • Tests: test/sim/feedback5-active-range.test.js, test/sim/feedback1-tank-triggers.test.js, test/sim/feedback1e-skillrange.test.js, test/data.test.js (the counts), test/content/kits_alt_t3.test.js (伺夜), test/content/kits_alt_t6.test.js (浊心斯卡蒂, 纯烬艾雅法拉), test/content/feedback5-we2-area-triggers.test.js.

25.6 Fidelity fixes, wave 1 (W5C, W5E, W5J, W5L)

25.6.1 琳琅诗怀雅 S2 “见面礼” throws onto an enemy's tile on x-6, with no enemy needed (GitHub #116 item 2) — kits/ops/chess_char_3_04-swire2.js (installS2)

  • Seen: the bomb landed only in front of her or nowhere. Official: PRTS S2 备注 — bomb range x-6; an enemy in range gets it on its tile, else a random deployable tile (the client needs a free x-6 tile and her coin buff). Now: with a coin and a free x-6 tile her attack turn throws: onto the first ground enemy's tile in her target order, else a random free tile (in the field, empty, walkable melee ground, not a knocked-out operator's tile); damage, 停顿 and the 3 s second hit unchanged. Item 1 of #116 (the solo 标准 shop prices) is not a bug. [ASSUMED]: the throw is instant and costs one tick of her attack. Tests: test/content/kits_t3.test.js (3 cases), summon_loadout_conflicts.test.js.

25.6.2 新硎 / 沉沙: one ATK layer while a 断刃 is held, gone with the last one (GitHub #107) — content/enemies/times.js (kitBlades)

  • Seen: 新硎's ATK kept climbing (1350 → 3510 after two hits). Official: PRTS talent — ATK +80 % (沉沙's blackboard 0.7) while holding 4 【断刃】, one spent per attack; the attack spending the last drops the bonus and clears its cooldown; unspent blades become 矛头 on death (at least 1). Now: one layer from the spawn, removed by the attack that spends the 4th blade, which is followed at once by the next (unbuffed) attack; death spawns max(1, unspent) 矛头 at once (the 0.7 s delay is not modelled). Tests: test/content/enemies_bosses.test.js.

25.6.3 A full 整备区 buys nothing; a freed slot pulls the 临时整备区 in (GitHub #82, second part, reported by Hucxious) — PlayerState.buy / pickReward / _fillHandFromTemp, match/invariants.js, match/bot.js

  • Seen: a full hand still bought a third copy (it merged), and a freed slot did not pull the overflowed operator back. Official: PRTS 卫戍协议/帮助 §手牌区 — with the 10 regular slots full, 招募 / 购入 are refused, merge or not; temp pieces move in when a slot frees; board ↔ hand swaps stay allowed. Now: buy / pickReward refuse with HAND_FULL (client: 整备区已满); every recompute() moves temp pieces into free slots (invariant: temp is non-empty only while the hand is full); bots never buy into a full hand and shed their weakest single for a third copy. [ASSUMED]: temp empties right → left into the hand's next free slot right → left. Golden: coop2-NORMAL-3, solo-HARD-9-boosted, coop2-ABYSS-10-boosted. Tests: test/match/feedback5-hand.test.js.

25.6.4 联防 plays on its own official map (GitHub #41 item 3, reported by Hucxious and shanzhaikabi) — tools/build-data.mjs buildStages, data/stages.json kind: 'unite', unite.js uniteStageId, match/unitePhase.js _uniteOpts

  • Seen: the 联防 field showed the round's battlefield (water pools, crates) opened to both halves. Official: act2autochess level_act1autochess_escaped_single / _multi — one 19 × 21 road grid, no devices, runes or terrain; the right half is the own board + 8 columns. Now: two kind: 'unite' stage records (1 / 2 helpers, active: false) carry the 联防 battle in both combat modes; the client draws that map and restores the board after. No water, crates, devices or terrain in 联防; the BGM (PR #110) is unchanged. [ASSUMED]: the records' names 联防阵地(1名玩家) / (2名玩家). Golden: unite-1, unite-2 and the co-op matches. Tests: test/match/feedback5-unite-map.test.js.

25.6.5 伺夜's 狼群 has the official 战术点形态; S1 acts by the pack's state (PRTS 备注; the owner's decision of 2026-10-05) — content/tokens.js wolfPack / installWolfTacticalPoint / wolfReturnNow, kits/ops/chess_char_3_19-vigil.js

  • Seen: a pack losing its last 狼影 died and came back after 10 s with 2 狼影; S1 fired and paid its DP with no pack on the field. Official: PRTS 伺夜 talent and 狼群 备注 — the last 狼影 lost or a 撤退 turns the pack into its 战术点形态 (无敌, no attack) for the 25 s talent interval, then back with 1 狼影; S1 casts only with the pack on the field: ① the form returns at once, ② +1 狼影, ③ at the cap HP to max. Now: installWolfTacticalPoint keeps a 'killed' / 'retreat' pack as a knocked-out piece (tile reserved) while 伺夜 stands, back after 25 s at full HP with 1 狼影; S1 is the kit's own cast in either form (+cost DP, ① ② ③). S2 in the form: §25.7.9. [ASSUMED]: the return is a redeploy (仇恨 order, deploy hooks); a form at the combat's end enters 联防 fresh. Golden: roster-007 / 009 / 026 / 028 / 040, bond-siracusaShip-high and the bot matches fielding 伺夜. Tests: test/content/kits_alt_t3.test.js, kits_t3, tokens_devices.

25.6.6 Touch: S3's extra heal, 「低于」 half HP, 攫升 on every heal, 超脱 on a dying exit (review of the stand-in kits) — content/tokens.js touchGospel / mapCharTalents, kits/ops/standin-acmedc.js

  • Seen: S3's extra heal copied the boosted main heal; the boost applied at exactly 50 %; 攫升 missed self / zero heals; 超脱 missed 余烬 exits. Official: PRTS Touch (卫戍协议) — the extra heal is 30 % of the main heal on x-5, lowest ratio first, boosted itself on units 「低于」 half HP; 攫升 3 SP for any heal; 超脱 5 SP on dying exits too. Now: the extra heal = battle.heal(Touch, recipient, base × 0.3) once per heal action with its own boost; the boost strictly below hp_ratio; 攫升 on every heal she outputs; 超脱 on a knock-out or Battle.retreat(…, { dying: true }) (the 外勤医疗 strategy's Touch shares the code). [ASSUMED]: equal ratios go to the main target; 骑士戒律 + 竞技旗's exit counts as dying. Golden: roster-041, standin-03 / 04 / 08 / 09 / 10, coop2-NORMAL-14-standins. Tests: test/content/standin_acmedc.test.js, tokens_devices.test.js.

25.6.7 突袭: a passive skill that is on counts as 技能就绪 (GitHub #49) — content/bonds/addon/battle.js raidPoll

  • Seen: 突袭 members with passive skills (缄默德克萨斯, 宴, 斯卡蒂 S2 …) waited for the 10 s idle trigger. Official: PRTS 【突袭】 — idle 10 s or 技能就绪, no enemy in range → redeploy beside a ground enemy; nothing on passives. Now: raidPoll counts a passive that is on, and since the PR #109 port a deploy-timed duration skill while its window runs (§25.9); §22.2's landing rule kept. [ASSUMED]: 「技能就绪」 = the skill is running. Golden: roster-039 / 046, bond-raidShip-low / high, boss-boss_2 / 4 / 6-pair, standin-01 / 06, coop2-HARD-4, coop2-NORMAL-14-standins. Tests: test/content/bonds_addon.test.js.

25.6.8 生命回复速度 is not 治疗 (GitHub #96 / #137; the separation proposed in PR #135 by @SpiritedAwayCN) — sim/damage.js (heal regen), professions.js bardRegen, kits of 魔王 ×2, 浊心斯卡蒂 (+ 海嗣), 调香师, 瑕光, 铃兰, 锡人

  • Seen: per-second HP effects were heals, refused on 无法被友方治疗 / 禁疗 units (#96: 隐德来希 got nothing from 浊心斯卡蒂; #137: 折桠 and 圣约送葬人 from 调香师). Official: PRTS 吟游者 raises 生命回复速度; the 备注 of 调香师, 瑕光 S2, 铃兰 S3, 锡人 S2 — unaffected by 治疗加成 and 禁疗. Now: hpRegen buffs the provider refreshes, applied as the target's own regen tick (no healing multiplier, no heal hook): the 吟游者 trait (bardRegen), 调香师, 瑕光 S2, 铃兰 S3, 锡人; direct heals unchanged (引星棘刺 S1 / S2, 塞雷娅 S3, 流明's and 纯烬's HoTs stay heals). kits/README.md checklist item 11. [ASSUMED]: the buffs refresh from the start; two providers add up. Golden: 50 scenarios that field a converted operator (listed in the merge). Tests: test/content/feedback5-regen.test.js, the kits' tests, test/sim/professions.test.js.

25.7 WE1 — engine-fidelity follow-ups (sim rules)

25.7.1 秘术师 store energy at the attack check, not one interval later (GitHub #181, reported by Paper-Yuan) — professions.js installMystic (storeEnergy / releaseEnergy / hitsFn), ai.js updateAlly / performAttack

  • Seen: 深靛 stood 4.2 s after binding the only enemy; with no enemy the first energy came after 6 s. Official: PRTS 分支特性信息 §秘术师 — storing shares the attack interval: at the check, a target → attack; none and energy not full → store (an attack action); idle only when full. Now: updateAlly stores at the check when nothing is valid and restarts the interval; releaseEnergy spends the energies with the next attack, one arts hit each; 维伊 and 黑键 use the shared check. [ASSUMED]: a redeployment holds no energy. Golden: roster-003 / 021 / 022, bond-preciShip-high, diy-032, diy-114 and 13 matches fielding 深靛. Tests: test/sim/feedback5-mystic-store.test.js.

25.7.2 Enemy attacks strike at their damage frame; a short stun before it cuts the swing (GitHub #187 point 1, #170) — ai.js updateEnemy / enemyAttack / attackWindup / attackTargets, battle/spawns.js (enemy.swing)

  • Seen: short stuns never stopped an enemy attack (忍冬 S3's stun showed, the hit still landed): a stun only paused the cooldown. Official: PRTS 异常效果 — STUNNED cannot attack, DISARMED interrupts; the damage lands at the clip's strike frame (data/enemies.json attackAnim.hit). Now: an enemy with an attack clip strikes at its damage frame; stun / freeze / sleep / 浮空 / 缴械 / 恐惧 or losing every target before it cut the swing (restart from the wind-up), a stun in the cooldown only pauses it, shots in flight land; 36 of 249 kinds without a clip keep the old rule (§5.5; withdraws §22.14's [ASSUMED]). [ASSUMED]: targets re-taken at the frame; an interrupted swing keeps its cooldown. Golden: 275 of 283 scenarios moved (solo-HARD-2 now wins, coop2-NORMAL-8-serverrun loses in round 13). The owner kept the rule on 2026-10-06 (follow-up 29, §25.10). Tests: test/sim/feedback5-enemy-swing.test.js, feedback2-enemy-attack-stand.test.js.

25.7.3 武者: the trait heal on every damage instance, through the own 禁疗 (follow-up 1) — professions.js installMushaHeal / installReaperHeal, kits/ops/op-helage.js, op-zuole.js

  • Seen: a 武者 healed once per enemy struck (a double strike healed once). Official: PRTS §武者 — 常态持有禁疗, ignored by the unit's own trait / talent / skill heals; the trait heal follows every damage output. Now: one heal per damage instance dealt to an enemy (skill, item and bond damage too; not dodged hits, 流失, element 损伤, talent or DoT damage), passing ignoreHealFree; 收割者 keeps its own rule. Golden: diy-007 / 008 / 009 (赫拉格), diy-033 / 034 / 035 (左乐); 宴 unchanged. Tests: test/sim/feedback5-musha-heal.test.js.

25.7.4 庇护 is one effect per unit, the strongest counts (follow-up 18) — kits/shared/tier1.js PROTECT / holdProtect, kits 宴, 余, 缪尔赛思, 赫拉格, 左乐, 赫德雷

  • Seen: the older kits used private 庇护 multipliers, so two sources multiplied (25 % + 18 % → 38.5 %). Official: ba.protect — same-name 庇护 take the highest (遥's bubbles use another key). Now: holdProtect applies the shared PROTECT effect (applyStrongest), refreshed by each kit; the 自选 kits already did. Golden: unchanged. Tests: test/content/feedback5-protect.test.js.

25.7.5 "治疗生命值低于50%…" compares as each module's client buff does (follow-up 19) — kits/shared/tier4.js installLowHpHealBonus, kits/shared/tier5.js lowHpHealUp

  • Official: the client module buffs — LT for 瑕光 / 古米 / 塞雷娅 / 录武官 / Touch, LE for 华法琳 / 闪灵 / 黍 / 凯尔希. Now: 华法琳's and 闪灵's PHY-X heal ×1.15 at or below 50 % (不高于), the LT ones strictly below; 黍 and 凯尔希 were already ≤. Golden: unchanged. Tests: test/content/feedback5-lowhp-heal.test.js.

25.7.6 链愈师: the heal jumps within the 3×3 around the last one healed (follow-up 27) — ai.js chainHealNext / doHeal, kits/ops/op-monstr.js

  • Seen: 莎草's chain jumped within 2.5 tiles, Mon3tr's kit within the 3×3. Official: PRTS §链愈师 — jump range x-4 (3×3), no repeat, ×75 % per jump, lowest HP ratio first then the latest deployed, full-HP allies selectable, bound by 禁疗. Now: chainHealNext serves the profession and Mon3tr. [ASSUMED]: a 禁疗 unit is never a jump target. Golden: roster-004 / 005 / 023 / 024, bond-sargonShip-low, diy-133 / 134. Tests: test/sim/feedback5-chain-heal.test.js.

25.7.7 One reading of charge_token[born]: the owner's redeployment readies a waiting summon (follow-up 20) — kits/ops/op-phatom.js, op-kalts.js

  • Official: the client buff template — charge_token[born] on the owner's birth (NORMAL timing); the other summoner kits read it so. Now: 傀影's (re)deployment readies a waiting 镜中虚影 at once, as 凯尔希's does Mon3tr. [ASSUMED]: NORMAL = usable now. Golden: unchanged. Tests: test/content/feedback5-summon-recharge.test.js.

25.7.8 Misery's S1 / S2 are deploy-timed duration skills (follow-up 12) — kits/ops/standin-acspec.js

  • Now: 物理的服从 / 战争的恭顺 (PASSIVE / ON_DEPLOY, 10 s) are kind: 'duration' with activateOnDeploy — a start and an end at every deployment, the PR #109 contract; numbers unchanged. Golden: standin-01, standin-06. Tests: test/content/standin_acspec.test.js.

25.7.9 伺夜 S2 casts while the pack is in its 战术点形态 (follow-up 2) — kits/ops/chess_char_3_19-vigil.js (installGift)

  • Now: S2 casts when ready while the pack is on the field in either form and holds no unused gift; in the form it pays its DP at once and the gift waits for the returning pack. [ASSUMED]: S2's 备注 uses S1's words, so the form counts as on the field. Golden: unchanged. Tests: test/content/kits_alt_t3.test.js.

25.7.10 「同时攻击阻挡的所有敌人」 takes up to the block count of targets (the queued centurion question; Mon3tr S3) — ai.js acquireTargets / targetCount (hitAllBlocked)

  • Official: PRTS §强攻手 / 重剑手 / 推击手 — max targets = the block count (≥ 1), blocked enemies first; the client flag _limitedMaxTargetNumToBlockedCnt. Now: an attack with hitAllBlocked takes max(1, ⌊block count⌋) + maxTargets mods targets from its range, blocked first (§5.5); Mon3tr S3 drops its maxTargets: 1. [ASSUMED]: the mods add to the block count. Golden: 52 scenarios with such an attacker. Tests: test/sim/feedback5-block-targets.test.js.

25.8 WE2 — fidelity follow-ups (data, match, UI)

25.8.1 Summon hand counts from the tokens' own talents (follow-up 21) — tools/build-data.mjs tokenTalentDeckBonus, match/player/diy.js placeableTokens

  • Seen: placing 麦哲伦 or 令 gave one summon piece, 白铁 one device, 夜莺 one 幻影. Official: PRTS 卫戍协议/帮助 §战斗部署 — the hand gets as many summons as the deploy limit, not the initial stock; the owners' talents give 3 at once / 5 held (麦哲伦, 令, 电弧), 2 / 3 (白铁, 娜斯提), 幻影 3. Now: the token talents' max_deploy_count / max_deck_stack_cnt join the variant stats; hand counts 麦哲伦 / 令 3 (4 with SUM-Y), 电弧 3, 白铁 2, 夜莺 3, 娜斯提 2, 望 6 (TRP-X 7), 多萝西 9 (the hand's cap), others 1. Golden: unchanged. Tests: test/content/feedback5-we2-summon-hand.test.js.

25.8.2 望's 棋子 on any tile; no hand card for 予愿安洁莉娜's “一会儿见!” (follow-ups 22, 24) — tools/build-data.mjs TOKEN_POSITION_CORRECTIONS / summonRecord

  • Official: PRTS 棋子 部署位置 全部位 (its 备注: the in-game summon info is wrong); PRTS “一会儿见!” is not selectable and her displayTokenDict is empty. Now: the 棋子's position is ALL (a 高台 too); a summon is placeable only when an owner displays it, so the marker stays out of the hand (the kit places it). Golden: unchanged. Tests: test/match/feedback5-we2-placement.test.js, test/content/op_aglna2.test.js.
  • Official: Mon3tr's 自我修复 — 重构体 on the ground inside her attack range; PRTS 战术锚点 — only ranged tiles outside 凯尔希·思衡托's attack range. Now: ownerRange from the owner's talent, new ownerRangeOutside / rangedTilesOnly; an owner turned or promoted so that her range covers the 战术锚点 sends it back with a toast; client and bot follow. [ASSUMED]: "ranged operators may stand on melee tiles" does not widen the 战术锚点. Tests: test/match/feedback5-we2-placement.test.js.

25.8.4 Triggers re-checked: 黑键 S2 at full SP with one free tile; 黍 S3's ally rule in the data (follow-ups 23, 17) — kits/ops/op-ebnhlz.js, tools/build-data.mjs TRIGGER_ALLY_RULES, sim/simdata.js normalizeSkill

  • Official: 黑键 S2 (AUTO) 备注 — no deployable tile in range → no cast, so the condition is a free tile, not an enemy; act2autochess TRY_SEARCH_ALLY_SKILL (黍 S3) = an injured ally on the skill range. Now: 黑键 S2 is the kit's cast at full SP with a free attack-range tile; 黍 S3 resolves to SKILL_RANGE + allies in the data (the kit override is gone); 温蒂's cannon (DEFAULT) and 凯尔希·思衡托 S2 (ACTIVE_RANGE on an injured ally) stay. Golden: diy-114 / 115 / 116. Tests: test/content/op_ebnhlz.test.js, op_shu.test.js.

25.8.5 Kit-side larger-range triggers: 谬因 S2 and 薇薇安娜 S3 (follow-up 16) — kits/ops/op-aphris.js, op-vvana.js, sim/skills.js setTrigger

  • Now: the scan of the 41 kitted 自选 MANUAL skills found two whose running range the data cannot hold (§25.5): 谬因 S2's beam path is a content trigger range; 薇薇安娜 S3 switches to ACTIVE_RANGE 3-2 after its first cast of a deployment (new additive skill.setTrigger). [ASSUMED]: the owner's rule read through these kit ranges. Golden: diy-045, diy-078. Tests: test/content/feedback5-we2-area-triggers.test.js. The skills that act through their summons' areas (麦哲伦 S1, 令 S3, 电弧 S2 / S3) waited for the owner: §25.11.2.

25.8.6 自选 rosters refuse SO-A / SO-B / RA-A like ISW-A (follow-ups 25, 10) — shared/diy.js DIY_EXCLUDED_MODULE_TYPE

  • Official: their texts name other modes (集成战略 / 生息演算); battle_equip_table gates them (validInGameTag). Now: /^(?:ISW|SO|RA)-/ modules are refused by validateDiyPicks, dropped by checkDiyPicks and hidden by the picker. [ASSUMED]: the reason of the owner's ISW-A decision applies. Tests: test/diy.test.js, test/ui/diy-ui.test.js.

25.8.7 自选 in the match: random grants draw 自选 pieces, the banned line, AI 托管 (follow-ups 9, 11, 13) — match/player/diy.js diyStockEntries, effectsMeta.js rollChess / rollPool, ui/gameLogic/diy.js diyBannedPieces, ui/matchInfo.js, match/bot.js buyScore / levelUp

  • Official: PRTS / the season notes — 自选 pieces widen the 调度中心's random range. Now: bands, items, 特质, 机变 cards and reward pools that grant a random operator also draw the player's own 自选 stock (never another player's); a slotted piece left out of the shop (every bond banned) is named under 本局禁用干员 in the briefing, the draft's match info and the in-game tab; AI 托管 scores the player's own 自选 piece higher and levels 调度中心 one round earlier for its tier. [ASSUMED]: the 托管 preference (bonus 12, one round). Golden: coop2-NORMAL-70-diy. Tests: test/match/feedback5-we2-diy-grants.test.js, test/ui/feedback5-we2-diybanned.test.js, test/match/feedback5-we2-bot-diy.test.js.

25.8.8 Smaller items: 塑心's cap, 斩业星熊's normal attack, GitHub #148, the shims (follow-ups 30, 15, 3, 5) — content/garrisons/battle.js fireGain, tools/build-data.mjs classifyAttack, content/bonds/addon/battle.js raidTile

  • 塑心 (garrison_90): the per-battle cap (10 / elite 20) is the trait instance's whichever bond is highest — a new highest bond no longer opens a fresh cap (found in the PR #178 review; test/content/feedback5-we2-garrison90.test.js).
  • 驭法铁卫 (斩业星熊): the data's normal attack is physical — arts only while a skill runs (display and AI only).
  • GitHub #148 reproduced: a melee 突袭 member never lands on a 高台; on 战场#08 it lands on a 围墙 tile (deployable, drawn raised), so no rule changed here (test/content/feedback5-we2-raid-148.test.js); the owner then chose a landing tile where it can block (2026-10-06, §25.11.3).
  • The kits/tier1..6.js re-export shims and shared/standIn.js diyRecord are removed.

25.9 Ports from 0.1.4 (W5M, W5R and the feedback4 merges)

0.1.4 (§24, CHANGELOG 0.1.4) shipped from feedback4 while the refactor ran; every fix was ported by hand into the split layout, with the golden moves each named: PRs #150, #66, #146, #74, #72, #103, #149, #109, #160 and W4A (高台 by the trait, the 阿戈尔 revive slots, the 联防 devour) in W5M; PR #129 (teammate scouting); the restarted-cast events and 伊内丝's 影哨 before 不屈; PR #73 (operator battle voices, §21.30) and the settlement line's speaker; PRs #186, #134, #185, #24, #183 (their 42 new strings through t()); W4B (阿戈尔's devoured ATK as a 最终加算, a fallen marker's marks, 缇缇 S2's pulses, §24.7 / §24.8); W4D (a sleeping enemy cannot be blocked, §24.9; W5R, §25.9.1) and W4C (a tap on the board stays on its tile, §18.1; §25.9.2). Where a 0.1.4 change met 0.2.0 work:

  • 突袭 × PR #109: deploy-timed skills became duration skills, which the #49 rule (a passive that is on) missed; raidPoll counts a skill that needs no SP while its window runs (test/content/bonds_addon.test.js).
  • 一击即退 × PR #109: the generic deploy-timed passives (the 预备干员-特种 stand-in) are kind: 'duration' too (content/generic.js; numbers unchanged).
  • The invite line (PR #103) × i18n: the copied line goes through t(), Chinese byte-identical to the PR's.
  • 高台 by the trait × 补位: a stand-in is placed by its own trait (placeClass, piecePosition); of the 自选 picks only 温蒂 carries the line.
  • Voices × 补位 / 自选: the stand-in or the 自选 operator on screen speaks for itself; the settlement line, which never played (it looked up chess ids as character ids), now names the record the own piece shows (screens/game.js ownShown).

25.9.1 沉睡 frees the block slot on the split layout; 酒神's 迷狂牢笼 skips a sleeper too (W4D, GitHub #140; ported by W5R) — sim/battle/blocking.js _checkBlock, sim/battle/status.js applyStatus, sim/ai.js updateEnemy, sim/buffs.js (sleep), kits/ops/chess_char_3_12-blemsh.js (瑕光 S2), kits/ops/op-phatm2.js cageBlock

  • Port: §24.9 (the rule, PRTS 异常效果 SLEEPING = 无法行动+无敌+不可阻挡, the owner's decision of 2026-10-06), its §24 intro entry, the §5.5 Blocking sentence and §22.8's "(since §24.9)" came over from feedback4 unchanged; §24.9's heading now names the 0.2.0 files above (0.1.4 had them in sim/Battle.js and kits/tier3.js). On the split layout _checkBlock (battle/blocking.js) refuses flags.sleep and applyStatus (battle/status.js) releases an enemy that falls asleep. WE1's swing cut (§25.7.2) runs before enemyAttack, but a plain-buff flags.sleep still makes the enemy stun (units.js), so its release stays in updateEnemy's cannot-act branch, after the cut: the swing is cut, the enemy is let go, and it stands until it wakes.
  • New on feedback5: 酒神's 迷狂牢笼 (O22, cageBlock) blocked every unblocked enemy on its tile but the old three 不可阻挡 flags; it now skips a sleeper too (PRTS BLOCK_FREE 「无法阻挡/被阻挡」: no blocker of any kind). Before, the 牢笼 held the sleeper, refreshed its own HP and block count for it and lost it on the enemy's next update. 予愿安洁莉娜's S3 helper needed no change (canTargetEnemy never returns a sleeper). Followed without code: blockedTargets, the 要塞 branch of acquireTargets (u.blocking), “庞贝”'s clock (blockedBy), the snapshot's UF.BLOCKED and the client's block marker; 死芒 S2 折朽 (自选) sleeps the enemies it links — one her 悲叹的仆役 held is let go, and SOC-X's ×atk_scale on "an enemy one of her summons blocks" no longer applies to it.
  • Golden (feedback5's own list; a probe over all 283 scenarios counted the sleeps of blocked enemies, in brackets, and every scenario that moved has some; the outcomes differ from §24.9's because feedback5's baselines differ, WE1's damage frame first): roster-003 (小满 S2 [5]: the same fight, blocked 27 → 29 and the snapshot hashes); roster-005 (elite 小满 S2 [3]: the 140 s timeout with 83 kills and 1 leak, where it cleared at 135.43 s with 84); roster-013 (缇缇 S3 [11]: the same kills, blocked 22 → 33); roster-032 (缇缇 S1 [10]: 71 kills instead of 68, 8 leaks instead of 11, 4 knock-outs instead of 8, 萨尔贡 layers 41 → 35); roster-038 (elite 缇缇 S2 [1]: ends at 197.40 s instead of 197.27 s); roster-039 (瑕光 S2 normal + elite [31 + 12]: the same kills and counted leaks, the 窃贼 gets away, blocked 53 → 87); roster-042 (缇缇 S2 [3]: ends at 168.77 s instead of 166.93 s); bond-maniShip-high (小满 [1]: blocked 29 → 30, the snapshot hashes); solo-FUNNY-1 (ai_0's 缇缇 [3]: only ai_0's damage dealt); coop4-FUNNY-6 (ai_0's 缇缇 [15 + 56]: from round 9 ai_0's 精准 / 萨尔贡 layers and boss damage move, ai_1 loses no LP from round 11, team LP 124 → 130); coop4-ABYSS-7 (ai_2's 小满 [18]: ai_2 one kill fewer, ai_0 one more LP lost in round 9); coop2-NORMAL-8-serverrun (ai_1's 缇缇 [83]: only ai_1's damage dealt — WE1 had already turned this match into a loss); coop2-NORMAL-70-diy (the 自选 seat h_0's 缇缇 [171], ai_0's 缇缇 [74] and 小满 [19 + 21]: h_0's layers move from round 4, then its AI 托管 buys differently, boards from round 7; ai_0 246 kills instead of 233, the same end); diy-131 (死芒 S2 at tiers 5 and 6 [1 + 1]: the snapshot hashes only); diy-132 (elite 死芒 S2 [2]: ends at 199.50 s instead of 198.13 s, the same kills). coop2-HARD-4 sleeps a blocked enemy [1] and did not move: the sleeper wakes with no room and is blocked further on, and a match digest has no snapshot hashes (its kills, leaks, damage and funds are the same). fields and standins did not move; no scenario spawns a 牢笼 over a sleeper.
  • Tests: test/sim/feedback4-sleep-unblock.test.js (5 cases, from feedback4) passes unchanged on the split layout; every case fails with the three sim hunks reverted, case 3 (the plain-buff sleep) with only the ai.js release reverted.

25.9.2 A tap on the board stays on its tile; long browser waits poll in slices (W4C, the 0.1.4 release candidate; PR #149) — render/app.js onTapTarget, test/e2e/client.mjs waitForFunctionLong

  • Seen (the 0.1.4 release candidate, test/render/models.browser.test.js #4.1 touch): at 844×390 a tap on the row-12 unit opened the bond popup instead of selecting it. Cause: the browser's touch adjustment moves a tap onto a nearby element that responds to clicks (pointer listeners do not count) when the finger's contact area reaches one, and PR #149's 收起 toggle (§10) had pushed the bond discs one button (≈ 55 px) to the right, over row 12. Now (§18.1 Touch): the field canvas is a click target of its own (a no-op click listener, app.js onTapTarget), so a tap on the board stays on its tile; the browser test passes with #149's client diff reverted and fails with it. Ported as is (render/app.js keeps createFieldView, §25.1).
  • Test harness: the real-server browser suites' long waits poll in slices under the browser's 90 s CDP protocol timeout (waitForFunctionLong, PROTOCOL_TIMEOUT_MS). Puppeteer runs a whole waitForFunction poll inside one Runtime.callFunctionOn, so watch-bonds' 240 s wait died with a bare "Waiting failed" whenever the own battle outlasted 90 s (playtest2.real's 120 s wait had the same gap) — a harness timing flake, no product change.
  • Golden: unchanged (client and tests only). Tests: test/ui/playtest2.test.js, test/e2e/client-wait.test.js; browser: test/render/models.browser.test.js (RENDER_E2E=1), test/ui/watch-bonds.e2e.test.js and test/ui/playtest2.real.e2e.test.js (SP_E2E=1).

25.10 Settled decisions, what stays [ASSUMED], and the questions left to the owner

Decision Settled
No web framework: plain node:http + ws (§25.1) the owner, 2026-10-04
The 补位 plan: the 55 NORMAL chess droppable, all owned by default, no borrowing, bots own everything (§25.3) the owner, 2026-10-05
A not-owned chess shows as its stand-in everywhere (§25.3) the owner's recall, 2026-10-06
自选编队: 2 + 2 slots, owned 6★ with a kit, any skill / module, prototypes locked, per-player shop, levels by the chess forms (§25.4) the owner, 2026-10-05
The collab 6★ out of the 自选 pool, 焰狐龙梓兰 included; ISW-A refused (§25.4) the owner, 2026-10-05
Chinese default, English switch, official EN game texts, PR #70 credited (§25.2) the owner, 2026-10-05
The larger-range rule (ACTIVE_RANGE) on DEFAULT, on SEARCH and for 深巡 S2 (§25.5) the owner, 2026-10-05 (deliberate deviation)
The 重装 stand-ins and every 自选 重装 cast with an enemy in range (§25.5) the owner, 2026-10-05 (deliberate deviation)
纯烬艾雅法拉 S1 at full SP; 伺夜 S1 only with his pack on the field (§25.5, §25.6.5) the owner, 2026-10-05
Enemy attacks strike at their damage frame; a short stun before it cuts the swing (follow-up 29, §25.7.2) the owner, 2026-10-06 (kept)
望's placed 棋子 take and return her stock, so S1 / S2 keep cycling (§25.11.1) the owner, 2026-10-06 (deliberate deviation from 「不消耗持有数量」)
Skills that act through summons cast on an enemy in a summon's area: 麦哲伦 S1, 令 S3, 电弧 S2 / S3 (§25.11.2) the owner, 2026-10-06 (the larger-range rule)
A melee 突袭 member lands on a tile where it can block before one where it cannot (GitHub #148, §25.11.3) the owner, 2026-10-06
华法琳's granted trait stops at the data's 7 / 14 (PRTS 3/27: lowered from 12 / 24; GitHub #175, PR #192, §25.13.2) the owner, 2026-10-06 (replaces the 12 / 24 lock)
A newly merged elite 拉普兰德 is a new 拉普兰德: +8 on its first manual refresh of the round (GitHub #169, §25.13.3) the owner, 2026-10-06 (was [ASSUMED]: the copies' count carried)
The leader pool = bloodPoint × the players alive when the fight starts, solo × 1 (PR #209, §25.13.4) the owner, 2026-10-06 (replaces 「保持固定血量」; config restores it)
Flying units hover 1.3 tiles; 帝国炮火先兆者 ×2 drawn 1.263× taller, 木制瑞印 mirrored (PR #211, §25.13.5) the owner, 2026-10-06
A bounty pays whenever its enemy dies — terrain and other 无来源 deaths included, in 联防 too (§25.14.3) the owner, 2026-10-06 (relayed with the community reports)

Settled by the sources in W5V, no longer questions: 泥岩 S3's dormancy is 闭锁 — she lets go and blocks nobody (PRTS, §25.11.4); 阿 S2 / S3 keep the data's DEFAULT, 忍冬 S2's and 深靛 S1's notes are what the kits do (§25.11.5).

Still [ASSUMED] (each also in its code comment): potential 0 for every 自选 pick; the per-player 自选 stock 8 / 5; bots field no 自选 piece; SO-A / SO-B / RA-A refused for ISW-A's reason; the AI 托管 preference for 自选 pieces; a stand-in placed by its own position; the 联防 map names; 伺夜 S2 in the 战术点形态; the enemy swing's re-taken targets and kept cooldown; a woken sleeper re-blocked in the enemy update order where it stands (§24.9); 望's card back in her hand the moment a 棋子 leaves, a 棋子 over the cap lost, TRP-X's 7th placed 棋子 taking none, her stock kept through her redeployment; 电弧 S2's area = 赛柯's 3-2 range, standing summons only; the official 突袭 landing taken as random among the legal tiles, ranged members on the old order, the target's own tile counted as a blocking tile; 泥岩's 反制 during 闭锁 not modelled; the English of the remake's own strings; the lite zip without public/fonts, the zips' unchanged package.json (maintainer scripts fail there); the 联防 payee of a bounty death no operator caused — the helper of the half it fell on (§25.14.3); the stand-in and operator kit items of the tables above.

Left to the owner (none changed in 0.2.0): 淬羽赫默's 丰润羽翼 as 生命回复速度 (the client template, no PRTS note) and 遥's break heal on the ATK cached at the grant (both kept, §25.11.5); whether a passive counts as 技能就绪 for the whole deployment or its window only (§25.6.7); Stormeye S2 / Pith S1 (AUTO attack buffs) on DEFAULT or at full SP; 赫默 S2 and 巫恋 S2 (wasted with nobody to heal / no enemy); the AUTO cast of an attacker of 白铁's 铁钳号. Answered on 2026-10-06 and moved to the table: 望's stock and 阻回, the 突袭 landing (GitHub #148), the trigger areas through summons; settled by the sources: the ally condition for 阿 S2 / S3, 泥岩 S3's 闭锁.

Open polish (no rule): 34 or more of the 38 summons of the 自选 kits have no Spine skeleton in the online sources, so the field draws them as the avatar placeholder (§25.12); scripts/make-windows-bundle.mjs still copies every tracked file but test/ (it could reuse the package selection); the THIRD-PARTY-NOTICES "In the release bundle" column does not tell the lite zip (no fonts) apart; the enemy prefabs whose default clips differ from the manifest's roles (猎狗pro, 大鲍勃, 碎骨, “斩胄之剑” / “破胄之锤”, §25.14.1).

25.11 The owner's decisions of 2026-10-06 and the items the sources settled (W5V) — kits/ops/op-wang.js, kits/shared/summoner.js summonTriggerArea, content/bonds/addon/battle.js raidTile, kits/ops/chess_char_4_18-mudrok.js

The questions WE2 and the kit waves had left open went to the owner as one batch on 2026-10-06; W5V implemented the answers and checked the remaining items against PRTS and the client (W5V's commits name every golden scenario moved).

25.11.1 望's placed 棋子 take and return her stock (the owner's decision of 2026-10-06) — kits/ops/op-wang.js

  • Seen (O19, follow-up 22): in this mode her 棋子 are hand pieces deployed with the board, 「无视所属干员的持有状态, 不消耗持有数量」, so her stock stayed at 6; one S1 / S2 cast (「立即获得两枚棋子」) reached the cap 7, and 阻回 held her SP for the rest of the battle — the skill cast once.
  • Official: PRTS 卫戍协议/帮助 §作战阶段 (placed summons ignore the holding; one that leaves is redeployed in place once the conditions are met); PRTS 望 铸子 (6 usable, 7 at most) and the S1 / S2 备注 (阻回 at the stock cap); the client's charge_token templates (the stock = cnt at her deployment, the skills' +cnt, S3's overflow built as 跟子); a summon that leaves returns to its owner's card while its redeploy time runs.
  • Decision: the owner's decision of 2026-10-06, a deliberate deviation from 「不消耗持有数量」 — the placed 棋子 count in her stock, and a 棋子 that goes off or returns replenishes it by the official rules, so her skills keep working.
  • Now: every deployment of one of her 棋子 (the battle start's and each in-place return) takes one from her stock (never below 0); a 棋子 that leaves the field (set off, or gone with her) puts one back at once, at most the cap; the in-place return also waits for one in stock. At the cap a 棋子 that goes off is lost to the cap and its return takes one, so the stock drops to 6, 阻回 lifts, her SP recovers and the next cast fills it again — S1 / S2 keep cycling. S3's "+8, the overflow as 跟子" starts from the lower stock (2 placed: 4 + 8 → 7 with 5 跟子, was 7).
  • [ASSUMED]: the card is back in her hand the moment the piece leaves (its redeploy time is the return's own wait); a piece over the cap is lost; 7 placed 棋子 (TRP-X's deploy limit) on a stock of 6 — the 7th takes none; the stock carries through her mid-battle redeployment.
  • Golden: unchanged (no corpus battle places her 棋子). Tests: test/content/op_wang.test.js (9: the stock after the placed pieces, a piece set off and its return, casts up to the cap, 阻回 only at the cap, a piece set off at the cap, TRP-X's 7). Docs: docs/SIM.md (token pieces).

25.11.2 Skills that act through summons cast on an enemy in a summon's area: 麦哲伦 S1, 令 S3, 电弧 S2 / S3 (the owner's decision of 2026-10-06) — kits/shared/summoner.js summonTriggerArea, sim/skills.js (addTriggerRange { keys, profile }), kits/ops/op-mgllan.js, op-ling.js, op-radian.js

  • Seen (WE2's scan, §25.8.5): their effects reach through the summons' areas, but the data's DEFAULT waits for an enemy in the operator's own 3-1 (no data range can carry the larger area).
  • Official: no trigger row (DEFAULT). The areas (PRTS, the client): 麦哲伦 S1 — 停顿 / 束缚 on her range and each drone's range, air units too; 令 S3 — the x-5 around each summon, ground only (备注 「不可对空」); 电弧 S2 — 赛柯's bullets, ground only; S3 — 停顿 + 法术脆弱 before her summons' damage, air units too (备注 「召唤物可对空」).
  • Decision: the owner's decision of 2026-10-06, the larger-range rule (§25.5) read through the summons: cast when an enemy is in the operator's range or in a deployed summon's area that the skill acts through.
  • Now: summonTriggerArea(battle, owner, skillId, area) registers a content trigger range (checked every tick, on top of the data's DEFAULT) built from the owner's standing summons, each giving { keys, profile } — tile keys with the enemy profile the effect selects by (canHitFly false: ground enemies only), a new additive entry form of addTriggerRange. 麦哲伦 S1: each drone's range, air units too; 令 S3: each summon's x-5, ground only; 电弧 S2: each summon's range (赛柯's 3-2), ground only; S3: each summon's range (桑特拉's 3-1), air units too. The other skills of the three get no area.
  • [ASSUMED]: 电弧 S2's area is 赛柯's 3-2 range, not its S2 bullets' longer flight (0.8 s ≈ 4 tiles at rank 7); only summons standing on the field count.
  • Golden: unchanged (no corpus battle places a 自选 summon). Tests: test/content/feedback5-wv-summon-triggers.test.js (an enemy in a summon's area outside her range casts the skill; a flyer there does not for 令 S3 / 电弧 S2 and does for 麦哲伦 S1 / 电弧 S3; nobody in an area, or the drone off the field: no cast; all three fail with the helper switched off). Docs: docs/SIM.md (content trigger ranges), the skills.js header.

25.11.3 A melee 突袭 member lands where it can block (GitHub #148; the owner's decision of 2026-10-06) — content/bonds/addon/battle.js raidTile

  • Seen (GitHub #148, reproduced in WE2, §25.8.8): a melee member never lands on a 高台, but it lands on a 围墙 tile (drawn raised; it blocks nothing there) when that is the nearest tile covering its enemy, even with a road tile covering it too.
  • Official: 「再部署至一名地面敌人周围」 — no tile preference stated.
  • Decision: the owner's decision of 2026-10-06 — prefer a landing tile where it can block an enemy over a tile where it cannot; keep the rest of the landing rule.
  • Now: for a melee member (position not RANGED) with a block count, raidTile ranks first the tiles where its block applies — ground units pass the tile (not a 围墙 / 围栏: Battle._blockerFor) and an enemy ground path runs through it (Battle.groundPathTiles) or its enemy stands on it — then the old order (nearest, then the facing frame). The rest of §22.2's rule is unchanged: the 8 most advanced targets in turn, a tile from which its range covers the target, grid.canStand (never a 高台, never the 深水区), free tiles, no useless jump. A 围墙 landing remains where nothing else covers the enemy. §24.2's note on #148 points here.
  • [ASSUMED]: the official landing picks among the legal tiles at random (the preference is the owner's); ranged members keep the old order; "its enemy stands on it" counts as a tile where it blocks (a target off the computed paths).
  • Golden: fields boss-boss_2-pair, boss-boss_3-pair, boss-boss_4-pair, boss-boss_6-pair, boss-boss_7-pair, hidden-boss_10-pair — their 突袭 members (伊内丝, 缄默德克萨斯, 仇白) now land on a road tile of the enemy path instead of a 围墙 tile (6 of the 8 changed landings) or an off-path road tile (2); roster / bonds / matches / standins / diy unchanged. Tests: test/content/feedback5-we2-raid-148.test.js (updated on purpose: every stage × every low enemy tile — never a 高台; whenever a tile where it blocks covers its enemy it lands on one; the old code fails at act1 m02).

25.11.4 泥岩 S3's 10 s dormancy is 闭锁: she lets go and blocks nobody (PRTS) — kits/ops/chess_char_4_18-mudrok.js

  • Seen: the kit kept her blocking through the dormancy of S3 秽壤的血脉 ("keeps blocking").
  • Official: PRTS 泥岩 S3 备注 — the first 10 s are 闭锁, not 无法行动, with 眩晕 / 冻结 / 沉默 反制; PRTS 异常效果 — 闭锁 (SHELTERING) = 强制缴械 + 无敌 + 不可阻挡, and 不可阻挡 (BLOCK_FREE) 「无法阻挡/被阻挡,自动解除阻挡」: on an operator, it blocks nobody and lets go of what it held (the enemy side of the same state: §24.9).
  • Now: the dormancy adds the flag noBlock (with invulnerable and disarm): the enemies she held walk on (ai.js lets go of a noBlock blocker) and she blocks none until she wakes. [ASSUMED]: the 备注's 反制 are not modelled.
  • Golden: roster-040 (it fields 泥岩 S3: the enemies she held walk on during the 闭锁). Tests: test/content/kits_alt_t4.test.js (updated on purpose: the 闭锁 releases the three walkers and blocks none — enemies placed on her tile neither —, awake she blocks again once their stun ends).

25.11.5 Settled by the sources with no rule change: 忍冬 S2, 深靛 S1, 阿 S2 / S3; the shared 庇护 — kit comments, test/content/feedback5-wv-sources.test.js, test/content/feedback5-protect.test.js

  • 忍冬: PRTS's 「无法选中被自身阻挡而没有位于技能范围内的敌人」 is the 备注 of S2 坠刃拷问 (技能范围 3-12), not of S3 (its only 备注 is the ASPD update; the client's S3 selector is the standard 「同时攻击阻挡的所有敌人」 one); the S2 kit already selects on the 3-12 keys only — pinned.
  • 深靛 S1: the S1 「每次攻击的攻击倍率」 applies to her stored energies (PRTS S1 备注; the client's ChargeAttackS1 reads the same atk_scale), as the kit does (ai.js resolveHit) — pinned.
  • 阿 S2 / S3: the data's DEFAULT — no trigger row names him; the skill prefabs cast with no target and keep the SP spent (_allowNoTarget 1, _checkHasTargetBeforeDoCast 0, _recoverSpIfNoTarget 0); PRTS has no ally condition. With no ally in his range the cast spends its SP and does nothing; the kit comment records the sources.
  • 庇护: 淬羽赫默, 黍, 斥罪, 森蚺 and 娜斯提 grant it through the shared holdProtect (kits/shared/tier1.js, §25.7.4), 遥's bubbles (their own key) through the shared protectMods; a guard test keeps kit-local copies out — a pure refactor.
  • Kept as they are: 淬羽赫默's T2 丰润羽翼 as 生命回复速度 (the client's buff template atk_to_hp_recovery: 禁疗 does not stop it, no 治疗加成) and 遥's T2 break heal on the ATK cached when the bubble is given (§25.10 keeps both open).
  • Golden: unchanged.

25.12 Release packaging and the asset manifest (W5U; the 自选 voices) — tools/package.mjs (npm run package / package:lite), docs/DEPLOY.md §7, tools/assets/plan.mjs, tools/assets/manifest.mjs, tools/fetch-assets.mjs, data/assets.json

  • Seen: 0.1.x releases came from a maintainer script outside the repository that zipped every tracked file (tests, design docs, research notes, dev pages, the Windows-bundle maker) plus all of public/assets as it lay on the maintainer's disk; there was no smaller download. 焰狐龙梓兰 left the 自选 pool (§25.4) but her art stayed in data/assets.json, because fetch-assets refuses to shrink the manifest. The first real build found both module/dec-X.png and module/dec-x.png listed — one file on Windows / macOS, so a zip built there carries one.
  • Official: no game rule (release tooling). The collab exclusion is the owner's decision of 2026-10-05 (copyright); the uniequip table spells one DEC X module dec-X (uniequip_003_aglina) and the others dec-x, and WAH-Y is the other mixed-case type id.
  • Now: tools/package.mjs builds Stronghold-Protocol-v<version>.zip (full) and …-lite.zip, each holding one Stronghold-Protocol/ folder, from an allowlist over git ls-files: server/, shared/, data/, public/ (not public/dev/), the start scripts and the tools a player runs (setup, vendor, fetch-assets + tools/assets/, doctor, and what setup starts: tools/local-extract/, crop-board-atlas), the four research tables the server and fetch-assets read (03-operators, 05-enemies, 05-maps, 07-assets), package.json / lock, LICENSE / NOTICE / THIRD-PARTY-NOTICES, README, CHANGELOG, docs/PLAYING.md and docs/DEPLOY.md; npm ci --omit=dev in the stage adds node_modules and public/vendor. The full zip adds the files data/assets.json and data/local-assets.json list, public/fonts and public/assets/local/ — nothing else on disk, so her 15 files and other orphans never ship. Every check also runs in --dry-run: the refusal list (0.1.x's plus test, .github, AGENTS.md, public/dev, .env / .venv anywhere); imports and the player npm scripts resolve to shipped files; all listed art is present; no two paths differ only in case; no shipped file holds a home-directory path or the machine's account name (text and binary, the name read from the OS at run time); no shipped tracked file has uncommitted changes; a real build also checks the stage against the plan. Measured on the 0.2.0 tree (before the voices below): full 421 421 613 bytes, 14 910 files, 589 MB unpacked; lite 22 088 912 bytes, 6 430 files, 85 MB; the 0.1.x layout of the same tree zipped to 429 644 767 bytes (569 files more, 25 MB more unpacked). Both stages boot; setup --check passes on the full stage and the lite stage plans the art offline.
  • The manifest: data/assets.json regenerated offline (--offline --add-only --allow-shrink) drops her 8 subtrees (163 entries); module icons get one lower-case file per type (module/<type in lower case>.png: modules.dec-X and modules.WAH-Y changed); resolveTemplate drops an array whose lines are all missing (it kept {select: [], place: []} for the 71 自选 voice entries this machine had never downloaded); fetch-assets compares orphans (the --prune targets) case-insensitively. Then (6499f4b) fetch-assets --add-only run online added the battle voices of the 71 自选 operators — 994 lines, 25.5 MB, voiceChars 120 → 191 (the manifest: 7 972 files, 485 MB) — and nothing else changed; the full zip grows by about as much.
  • Open (polish): 34 or more of the 38 summons of the 自选 kits (Mon3tr, 麦哲伦's drones, 令's summons, 电弧's towers, 白铁's piles, 娜斯提's devices, 望's 棋子 …) have no Spine skeleton in the online sources (fetch-assets: "missing skel"), and the manifest lists none of the 38 with a model, so the field draws them as the avatar placeholder; extracting them from a local client (tools/local-extract) or keeping the placeholder is left for later.
  • [ASSUMED]: the lite zip leaves out public/fonts too (fetch-assets downloads the fonts with the art); package.json ships unchanged, so the maintainer scripts (npm test, golden, package) fail inside a zip; docs/img (the README screenshots), docs/WINDOWS.md, docs/ASSETS.md and the Dockerfile stay out (Docker builds from a git clone).
  • Golden: unchanged. Tests: test/package.test.js (9, no network: the selection on samples and on the real tracked tree, the refusal list, a dry run and a --no-install build of a temporary checkout, the scans), test/assets-diy.test.js (no diy.excluded operator keeps art; one lower-case module icon per type; no two manifest URLs differ only in case; orphans compared case-insensitively), test/assets.test.js (the empty-array rule). Docs: docs/DEPLOY.md §7, the README download section (full and lite), docs/ASSETS.md.

25.13 Round 18: GitHub #207, #175, #169 and the community PRs #192, #209, #211 (W5Y; the owner's decisions of 2026-10-06) — server/match/builtinMeta.js, server/sim/content/garrisons/battle.js, server/match/player/acquire.js, server/match/gamedata.js bossPoolShareOf, render/units.js FLY_HOVER

The round-18 triage of the open issues and pull requests left three bugs and three community changes for 0.2.0; the owner decided the open questions on 2026-10-06. Each item is one commit (W5Y) naming the golden scenarios it moved; a ported PR credits its author there and here.

25.13.1 拟态物质 with 2 copies owned and the pool out gives nothing (GitHub #207) — server/match/builtinMeta.js (use_equip_reward_char_chess), server/match/bot.js itemTarget

  • Seen (GitHub #207, reproduced in the triage): an elite 溯光星源 plus 2 normal copies hold all 5 of the Ⅵ阶 pool's copies (3 + 2); 拟态物质 on one of the normal copies gave a random same-bond operator (白面鸮 on that seed; 至简 is in the same roll). Also in 0.1.4.
  • Official: the item text 「若已拥有至少2名该初始干员,则再获得1名该初始干员;否则随机获得1名同盟约初始干员」 — the 否则 is the case of fewer than 2 owned; research 06 §7: some effects fail when the copy cap is hit.
  • Cause: with 2 owned the handler returned only when grantChess(base) gave a piece; the null of an empty pool fell through to the same-bond roll.
  • Now: with 2 normal copies owned the item asks for the 3rd and stops there — none left in the pool, nothing is given (the item is still consumed); the same-bond operator is only for fewer than 2 owned. With copies left the 3rd copy still merges into the elite. The bots' carrier choice skips 2 copies whose pool is out.
  • Golden: unchanged (no corpus match equips it on a pair whose pool is out). Tests: test/content/items.test.js (the report — an elite and 2 normal 溯光星源, normal and golden item: nothing granted, no other operator, consumed; 2 copies with the pool drained: nothing; 2 copies with copies left: the elite), test/match/bot-smarter.test.js (no bot carrier on a pair whose pool is out). Docs: docs/META.md (built-ins, the bots' items), research 04 / 06.

25.13.2 华法琳's granted trait stops at the data's 7 / 14 layers (GitHub #175; PR #192 by @kukiC; the owner's decision of 2026-10-06) — server/sim/content/garrisons/battle.js, server/match/fields.js layerAllowanceOf

  • Seen (GitHub #175): the card says 至多7层, but the operator in front of 华法琳 gained up to 12 layers per bond in a battle (the elite: 24). The cause was the remake's own override GRANTED_CAP_OVERRIDE (12 / 24 for garrison_95_a / _b, in the battle and in the server's client-result layer bound), taken from research 02 Addendum 1. Also in 0.1.4.
  • Official: PRTS 卫戍协议:盟约 下半, 3月27日更新#2调整 (revision 439189): 「[Ⅳ阶]华法琳:赋予的特质的叠层上限从 初始12/精锐24 降低至 初始7/精锐14」 — a nerf; the current 参战干员 record (下半/PRTS盟约记录) says 至多7层 / 14层, and the client blackboard (max_add_count_per_battle 7 / 14) carries it with the other 3/27 numbers. The official notice 8584 says only 「调整每场战斗至多获得的层数」. Addendum 1 had read the PRTS line backwards (7 / 14 raised to 12 / 24).
  • Decision: the owner's decision of 2026-10-06 — follow the current page and the data: 7 / 14 (it replaces the earlier 12 / 24 lock).
  • Now (ported from PR #192 by @kukiC): the override is gone; a granted trait keeps its data cap like a native one — the 7th cast in front of 华法琳 reaches 7 (elite: 14) and later casts add nothing; the client-result bound counts the handed-out trait at the same cap. Research 00 / 01 / 02 corrected (02-bonds.json's note, copied into data/bonds.json miraShip.spec.layerGain, regenerated offline — the only data change).
  • Golden: unchanged (no corpus battle casts 8 times in front of 华法琳). Tests (from PR #192): test/content/garrisons_battle.test.js (updated on purpose: the cap is the data's 7 / 14 — the 7th cast reaches it, the 8th adds nothing), test/match/feedback1-gaps.test.js (the client-result bound with 华法琳 + 古米: 60 + 4·3 + 2 × cap passes, one more is refused), test/ui/playtest3.test.js (both cards show 至多7层 / 至多14层).

25.13.3 A newly merged elite 拉普兰德 is a new 拉普兰德: its first manual refresh this round adds +8 (GitHub #169; the owner's decision of 2026-10-06) — server/match/player/acquire.js _mergeChess, sim/content/garrisons/meta.js SERVER_GAIN_BOND_LAYER_BY_REFRESH_CNT

  • Seen (GitHub #169, new comments): two white 拉普兰德 that had already fired this round merged into the elite, and the elite's next refresh added nothing. The triage confirmed the rest of the report is what the code does: each white copy fires +4 on the first manual refresh it witnesses, also in the 整备区 (§21.1).
  • Official: garrison_123 「<刷新时>若为本回合首次主动刷新,使已激活的【叙拉古】层数+4,此干员在整备区时也有效」 (the elite +8; bb refresh_cnt 1); PRTS 下半 3月27日更新#2 changed only the amounts (+3 / +6 → +4 / +8). No source says whether a merge resets the count; the code carried the copies' highest count onto the elite ([ASSUMED], conservative, since 0.1.1).
  • Decision: the owner's decision of 2026-10-06 — a newly merged elite counts as a new 拉普兰德.
  • Now: _mergeChess carries no per-piece round counter over (拉普兰德's refresh count is the only one): the elite starts at 0 like any new piece, so its next manual refresh this round adds +8 and a later one nothing; an in-place promotion (升华, 博士投影) keeps the piece and its count. A copy re-bought after a sale is still a new copy ([ASSUMED], unchanged).
  • Golden: unchanged (no corpus match merges 拉普兰德 after a refresh in the same round). Tests: test/match/feedback1-meta.test.js (updated on purpose: the elite made from copies that already fired adds +8 on its next refresh, once; new: two white copies refreshed this round, merge, refresh → +8). Docs: docs/META.md (the counter API, the edge cases), docs/PLAYING.md, the code comments.

25.13.4 The leader pool is bloodPoint per player alive when the fight starts (PR #209 by @qingjingshenghuo; the owner's decision of 2026-10-06); 胄's drone share passes 限伤 — server/match/gamedata.js bossPoolShareOf, server/match/finalAssault.js bossPoolHp, tools/build-data.mjs (config bossHpScale), sim/battle/combat.js loseHp, sim/content/bosses.js (死亡集群)

  • Seen (PR #209): the author's play of the official mode — the leader pool grows with the number of players; the remake kept one fixed pool (co-op bloodPoint whatever the count, solo × 0.25 [ASSUMED]) since the owner's 「保持固定血量」 (§20.9 / §20.10).
  • Official: activity_table bossInfoDict bloodPoint per difficulty, no per-player factor in the data; notice 5114's 「两侧的敌方领袖共享生命值(敌方领袖的总生命值不变)」 is about the mirrored copies of a pair field; the PR's rule is the author's observation (实玩反馈), not an official text. 假想敌:胄 (PRTS): a drone that dies 「令假想敌:胄受到最大生命值2%的真实 伤害」; 限伤 (research 11 §2.1) cancels any damage modifier of ceil ≥ 300000 on a leader.
  • Decision: the owner's decision of 2026-10-06 — adopt #209: the pool is the solo table value × the players alive when the fight starts; it replaces 「保持固定血量」.
  • Now (ported by hand onto 0.2.0's files; the PR's one-line data/config.json and its docs/BOSS_HP_ZH.md table were not taken — the data is regenerated, PLAYING §7 gives the examples):
    1. bossPoolShareOf (gamedata.js, used by GameData.bossPoolShare and by finalAssault.bossPoolHp for a plain game-data object): solo × solo (1); co-op with perPlayer true × coop (1) × the players alive at the Final Assault's / Hidden Core's start (alivePlayers(): bots and AI 托管 seats count, eliminated and departed seats do not; at most aliveFull 4; a count left out = a full team). The global config.bossHpScale carries the rule (perPlayer: true, solo: 1); the modes' entries keep only bloodPointKey (one place restores the fixed pool: perPlayer: false, solo: 0.25, its optional × alive / 4 aliveScaling too). data/config.json regenerated offline: only bossHpScale (global and per mode) changed. Leaders still ignore enemyScale; parts and escorts keep their HP.
    2. #209 documented, without fixing, that 胄's drone link (2 % of the pool, DRONE_LINK_BASE 'pool') meets 限伤: the hidden 胄 终极 pool is 21 600 000 / 28 800 000 at 3 / 4 players, a drone 432 000 / 576 000, which the limit would cancel — drones would stop counting exactly in the largest fights. The share is no hit: Battle.loseHp takes noHitLimit and the drone link passes it; a part's 传递 and every hit stay under the limit.
  • [ASSUMED]: the drone share past the limit (the official client checks every damage modifier on a leader and PRTS calls the 2 % 真实伤害, but which max HP it reads is not documented — the remake reads the pool and keeps the share). The count is taken at each boss round's start, as #209 states (a seat that leaves before the Hidden Core no longer counts there).
  • Golden: fields — the 20 boss / hidden scenarios (boss-boss_1..7-pair / -solo, hidden-boss_8..10-pair / -solo): a pair field's pool × 2 (two players), a solo template's × 4 (solo × 1 instead of × 0.25); seven of them (boss-boss_1-pair, boss-boss_1-solo, boss-boss_3-pair, boss-boss_3-solo, boss-boss_5-solo, boss-boss_7-pair, hidden-boss_9-solo) now end at the 200 s cap instead of clearing; the two 联防 scenarios are unchanged. matches — 11 of 18: solo-FUNNY-2, solo-NORMAL-1, solo-HARD-1, solo-HARD-2 now lose the Final Assault (team LP 0 with 93 088 – 176 395 of the pool left; solo leaders have 4× the HP); solo-FUNNY-1, solo-NORMAL-2, solo-HARD-9-boosted (its Hidden Core too) win with more leader damage (team LP −6 / 0 / −1); coop2-NORMAL-3, coop4-FUNNY-6, coop2-ABYSS-10-boosted, coop2-NORMAL-70-diy win with more leader damage (the pool × the players alive). Unchanged: the matches that end before R14 and coop2-NORMAL-8-serverrun / coop2-NORMAL-14-standins (one player left at R14: × 1, as before); roster / bonds / standins / diy unchanged.
  • Tests: test/match/boss-hp-scaling.test.js (from PR #209, adapted: every leader and difficulty × 1–4 players and solo; R14 / R15 leave out two departed players; AI 托管 and bots count, an eliminated player does not; config restores the fixed pool — the PR's test said the opposite —, the plain-object fallback agrees; only the leader's HP scales), test/content/enemies_bosses.test.js (hidden 胄 终极 at 3 / 4 players: the drone's 432 000 / 576 000 land, a hit of the same size is cancelled), updated on purpose: test/match/finalAssault.test.js (the per-player pool, the fixed pool restored through config, a mode entry overriding the global one), test/match/balance.test.js, test/match/bosshp.test.js, test/match/connection.test.js, test/docs-consistency.test.js (the §20.9 record points here; the data carries perPlayer: true), test/sim/playtest6_limits.test.js (the drone share lands past the line), test/match/playtest6-bosspool.test.js (its 5 % knob halved to keep the two-player pool's size: that test is about the pool's arithmetic). Docs: PLAYING §7, DATA (bossHpScale), META, BALANCE, SIM, the design (engine.md 限伤, network.md boss rounds), the §20.9 / §20.10 records, research 00 / 01 / 08.

25.13.5 Flying units hover 1.3 tiles; the 帝国炮火先兆者 pair is drawn 1.263× taller and 木制瑞印 mirrored (PR #211 by @xcdoge; the owner's decision of 2026-10-06) — render/units.js FLY_HOVER / enemyModelScaleY (the mirror flip), tools/build-data.mjs MODEL_STRETCH_Y / MIRRORED_PREFABS, tools/local-extract/enemy_model_offsets.py, docs/research/12-flying-visuals-official.md

  • Seen (player report of 2026-10-05, PR #211): 无人机等飞行单位位置明显偏低 — every flyer drew ~1 tile too low; the two 妖怪 drones had their art under the tile (the flat lift was 0.32 tiles).
  • Official (read from the client by the PR's author, research 12): the fly offset is one model-independent constant, Vector3(0, 0.35, 0) set by CharacterAnimator's constructor and added while flying; 0.35 is in the client's character space, whose unit is the standard battle-prefab scale 0.27 — 0.35 / 0.27 ≈ 1.3 tiles, and an official screenshot measures 1.2–1.4. The battle prefabs carry no per-model vertical correction for flyers. The same prefab sweep found two model quirks: the 帝国炮火先兆者 pair's Graphic scale (0.19, 0.24, 0.24) (sy / sx 1.263) and 木制瑞印's negative X scale (a mirrored model).
  • Decision: the owner's decision of 2026-10-06 — take all of it (the lift and both quirks).
  • Now (ported by hand; 0.2.0 regenerated its own data/enemies.json offline — the JSON compare shows only enemy_1112_emppnt.modelScaleY, enemy_1112_emppnt_2.modelScaleY 1.263 and enemy_1196_msfyin.mirrorX; the PR's one-line enemies.json was not taken): FLY_HOVER 0.32 → 1.3, flat; enemyModelScaleY multiplies the skeleton's Y scale and the head (bar) height, also in the impostor path; a mirrored model flips on top of the direction flip. The body, its HP bar, damage numbers, projectile hits and skill rings ride the lift; the shadow stays on the ground tile; range highlights are tiles; one camera drives the Pixi units over both the 2D and the three.js board, so both boards show it. Research 12 ported without the PR's claim that the owner had said 「都要」 on 2026-10-05, and corrected where the quirk enemies appear: 帝国炮火先兆者 is a FLY special enemy of the mode, 帝国炮火中枢先兆者 comes with the bounty 战术特训·飞行III, 木制瑞印 is in no wave of the mode. No screenshot or extracted art is committed; the extractor stays optional.
  • [ASSUMED]: the character-space → tile factor (1 / 0.27; the screenshot agrees within ±0.2 tiles).
  • Golden: unchanged (drawing only; the sim reads neither field). Tests (from PR #211): test/render/unitview.test.js (FLY_HOVER ≈ 0.35 / 0.27; the same lift for every flyer, the 妖怪 drones under the tile at the old 0.32; a flying view lifts by FLY_HOVER — added here: its body and HP bar ride the lift, its shadow stays on the ground), test/render/playtest6-enemies.test.js (exactly the two 先兆者 stretch and 木制瑞印 mirrors; the 1.263 aspect and the bar height; the mirrored model's opposite x sign). Docs: DATA §9 (modelScaleY, mirrorX), ASSETS (model size, flying units), research 00 / 12.

25.14 Community reports of 2026-10-06 (W5AA) — content/enemies/archetypes.js prisoner, render/units.js FORMS, tools/assets/spine.mjs PREFAB_SPINE_ROLES, sim/battle/economy.js _bountyPayee

The owner collected four community reports on 2026-10-06. Each was reproduced on the 0.2.0 tree with a script (and, where the report could be older, on the v0.1.2 / v0.1.4 sources), checked against the official data — the battle prefabs of the local client, ArknightsGameData's buff templates, PRTS — and the earlier decisions. Two were real and are fixed (W5AA's commits name every golden scenario moved); two were already fixed and are pinned.

25.14.1 「囚徒类敌人出门就是已解放的模型」: the 孤岛风云 prisoners leave the gate confined and change model as they are freed — content/enemies/archetypes.js prisoner, render/units.js FORMS, render/fx/kinds.js SILENT_PHASES, tools/assets/spine.mjs PREFAB_SPINE_ROLES

  • Seen: 普通囚犯 / 老练囚犯 walked out of the gate drawn freed (a red collar light); no prisoner changed its model when freed (强壮囚犯, 拳师囚犯, 重犯 and 传奇重犯 stayed grey all battle), on the 2D and the 3D board alike.
  • Official: each prisoner skeleton holds three clip sets whose collar light is grey, blinking orange and red. The battle prefabs (local client battle/enm_pfb_6.ab, read with UnityPy: the Graphic component's anim key → clip table, the modes' replace pairs, the clip the Spine starts on) draw mode Default — 【禁锢】 — on the grey set and start on it, mode R on the orange set and mode L — 【解放】 — on the red one. The buff templates (ArknightsGameData battle/buff_template_data.json): enemy_confinement starts confinement.times charges; enemy_confinement[atk_cnt] spends one ON_BEFORE_ATTACK unless the prisoner has 麻痹 (PRTS 拳师囚犯 「在持有麻痹的情况下,攻击不计数」) and switches to mode R when one is left; enemy_liberty_listener[warning] switches to mode L before the next attack — PRTS 「进行第4次攻击前,切换至解放状态」.
  • Cause: the client's name-based role resolver gave 普通 / 老练囚犯 their unnumbered clips, which are the red (freed) set — their grey set is *3; FORMS had no prisoner entry and the sim sent no form. The sim also counted on the 'attack' hook, after the damage, so the 4th attack still hit confined.
  • Now: prisoner() counts in the before hook (an attack 麻痹 interrupts never reaches it), sets the form 'warning' before attack times − 1 (fx 'phase', drawn silently — the blinking light is the model's) and 'liberty' before attack times (fx 'liberate', the white blast as before), so that attack already hits with the liberty bonus; 重犯's free-all and 杰斯顿's knock-out switch every freed prisoner too, and UnitInfo form gives a view built later the right set. FORMS maps 'warning' / 'liberty' for the six skeletons (普通 / 老练囚犯 *2 / unnumbered, 强壮囚犯 *2 / *3, 拳师囚犯 / 重犯 / 传奇重犯 *_orange / *_red). PREFAB_SPINE_ROLES gives 普通 / 老练囚犯 their grey *3 set as the manifest's roles: data/assets.json regenerated (fetch-assets --offline --add-only, JSON compare: those two anims and the hash), data/enemies.json too (build-data --offline: their attackAnim.clip Attack → Attack3, the same length and strike frame). The 3D board draws the same Pixi views, so both boards follow.
  • [ASSUMED]: none (the modes and their timing come from the prefabs and the buff templates).
  • Found by the same audit (all 242 enemy prefabs against the manifest's roles), not changed, for later: 猎狗pro moves on Run_Loop (manifest Move_Loop), 大鲍勃 attacks on Attack2, 碎骨 moves on Move_02, “斩胄之剑” / “破胄之锤” idle on Idle_A (a 0 s pose the resolver skips on purpose); 杰斯顿, 扎罗 and 再生 differ only in the death clip their FORMS already set.
  • Golden: 28 scenarios — the warning's 'phase' fx only: roster-001 / 003 / 007 / 024 / 033, bond-miraShip-low; the freed 4th attack: roster-004 / 021 / 022 / 026, bond-kazimierzShip-low / raidShip-low / soloShip-low / victoriaShip-low / visiShip-low, standin-01, diy-017 / 024 / 061 / 065 / 066 / 094 / 097 / 104 / 105 / 127, and the bot matches solo-ABYSS-2, solo-HARD-9-boosted. Tests: test/content/feedback5-prisoners.test.js, test/render/feedback5-prisoners.test.js, test/render/feedback5-prisoners.browser.test.js (RENDER_E2E=1, both boards). Docs: docs/ASSETS.md, docs/SIM.md, DESIGN §9 (design/client.md).

25.14.2 「敌人在忍冬技能范围内没有开技能,在攻击范围才开」: already answered by the larger-range rule (§25.5) — no change

  • Checked: data/chess.json — S1 小施惩戒 DEFAULT (AUTO, no larger range), S2 坠刃拷问 SKILL_RANGE on its 技能范围 3-12 (§25.11.5), S3 隐狐之艺 (her default) ACTIVE_RANGE on its running range 2-2 (rawRule DEFAULT); she is in neither part of data/backups.json. On v0.1.4 S3 was on DEFAULT and cast only with an enemy in her 1-tile range — the report; on this tree it casts with an enemy two tiles ahead (not three), S2 on any tile of the 3-12.
  • Golden: unchanged. Pinned: test/sim/feedback5-community-pins.test.js.

25.14.3 「被源石地板烫死的悬赏没给赏金」: a bounty enemy that dies with no operator behind it pays in 联防 too — sim/battle/economy.js _bountyPayee, sim/battle/deploy.js _remove

  • Seen: a bounty enemy burned to death by the 活性源石 paid no bounty.
  • Checked: on its own field it always paid its owner — all 96 walking kill-bounty enemies on 战场#04 with no operator, on this tree and on v0.1.4: none unpaid. The loss is in 联防: a leaked bounty enemy re-enters carrying bounty.ownerPlayerId = the leaker (unite.js planUnite), and a death without an operator killer fell back to that owner, who has no per-player entry in the 联防 battle, so addCoins dropped the coins. Until 0.1.3 the 联防 field was the round's map (GitHub #41), so 战场#04's 活性源石 burned leaked bounty enemies for nothing; the 联防 maps have no terrain now, but a 无来源 tick, the enemy's own HP loss or another enemy's hit still paid nobody.
  • Official: PRTS 卫戍协议:盟约 决策 「该敌人于对应玩家所属区域倒下时,使相应玩家获得额外资金」 — no exception for 环境伤害; the owner's decision (relayed 2026-10-06): the bounty pays whenever the body dies.
  • Now: _bountyPayee — an operator or summon of a player in the battle that dealt the blow pays its player (as before, a 联防 helper included); any other death pays the card's owner when that player fights there (own field, Final Assault / Hidden Core: unchanged), else the player whose half of the field the enemy fell on (_ownerForTile).
  • [ASSUMED]: the 联防 payee of a death nobody caused (no source names one; PRTS's 「于…区域倒下」 reads as the area's player).
  • Golden: unchanged. Tests: test/sim/feedback5-bounty-payee.test.js (the 联防 cases fail on the parent; the own-field 活性源石 death, a helper's operator on the other half and a pair boss field pinned). Docs: docs/SIM.md §1.2, docs/META.md, docs/PLAYING.md.

25.14.4 「悬赏敌人枯朽萃聚使徒召唤的枯朽之种被击杀也会获得赏金」: already fixed in 0.1.3 (§23.25) — no change

  • Checked: the 使徒's BornBugs spawn its seeds through spawnChildren, which drops the card's bountyId / bountyCoins (§23.25); a seed never had a bounty on the own field, so killing one never paid. On the v0.1.2 sources a bounty 使徒 whose seeds leaked sent every seed into 联防 carrying the card — 15 of 16 re-entries paid it again — which is the report; on this tree only the 使徒's own leak carries it.
  • Golden: unchanged. Pinned: test/sim/feedback5-community-pins.test.js.

25.15 Rebindable shortcuts (W5AC; the community request 「快捷键可不可以自己设置」, the owner's decision of 2026-10-07) — ui/gameLogic/shortcuts.js, ui/gameLogic/settings.js sanitizeSettings, ui/settings.js (HotkeySection, hotkeyLabelOf), screens/game.js, ui/facingWheel.js

  • Asked: players wanted to choose their own keys.
  • Before: R 刷新, F 冻结, D 升级, Q 撤退 / X 出售 the selected operator (PR #114, 0.1.4), Space 准备就绪 (in a solo battle: pause / resume) and Esc (close) were fixed in shortcutFor; the shop bar, the ready / pause button and the underframe drew them as fixed text, the facing wheel swallowed Space and KeyR / KeyF / KeyD. No camera, speed or panel keys exist; the other keys are the dialogs', the guide's, 干员调配's and the facing wheel's own (Esc, Enter, the arrows, paging), listed with the shortcuts in docs/PLAYING.md §11. Nothing official to follow: the shortcuts are the remake's.
  • Now: the settings (localStorage sp.pref.settings) hold keys, action → KeyboardEvent.code; the defaults are the keys of 0.1.4, so nothing changes for a player who never opens the section. sanitizeSettings keeps each saved key that may be a shortcut and gives any other its default; not a map, or two actions on one key, gives the defaults. One lookup, actionForKey — the action of the character typed (the keycap: AZERTY, Dvorak), else of the physical key (a Russian layout, an IME), the 0.1.4 code === 'KeyR' || key === 'r' match generalised and identical for the defaults — serves shortcutFor(e, keys) in game.js and the facing wheel's facingSwallows; the modifier, text-field, repeat and overlay guards are unchanged. The key hints read hotkeyLabelOf. 设置 → 快捷键 (the one SettingsModal, in a match and on the title screen): each action with its key; a click — or Enter on the focused key — waits, the next key binds; Esc cancels and the dialog stays open, Tab leaves; a key held with Ctrl / Alt / ⌘ or one the interface keeps (Esc, Tab, Enter, the arrows, F1–F12) is refused and the wait goes on; a key another action has is swapped (that action takes the old key) and the line under the list names both; 恢复默认. Bindable: A–Z, 0–9, Space, the punctuation keys, Delete, Insert, Home, End, PageUp, PageDown; Esc stays fixed. On a touch-only device the section says the keys need a keyboard and folds the list; under 768 px it is one column.
  • [ASSUMED]: a conflict swaps rather than refuses (the request names neither, a swap never leaves an action without a key); Esc and the dialogs' own keys stay fixed; the bindable set; no modifier combinations (no shortcut used one).
  • Golden: unchanged (client UI only). Tests: test/ui/feedback5-hotkeys.test.js (the map, sanitising, conflicts, the lookup equal to 0.1.4 for the defaults on four layouts, capture, handlers and hints with a rebound key), test/ui/gameLogic.test.js (sanitizeSettings), test/ui/hotkeys.e2e.test.js (SP_E2E=1: 撤退 rebound to W with the 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 <kbd> 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).