131 KiB
DESIGN §21 — 0.1.1 — player feedback after 0.1.0
Part of DESIGN.md (the index; section numbers are global).
21. Player feedback after 0.1.0 (v0.1.1)
Thirty-two reports after the public 0.1.0 release (2026-10-02), relayed by the user in six batches; most were not tested by the user. Verdicts: real (fixed), partly (the rule was right, what a player sees was wrong or a related part was wrong — fixed), not real (not reproduced; tests lock the rule). Where each is handled: batch 1 — #1 拉普兰德's "获得该干员后该回合的首次刷新" should stack 叙拉古 → §21.1 (real); #2 late bounty enemies offered in early bounty drafts → §21.2 (real); #3 the water pool map: operators deployable in the water, 突袭 operators land in it → §21.3 (real); #4 昆图斯's 突变细胞 gone after one use → §21.1 (real: not consumed; its new operator joins the bench, not the carrier's tile — corrected after the merge, pointed out in PR #2); #5 转译基底 does not transform → §21.4 (real); #6 凯瑟琳's three upgrade items shown wrong → §21.2 (real); #7 鸭爵's 圆仔 blockable, audit the other 鸭爵 enemies → §21.5 (real); #8 深池逐火战士's 怨恨的余烬 cannot be killed → §21.4 (real); #9 tacticians (伺夜) can place the tactical point outside their attack range → §21.3 (real); #10 bots too dumb, smarter without more CPU → §21.6 (real). Batch 2 — B1 奥术 not working → §21.7 (partly: the bond works, 标准 switches it off); B2 源石溶剂 white + white → gold, then an extra white → §21.7 (not real); B3 维娜·维多利亚 S3 summons one 黄金盟誓 → §21.8 (real); B4 玛恩纳's skill deals no damage → §21.8 (real, on flying waves); B5 薄绿 can pull flying enemies → §21.9 (real). Batch 3 — C1 the hidden leader's "造成50%伤害" broadcast before the fight → §21.10 (real); C2 变形同构体 + class item: the bond member count excludes the wearer → §21.11 (partly: display only). Batch 4 — D1 信仰搅拌机: 源石溶剂's HP drain should trigger S3 → §21.12 (real); D2 野鬃's skill sometimes pushes the wrong way → §21.13 (real); D3 special 源石虫 all drawn as the plain one → §21.14 (partly); D4 暴鸰's bombs do not drop properly → §21.14 (partly); D5 the originium-floor map's lower route should cut diagonally from row 4 to row 3 → §21.15 (real). Batch 5 — E1 烛煌 S3's attack range does not change → §21.16 (partly: the card); E2 medics that block a roaming leader stop healing and attack it → §21.17 (not real); E3 卡涅利安's attack is not a true AoE → §21.18 (real). Batch 6 — F1 坚固维式重锤's lock does not work → §21.21 (partly: the lock works; it was once per battle, now once per deployment as the user settled, and an M3茧甲 equipped first took the first lethal hit); F2 a free 谢拉格 uses up 休露丝's discount → §21.21 (not real); F3 蒂比 still hit after taking off → §21.22 (real); F4 深巡 on a fenced tile does not attack → §21.23 (not real; a fence blocking fault found on the way is fixed); F5 突袭 operators land on a knocked-out operator's body → §21.24 (real). Hardening credited to pull requests (PR #2, PR #7, PR #10, PR #14) → §21.25. Closing additions from the public GitHub issues and pull requests: the gaps found while triaging issues #1 / #8 → §21.26; the client fixes for issue #5 (the folded shop's camera) and #8 item 5 (operators left as placeholders) → §21.27; the strategy draft's match info (#8 item 1) → §21.28; six 重装 skills cast with an enemy in range, the owner's deliberate deviation (issue #4, PR #12) → §21.29.
Eighteen workstreams (WA–WR, each on its own branch from master 7f13473) were merged into feedback1 in the order WB, WA, WE, WD, WC, WI, WM, WN, WO, WL, WG, WH, WJ, WK, WP, WR, WQ, WF, then WB's follow-up from 66 screenshots of 22 matches (bounty lists, the 机密商店 and 战术决策 with repeats) after QA, then batch 6's four in the order WS, WU, WT (on WI's branch), WV (on WC's), then the four closing branches (the gaps, the 突变细胞 bench rule, the issue #5 / #8 client fixes, the draft's match info — each adding its own subsection, numbered §21.26–§21.28 in that merge order) and GitHub's master (PR #10, PR #12); data/*.json was rebuilt by tools/build-data.mjs --offline after every builder merge, never hand-merged. The integration fixed a bug already in 0.1.0 (a 联防 battle timed out during 隐德来希 S3 reported its 心烛 as leaks, so the server refused an honest client result — candles carry mem.noLeak), the last review of the transform workstream (form fx were still dropped by the render engine), every cross-workstream note worth doing with a primary source, and reconciled the bot with the merged rules — §21.19. §21.20 lists the settled decisions and what stays ASSUMED; batch 6 and the pull-request fixes follow it (§21.21–§21.25), then the closing additions (§21.26–§21.29), with what QA of that integration found and the integration fixed: a lent 坚固维式重锤 and in-place revives against the once-per-deployment lock (§21.21), 卢西恩 / 锏 casting for an airborne 蒂比 alone (§21.22), rule 3 for a summon that is gone (§21.24) and when the dispatcher takes its item snapshot (§21.25). Normative lines rewritten with it: §2 (grid.js), §3 (the effective buildable, the 深水区, range-bound summons, 攻击距离 vs skill ranges), §5.2 (liveRangeGrid, form), §5.3 (stealth vs area damage, the 奥术 refresh), §5.4 (kill / death, foesInRadius, loseHp vs damage, 静态刚体), §5.5 (healers that block, rangeAoe, armed enemies, results), §5.6 (TAKE_DAMAGE, noRangeExtend / showOwnRange, the profession splash radii), §6.2 (rewardOffer, piece meta.round, summons in range, returned equipment), §6.3 (变形同构体), §6.6 (the bot), §7 (bounty drafts, 鸭爵, knock-out forms), §8.2 / §8.3 (fx form, m.ticker, range, UnitInfo items / form, the stealth bit, rewardOffer), §9 (forms, alias tint), §10 (offers, ticker, 本局禁用), §13 (spine/enemy), §14 (ground pathing, boss tickers, the runner's form fx), §16, §17.3, §18.5, §18.6 #10, §19.1, §19.7, §20.1, §20.3, §20.6, §20.9; with batch 6: §3 (fenced tiles), §5.3 (liftoff), §5.4 (fatal, ignoreSelect, redeploy), §5.5 (bodies and redeploys, blocking, enemy AI), §6.4 (onBuy, the item snapshot), §8.2 (down row, col), §18.3, §19.7; with the closing additions: §5.6 (TAKE_DAMAGE), §6.2 (the 突变细胞 gain), §6.3 (harmony), §6.6 (the band pick, the gained operator), §8.2 / §8.3 (harmony?), §9 (setCamera shop, the model fallback), §10 (the draft's match info), §11 (the boss-field speed bar), §14 / §20.12 (the layer bound), §17.1, §19.2, §19.8, §20.2, §20.9, §20.11.
21.1 拉普兰德's first refresh; 突变细胞 is not consumed and its new operator joins the bench (#1, #4) — sim/content/garrisons/meta.js SERVER_GAIN_BOND_LAYER_BY_REFRESH_CNT, match/PlayerState.js pieceRoundCount / bumpPieceRoundCount / _mergeChess / transformChess / acquireChess, match/effectsMeta.js ctx.pieceCounter / incPieceCounter / transform, match/builtinMeta.js char_chess_transformation_equip, match/audit.js, match/bot.js cellTarget / makeHandRoom, tools/build-data.mjs ITEM_RULES, ui/detailPanel.js ItemDetail
- #1 seen: with 普罗旺斯 + 德克萨斯 deployed — refresh, buy 拉普兰德, deploy her (3 叙拉古), refresh again: no 叙拉古 layers. Players: officially "获得该干员后该回合的首次刷新" also stacks. Cause: the handler compared the PLAYER's round refresh count with
refresh_cnt1. - #1 official: garrison_123_a/_b = SERVER_REFRESH_SHOP / SERVER_GAIN_BOND_LAYER_BY_REFRESH_CNT, bb
refresh_cnt1, layer 4 / 8, "若为本回合首次主动刷新…此干员在整备区时也有效" (activity_table act2autochess). Prep effects run on the scene server (research 11 §1.2); PRTS has no timing note. Text and report agree when the count belongs to the trait instance; the "本回合每刷新过1次" multipliers (阿罗玛 122, 安洁莉娜 147) fire on other events and keep the player's count. - #1 now: each copy counts the manual refreshes it sees this round, on the board or in the hand (
piece.meta.round, module-prefixed keygarrison:SERVER_GAIN_BOND_LAYER_BY_REFRESH_CNT:refreshes), and fires on therefresh_cnt-th. A new piece starts at 0; a move or an in-place promotion keeps the count; a re-triggered trait neither fires nor counts; temp pieces do not count. - #4 seen: "昆图斯策略给的突变细胞用完就没了". Cause: research 04's ASSUMED "item consumed"; the built-in and a content override both destroyed the cell.
- #4 official: the text "战斗结束后,装备者替换为高一阶的随机干员" never says 销毁 (every consumable's text does; data EQUIP, upgradeNum 100); PRTS 下半/PRTS盟约记录 备注 "生效时,原干员销毁,获得一名高一阶的随机初始干员(最高六阶)"; PRTS 卫戍协议/帮助 "佩戴的装备无法手动卸除,在失去该干员(干员出售、销毁、合并等)或装备合并为进阶品质时自动卸除"; community guides re-inject it every round (bilibili cv47000418, cv48003106; cg.163.com "这个道具可以无限使用").
- #4 where the new operator goes (corrected after the WA merge; pointed out in the closed PR #2, whose playtest claim the footage confirms): official gameplay footage — bilibili BV1vzyVBuEN9 (上半, ≈ 8:24) and BV1Qkw1zMEoR (下半, ≈ 7:25) — shows the next prep with the carrier's tile empty, 剩余可放置角色 one higher and the 整备区 holding the returned equipment and the new tier+1 operator, which the player then deploys by hand. It reads the PRTS 备注 as written — a destroy followed by a gain ("原干员销毁,获得一名…") — and PRTS 帮助 sends whatever a player recruits or gains to the 手牌区 (= 整备区; "被发送至手牌区的物资优先从右到左填充空位"). WA had kept the new operator on the carrier's tile when legal ASSUMED and counted that tile for a merge the transformation completed ASSUMED; both are withdrawn (PR #2 itself still let the merge use the carrier's tile — not adopted).
- #4 now: after every battle the carrier is destroyed wherever it stands (board, hand or temp — a board tile is freed and the deploy count drops; its summons are removed, its copies return to the pool). Its equipment, the cell included, returns to the hand first (overflow temp; auto-merging like any gain). Then a random NORMAL operator one tier higher (max 6; an elite carrier too) is gained like any gained operator (
PlayerState.transformChess→acquireChess, onGain source'transform'): the hand (right → left), overflow temp, and with both full the no-room rule of every gain (back to the pool, toast 整备区已满,获得的干员已返还; an item that found no slot takes one the gain freed, else stays on the gained operator up to 2 slots, else it is destroyed with a log warning, as in a merge). Like any operator on the bench it brings no summon card until it is deployed. A gain that completes a merge merges like any gained copy (§20.11): the elite on a consumed deployed copy's tile, else the hand — the carrier's freed tile is no copy's (_mergeChesslost itsfromKey/fromDiroptions,audit.jsits exception for them); the returned cell is stowed before the elite's summon card. Per-piece state stays with the returned items (商业包装方案's sell count, keyed by the item). One implementation (the built-in); the item card states the rule (items.jsonnote). Bots inject their least valuable single normal operator below 6阶 (cellTarget: never an elite, a merge pair or a carrier of a cell), make a hand slot for a cell left in temp (makeHandRoom), and — AI 托管 too — deploy the gained operator in the next prep's lineup step like any owned unit (a real co-op round in the tests: SETTLE → the next prep). The buy loop's pre-emptive bench shed, which sells the weakest bench single by piece value while the bench is crowded, no longer sells a piece gained since the bot's last prep ended (bot.js rememberOwned): on the new rule it had sold 44 of 222 gained operators in 20 solo 绝境 matches on 昆图斯 before any placement step saw them (now none; a gained operator stays off the board only when the board is full). Measured (tools/botbench.mjs, decisions only): strategy 昆图斯 (--band band_quintus, seeds 201–220) 绝境 8 / 20 wins on the WA build → 7 with the bench rule alone → 11 with the shed fix (LP left 16.8 → 13.3 → 17.1), 险境 15 → 16 → 17 (16.3 → 16.2 → 17.6); the bots' own strategies, bench rule alone → with the fix: solo 绝境 (seeds 1–12) 8 → 8 / 12, solo 险境 (seeds 13–36) 19 → 19 / 24, co-op 绝境 4 AI (seeds 1–36) 9 → 8 / 36 (LP left within 0.6) — noise; the shed's cost is a set of the owned uids per prep. The client needs no special case:render/app.js setPrepdrops the carrier's view and draws the new piece on its bench slot (checked in headless Chrome: the tile empty, the operator and the cell on the bench, no promotion cue, no console error). - ASSUMED: an elite merged this round keeps its copies' highest count (no second firing that round); a copy re-bought after a sale fires on its own first refresh ("获得该干员后"; each extra +4 costs 3 + 1 − 1 = 3 funds); a bench carrier (hand or temp) transforms too (the footage shows deployed carriers); a 6阶 carrier becomes another 6阶; with the hand and temp both full the gained operator and an item without a slot follow the rules of every gain (no source shows that case).
- Tests:
test/match/feedback1-meta.test.js(a deployed and a bench carrier, the hand full → temp, the hand and temp full, a merge with and without a deployed copy, 商业包装方案's count, bots and AI 托管 redeploying, a real solo 绝境 昆图斯 bot match: no gained operator shed in its first prep, none waiting on the bench beside a free deployment),test/ui/feedback1-cell.test.js;items.test.js,merge.test.js,facing.test.js,playtest6-elite-to-board.test.jsupdated.
21.2 Bounty drafts by the official round structures; special offers shown as what they hold (#2, #6) — tools/build-data.mjs BOUNTY_INITIAL_SETS / BOUNTY_BOSS_GROUPS / BOUNTY_HUNTER_GROUPS / SHOP_DRAFT / TACTIC_DRAFT, match/choices.js bountyDraftCards / shopDraftCards / tacticDraftCards, choices.json bountyDrafts / shopDraft / tacticDraft / draftPool / schedule bountyDraft, test/fixtures/official-bounty-drafts.json; match/PlayerState.js pushRewardOffer / pushItemOffer, match/effectsMeta.js offerLabel, ui/gameLogic.js offerHeader, ui/shopBar.js RewardCards, ui/rewardOverlay.js
- #2 seen: "本来应该后期出的悬赏的怪物在前期的悬赏就出现了,导致选了打不过". Cause: every draft drew uniformly from 105 cards (§20.6), so R3 could offer the R9 bosses and the R11 giants.
- #2 official (66 screenshots of 22 co-op 绝境 / 终极 matches of this season at R3 / R9 / R11, collected by the user 2026-10-03; readings in the fixture, no player names): every card is an act2autochess (下半) effect whose enemy is the effect's
enemy_id(the title only names category and tier: 悬赏·损伤I = 底海滑动者 e12_4 / 临时收音师 e18_1 / 心虚设计师 e16_11); each 悬赏决策 event is a fixed card list and a draft shows 6 different cards of it in varying positions.- R3 (22 / 22): six 接下来两场作战 cards, I I I II II III (10 coins), one of 10 fixed sets (enemy_initial_1..10), all six shown; 9 sets seen — series 17 / 18 / 19 whole, and hand-made mixed sets (one holds two series-20 cards, one holds 自制投石机 e17_6), so no simple rule describes them.
- R9 (22 / 22): boss bounties X·悬赏 + 源石虫·特训, 下场作战 — one of 6 groups (one per bossInitial event) of 9, 9, 9, 9, 8 and 6 cards; 庞贝 and 鼠王 never share a draft; named bosses do not always come together (match 19 = 自在 + 杰斯顿 + 陷落雪祀, match 12 = 杰斯顿 alone). The 鼠王 group came in 14 of 22 matches, every dark-grey-board match among them.
- R11: 悬赏决策 14, 机密商店 4, 战术决策 4, 道具补给 0 of 22. A bounty draft is a list of 7 下场战斗 cards — one 特异III giant, one card per faction series — of which the draft leaves out one (match 15 leaves out the giant; matches 1 / 6 / 21 / 22 leave out four different cards of one list); 7 lists seen.
- 机密商店 (matches 2, 4, 5, 8): six free items with repeats (盟约之币 ×2, 变形同构体 ×2); each shop exactly two tier VI, at least one V and one 盟约之币, no tier I / II. 战术决策 (matches 7, 9, 18, 20): six ally cards each (no 排斥 / 责罚 / 裁决, no 模拟战场演变), 补给 twice in match 7.
- Never in a draft: the 7 multi-round cards (the 假想敌 question is answered), enemyeffect_3_*, 战术特训, the 鸭爵 set.
- #2 now: cards.bounty
draftPoolinitial 42, boss 20, hunter 24 (86 drafted);draftExcluded'unseen' (19: multi-round, e3_*, 凋零骑士 / 遗弃者 / 锏 / 扎罗 / 迷路的巨像). A draft = one group, then 6 different cards of it, each drawn with weight 1 + the official drafts of its group it showed in. R3 = one of 10 slots: a seen set, or for the unseen 10th a rule set (tiers I I I II II III, ≤ 2 per series, the III 法术大师A1 e10_6 or 鼎沸 e20_6 — the two-battle cards no draft showed); R9 = a seen group, picked by the matches it came in; R11 = one of the 7 seen lists, uniform (the 4 lists seen with 6 cards completed by the list shape, so 12_8 / 16_2 / 16_6 come in about 1 R11 bounty draft in 15); 绝境 / 终极 R11 families: bounty 14 / shop 4 / tactic 4 (the official counts; was supply 50 / shop 50). The R11 机密商店 (shopDraft): six slots drawn with replacement — VI, VI, V, 盟约之币 and twice V / IV / III / 盟约之币 — items weighted by the official cards they showed on; the earlier 标准 / 险境 shops keep any tier I–VI per card, also with replacement. The 战术决策 (tacticDraft): every card drawn on its own with replacement; at R11 only the ally cards, weighted 1 + the official cards each showed on; other rounds every card, uniform. Identical cards are separate cards (picks go by index; two picks of a team card stack).MULTI_ROUND_BOUNTY_BATTLES = 2stays (it now only affects 教鞭's 法术大师A2·多轮战术特训). The mode's inactive enemy list no longer removes bounty cards (PRTS 11/18 note "不影响悬赏决策出场"). Flip: add a newly seen set, group or list to the build-data constants and rebuild. On the 22 official bounty drafts the generator reaches every one (60 000 seeds per round,mode_multi_hardandmode_multi_abyss). - #6 seen: 凯瑟琳's three items appeared as blank operator cards with raw ids under 晋升奖励. Now: a special-refresh offer is drawn as what it holds — free item cards (icon, name, tier, description, FREE) or chess cards under its source's label (凯瑟琳 定向投放, 娜仁图亚 见者有份, 寻呼模块, 信标, 松果) + SPECIAL + 免费选择 1 件 / 名; m.private
shop.rewardOffer = { tier, source: 'merge'|'special', label, queued, slots }; offers waiting behind the shown one show as "之后还有 N 项" and a +N on the reminder pill; a special offer plays the refresh sound. - ASSUMED: the card weights inside a group; the unseen 10th R3 set (rule) and 险境 R6 drafting like R3; R9 groups picked by the matches they came in (what of the match decides — map, leader or difficulty — is open) and the three single-draft groups completed only with the base cards they lack; R11 = one of the 7 seen lists (which of the 15 events R11 fires is open: by the blocks of effectChoiceInfoDict it would be bounty_hunter_8..15, and with 8 events an unseen list would come in about 1 R11 bounty draft in 8); the R11 family weights = the official counts; the 机密商店 slot split and item weights, and the earlier 标准 / 险境 shops; the 战术决策 weights, R11 without terrain cards, independent draws (open: all 4 official drafts hold a 驰援, a 盟誓 and two of 列装 / 财富 / 补给 / 整备 / 升华), twin battle passives stacking; solo 绝境 / 终极 R11 = the co-op weights (no solo screenshot; the data has 15 solo
bounty_hunter_*_sevents); solo shows 3 of 6. - Tests:
test/match/feedback1-bounty.test.js,test/match/feedback1-secret-shop.test.js,test/match/feedback1-tactic.test.js,test/ui/feedback1-offers.test.js,test/ui/feedback1-secret-shop.test.js,test/ui/feedback1-tactic.test.js,test/match/playtest6-matchflow.test.js,test/ui/playtest6-bounty.test.js,test/content/choices.test.js; opt-intest/ui/feedback1-offers.e2e.test.js,feedback1-secret-shop.e2e.test.js,feedback1-tactic.e2e.test.js(SP_E2E=1).
21.3 No deployment in the water; tactical points inside the tactician's range (#3, #9) — sim/grid.js DEPLOY_REFUSED_TILES / canStand, tools/build-data.mjs effectiveBuildable / ownerRange, match/board.js ownerRangeKeys, match/PlayerState.js _legal / summonRange / _liftOutOfRange / _reorient, ui/gameLogic.js summonRange, match/bot.js, match/invariants.js, content/kits/tier3.js tacticalPoint
- #3 seen: "有水池的那张图干员可以错误的被部署到水里,突袭分队干员也会自动落到水里" — the 深水区 (
tile_deepsea) of 战场#08(下半) 涨潮控制 (act2 m04): board (10–12, 6), partner (10–12, 14), boss (3–5, 6) / (3–5, 14). Cause: the level gives it buildableType ALL and the legend copied it; the deploy maps (server, client, bots) andgrid.canStand(the 突袭 landing tile, tactical points, summon tiles) treated it as road, and a tactical point even preferred it (it lies on the enemy path). - #3 official: PRTS 深水区, 地形信息 tile_deepsea: 部署类型 全部位, 地形机制 "拒绝部署(待补充)"; PRTS 作战机制: the 地形标记 AdvancedBuildableMask (values include 深水) "限制玩家仅能部署匹配的单位于其上";
trap_040_canoe特制水上平台 "在水上建立可以部署任意单位的平台" (season 1's 战场#05 puts one on each water tile). There is no PRTS page "地形". - #3 now:
DEPLOY_REFUSED_TILESmakes the effective buildable NONE: stages.jsontiles.d.buildableNONE (buildableTypeALL kept),deployTileswithout the water,canStandrefuses it (and melee units on a hard-blocked 射击台 / mound); every automatic placement and the bots follow. An active 特制水上平台 makes its tile deployable in the prep deploy map only (act1 m05, weight 0; the sim models no canoe, its Grid keeps those tiles NONE ASSUMED). Gate routes are unchanged; the water counts as non-blockable in the path tie-break, so a few off-route smoothed steps of the m04 boss field (rows 4–5) go around it. 维娜·维多利亚's lions skip it too (§21.8). - #9 seen: "干员伺夜这样的战术家的战术点能被错误的被放到攻击范围之外". Official: 狼群 / 流形 token text "只能部署在召唤者攻击范围内" (character_table; PRTS 狼群 特性); the tactician trait "可以在攻击范围内选择一次战术点来召唤援军"; PRTS 卫戍协议/帮助 "移动干员时,其所属召唤物全部退场并重置至手牌区".
- #9 now: tokens.json
ownerRange; such a piece stands only onownerRangeKeys(loadout attack grid, owner tile, owner dir)— enforced by the server (_legal/summonRange), the client (drag highlights; toast "只能部署在召唤者攻击范围内"), the bot and the invariants. Moving the owner sends its summons back (unchanged); in swaps each piece is checked from the tile it ends on; an in-place re-orientation or promotion that leaves the summon outside sends it back to its stack with a toast — one still inside stays (user-confirmed, §21.20); with nowhere to go (hand and temp full) the re-orientation is refused withHAND_FULL, on the other paths the summon leaves the board and comes back at the next round start (grantTokensFor). No out-of-range placement reaches the battle. - ASSUMED: the range is the loadout's grid (
attackRangeGrid, §21.16; items and bonds do not widen it); automatic placements never put a melee unit on a 射击台; the canoe in the deploy map only. - Tests:
test/match/feedback1-placement.test.js,test/sim/feedback1-water.test.js; board, playtest5-deploy, playtest6_summons, ui/gameLogic, pathing-blockable / -crosscheck updated; opt-intest/ui/feedback1-placement.e2e.test.js(SP_E2E=1; fastServerSP_STAGE).
21.4 Enemies that change form when knocked out (#5 转译基底·α, #8 深池逐火) — content/enemies.js (husk, kitTranslator, reborn, statue, setForm, rebirthCleanse), sim/ai.js enemyAttack, sim/snapshot.js, shared/protocol.js fxForm, battle/runner.js keepsState / hold / compactHeld, screens/game.js keepEarly, render/interp.js isCosmeticEvent, render/app.js renderInfo, render/units.js FORMS, render/spine.js
- #8 seen: the 怨恨的余烬 could not be killed. Cause: the ember stood still, 隐匿 AND unblockable, so no operator could ever target it (a real R3 round on 战场#01: 0 of 7 killed, 7 leaked).
- #8 official (PRTS 深池逐火战士 / 护卫 天赋;
Revive[Trigger]5 / 10,interval10; PRTS 特殊机制 §重生 / §特殊生命值机制; 作战机制 §隐匿): every knock-out → a 1 s 重生 (immobile, 无敌 + 无法阻挡 + 失衡免疫); then the ember — max HP 5 (护卫's 火灰 10), 1 per damage instance, no attack, 隐匿 + 缴械; not killed within 10 s → the warrior with full HP. A blocked enemy loses 隐匿. - #8 now: the ember walks on; blocked, it is targetable and drawn solid (the stealth bit only while its 隐匿 is on). The same round: 7 / 7. An operator's radius area damage reaches it only while blocked or revealed (§21.19) — and, since 0.1.2, for 3 s after a block ends (§22.8).
- #5 seen: 转译基底 played a "transform" clip and vanished. Causes: no clip sets for its forms (it stayed on A_* and died on B_Die, the 寻仇者's death clip); its first form took damage; and the client dropped the change fx in catch-up frames, hidden tabs, the pre-entry buffer and the render engine's stale-event drop (b.snap carries no form).
- #5 official (PRTS 转译基底·α 天赋; talents Passive / Mode_*_Passive / Mode_Fuchou_Anger): no attack; every damage instance cancelled; 失衡免疫 + control immunities; the 4th physical hit → 寻仇者, the 4th arts hit → 特战术师 (2 targets), a block → 幽灵 (unblockable, no attack — PRTS 幽灵 "不进行普通攻击,无法被阻挡"); once, 2 s.
- #5 now: A_Die_B / C / D, then B* / D* / C_*; no HP lost before the change (an HP loss stops at 1); the armed forms attack through
ai.js enemyAttack(e.profile). - Kill accounting: a survived knock-out gives no kill, credit, bounty, die event or death hook; wave completion and 联防 counters see the enemy standing; a bounty coin is paid once, at the final death.
- 重生 (PRTS §重生 "清空自身身上除白名单外所有Buff"):
rebirthCleanseremoves ally-sourced buffs and source-less statuses, keepingpersisttalents, enemy-given buffs (锏's 抵抗) and source-less field state; its skills restart from their initial cooldown when it ends (§21.19). - Forms on the client:
setFormkeepse.form(UnitInfoform) and putsformon its fx (fxForm, the 'phase' fx too, so barrier / charge phases are no forms). No client stage drops a form fx: runner catch-up frames (keepsState), the hidden-tab backlog of the battle on screen (hold; pastHELD_MAXsuperseded status / skill toggles are compacted away,compactHeld, and only a backlog still past it re-enters the view from the field meta), a battle that ended while hidden (delivered when the tab is shown; every batch keeps the game time it was drained at,EV_SLICE), the game screen's pre-entry buffer (keepEarly, also while a re-sent field meta re-enters the field on screen) and the render engine's queue (render/interp.js isCosmeticEvent: a form fx is never dropped as stale nor shed; one handed out late switches the model without its telegraph or a change clip that would already have ended, its closing clip shortened — the last three since the integration, §21.19). Late views:renderInfokeepsform. Closing clips are timed from the fxdurand keep the current form's death clip until the next form's fx. - Audit: 锏 / 扎罗 had #5's look (fixed: their 重生 clips); 杰斯顿 → C2_*; 守墓石像 per PRTS (statue, then flight, §21.9); 再生: 1 s 重生, then shields within 1.8. No unit stays untargetable. “余音” stays unmapped (no source says which form is A).
- ASSUMED: the translator is immobile during its change and stays damage-cancelled; the HP-loss floor 1; form letters B / C / D; the 特战术师 form also hits flyers (the standalone PRTS 特战术师 "不会攻击飞行单位" may carry over — one line,
canTarget: (u) => !u.isFlying); the ember's 10 s counts from the end of the 重生; 再生's 傀儡 walks; the statue does not attack (clip Sleep); the 重生 whitelist. (隐匿 returned at once after a block until 0.1.2 — now 3 s, the knock-out's own release included: §22.8.) - Tests:
test/sim/feedback1-transform.test.js,test/match/feedback1-runner-forms.test.js(stalls, hidden tabs, a battle ending while hidden, compaction, the runner feeding the render buffer),test/render/interp.test.js,test/render/forms.test.js,test/content/enemies_bosses.test.js; opt-intest/render/feedback1-forms.browser.test.js(RENDER_E2E=1; blocking 1 s long tasks).
21.5 The 鸭爵 strategy's enemies (#7) — content/enemies.js (runWhenHit, 圆仔 kits), content/bands/meta.js duckReplace / duckGlobal, Match.startFinalAssault (event routes / side), tools/build-data.mjs bandSwapEnemyKeys
- Seen: "鸭爵策略带来的怪物圆仔错误的能被阻挡"; the user asked for an audit of the other three. Cause: 圆仔's kit had only the front guard (its unblockability is in PRTS's talent text, not in the handbook ability list). The audit found more: 鸭爵's run multiplied the speed by
run.attack@move_speedinstead of adding it; the swap ran only in normal battles and only on NORMAL-rank enemies; the four kept the database's roguelikelifePointReduce0; on a pair boss field a player's swaps came from both halves. - Official: PRTS 圆仔 天赋 "无法攻击/被阻挡;受到来源于正面的物理和法术伤害-80%;自身始终朝向我方干员数量最多的方向"; PRTS 鸭爵 天赋 "受到伤害时开始奔跑,移动速度+400%"; PRTS 卫戍协议:盟约 下半/PRTS盟约记录 §策略 鸭爵 备注: "每回合将有0~2名敌人被替换为上述敌人之一,且上述敌人为特殊版本,相较于原版更弱" (popup: 鸭爵 6000 HP, +300 % speed when hurt; 高普尼克 11000 / 1600 ATK; 流泪小子 4500, 3.5 s stun; 圆仔 13000), "但进入保护目标点将减少1点目标生命值,且在最终回合和隐秘核心回合中仍然生效". Data: the band's enemylist names the
_2versions (blackboard min 0, max 2, round 5, minweight 0.6, maxweight 0.99). How the swap is drawn is in no source. - Now: 圆仔 / 圆仔_2 unblockable + front guard; 鸭爵 / 鸭爵_2 run ×(1 + v) once (+400 % / +300 %). The swap runs in normal, Final Assault and Hidden Core battles (联防 adds none); candidates: any ground wave enemy, normal or elite, except the leader, its parts, bounties, earlier swaps and uncounted units — each swap takes the replaced unit's time and route; on a pair boss field each player's 0–2 come from the enemies whose route ends on that player's half (
ev.side+ev.routes, the bounties' half rule), the 60–99 % share taken over that half. The four_2enemies havelpr1 (builder): the Final Assault / Hidden Core LP and the card's 目标价值 read it. - ASSUMED: the uniform 0–2 count and the uniform pick; minweight / maxweight = the 60–99 % part of the player's enemies in time order; ground only; elites eligible (about two thirds of the leader rounds' escorts in that share are elites; NORMAL-only would leave ≈ 80 % of R14 pair halves without a candidate vs ≈ 36 %); per-half swaps; decided at battle start (not in the prep preview); 圆仔's backward pose not rendered.
- Tests:
test/match/feedback1-ducklord.test.js(a real R6 wave; solo Final Assault + LP; co-op Final Assault → Hidden Core pair fields on the real match path),test/content/bands.test.js,test/content/enemies_bosses.test.js,test/data.test.js.
21.6 AI players (#10 "人机有点太笨了") — server/match/bot.js, tools/botbench.mjs
- Measured on the 0.1.0 bot (
tools/botbench.mjs, same seeds on two builds): 0–2 refreshes a prep and side-grade singles sold again for 1 (≈ 2 merges a match, ≈ 2 elites at R13); a level-up could spend the funds a third copy needed; the bounty score ignored the enemy (co-op 绝境: 155 of ≈ 310 bounty enemies leaked); 信标 (destroys its carrier) went on the best operator; DPS ignored DEF / RES; the focus bond flip-flopped. - Now (decisions only, no rule or balance change): refresh vs buy by the shop odds of completing held pairs (a full board buys only merge progress or a lineup upgrade worth more than the refreshes its price would pay for; an elite's value includes the merge's free pick); ≤ 4 pairs on the bench, merges before level-ups, a freeze for an unaffordable third copy; one committed focus core bond + a second bond, a teammate's main core bond read from its public bond strip (humans too) counting against it; dealers valued by DPS against the round's DEF / RES; 坎诺特 keeps its interest capital (5) back; bounty picks by expected payout − expected leaks × (2 + 20 / LP) with the kill chance from the exposure model — an expected-value pick (co-op leaks are bound by the drafts, §21.2); items by effect (信标 on a bench single, 拟态物质 on a pair, both 博士投影 on the strongest normal operator, 突变细胞 per §21.1, bond items on a focus member, survival items on blockers); 教鞭 / “神秘顾客” used after a perfect battle and kept otherwise — a bot drops a kept one only for a hand slot, a human's seat under AI 托管 never; a tactician's 援军 inside
PlayerState.summonRange. - Result (seeds 1–40, rehearsal 3, on the WF branch): 独立 绝境 19 → 26 / 40 wins (LP left 14.3 → 16.6), 同盟 绝境 (4 AI) 5 → 12 / 40 (5.1 → 12.7), 标准 unchanged; merges per bot ≈ 2 → 3–4; forced strategies (solo 绝境, seeds 201–220): 坎诺特 9 → 14, 昆图斯 9 → 11, 杜宾 8 → 9. Decision time unchanged: heuristics CPU p50 / p95 31 / 74 → 28 / 70 ms solo and 17 / 39 → 17 / 38 ms co-op; the whole prep with its rehearsal 89 / 205 and 83 / 162 ms wall.
- Reconciled with the merged rules (§21.19): the server's legality (deploy map without the 深水区,
summonRange), WA's cell rule (and its destroy-then-gain correction: the gained operator is deployed by the lineup step, the bench shed spares a piece gained since the last prep — §21.1), WB's draft structures (it scores whatever is offered), 本局禁用 bonds (never planned), the range a unit is deployed with, and the AoE of §21.18 in its DPS value. On the merged build, seeds 1–40: solo 绝境 27 → 27 wins, co-op 绝境 9 → 11 (within the noise); decision time stays (heuristics CPU 24 / 57 ms solo, 16 / 36 ms co-op; the whole prep 78 / 153 and 73 / 151 ms wall; BALANCE §5). After the QA: WB's 22-match bounty lists (a giant in every R11 bounty list, 14 of 22 R11s a 悬赏决策) take the same bot to 22 / 40 and 9 / 40 — a rule change; the bench-shed fix (never sell a piece of this prep, §21.19) then 24 / 40 and 10 / 40. The 0.1.1 build against 0.1.0 (19 / 40, 5 / 40): 24 / 40 solo 绝境, 10 / 40 co-op 绝境 (4 AI) — part of it is the rules: 0.1.0's bot on the 0.1.1 rules wins 23 / 40 and 7 / 40 (LP left 14.6 / 5.2 against 17.4 / 13.1, merges per bot 2.2 / 2.0 against 3.8 / 3.1); same-prep buy → sell in co-op 绝境 (2 AI 托管 + 2 bots, seeds 21–26) 29 → 3; decision time unchanged (one thread, seeds 1–3: the whole prep 81 / 187 → 81 / 184 ms wall solo, 71 / 153 → 71 / 129 ms co-op; heuristics CPU 36 / 88 → 35 / 86 and 16 / 40 → 16 / 40 ms). - ASSUMED (tuned; the official AI is undocumented): MERGE_HIT 36, MAX_PAIRS 4, BENCH_BUDGET 6, FOCUS_MEMBER 4, ARMOR_WEIGHT 10, BOUNTY_KILL 2, the LP value 2 + 20 / LP, the teammate penalty 6, the 坎诺特 reserve = the band's interest capital,
CROWD(×2 / ×1.3 / ×1.4). - Tests:
test/match/bot-smarter.test.js(bounty picks, item carriers, Arts kept / AI 托管 safe, tactician range, pair via buy or freeze, 坎诺特, the bond strip, the merged rules, a CPU cost guard < 300 ms),test/match/feedback1-meta.test.js(AI 托管 never destroys an item to clear temp).
21.7 奥术 works; 本局禁用 bonds; the 源石溶剂 merge (B1, B2) — ui/gameLogic.js modeOffBonds, ui/detailPanel.js BondChips, ui/shopBar.js ChessCard, ui/bondStrip.js BondPopup, screens/game.js, match/PlayerState.js _mergeChess, match/effectsMeta.js destroyPiece, content/bonds/addon/battle.js
- B1 measured — the bond works (a real 险境 battle through
Match._normalOpts, and the same battle from its BattleSpec, the browser path — identical): each member's arts hit debuffs its target ×(1.2 + 0.01·L) arts damage taken for 3 s and later arts hits land multiplied (深靛 + 洛洛, R5, seed 7: 41 / 54 arts hits debuffed at L0, 0 without the bond); 3 different members: below 50 % HP ×1.4·(…) (seed 7: 9 hits at ×1.68 and, after 海霓's live +3 layers, 19 at ×1.722); all 10 members × S1–S3 × normal / elite apply it, summons do not (not 干员); leaders too (Final Assault 绝境 R14, 999 谢拉格: 329 819 → 426 911 leader damage in 700 s at L0). - What a player saw — fixed: 标准模拟 switches 奥术 off (official modeDataDict inactiveBondIdList: 拉特兰 阿戈尔 卡西米尔 灵巧 奥术 奇迹 投资人 突袭 独行 绝技), while 深靛 洛洛 阿罗玛 夕 圣聆初雪 stay in the pool through their other bond; their cards showed 奥术 normally, the chip read 「0/2 未激活」 with three deployed and the popup 「在场 0/2 未激活」. Now such a bond is struck through on shop / reward cards, the chip reads 本局禁用, the popup reads 本局禁用 with a note and no 在场 count or 当前效果 block (its numbers would promise an effect) [ASSUMED look].
- Official, unchanged: 限伤 cancels a leader hit ≥ 300 000 in boss / hidden battles (§20.12), so very large multiplier stacks can make huge hits vanish (巴哈姆特 12316); our measured boards stay far below it (largest 24 813).
- The stacking rule, reconciled — §20.10 stands: PRTS 盟约记录's current note 「※同一单位仅可对同一目标同时施加1个该盟约法术伤害提升效果」, read with the engine default (PRTS 常见同名状态) and 巴哈姆特 12316, gives one effective instance per target (applyStrongest). Known deviation ASSUMED: an earlier revision (oldid 386936) said a unit cannot re-apply while its own instance lasts; an equal re-hit refreshes it here (slightly stronger). Strongest rather than earliest stays ASSUMED.
- B2 — not reproduced: every PlayerState path ends with exactly one golden and no white (buy next to a white in the hand or equipped, two buys, equip on another or a full carrier, a reward pick, 道具补给, 列装, 整备 + buy, 画卷, the carrier's sale); random intents over all 50 mergeable items conserve W + 2·G and never leave a pair (upgradeNum 2 → _b; PRTS 帮助 「已拥有2件同一初始装备时…自动合并」). A latent gap closed: equipment returned by a promotion (
_mergeChess) or byctx.destroyPiece(突变细胞, the round-start promotion item) now runscheckItemMerges(unreachable by a player before). - ASSUMED: 整备 bought next to an owned white merges the two first and keeps the effect for the next purchase (the official text may mean the bought item turns golden); the 标准 popup lists members the mode keeps out of the pool dimmed as not owned.
- Tests:
test/sim/feedback1b-arcane.test.js,test/ui/feedback1b-bonds.test.js,test/match/feedback1b-items.test.js.
21.8 维娜·维多利亚's lions; 玛恩纳 S3 hits air units (B3, B4) — kits/tier6.js siege2, tokens.js SKILL_SUMMON_UNCAPPED, kits/tier5.js 玛恩纳
- B3 seen: her S3 (default, defaultSkillIndex 2) summoned one 黄金盟誓. Real: on the real match path (战场#05 下半, R8, (10,5)) every cast placed 1 lion beside 3 free melee tiles (the reviewer's sweep of m01–m04: 74 casts, all 1 lion). The kit picked one tile, and the per-owner deploy limit (the token's phase maxDeployCount 1, a hand count) would have withdrawn a second.
- B3 official: skill_table skchr_siege2_3 "立即在天赋一生效范围内可部署地面召唤“黄金盟誓”"; EN client "Summons Golden Vows on deployable tiles within Talent 1's range"; wiki.gg "only be spawned on open low ground tiles"; PRTS 备注 "于技能结束时/维娜退场时自动退场".
- B3 now: one lion on every free tile of her 3×3 talent area where a melee piece could deploy (LOW, ALL / MELEE, unoccupied, not a reserved home tile;
grid.canStand, so never the 深水区), at most 8, for the skill's duration;SKILL_SUMMON_UNCAPPEDkeeps the deploy limit off them; each counts for 诸王的叹息 (546 × (1 + 1.45 + 0.30) with 6 lions). 1 → 3 lions at (10,5) on 战场#05, 6 at (11,4) on 战场#01. - B4 seen: 玛恩纳 "开技能没伤害". Real, on flying waves: his default S3 fires (CUSTOM_RANGE 3-18) on any enemy, flyers included, but his attacks were ground-only melee — a flyer-only cast hit nothing for 26 s, then the trait ramp reset (4 of 19 real casts; seed 21 R2 dealt 0).
- B4 official (PRTS S3 备注): "※可对空"; "每击倒一名符合条件的敌人,特性提供的攻击力加成-10%,特性最低降低至+0%" — only "自身普通攻击(与该次攻击附带的伤害)" kills count, "于当次攻击后统一结算", not kills by 第二天赋 (reflection), by a mark another unit's attack set off, or sourceless ones; S2's "不重置" follows the same rule.
- B4 now: S3
targeting.canHitFly(flyer-only casts 0–10k → 14k–93k damage); bonus = max(0, ramp × trait_up − 0.1 × own kills), settled after the attack; an own kill = an enemy his current attack (or the mark it set off) damaged — the set is cleared after each attack, so carried damage counts and reflection / teammates' marks do not. His trait sets block 0 only while the skill is off: during S2 / S3 he blocks up to 3, and the 卡西米尔 bond's true damage rides his attacks only while he blocks nothing. - ASSUMED: fence tiles (low, deployable, not walkable) count as 可部署地面; "−10 %" = absolute points of the bonus.
- Tests:
test/content/feedback1b_kits.test.js,kits_t6(every lion off the reserved tile),test/sim/feedback1-integrate.test.js(no lion in 战场#08's 深水区).
21.9 Air units are 静态刚体 (B5) — tools/build-data.mjs STATIC_BODIES, enemies.json staticBody, sim/Battle.js _displaceable, content/bosses.js (胄 parts), content/enemies.js (statue, 喷气人)
- Seen (relayed: "飞机可以被薄绿的技能拉走(难道不能拉动吗?…你去复核下)"). Real:
_displaceablechecked only 失衡免疫 and leaders, so every flyer moved by the 力度 − 重量 tables — 薄绿 S2 dragged 妖怪 1.33 tiles per hit and an act2 m01 drone reached the goal 6.6 s late; with client combat drones were dragged 43–55 times per match; 崖心 S1 moved a 刺胄之弹 0.87 tiles; 守墓石像 moved 0.03–0.44 tiles (1.4–1.7 under 失重). - Official: PRTS 特殊机制 静态刚体 "可以进入失衡状态…但物理层面上无法产生任何速度或移动…※与失衡免疫不同…※是否为静态刚体与单位的行动方式无关"; PRTS 失衡位移机制 (air units count, "但会因下述的判断而被取消位移"); every air unit of the mode except “炎佑” (weight 10, 我方单位) lists {{特殊机制|静态刚体}} in its PRTS 天赋, and so does 盐风主教昆图斯 — 28 keys (all 249 enemy pages checked). 失衡免疫 also: 刺胄之弹 / 斩胄之剑 / 破胄之锤 (their talent also lists 不可阻挡, so a grounded 剑 / 锤 stays unblockable); 守墓石像 转换模式 "无法被阻挡,自缚,失衡免疫,免疫浮空" and 飞行模式 "变为飞行单位,失衡免疫"; 喷气人 飞行模式 "近地悬浮,不可阻挡,失衡免疫,移动速度+50%", −90 % for 1.5 s after take-off and 1.333 s unblockable at −90 % after landing.
- Now: a static body moves 0 tiles from every push or pull; skills keep their reach and still hit it (薄绿 S2, 锏 S3, the hookmasters, 莫斯提马 S3 …; distance effects come to nothing). 守墓石像's statue is unblockable, rooted,
noDisplaceand refuses 浮空, its flight keeps a persistentnoDisplace; 喷气人's 7 s flight is an air unit withnoDisplace, then the brake and landing windows. Ground enemies keep §20.3's tables; 吉兆飞鳞 / 掠海漂移体 are 失衡免疫 while hovering and movable once grounded. - ASSUMED: a static body's 0.1 s 失衡硬直 is not modelled; a 浮空 that 守墓石像 carries at its knock-out runs out; 见行者 S2 / 歌蕾蒂娅 S3 reach air units, 乌尔比安 S1 does not.
- Tests:
test/sim/feedback1b-displacement.test.js(the data guard, 薄绿 vs drones, the act2 m01 flight, the 16 × 5 audit, coins / 流形 / links, the 胄 shell, 喷气人, 守墓石像).
21.10 Damage broadcasts in the Hidden Core; the strip by priority (C1) — match/Match.js startFinalAssault / tickerText FLOW_TICKER_PRIORITY, match/finalAssault.js BOSS_HIT_STEPS / SharedBossPool.byPlayer, ui/ticker.js, main.js (m.ticker)
- Seen: "隐藏boss还没打就出了造成50%伤害播报". Reproduced in real bot matches into the 隐秘核心: solo 绝境 seeds 3–5, the hidden leader's first hit (seed 3: 2034 of 900000) announced "超过50%"; co-op 2 seats 险境 seed 3: "超过80%" at 36 % dealt.
- Cause 1 (server): share =
stats.bossDamage / pool.maxHp, andstats.bossDamage(the result's 领袖伤害) sums both boss rounds. Cause 2 (client): the strip played its queue first in, first out, 5.2 s a line; a Final Assault line played 2–8 s into the Hidden Core over its fresh leader. - Official [DATA]: activity_table
autoChessData.broadcastListcomment_boss_hit_1..3 "{0}博士对敌方领袖造成的伤害超过20% / 50% / 80%!" (BOSS_HIT, paramList 0.2 / 0.5 / 0.8, priority 30); constDatabroadcastBeginDelay1; client 2.7.71 keepsbossDamage[localPlayer]per boss battle (research 11 §2.1); research 06 §9.2 "The highest priority wins": BOSS_HIT 30 > CHAR_DAMAGE 20 > SHOP_LEVEL 11 > (bond 10) > GOLDEN_CHAR 2 > CHAR_GIFT 1. - Now (server): share = the player's damage to that round's
SharedBossPool(byPlayer, fed by every credit path) ÷ itsmaxHp; each threshold once per player per round, a hit passing several announces the highest;stats.bossDamageunchanged (both rounds). - Now (client): lines keep
type,playerId,priorityand the round they arrived in; a BOSS_HIT line from another round leaves the queue and the strip; a player's newer BOSS_HIT line of the round replaces their older one, queued or on screen; and (integration, §21.19) the queue is ordered by priority, first in first out among equals, the line on screen never cut short, the lowest priority's oldest dropped past 4 queued — the remake's match-flow notices (隐秘核心已解锁, 联防阶段, a player out or gone) atFLOW_TICKER_PRIORITY25 ASSUMED, contentticker()lines 0. - ASSUMED: the threshold test ≥ ("超过"); the per-battle reset of
bossDamage(inferred); a dropped line is not replayed; the 1 s begin delay is not modelled. - Tests:
test/match/bosshit-ticker.test.js(solo 绝境 server-run, 2 and 4 seats client combat, SP_COMBAT=server, a takeover),test/ui/bosshit-ticker.test.js(the queue helpers across a 14 → 15 round change, the replacement rule, the priority order, timeline replays).
21.11 变形同构体 wearers in the member list and on the card (C2) — ui/gameLogic.js grantedBonds / pieceBondIds / bondMembers, ui/bondStrip.js BondPopup, ui/detailPanel.js BondChips / ChessDetail / resolveDetail, ui/watchBonds.js ownerBoard, battle/runner.js ownerOps, sim/snapshot.js unitInfo, match/Match.js prepFieldMeta
- Seen: with 变形同构体 (缪尔赛思's 特质 garrison_76 / band 变形者集群) and the matching item the wearer gets the bond's effect, but "那个盟约的人口数量没有把转职之后的干员算进去". Partly real: the count that activates the bond already included the wearer everywhere (
bondsMeta.pieceBondsfor the prep count, m.private / m.public, the battle input;support.unitBondsin the battle — checked on real matches for all 36 pairings). The client did not: the popup's member list and the wearer's card read only the records' bonds (在场 3/6 next to 成员 2/10, no chip). - Official: band 变形者集群 "与特定装备一同装备时装备者将视为特定盟约成员"; eff_acarm073 "携带者获得额外盟约(由另一件携带装备而定)"; the item's talent lists the 14 pairings; a member counts on the board only (BOARD) and once per operator.
- Now:
gameLogic.grantedBonds= the server's rule (a test pins the three together); the popup lists wearers tagged 同构, the card shows the granted bond as a dashed 同构 chip; a 同构 row opens the wearer's card with its item ids; a card without an own piece lists the carried items read-only under 装备. UnitInfoitems?(simunitInfo,Match.prepFieldMeta) carries an ally operator's item ids, so teammates' popups and cards show wearers too. - ASSUMED, unchanged: operator talents that test "【拉特兰】/【卡西米尔】/【叙拉古】… 干员" read the record's bonds (kits tier1 内线 ammo pick, tier5 inFaction, tier6 hasBond, tokens.js 卡西米尔 sword) — no source says whether a converted operator counts for them; 调和's +1 raises 在场 for core bonds without a member row (explained since §21.26: 含调和 +1 and a 调和 row).
- Tests:
test/ui/morph-bonds.test.js,test/ui/watch-bonds.test.js, server-side locks in the match suite.
21.12 源石溶剂 is damage; 信仰搅拌机 S3 counters any damage (D1) — sim/damage.js periodicDamage / isHpLoss / applyHpLoss, items/battle.js periodic_damage, kits tier1 / 2 / 4 / 5, content/enemies.js, bonds/addon/battle.js, choices.js
- Seen (community): officially the 源石溶剂 drain sets off 信仰搅拌机 S3, and next to 莫斯提马 it stacks 拉特兰 on its own; the remake never cast S3. Reproduced on the real match path (R3; default S3 + 源石溶剂, 莫斯提马 above her, 隐现): v0.1.0 never cast S3 (SP full at 23 s); without enemies she drained to death with 0 layers.
- Cause: the drain was
loseHp— a 流失: no 受击回复 SP, no TAKE_DAMAGE, ignored by shields — and the counter needed an enemy attack and a target. - Official: PRTS 盟约记录 源石溶剂 "受到60真实伤害" (修正 "并非流失"; 备注 "造成无来源真实持续环境伤害" and "携带后,全场范围内的所有敌人类我方单位也会获得此装备的…效果(该效果的付与为我方阵营索敌,可对空,无视目标可选性)"); PRTS 作战机制 (生命流失 "不会触发反伤、受击回复"; "同名buff的默认叠加策略buff只能表现出一个"); PRTS 伤害分类 (BUFF damage includes "自残类型", "弧光锋卫失衡状态下的自残伤害", "深水区/涨潮水蚀"); PRTS 帮助 重装 "受到伤害时释放技能"; PRTS 信仰搅拌机 S3 备注 "反击受到任何伤害后均可触发,无需目标,视为普通攻击"; garrison_22 (莫斯提马): every 6 bullets spent on her 4 tiles → 【拉特兰】 +1 / +2.
- Now:
periodicDamage(n)= 无来源 true damage (canDodge false, tags dot / periodic) with the old credit — shields, 脆弱,hiteffects, 受击回复 SP and TAKE_DAMAGE all see it, the kill credit is unchanged. S3 counters every damage but a 流失 or an element 损伤, spending a bullet even with no enemy in range (measured without enemies: +5 拉特兰 per cast, elite 2 casts +22; with the R3 wave +7; the browser sim gives the same digests). The drain also ticks on every 敌人类我方单位 of the field (炎佑, a partner's too), credited to nobody, once per second however many carriers, while a carrier is on the field. Real damage instead of 流失 also for 狂暴宿主组长 (悬赏·特异III), 码头水手's drowning ("每秒受到1000点无来源真实伤害"), 弧光锋卫's 失衡 bleed and the 孽罪奇美拉 aura (now radius 1.2, a tick every 0.5 s, one tick per unit — "同类效果取最高"). One rule both ways (isHpLoss): 雷蛇 战术防御 and 蒂比 S2 react to real damage; 乌尔比安, 伪装服, 远牙, 录武官's guard, 坚守 thorns and 机变 自愈 skip a 流失; operator 流失 (宴, 风丸, 水月, 华法琳, 烛煌, 史尔特尔, 号角 …), 传递 and the drone link stay 流失. Damage dealt (unitdmg, the results' 造成伤害) never counts the source's own side. - ASSUMED: the drain gives 受击回复 SP; the chimera aura is 无来源; a target-less counter is one bullet with no hit; the 炎佑 tick ends with the carriers; drowning keeps its 0.25 s cadence and the 失衡 bleed stays per distance moved (the amounts are official); 心烛 and 扎罗's transfers stay 流失.
- Tests:
test/content/feedback1d-solvent.test.js,items.test.js,kits_t1t2.test.js(蒂比).
21.13 野鬃's push direction (D2) — kits/tier1.js (野鬃), kits/tier3.js (琳琅诗怀雅 S3), content/generic.js, sim/Battle.js push, constants.js
- Seen (community): "干员野鬃的技能有时会把怪物往攻击方向相反方向推". Real: S2 夹枪冲锋 pushed radially at full force (§20.3's table), so a target behind her centre — an enemy that walked through her tile while her single block was taken, grabbed by the §19.2 hand-over — was thrown 1.7–2.14 tiles towards the objective. Measured on act2 m01–m04 (R3 / R5 / R7 official waves, every melee tile × 4 facings): 2689 of 15915 pushes (16.9 %) went more than 0.5 tile against her facing (LEFT 47 %).
- Official (client data): her S2 attack ability (charpack char_496_wildmn, anim Skill_2) carries buff
wildmn_s_2[force]of templateknockback[dir]= Knockback {_useSourceDirectiontrue,_decreaseForceLevelWhenNotInDirection2} (battle/buff_template_data.json): a 方向力 along her deploy direction with the PRTS 推与拉 特殊修正 (> 45° off or < 0.25 tile → radial at 受力等级 −2). Radial =knockback[relative]: 琳琅诗怀雅 S3 (swire2_s_3[knockback]; PRTS 备注 "推开效果为径向推动"), 山 S3, 莫斯提马 S3. 圣聆初雪 S1 = KnockBackWithCharacterDirection (no correction). Cross-check: 莱伊 S1ray_s_1decreases by 1, matching its PRTS 备注. - Now: 野鬃 S2 =
Battle.pushwithdir=unit.fwd(within 45° the full 中力 table along it: w0 2.14, w1 1.7, w2 0.44, w3 0.12; behind, beside or < 0.25 tile radial at −2: w0 0.44, w1 0.12, ≥ w2 0) — 0 pushes over 0.5 tile against her facing in the same sample; 琳琅诗怀雅 S3's coins are radial from her centre at full force (they went along her facing). The generic kit's text keywords (now also 往攻击方向) are a fallback only. §20.3's table is rewritten accordingly; nothing goes into ASSUMED. - Tests:
test/sim/feedback1d-push.test.js(4 facings × ahead / ≤ 45° / behind / beside / < 0.25 tile, the mirrored boss half, a real act2 m01 R5 hand-over, 琳琅 S3).
21.14 Special Originium slugs and 暴鸰's bomb (D3, D4) — tools/local-extract/extract.py ENEMY_SPINES, tools/assets/local-enemy-spines.json, plan.mjs localEnemySpines, fetch-assets --local-spines, assets.js spineEntry, render/units.js ALIAS_TINT / FORMS, content/enemies.js kitBombd, snapshot.js form, style.js PROJ.droneBomb
- D3 ("所有特殊源石虫的模型全表现为普通源石虫"), partly real: 高能 / 冰爆 / 简饲源石虫 and “庞贝” always had their own models; 灼热 / 炽焰源石虫 (enemy_1305_mhslim / _2, the ELEMENT slugs, up to 10 a round) were the plain 源石虫 — no web dump has their models (the asset plan aliases them); the official skeletons exist only in the client (refs/arts/enm_art_12.ab). Root cause located 2026-10-04: Ark-Models indexes
1305_mhslim/1305_mhslim_2with an emptyassetList(registered, never uploaded), so the alias chain falls toenemy_1007_slime— a different enemy rather than a variant of it. The mobile build ships their own model (enemy_spine/<enemyId>/<enemyId>.{skel,atlas,png}, straight-alpha pages), but its only public mirror is a community wiki and not a GitHub dump, so it is deliberately not an asset source (docs/ASSETS.md "Enemy aliases"): the tinted alias stays the web model until a GitHub dump carries these two. - D3 now: an optional overlay — the manifest keeps the alias as the web model and
enemies[id].spineLocalnames the files of the data/local-assets.json groupspine/enemy/<id>with the metadata of the committedtools/assets/local-enemy-spines.json(data/assets.json is byte-identical with or without the extraction). The client draws the official model when the local manifest lists every file, else (or on a load failure) the web alias tinted toward the slug's own lava colours (ALIAS_TINT: 灼热 orange, 炽焰 red-orange, research 07 §5.6) [ASSUMED look]. The 0.1.1 release bundle carries the models (extracted withnode tools/setup.mjs --local, thespine/enemyjob). Audit (249 enemies vs the client's prefabs): no other enemy is drawn with another's model; 伊利昂的木驮兽 starts on skinFullin the game and is drawn withdefault(unchanged).fetch-assets --prunenever deletespublic/assets/localany more. - D4 ("暴鸰的炸弹无法正常投放"), partly real: the bomb hit in the trigger tick, with no projectile, and stayed under the drone. Official (prefab enemy_1040_bombd / projectile_bombd; PRTS 暴鸰): Boomb plays Attack, the bomb leaves on OnAttack (frame 8) and flies homing at 5 tiles/s; the cast ends when the bomb is gone, ≥ 0.667 s after the release; buff bomb_s: the bomb-less mode S1 (*_2 clips) + speed ×2 ("技能结束后");
_immuneStunWhenAffecting0. - D4 now: the drone holds still at the trigger; 8 ticks later (8/30 s) the release ('atk' droneBomb + the form 'bombed' — UnitInfo
form); on landing the target 100 % ATK + the 8 tiles around 100 % splash (a target gone mid-flight: the bomb lands where it was; the drone's death after the release does not stop it); at max(landing, release + 0.667) ×2 speed and it flies on. A stun / freeze / sleep before the release interrupts the cast (no drop; the skill re-arms with its 1 s cooldown). - ASSUMED: no drop by a dead drone; the ATK at the release; holding still through the cast; the bomb-less look from the release; an interrupted cast keeps its one trigger; a stun after the release does not stop the cast end; the trigger radius 2 (§20.4).
- Tests:
test/feedback1d-models.test.js,test/sim/feedback1d-bombd.test.js(a real wave),test/render/feedback1d-bombd.test.js,test/static-local-art.test.js.
21.15 Enemy ground routes: 战场#04's official diagonal (D5) — server/sim/grid.js (_buildField, _spfa, smoothChains, crossTiles / segmentTiles), data/stages.json groundPaths*, test/sim/pathing-official / -blockable / -crosscheck / pathing
- Seen (real): on 战场#04 活性源石 (act1autochess_m04, the only stage with tile_infection) the lower-gate enemies walked (9,10) → (9,8) along row 9, then straight up into (10,8).
- Official: every autochess route has
allowDiagonalMovetrue andvisitEveryTileCenter/visitEveryNodeCenterfalse; normal WALK routes have no checkpoints; the client SPFA flow field + Bresenham smoothing (research 08 §3.1; PRTS 作战机制 "所有敌人始终选择最短路径行动…") gives (9,10) → (10,7) → (10,4) → (9,4) → E — a diagonal that touches the floor (10,9) only at a corner. - Cause: playtest #2's road-over-floor smoothing (§14 corrections) counted a diagonal step's corner tiles as floor and bent official diagonals into L shapes even where they cross no floor.
- Now: each flow field keeps two pointers per tile with the official distances — the official one, and 0.1.0's playtest #2 preference one restored exactly;
nextis the preference pointer only if its route crosses strictly fewer non-blockable tiles (exact geometry: a segment crosses the tiles whose interior it passes through, a corner touch is not a crossing), ties keep the official pointer (unless the preference one only skips a collinear official waypoint). Kept (playtest #2): 战场#01's lower-gate enemies take the col-8 road; on 战场#02's Final Assault both halves leave the exit via (2,9) / (2,11), so a blocker beside the exit holds them. - Audit (11 stages × 14 gate / field legs = 154 routes): 140 official as merged by the workstream (0.1.0: 109); with the 深水区 refusal (§21.3) 7 routes of the inactive 战场#05(上半), whose canoe water now counts as non-blockable, differ too — 21 of 154 (pathing-official
DEVIATIONS), each crossing strictly less floor. Visible: the boss exits of 战场#04, #05(下半) and #07(下半) are official diagonals — at #07(下半) the enemy passes (2,9) / (2,11) at √0.5 ≈ 0.70711 > the block radius 0.70709997 (§19.2), so an operator there no longer blocks them, as with the official route. - Unchanged: movement tile centre to tile centre along
next[][ASSUMED simplification];findPath/groundPathTilesreturn Bresenham tiles. One-line flip: alwaysuse = oin_buildField(pure official routes; reverses 战场#01's col-8 road).
21.16 The live attack range on the card; 烛煌 S3 (E1) — sim/Battle.js _refreshRange, sim/skills.js changesRange, shared/loadoutRecord.js attackRangeGrid / traitRangeExtend / extendedGrid, shared/protocol.js unitStatsEntry, ui/detailPanel.js, kits tier4 (信仰搅拌机) / tier5 (烛煌) / tier6 (荒芜拉普兰德)
- Seen: "干员烛煌开启3技能时候攻击范围不会变". Partly real: the sim was right (real 绝境 matches: 80 of 152 S3 hits on tiles only 4-11 reaches, none on 4-11 missed; the cast keeps the initial 3-1 per §20.2), but the battle card's 攻击范围 mini-map stayed 3-1 while the live ATK / interval beside it changed — the battle board has no range overlay.
- Official: skill_table skchr_blaze2_3 rangeId 4-11; PRTS 烛煌 S3 备注 "攻击溅射半径1.7;于攻击造成伤害前判定元素爆发并造成元素伤害", "补充后的弹药数量无法超过上限"; PRTS 信仰搅拌机 S3 备注 "此技能的攻击范围不受“攻击距离”属性影响".
- Now (card):
unit.liveRangeGrid(the running skill's grid, else the unit's own, plusrangeExtend) →unitStatsEntry.range(allies only) → drawn in battle 4× a second and in the prep preview; 400+ tiles read 全场, a grid beyond 6 × 7 draws smaller cells in the same box. - Now (record range):
attackRangeGrid= the range a unit is deployed with — a selected "被动效果:攻击范围扩大" skill (引星棘刺 S3 3-9), else a module's 攻击范围扩大 grid, else its own, plus the 特性's permanent 攻击距离 (traitRangeExtend: 信仰搅拌机 SPT-Y +1; 空弦 ISW-A works in 集成战略 only) — used by the card without a live entry, the board overlay, the deploy wheel, the server'ssummonRangeand the bot; over all 1284 loadouts it equals the battle's range at deployment. - Now (kits): 烛煌 S3 = one target on 4-11 + a 1.7 splash (中点判定; it used to hit every enemy inside 4-11); the 灼燃 burst bonus is dealt before the hit, on main and splash victims, from the attack as fired (her last shot lands after
end('ammo')and keeps it); each landed shot emits fx 'splash'; a burst refills ammo only up to 18 / 21. 信仰搅拌机 精锐 + SPT-Y + S3 was 11 tiles, now exactly 3-13 (targeting.noRangeExtend). 荒芜拉普兰德 S1's target grid only selects (targeting.showOwnRange: the card keeps 3-1). - ASSUMED: 技能范围 areas (古米, 泥岩, 山 S3, 锏) show while their skill runs; content extra tiles (蕾缪安, 维娜 S3) are not drawn; the 全场 / 6 × 7 thresholds (远牙's line is 2.1 px per cell on an 844 × 390 phone); the bonus also lands on a hit that is then dodged.
- Tests:
test/sim/feedback1e-skillrange.test.js,test/ui/feedback1e-range.test.js,test/render/feedback1e-splash.test.js,test/ui/playtest2.test.js.
21.17 Healers that block keep healing — not reproduced (E2) — sim/ai.js acquireTargets, sim/skills.js _defaultCondition, Battle.blockedTargets, test/sim/feedback1e-healers.test.js
- Seen (relayed): "走炎盟约的时候,如果是那种会到处跑的BOSS,被医疗阻挡之后,医疗不奶人了,会输出BOSS而且如果是那种强力击类型的技能也会在BOSS身上打出正确数值的伤害"; the user asked to check every healer under every bond.
- Official: PRTS 卫戍协议/帮助 (技能策略) "对于医疗干员(咒愈师分支除外),攻击目标为需要治疗的单位"; PRTS 仇恨: a medic's heal uses HP_RATIO_NOT_FULL_ASC (full-HP units excluded, itself counted); PRTS 选择器: blocked units are added only if the selector's side can pick them; 咒愈师 "攻击造成法术伤害".
- Verified, not reproduced (real Battle, skills made ready while blocking): the 炎 lineup (9 炎, tier 3, 2 炎佑) — 录武官 normal / elite × S1 / S2 holds 卢西恩 / 不祥幻影, casts while holding, deals 0 damage and keeps healing; every pure healer (医师 / 群愈师 / 疗养师 / 链愈师 / 行医: 26 records, 56 loadouts, each skill cast while blocking) × 23 bonds as a member, and 112 items: 0 damage; the 20 boss / hidden fields: 0 healer damage (healers block 卢西恩 / 不祥幻影 and a 碎铳之簧 with its shield down; the other leaders and parts cannot be blocked by a block-1 unit). 古米 S1 / S2, 塞雷娅 S1, 波登可 S1 heal allies while blocking; 瑕光 S1 / S3 strike + heal by text; 咒愈师 hit what they block (§20.3); 吟游者 make no normal attack.
- Likely what players saw (all official): 卢西恩 evades only while unblocked (in the 炎 run 11 % of hits dodged and 1,162 dmg/s while it roams, 0 dodged and 6,751 dmg/s while the medic holds it — the numbers come from the others and 炎佑); a blocking medic heals itself first when it is the most injured; 神经 bursts stun it for 10 s; operators do not turn, so its heal animation faces the boss.
- Now: no behaviour change; the rule is written in §5.5, SIM.md, the ai.js header,
Battle.blockedTargets, targeting.js and PLAYING, and locked by tests. Not checked: whether 华法琳 S2's official 流失 can kill an ally (the remake's can; PRTS gives no non-lethal note).
21.18 阵法术师 / 轰击术师 strike every enemy in range (E3) — sim/professions.js (SUB.phalanx / blastcaster, resolveProfile rangeAoe), kits/tier4.js 卡涅利安, the 流形 copy (content/tokens.js, kits/tier6.js), render/fx.js
- Seen: "干员卡涅利安的攻击不是真群攻". Real: the trait "技能开启时攻击造成群体法术伤害" was one bolt at the priority target + a 1.1 splash — spread enemies took 1 hit of 5 per attack; on act2 m01 R10 the elite 卡涅利安 hit 2.3 of 12.4 enemies in range per attack, 薄绿 2.0 of 12.6; on a real Match every 阵法术师 and 轰击术师 hit exactly 1 target while 2.7–4.7 stood in range.
- Official: a 阵法术师 / 轰击术师 attack strikes every selectable enemy on its range at once, the same damage near and far — primary for the 轰击术师: PRTS 作战机制 §AOE伤害判定, a 锁定攻击范围 AoE "在攻击前摇结束时选取范围内的全体目标,同时造成伤害" (its example, 伊芙利特's 炎爆, is her next-attack skill "下次攻击造成…"); for the 阵法术师 Terra Wiki Phalanx Caster (secondary) and PRTS 林 S3 备注 (one normal attack can kill several); supporting only: PRTS 溅射半径一览 has no radius for either (it omits 撼地者 too). 扩散术师 keep a 1.1 splash around the target (格雷伊 1.0, §21.19).
- Now: phalanx and blastcaster are
rangeAoeprofiles —resolveProfileapplies the flag after every override (kit trait included):allInRange+ instant'beam'hits drawn as a line to each victim (no splash ring, no shells). Blocked enemies are struck too (§20.3); a stealthed one only when revealed or blocked. Per-hit kit effects run on every enemy struck (薄绿 S2 拖拽 — a 静态刚体 is not moved, §21.9 —, 卡涅利安 S2 停顿 / 束缚, 阿罗玛 浮空, whose first-attack mark is shared between 阿罗玛 since §21.19). 卡涅利安 (PRTS 备注): a charged S1 keeps the skill-off trait — the guard AND 不攻击 for the whole skill (players will notice); S3's ATK climbs in 1 s steps to +140 % / +200 % at 20 s; the charged mark stacks before the damage, onebillro:markper enemy (another 卡涅利安 only adds stacks), ending with its setter's skill. A 流形 copy of a 'beam' becomes a bolt. - ASSUMED: no target cap (no source states one); damage at the attack instant (no wind-up modelled); the beam look.
- Tests:
test/sim/feedback1e-aoe.test.js(incl. a real wave counting the damaged enemies per attack id); opt-intest/render/phalanx-aoe.browser.test.js(RENDER_E2E=1).
| Branch (pool) | Official | 0.1.0 | Now |
|---|---|---|---|
| 阵法术师 (薄绿, 蜜蜡, 卡涅利安, 圣聆初雪) | every enemy in range | bolt + 1.1 splash | all in range, instant |
| 轰击术师 (协律, 阿罗玛) | every enemy on the line | bolt + 1.1 splash | all on the line, instant |
| 扩散术师 (格雷伊, 莫斯提马, 莱恩哈特, 夕, 寒檀) | 1.1 splash (格雷伊 1.0) | 1.1 | 1.1, 格雷伊 1.0 (§21.19) |
21.19 Integration follow-ups (feedback1) — render/interp.js, render/app.js, battle/runner.js, screens/game.js, sim/Battle.js foesInRadius, sim/professions.js, sim/constants.js CHAIN_RADIUS, kits tier1–6, content/{enemies,tokens,generic}.js, content/items/battle.js, content/bonds/core.js, ui/ticker.js, match/Match.js, match/bot.js
The last review of §21.4's client fix, the cross-workstream notes and the last review rounds were checked on the merged branch; the worthwhile items with a primary source are done here (the rest: §21.20):
- Form fx in the render engine (review of the final §21.4 fix, major):
render/interp.js isCosmeticEventcounted every 'fx' as cosmetic, soprocessEventsdropped one stamped > 1.5 game s behind the render clock and a full queue shed it — after a main-thread stall over ≈ 0.78 s real at 2×, on a watched replica after a hidden tab (catch-up frames of 8 game s), after Chrome's intensive throttling, and for the game screen's replayed early buffer (stamped 0). Reproduced in headless Chrome: 转译基底·α endeddead:null:B_Die— report #5's look. Now a form fx is state for the queue too; one handed out late switches the model without its telegraph, with its closing clip shortened by the lateness and without a change clip that would already have ended. The runner compacts an overflowing hidden-tab backlog (compactHeld) instead of re-entering the view, flushes the backlog and a last snapshot of a battle that ended while the tab was hidden, and the game screen buffers the frames of a re-sent field meta for the field on screen until it re-enters it. The browser test stalls the main thread (blocking 1 s long tasks; the oldsleepleft the renderer's ticker running). - Area damage and 隐匿 (PRTS 作战机制 §AOE伤害判定 "AOE的判定是对攻击范围内的每个可以被选中的敌人进行判定"; 隐匿 "隐匿状态下的单位一般无法被敌方的索敌机制和Buff选择器选中为目标"): an operator's radius area effects — profession splash, aftershocks, skill circles and zones, counting conditions — use
Battle.foesInRadius(no untargetable, no unblocked unrevealed 隐匿 enemy); enemy-side auras / heals and physical collisions keepenemiesInRadius. The 逐火 embers and every 隐匿 enemy take less splash than in 0.1.0 (officially they took none). - Splash radii (PRTS 溅射半径一览, 特性 / 特殊): 投掷手 0.9 (was 1.0), 链术师 jumps 1.7 (1.8;
CHAIN_RADIUS), 格雷伊 1.0 (1.1); 迷迭香 S2 末梢阻断 1.5 (the table's skill row; was ×1.3 ASSUMED). - 阿罗玛's 非首次标记 (PRTS 阿罗玛 天赋 备注 "…自身离场时移除自身已施加的标记,不同阿罗玛之间的非首次标记通用"): one mark per enemy (
aroma:bubbled, the setter as source) shared by every 阿罗玛, removed when its setter leaves (two used to trigger on one enemy; E3 made it show). - 奥术法阵 on the field (PRTS 盟约记录 备注, the same as 源石溶剂's "…敌人类我方单位也会获得此装备的“造成伤害时使目标失去特殊能力5秒”效果"): 炎佑's damage silences its target for 5 s while a carrier is on the field [ASSUMED: it ends with its carriers].
- 重生 cooldowns (PRTS 特殊机制 §重生 "重生结束时,重置自身的通用技能与当前形态的技能冷却为初始冷却"): every ability with a cooldown restarts from its initial cooldown when a 重生 ends (
rebirthCooldowns:reborn(), the husk's 1 s 重生, the statue's instant one). - 高准度伦蒂尼姆城防自行炮 (悬赏 target; PRTS 技能 "以攻击范围内生命上限最高的我方单位为中心,生成9格的炮击区域,6s内每0.5s对炮击区域中生命比例最高的我方单位进行攻击…(最多进行10次攻击)", 备注 失衡 / 晕眩 / 冻结 / 浮空 / 沉睡免疫 during the animation): the lock is in its range (it was any ally), the area the 3×3 tiles, 10 shots 0.5 s apart (were 3, 1 s apart ASSUMED), 失衡免疫 + those immunities over the bombardment [ASSUMED: the animation's length].
- 琳琅诗怀雅 S3's coins (PRTS 备注 "前方范围2-4内的地面敌方单位与自身阻挡的所有单位"): range_table 2-4 in front plus the units she blocks (her 1-1 attack range before).
- 阿戈尔's devour (PRTS 盟约记录 "造成5000点物理流失", 修正 "【吞噬】的物理流失来源为被付与目标自身"; PRTS 作战机制: a 物理流失 "会受到目标当前防御力…影响而相应衰减"): 5000 less the target's DEF, still a 流失 (no shields, dodge or multipliers), the kill to the marker (DEF-free ASSUMED in 0.1.0).
- The ticker by priority (research 06 §9.2, WJ's note): §21.10.
- AI 托管 and temp (the promise "你开 AI 托管时,它不会销毁你的道具"): on a human seat under AI 托管 an item left in temp with a full hand gets a hand slot (a bench operator sold) instead of being destroyed; only a hand of nothing but items still drops it, as the temp deadline would.
- The bot (§21.6): reconciled with the merged rules as listed there.
- 0.1.0 bug found in the merge: a 联防 battle timed out during 隐德来希 S3 reported its 心烛 as leaks, so the server refused the honest client's result (fullmatch-coop3 seed 116) — candles carry
mem.noLeakandBattle._timeoutskips them. - Checked, no change: 斩胄之剑 / 破胄之锤 stay unblockable while grounded (their PRTS 天赋 lists 不可阻挡); the database's lpr-0 enemies (简饲源石虫, 节日气球, leader parts, tokens) still cost 0 LP in a leader round (the official
lifePointReduce); 鸭爵 swaps skip bounty-tagged spawns; 转译基底's bounty coin is paid once. - After the independent QA of the merged branch (residuals, each checked against its source first):
- WB's 22-match follow-up merged (QA major): on the 11-match model 16 of the 58 official bounty drafts of the 22 matches could not be generated (two series-20 cards in an R3 set, named bosses apart at R9, an R11 draft without a giant); with the follow-up every one is reachable — §21.2.
- A status of the knocking-out hit (§21.4): 妮芙 S2's fear (and a 叙拉古 6 proc, 铃兰's slow…) landed after the 重生's cleanse — the same hit runs
if (target.alive) applyStatusafter the kill, and the husk is still alive — so an ember walked unblockable + 隐匿 for ≈ 3 s more. A unit both 无敌 and 无法选中 now takes no status from the other side (PRTS 无敌 "无法被不同阵营选中";Battle.applyStatus). - 余 S2 and 自缚 (in 0.1.0 already): 厚礼上宾 teleported a 守墓石像 in its 转换模式 onto 余's tile. PRTS 余 S2 备注 "处于消失状态的/持有自缚的单位不视为可达目标"; the statue's PRTS 天赋 lists 自缚. Flag
selfBound(the statue, the 自缚 leaders, the 胄 parts' anchor) besidenoMove— notnoMoveitself, which 束缚 sets too. - 信仰搅拌机 S2 (in 0.1.0 already, ASSUMED then): with fewer than 30 bullets the lethal hit went through. PRTS 备注 "弹药量不足时仍可抵挡致命伤害,此时将消耗所有剩余弹药并退出技能状态" and "自身持有不死时此效果不会生效": it blocks, spends what is left and ends; while 坚固维式重锤's 不死 window holds her (
items/battle.js holdsUndying: this deployment's window) it steps aside (骑士戒律's earlier 不死 already did). - Late form fx after a hidden tab or a catch-up (§21.4's client half): the runner sent the hidden-tab backlog and a catch-up frame's events stamped with the frame's game time, so the render engine saw a 16 s old form fx as on time and replayed the 2 s change clip on a walking 转译基底·α (the final form was right). Such a step now drains its events every
EV_SLICE(15) ticks and each batch keeps its own game time (battle/runner.js stepEntry / drainSlices / heldAt); render/app.js then skips the change clip as for a stall. - The bot's bench shed (§21.6): the buy loop's pre-emptive shed (one hand slot left) sold the single it had just bought for 3–5 back for 1 — the buy picks by lineup gain, the shed by piece value (co-op 绝境, 2 AI 托管 + 2 bots, seeds 21–26: 29 same-prep buy → sell, all from that line, vs 7 in 0.1.0). It now never sells a piece that came this prep (
sellWeakestHand keepFresh); 3 remain, from other paths (a reward pick's room,arrange,sellJunk). - Checked, not changed: the QA's other notes are on the user-facing list (wording) or the release packaging (the 灼热 / 炽焰源石虫 models come from the gitignored local extraction:
public/assets/local/spine/enemy/enemy_1305_mhslim{,_2}+data/local-assets.json).
- Tests:
test/sim/feedback1-followups.test.js,test/sim/feedback1-integrate.test.js,test/sim/feedback1-residuals.test.js,test/sim/feedback1-transform.test.js,test/match/feedback1-runner-forms.test.js,test/render/interp.test.js,test/ui/bosshit-ticker.test.js,test/match/feedback1-meta.test.js,test/match/bot-smarter.test.js,test/content/{enemies_bosses,bonds_core,kits_alt_t4,kits_alt_t6}.test.js.
21.20 Settled decisions and what stays ASSUMED
Settled by the user:
| Decision | Settled rule | One-line flip |
|---|---|---|
| A tactician re-oriented in place (§21.3) | a range-bound summon still inside the new range stays; one outside goes back to its stack (user-confirmed 2026-10-03; PRTS only says a MOVED owner resets every summon) | PlayerState._liftOutOfRange lifting every range-bound summon of a re-oriented owner |
| Bounty drafts (§21.2) | the official structure of each round from the user's 66 screenshots of 22 matches (R3 fixed sets, R9 boss groups, R11 seen 7-card lists; no multi-round card in a draft) | add a newly seen set / group / list to tools/build-data.mjs BOUNTY_INITIAL_SETS / BOUNTY_BOSS_GROUPS / BOUNTY_HUNTER_GROUPS and rebuild |
| 机密商店 / 战术决策 repeats (§21.2) | the same card can be offered twice, as officially (the user, 2026-10-03: "机密商店按官方改成可以重复吧", "战术决策也按官方改成可以重复吧"); R11 机密商店 = two VI, ≥ 1 V, ≥ 1 盟约之币 | choices.json shopDraft / tacticDraft removed (choices.js falls back to the previous draws, still with replacement) |
| 坚固维式重锤's scope (§21.21) | one 不死 lock per DEPLOYMENT: the carrier's own redeploy (after a knock-out, a 突袭 retreat + redeploy) re-arms it and ends a window still running (the user, 2026-10-03: "每次部署一次"; the text says only 首次); the lock belongs to the deployment, so a borrowed hammer (萨尔贡 × 娜仁图亚) follows the same rule | content/items/battle.js deploymentOf returning one key for the whole battle (one lock per battle) |
| Six 重装 skills' trigger (§21.29) | 深巡 S2, 雷蛇 S2, 号角 S2 / S3 and 灰毫 S1 / S2 cast with an enemy in range (the basic strategy, DEFAULT) instead of the official 下半 TANK row's TAKE_DAMAGE — a deliberate, documented deviation (the owner, 2026-10-03: "反馈的人太多了"; GitHub issue #4, PR #12); every other MANUAL 重装 skill, 深巡 S1 included, waits for a hit (余 S2 excepted since 2026-10-04: the owner extended the deviation to 余 S2, which casts with an enemy on its x-1 — SKILL_RANGE, §22.10) |
remove the entry from tools/build-data.mjs TRIGGER_DEVIATIONS (for 雷蛇 also PR #12's trigger: 'DEFAULT' kit line; 深巡 S2 reads its data's ACTIVE_RANGE since 0.2.0) and rebuild; for 余 S2 remove its TRIGGER_DEVIATIONS entry (chess_char_6_03_a) and rebuild |
| Multi-round bounties (§20.6, unchanged) | last two battles — now only 教鞭's card | server/match/choices.js MULTI_ROUND_BOUNTY_BATTLES = null |
| §20.9's decisions | unchanged (start-deploy summons, 凯瑟琳's hand card, every blocker hits, the collider reach, the elite to the board, the fixed leader pool, the watched bond strip) | as §20.9 |
Official since this release (each with its source in its section): 突变细胞 not consumed, its carrier destroyed (tile freed) and the new operator gained into the 整备区 — official footage, pointed out in PR #2 (§21.1); the 深水区 refuses deployment and a tactical point stays in range (§21.3); the knock-out forms and kill accounting (§21.4); 圆仔 / 鸭爵 unblockable, +300 % speed, swaps in leader rounds, lpr 1 (§21.5); 标准's 本局禁用 bonds (§21.7); 维娜's lions on every free tile, 玛恩纳 S3 可对空 and his per-kill rule (§21.8); 静态刚体 (§21.9); the per-round BOSS_HIT share and the priority strip (§21.10, §21.19); the 变形同构体 member (§21.11); 源石溶剂 as damage, 信仰搅拌机's counters, the BUFF-damage enemies (§21.12); 野鬃's directional push and 琳琅's radial coins (§21.13); 暴鸰's drop (§21.14); diagonal routes (§21.15); 烛煌 S3's splash, 攻击距离 vs 信仰搅拌机 S3 (§21.16); medics heal while blocking (§21.17); 阵法术师 / 轰击术师 strike all in range (§21.18); area damage vs 隐匿, the splash radii, 阿罗玛's shared mark, 奥术法阵 on 炎佑, 重生 cooldowns, the 自行炮, 琳琅's coin range, 阿戈尔's 物理流失, no status on a 无敌 + 无法选中 unit, 余 S2 vs 自缚, 信仰搅拌机 S2 with too few bullets (§21.19); the R11 机密商店 composition and repeats in the 机密商店 and the 战术决策 (§21.2); M3茧甲's revive after every 不死 (§21.21); 起飞 = 对地规避 (§21.22); no ground blocking from a fenced tile (§21.23); a knocked-out operator keeps its tile and redeploys there, rule 3 included (§21.24).
Still ASSUMED (each in its section):
- §21.1: an elite merged this round keeps its copies' refresh count; a 拉普兰德 re-bought after a sale fires again; a bench 突变细胞 carrier transforms too; a 6阶 carrier becomes another 6阶; with the hand and temp both full the gained operator and an item without a slot follow the rules of every gain.
- §21.2: the weights inside a group; the unseen 10th R3 set and 险境 R6 drafting like R3; R9 groups by the matches they came in; R11 one of the 7 seen lists and the family weights (solo = co-op); the 机密商店 slots / weights and the earlier shops; the 战术决策 weights, independent draws and twin passives stacking; solo shows 3 of 6. Open for the user: each match's 确认本局信息 page (what decides the R9 group), more R11 悬赏决策 / 机密商店 / 战术决策, solo 绝境 / 终极 R3 / R9 / R11.
- §21.3: the range is the loadout grid (items and bonds do not widen it); the canoe in the deploy map only.
- §21.4: the translator immobile and damage-cancelled through its change; the 特战术师 form hits flyers (open for the user); the ember's 10 s after the 重生; 隐匿 back 3 s after a block ends (§22.8; it was "at once" until 0.1.1); the 重生 whitelist; “余音” unmapped.
- §21.5: the 鸭爵 swap's 0–2 uniform count and pick, the 60–99 % window, ground only, elites eligible, per-half on pair fields, decided at battle start.
- §21.6: the bot's tuning constants and
CROWD. - §21.7: strongest rather than earliest for 奥术, an equal re-hit refreshing it; the 本局禁用 look; 整备 next to an owned white.
- §21.8: fence tiles as 可部署地面 for 黄金盟誓; the absolute −10 % per kill of 玛恩纳 S3.
- §21.9: a 静态刚体's 0.1 s 失衡硬直 not modelled; 守墓石像's carried 浮空 runs out; which pushes reach air units without a note.
- §21.10: ≥ for "超过"; the per-battle
bossDamagereset; a dropped line not replayed; the flow notices at priority 25; no begin delay. - §21.11: operator talents reading the record's bonds for "【X】干员" (a 变形同构体 wearer does not count there).
- §21.12: the drain's 受击回复 SP; the chimera aura 无来源; a target-less counter = one bullet; the 炎佑 tick ending with the carriers; the drowning / bleed cadences; 心烛 and 扎罗 as 流失.
- §21.14: the alias tint; 暴鸰's cast details.
- §21.15: tile-centre movement along
next[]. - §21.16: the 技能范围 areas on the card, extra content tiles not drawn, the 全场 / 6 × 7 thresholds, the bonus on a dodged hit.
- §21.18: no target cap; damage at the attack instant; the beam look.
- §21.19: the 奥术法阵 rider ending with its carriers; the 自行炮's immunity window = its bombardment.
- §21.21: a 突袭 retreat + redeploy is a deployment (it re-arms the lock); an in-place 复活 (M3茧甲, 埃芒加德) is a new deployment too (PRTS's 复活 is 退场 + a 0-time / 0-cost redeploy); the 阿戈尔 devour and the carrier's own 源石溶剂 spend the lock; kits' own savers before the item's 不死; M3茧甲 before 埃芒加德; a running window ends with its deployment and outlasts the lend that started it; a free pick or grant is no 购买; a 谢拉格 already priced ≤ 1 still counts as the round's first.
- §21.22: a ground enemy's shot in flight when 蒂比 takes off lands on nothing; ground-enemy auras still apply; 流失 DoTs keep ticking; an area skill cast because allies are near counts only those it can hurt (卢西恩's 【aoe】, 锏's CircleAttack).
- §21.23: air blocking (blockFly) on a fenced tile stays; the gate reads ground passability.
- §21.24: the body goes home once, at the knock-out, only when its home is free; 初始位置 = the homes of the board pieces of every player on the field, on the field or not (a summon that has expired or been killed included); 歌利亚头盔 ignores bodies; every 退场 leaves a body (§22.15), except the 突袭 retreat, which redeploys at once.
Not done (noted, no primary source or out of scope): whether 华法琳 S2's 流失 can kill an ally (§21.17); 伊利昂的木驮兽's Full skin (§21.14); the render-demo recording public/dev/recordings/normal-a1m04.json still shows 战场#04's old L-shaped lower route (display only); M3茧甲's "同名装备的效果不可叠加" (two copies give two revives, a + b three) and two own 坚固维式重锤 giving a 16 s lock; the M3茧甲 / 埃芒加德 revive in PRTS's form (退场, then a 0-time / 0-cost redeploy: the SP reset and the other per-deployment effects — only 坚固维式重锤's lock treats the in-place revive as a new deployment) and a status icon for the 不死 window (§21.21); 深巡 S2's nearest-first order (§21.23); 【盲信之誓】 hurting once per chain (PRTS "多条锁链的伤害不叠加") and the “帝国的甲胄” barrage's timing (§21.22's audit).
21.21 坚固维式重锤's lock, once per deployment; 休露丝's discount (F1, F2) — content/items/battle.js hammerAcquire / deploymentOf / holdsUndying / revivedInPlace / PRIO_REVIVE / PRIO_RESPAWN, content/bands/battle.js PRIO_BAND_REVIVE, kits/tier4.js (信仰搅拌机 S2), content/bands/meta.js (休露丝), match/PlayerState.js buy / pickReward
- F1 seen: "维多利亚坚固锤子的锁血没生效". Official (act2autochess eff_acarm043 / eff_acgarm043): "首次受到致命伤害时生命值不低于1,持续8秒",
undeadable_duration8 (the latest upstream data agrees; PRTS still prints the act1 15 s); 异常效果 不死 "重设常规生命值时不会使其低于1". - Measured on 0.1.0, the lock works: every HP loss of a unit without a boss pool fires
fatal(hits of every type, element bursts, 无来源 damage,loseHp流失) — on every visible chess against scaled real enemies, in forced-hammer bot matches and in the browser's local battle. - Why it looked missing: it was once per battle — the hammer state lives on the unit, which a redeploy reuses, so a carrier knocked out after its lock came back with none (forced-hammer co-op matches, 3 绝境 + 4 困难: 27 of 382 carrier knock-outs); the 阿戈尔 battle-start devour (a lethal hit on the operator in front: 10 of 179 locks on 绝境) and the carrier's own 源石溶剂 spend it; with an M3茧甲 equipped first, its revive (same priority, registered first) took the first lethal hit.
- Now: once per deployment — the user's first-hand memory of the official mode (2026-10-03, "每次部署一次"; the text says only 首次 where M3茧甲 says 一场战斗 and 拉特兰桥夹 每次部署): a new deployment of the carrier (every deploy — the redeploy after a knock-out, a 突袭 retreat + redeploy, 阿戈尔's 立刻复活 — and an in-place 复活, below) re-arms the lock and ends a window still running. The
fatalorder is kits' own savers (10 … −60) → items' 不死PRIO_REVIVE−100 → items' 复活 (M3茧甲)PRIO_RESPAWN−101 → 埃芒加德 −110: PRTS (M3茧甲 / 埃芒加德 / 阿戈尔 备注) "“复活”的实现方式为:受益者因移动之外的原因退场时下次部署的再部署时间和费用归零" — a revive acts on a knock-out, which a 不死 prevents. No status icon shows the 8-s window (the client has none for 不死; a one-shot fx marks the lock). - QA after the integration, fixed: (1) the lock lived in the hooks of the carrier's hammer grants — a hammer lent by 萨尔贡 × 娜仁图亚 (60 s) took its re-arm hook along when the lend ran out, so a borrower knocked out while lent came back with its lock spent when lent again (real Match, 萨尔贡 6: no lock in the second deployment), and a window started through the lend ended with it (knocked out 4 s into its 8 s). The lock now belongs to the deployment —
deploymentOf(the deploy counter plus the in-place revives) marks the deployment whose lock is spent — and one battle-level hook holds every running window (holdsUndying, also what 信仰搅拌机 S2 steps aside for): a borrower is re-armed in its next deployment like an owner, a lend refreshed in the same deployment re-arms nothing, and a window outlasts the lend [ASSUMED: the 异常效果 不死 outlasts its source]. (2) M3茧甲 and 埃芒加德 revive in place, which kept the lock spent, while 阿戈尔 5's 立刻复活 — a real redeploy — re-armed it; PRTS describes all three the same way ("“复活”的实现方式为:受益者因移动之外的原因退场时下次部署的再部署时间和费用归零"), so under the per-deployment rule a revive is a new deployment: both in-place revives now callrevivedInPlaceand the revived carrier locks again on its next lethal hit ASSUMED. - F2 seen: a 谢拉格 taken from the promotion reward used up 休露丝's "每回合购买的首名<谢拉格>干员消耗资金为1". Not real: only
g.buydispatchesonBuy, where every 购买 counter lives (休露丝, 玛恩纳, 升华, 整备); the promotion reward, 寻呼模块's pick and the 拟态物质 / 紧急调度券 / 简易通讯机 grants never touch it (pinned through real intents; METAonBuy). Buying the copy that completes a merge uses it, as it should. - Tests:
test/content/feedback1f-hammer.test.js(per deployment, a retreat + redeploy, the devour, the M3茧甲 order, the M3茧甲 / 埃芒加德 revives, a lent hammer),test/sim/feedback1-residuals.test.js(信仰搅拌机 S2 and the window of an earlier deployment),test/match/feedback1f-sciurus.test.js.
21.22 蒂比's 起飞 is 对地规避 (F3) — sim/targeting.js evadesGround / canTargetAlly, sim/damage.js liftoffEvades (DamageInfo ignoreSelect), Battle.applyStatus / _blockerFor, buffs.js (liftoff), kits/tier2.js LIFTOFF_FLAGS, kits/tier5.js (隐德来希), content/enemies.js, content/bosses.js
- Seen: after 蒂比 took off, enemies still hit her. Real: 起飞 was
unit.ground = false, which freed her from melee enemies only; ranged ground enemies kept shooting her and their areas and statuses kept landing (46 of 241 enemy types hit her while airborne; the client replica the same). - Official: ba.liftoff "不阻挡地面敌人且不会被地面敌人攻击,可以阻挡飞行敌人"; PRTS 术语释义: 起飞 includes 对地规避, a 无法选择 effect only selectors whose 行动方式 is ground respect, and she keeps a ground 行动方式; PRTS 异常效果: direct picks and abilities that "无视无法选择" are not stopped (these effects act "仅在选择时"); PRTS 作战机制: an AoE judges only selectable units.
- Now: 起飞 is the skill flag
liftoff(S1 and S2, normal and elite). No ground enemy (notUnit.isFlying) selects her — normal attacks, ability picks, 周围四格 additions, chains, bounces, shells, the “帝国的甲胄” barrage, 鼠王's 沙狱, 烹泉's blast and steam, ground zones (集团军重型火炮's 燃烧区域 is "不可对空", 迷彩 alone exempted) and their statuses; the pipeline refuses a ground enemy's damage on her before thehithook, so a refused hit neither sets off her S2 nor restarts 片场工作指南. She blocks flyers only; flyers, 近地悬浮 and 浮空 enemies still attack her with no 对空 check; she stays a ground unit (污染秽蚀 at the low-ground 50/s; 隐德来希 S2's 血镰 on her cuts air). - Still reaches her (no selection — DamageInfo /
applyStatusignoreSelect, or 无来源): the "无视无法选择" abilities (【污染秽蚀】, 【盲信之誓】, 萨卡兹悖谬暴虐兵长's 暴击 splash — her 片场工作指南 may dodge it); 碎铳之簧's counter and 重装侦察兵's 暴露 on their attacker; the 刺胄之弹 / 剑 / 锤 blasts (flying units); the ticks of a debuff a ground enemy put on her before take-off (出血, 沙狱, burning, 淤困, 【自然涌动】 — their mods stay); sourceless damage (毒雾, the chimera aura, 源石溶剂). - QA after the integration, fixed: 卢西恩,“猩红血钻”'s 【aoe】 (radius 2) and 锏's CircleAttack (first form, radius 1.5) decided to cast by counting every ally in their radius, so an airborne 蒂比 alone in range set them off and the blast skipped her (6 casts in 60 s, 0 hits on her); they count only the allies they can hurt (
!evadesGround) [ASSUMED: no PRTS line states the trigger]. The player text (PLAYING §10, CHANGELOG) now says that ground-enemy auras / field effects and counters on her as an attacker still reach her. - ASSUMED: §21.20. Audit: 蒂比 is the mode's only 起飞 / 对地规避 operator; outside F3, ranged enemies still pick invulnerable allies (锏 S3, 泥岩 S3) and enemy splash still hits 隐匿 allies (§19.1).
- Tests:
test/sim/feedback1f-liftoff.test.js(14), opt-intest/render/feedback1f-liftoff.browser.test.js(RENDER_E2E=1),kits_t1t2/kits_alt_t2.
21.23 深巡 on a fenced tile; fenced units block no ground enemy (F4) — sim/Battle.js _blockerFor
- Seen: "干员深巡放在围栏围着的地块里不会主动攻击". Official: the fenced tiles are 围墙 (
tile_fence_bound, "可放置近战单位,敌方无法进入") and 围栏 (tile_fence, "不可以通行") — LOW, buildable ALL, passable to flyers only; PRTS 围墙 / 围栏 地形机制 "部署在其中的单位,若当前阻挡类型为'地面阻挡'则无法阻挡敌人"; 深巡 is a 哨戒铁卫 ("能够阻挡三个敌人,可以进行远程攻击", range 2-2); defender skills fire "受到伤害时" (PRTS 卫戍协议/帮助). - Not reproduced: from a fenced tile she attacks every targetable enemy on her range tiles — every deployable fenced tile of the 7 stages that have them, every direction facing walkable ground, normal and elite; the Final Assault halves and 联防; a real Match; the browser's battle (7 attacks, 1977 damage). Targeting reads range tiles only (no path or line-of-sight condition).
- Likely what players saw (official): in some directions her 3-tile line faces only fence or forbidden tiles; 阻隔工事 crates hold some fenced tiles from the start (act1 m01: 3 of each 6-tile block; act1 m04, act2 m03); nothing hits her there, so her skill rarely fired (since §21.29 her default S2 casts with an enemy in range, a deliberate deviation; S1 still waits for a hit); an own-tile melee unit never attacks from a fenced tile.
- Fixed (found while checking F4): a push or pull stops an enemy at the fence edge, 0.5 from the fenced unit — inside the 0.7071 ground radius — and 0.1.0 let that unit block it (薄绿 S2 on a fenced tile held the enemies she dragged).
_blockerFornow refuses ground blocking from a tile ground units cannot pass (on the stages exactly thebtiles). - ASSUMED: §21.20. Tests:
test/sim/feedback1f-fence.test.js; PLAYING §10 tip.
21.24 Knocked-out operators keep their tile (F5) — sim/Battle.js downOn / restTile / _layBody / isReservedTile / redeploy / _checkRedeploys / _downState / snapshot, sim/units.js body, content/bonds/addon/battle.js (突袭, 不屈), content/bonds/core.js (阿戈尔), kits tier1 / 3 / 4 free-tile helpers, render/interp.js, render/units.js setDown
- Seen: "突袭盟约的干员会移动到濒死干员的尸体上". Real: a knocked-out operator left the occupancy map at once and only its home tile stayed reserved — a 突袭 member that fell after its jump lay on its landing tile, which the next jump picked; a timed redeploy came back on its home, where a teammate that had jumped there lay (96 real battles: 6 placements on a body; the shared boss field mirrored: 38).
- Official (PRTS 卫戍协议/帮助 §作战阶段 单位部署): "干员退场后…原地留下一个“倒地干员”…满足再部署条件时,移除场上的该倒地干员并自动部署至该位置"; "倒地干员所在地块视为可部署,但所有我方单位在此处的部署行为将被阻止"; "若干员被击倒的位置为其他干员或召唤物的初始位置,则在被击倒后,尝试返回其自身的初始位置". The 突袭 text "再部署至一名地面敌人周围" adds no tile rule.
- Now: a knocked-out operator lies on
body— where it fell, or its own home when it fell on another board piece's home and its home is free (x / y / tile keep where it fell for the 被击倒时 handlers). Every redeploy comes back there (timed, 不屈, 阿戈尔), also for an operator forced out by its own effects (余烬, 耀骑士临光 S2, 骑士戒律 + 竞技旗, 伊内丝 S3, a 商人 that cannot pay; §22.15); only the 突袭 retreat leaves no body.downOn:_deploy(redeploys, the 突袭 landing, summons),spawnDeviceandrelocaterefuse the body tile for every ally;isReservedTilereports it, so the 突袭 landing, tactical points, summon / device pickers and 维娜's lions skip it. b.snapdowncarries the tile (§8.2) and the client draws the body there; WAIT_TILE matches the_checkRedeployshold-back and is a safeguard. After the fix: 0 placements on a body in 526 jumps. Superseded: "later redeploys use the board tile" — an operator moved off its board tile (a 突袭 jump, 乌尔比安's anchor) now redeploys where it fell; research 01 §4.3 carries the PRTS quotes. - QA after the integration, fixed: rule 3 counted only the board pieces that were not removed, but a summon leaves its home free only once it has expired or been killed (a docked one keeps its tile reserved), so the 召唤物 clause never applied — a 突袭 member that landed on the home of 浊心斯卡蒂's expired 海嗣 and fell there lay on it and kept the 海嗣 (远古血亲) from coming back. Every board piece's home counts now, on the field or not. Cost: the body bookkeeping added about 2.5 % per tick on 519 real specs (8 % on boss fields; whole matches and the perf budgets unchanged);
_checkRedeploysnow checks the DP before the tile scans, so an operator waiting for DP costs no ally scan — on the same 519 specs, against the integration head, the same tick counts and boss fields 11.9 → 10.9 µs per tick (a tile → body map is left for later). - ASSUMED: §21.20. Tests:
test/sim/feedback1f-downed-tile.test.js(rule 3 for a summon that has expired or been killed),test/render/feedback1f-down-tile.test.js, opt-intest/render/feedback1f-down-tile.browser.test.js;downelem,bonds_addon,engine-requests,robustnessfollow the rule.
21.25 Hardening credited to pull requests (PR #2, PR #7, PR #10, PR #14) — match/effectsMeta.js dispatch / stillEquipped, tools/fetch-assets.mjs shrinkGuard, tools/assets/manifest.mjs droppedEntries, assets.js loadImageElement, ui/guide.js preload, css/screens/loadout.css
- The prep dispatcher's equipped items (pointed out in PR #2, whose 突变细胞 bench rule §21.1 now follows — official footage confirmed it —, its merge path excepted): step 5 iterated
holder.itemslive while handlers move and destroy pieces — normal 博士投影 destroys itself at the round start (the splice skipped the next item on that holder), 突变细胞 transforms its holder (the next item ran with a holder that was gone). It now walks a snapshot of the owned chess and of each holder's items and runs an item only while it is still equipped on its still-owned holder; a piece gained or equipped meanwhile waits for the next dispatch (effectsMeta header, META §2.2). No shipped item has a second hook on those events, so play is unchanged; a synthetic spy handler pins the rule (test/match/feedback1-dispatch-snapshot.test.js). QA after the integration: the items were read per holder as the walk reached it, so an item equipped meanwhile onto a holder not yet walked ran in the same dispatch and an item moved to a later holder ran twice (latent: no shipped item equips or moves items on those hooks); every [holder, item] pair is now taken before the first item runs, and two more synthetic cases pin it. - fetch-assets never shrinks the committed manifest by accident (pointed out in PR #7, whose
data/assets.json, regenerated on a machine where some downloads failed, was 42 audio entries short — all 42 still resolve upstream): a rebuilt manifest leaves out every entry whose files are missing on that machine. When it would lose an entry of the currentdata/assets.json, the run keeps the current file, prints the entries it would drop (also the report'sdroppedEntries/manifestWritten) and exits 1;--allow-shrink(or--prune) writes it (ASSETS.md;test/fetch-assets-shrink.test.js). - crossOrigin on the two
new Image()loads (PR #10, merged on GitHub): with/assets/served from another origin (a CDN or object storage) an image loaded without CORS approval taints the canvas and breaks the WebGL textures (an empty map, a white 3D board).assets.js loadImageElement— the asset store's only image path, §21.27's seeded store included — andui/guide.js preloadsetcrossOrigin = 'anonymous'; a same-origin page is unaffected. - The loadout screen's background layer (pointed out in PR #14):
css/screens/loadout.css.lo > * { position: relative }— one class, a later rule — overrode.lo__bg { position: absolute }, so the mint glow and grid layer was a 0-px flex item and never drew. Now.lo > :where(:not(.lo__bg)): the same one-class specificity for every other layer, never the background. Checked in headless Chrome at 1920×1080 and 844×390 (touch): the layer covers the overlay and every other box of the screen is where it was (to 0.1 px). Tests:test/ui/loadout.test.js(the cascade), opt-intest/ui/loadout.e2e.test.js(SP_E2E=1: the layer's box and stacking at both sizes).
21.26 Gaps found while triaging GitHub issues #1 / #8 (v0.1.1)
Where: ui/gameLogic.js morphPairings / HARMONY_BOND / harmonyMembers, ui/detailPanel.js MorphPairings / MorphGrantLine / ItemDetail / ChessDetail / BondChips, ui/bondStrip.js BondPopup / BondStrip, match/bondsMeta.js computeBonds / bondList, shared/bandBonds.js (→ bands.json bondIds), match/gamedata.js bandBondIds, match/bot.js botPickBand, screens/bandDraft.js BandOffTag / BandOffNote, ui/gameLogic.js bandOffBonds, tools/botbench.mjs (--band), match/fields.js layerAllowanceOf, public/dev/game-mock.js (variants funny / morph / harmony). Three gaps the user decided to close in 0.1.1 (no rule changes), and a false rejection of honest client results found on the way.
- 变形同构体 shows its pairings (issue #1: players read the "转职球" as doing nothing). Its text says "具体对应关系可在模拟中查看本装备天赋栏", and no screen showed that 天赋栏. Now the item card (shop, reward, hand / temp:
ItemDetail) has a 天赋 section: "搭配以下装备时,携带者视为对应盟约的成员:", then one line per bond in bonds.json order — 【bond】 and the items whosegiveBondIdis it (one name per item family, by tier): the 14 pairings of the official talent (character_table trap_1073_acarm073, "“炎国短刀”→【炎】盟约 …"; a test checks the two against each other), its "“维式重锤”系列装备" as the 5 hammers — 18 items, built from the samegiveBondIdthe server counts with (morphPairings;pieceBonds/grantedBonds). A bond the mode never activates is struck through with 本局禁用 (modeOffBonds, §21.7; 标准: 拉特兰 阿戈尔 卡西米尔 奥术 突袭). On a wearer's card (its 装备 rows; also a teammate's unit and the card a 同构 row opens) the pairing it wears is highlighted — 生效中, or 已搭配 when that bond is off — and a wearer without one reads 暂未生效. A bond item's card and row say "与变形同构体一同装备时,携带者视为【X】成员" (MorphGrantLine). The 机密商店 cards have no detail view and keep the official text. Phones: the list keeps ≥ 9 px (px floors ingame-panels.css). - 调和's +1 explained (issue #1: "一些不考虑整备区的盟约计算人数时也会把整备区的干员算进去" — the +1 read as a miscount; §21.11 had left "调和's +1 raises 在场 for core bonds without a member row"). The rule is unchanged (§6.3; one +1 total, research 02 §2.1 ASSUMED). Now a bond state whose count holds the +1 carries
harmony: 1(computeBonds) and both views send it — m.privatebonds[]and m.publicplayers[].bonds[]entries{ …, harmony? }(absent otherwise; not in the battle input,bondSnapshot) — so the client never re-derives the rule. The popup says 在场 n(含调和 +1) and a 调和 row heads its member list, naming the 调和 operators on that board (harmonyMembers: 缪尔赛思, Pith's 盟约·辅助干员; a tap opens the first one's card; with that board not on screen "调和已激活:核心盟约激活人数 +1") — for your own strip and a teammate's (§20.15: the entry from m.public, the operators from the field on screen). The strip disc's and the card chip's titles add (含调和 +1). - Bots skip strategies built around a switched-off bond: in 标准 the bot took 潘格尼尼 (【定制铳械】 an elite <拉特兰>), 克莱门莎 (<阿戈尔>) or 玛恩纳 (<卡西米尔>) in 5 of 200 solo and 23 of 800 co-op picks (seeds 1–200). bands.json
bondIdsholds a band's tie, read from its own data at build time (tools/build-data.mjsthroughshared/bandBonds.js;GameData.bandBondIdsand the client read the field) — the bond names in <…> of its text (the official note "在部分干员缺席时体验可能不完整" among them; a bracketed item or enemy adds nothing) and the bond ids / bond pools of its blackboards (bond,bond_id;pool→ choices.jsonpools[pool].bond, 潘格尼尼's pool_char_later) — nine bands are tied (杜遥夜 炎, 佩佩 / 娜仁图亚 萨尔贡, 哈洛德 维多利亚, 休露丝 谢拉格, 潘格尼尼 拉特兰, 克莱门莎 阿戈尔, 玛恩纳 卡西米尔, 贾维 叙拉古).botPickBandweighs a band tied to a bond ofgd.modeInactiveBonds0 — one rng draw per pick, the default band when every offered band is excluded: 0 of 200 and 0 of 800 in 标准, while the other modes keep exactly the earlier picks (绝境: 5 / 200, 23 / 800).tools/botbench.mjs --bandstill forces any band. - The strategy draft marks them 本局禁用 for human players (the owner's follow-up): a band whose
bondIdsmeet the mode's inactive bonds (gameLogic.bandOffBonds, the same field and rule as the bot — a test pins the two in every mode) carries a 本局禁用 tag on its card and, in the detail pane, "本局禁用【拉特兰】盟约,此策略效果可能无法发挥" with the bond struck through (§21.7's look); it stays selectable — the official game lets you pick it. - Found on the way — the client-result layer bound (
match/fields.js layerAllowanceOf / validateClientResult): with the bots' new picks the full-match suite's coop/FUNNY/107 (4 humans on AI 托管) reached a 6-谢拉格 freeze board at R12 (初雪 + 银灰: garrison_28, 50 % +1 per freeze in range, no per-battle cap in the data; 凛御银灰 hands out a 60 % +2) whose honest +112 谢拉格 layers passed the flat 60 + 4·12 = 108, so the server rejected the client and re-simulated (SP_VERIFY=all agrees with the client on all 57 battles of that match). Uncapped traits are common (菲莱 / 百炼嘉维尔 per skill, 斯卡蒂 per 2 kills, 莫斯提马 per 6 bullets …) and capped ones reach 100–200 (史尔特尔, 荒芜拉普兰德), so the flat bound now adds, per bond, what the player's IN_BATTLE layer 特质 can add to it — each trait of its units and each one their ADD_BOND traits hand out (once per operator), on the bonds it names, up to the cap the sim applies (max_add_count_per_battle, the handed-out 华法琳 trait'sGRANTED_CAP_OVERRIDE); a trait without a cap — or a 魔王, whose +extra counts toward none — leaves its bonds bounded only by the 999 room. A forged gain on any other bond meets the flat bound as before. - Normative lines rewritten with it: §6.3 (
harmonyin the views), §6.6 (the band pick), §8.2 / §8.3 (bonds[]entriesharmony?), §14 / §20.12 (the layer bound), §21.11 (its 调和 note). - Tests:
test/ui/feedback1-gaps.test.js,test/match/feedback1-gaps.test.js; opt-intest/ui/feedback1-gaps.e2e.test.js(SP_E2E=1; 1920×1080, 844×390, 756×366;SP_E2E_OUTmoves the screenshots).
21.27 Client fixes for GitHub issues #5 and #8 (v0.1.1) — ui/gameLogic.js prepCameraFor / foldCamera, screens/game.js, ui/fieldHost.js hudBands / cornerBand / seedAssets, render/app.js, css/devices.css, assets.js, render/units.js
Two public GitHub issues after 0.1.0, both client-only. Normative lines rewritten with them: §9 (setCamera shop, the model fallback), §17.1 (the quiet budget), §19.8 (the collapsed shop).
- #5 seen: "准备阶段,收起商店界面时,界面并不会进行缩放(观战及作战时无此现象)". Real: folding the shop (收起) was UI state only — the own prep camera was requested once on entering prep, never with
shop, so render/projection.js's official shop-collapsed camera (left_prepare_camera_param/*_boss_prepare_*,presetCamerashop: false) was never used, andhudBandsalways reserved the bar (measured: 110 px per tile at 1920×1080, 36 at 844×390, 31 at 756×366, the same folded). - Now: while the own prep board is on screen and the bar is shown folded, the game requests
prepCameraFor(pub, myId, folded)— the prep camera withshop: !folded— whenever the fold changes, with the normal 0.75 s flight (foldCamera): never over the pen (it folds the bar itself and returns to the camera it left), on a teammate's board, in battle or for an eliminated player (no bar); deferred while a piece is dragged or its direction is chosen (the drop target and the wheel sit on tiles of the camera in use) and applied when that ends; unfolding returns to exactly the shop camera; the fold still resets at combat start. The view hands the request's flag tohud(kind, size, { shop })(also on resize). The folded band is the higher of the tab (.shopbar-tab: .8rem + 3 px, above the bottom safe-area inset) and the corner buttons' hit areas (交流 / ⚙ / 📖 / ⛶, measured bycornerBand; on touch screens a 34 px button takes taps 5 px above itself,--tap-min44 px): with the tab alone the corner covered bench pad 0 at 756×366 and the near edge of pads 0–2 at 844×390. Below 768 CSS px the corner wraps into two rows only because the open bar would cover it; with the bar folded it stays on one row (css/devices.css). Picking reads the camera of every frame. ASSUMED the folded band (the official collapsed HUD is not in the extracted bundles). - Measured (headless Chrome; px per tile at row 10; every bench pad fully pressable open and folded, also the Final Assault prep halves): 1920×1080 109.8 → 130.7 (the official camera at its own scale, panned up 46 px to clear the tab); 844×390 35.9 → 44.3; 756×366 (touch) 31.4 → 40.1. A real drag deploy and a tap on a piece work folded; a drag or the wheel defer the flight; scouting (the 'normal' camera), the battle camera and the next prep are unchanged.
- #8 item 5 seen: "游览器切换页面后所有干员会变成方块+水滴的图标" — the image-less placeholder (render/textures.js) on every operator. A plain tab switch does not reproduce it; a reload does (phone browsers and Chrome's Memory Saver discard background tabs and reload them on return; the game resumes into the match): the asset store downloaded /data/assets.json a second time beside the game data's copy,
createFieldViewwaited ≤ 4 s for it, every view looked its model and avatar up once in its constructor, and a failed download kept{}for the page — placeholders for good. Related without a reload: a battle that began in a hidden tab built no views (they are built in animation frames), nothing referenced a skeleton, and the quiet budget (§17.1) unloaded all of them ≈ 18 s later, so back in the tab every unit was an avatar diamond until its model downloaded again; a model that failed or timed out (20 s) was never retried. - Now: the field host seeds the asset store with the game data's copies (
seedAssets→assets.seed/seedLocal; the match screen already waits for them, so no second download). Without one,ready()retries a transient failure after 600 ms and 2 s (like data.js), then resolves{}without keeping it: the nextready()and a bounded background backoff (5 / 15 / 30 / 60 s) fetch again, andonChangetells the field view when a manifest arrives late — every view re-resolves what it could not draw (UnitView.retryAssets; prep item icons throughsetPrep/ItemView.setIcon). A view whose model failed or timed out loads it again after 2 / 6 / 15 / 30 s (SPINE_RETRY_MS; frames only, so never while hidden),acquire(entry, { retry: true })dropping the remembered failure; showing the tab again retries at once (also a missing manifest). Battle mode holds the Spine cache (spine.hold(), fromenterBattleto the nextsetPrepor destroy): no quiet budget meanwhile, the 48 MB idle budget still applies, and the quiet budget keeps freeing everything in the lobby, room and result screen (the policy is in the assets.js header). - Verified (headless Chrome, no console errors): a reload with every assets.json request delayed 6 s, with only a second download delayed (the 0.1.0 race: none is made now) and with the first 4 requests failing — operators drawn as Spine models at once; the first 6 failing — placeholders until the store's first background try 5 s later, then Spine models; a battle started in a hidden tab, back after 25–30 s (still running, ended meanwhile, or paused with Spine downloads slowed to 4 s) — every unit a model within 0.3 s of the return, no diamonds (before: the cache was empty from ≈ 18 s); six solo rounds with two battles started hidden — the cache follows the roster (4 → 24 skeletons, 6.9 → 25.2 MB estimated, idle ≤ 11 MB, under the 48 MB budget), never a diamond or a placeholder.
- Tests:
test/ui/issue5-fold-camera.test.js,test/render/issue8-placeholders.test.js,test/ui/playtest5-ui.test.js(wiring),test/render/assets.test.js; opt-inSP_E2E=1 node --test test/ui/mock.e2e.test.js(fold / unfold at 1920×1080 and 844×390).
21.28 Match info in the strategy draft (GitHub issue #8 item 1, v0.1.1) — ui/matchInfo.js, screens/briefing.js, screens/bandDraft.js draftInfoStatus, ui/enemyDrawer.js InfoTab, css/screens/draft.css .minfo-dlg
- Reported (public issue #8, item 1, "选策略时没法返回查看禁用的干员和盟约"): the disabled bonds and the banned operators were shown only in the INFO_CHECK briefing, but a strategy is chosen partly by them. The owner took it into 0.1.1 as a remake addition (no official screen is copied; the look is the briefing's).
- One component:
ui/matchInfo.js—matchInfoModel(pub, { bonds, chess, mode })(pure:gameLogic disabledBondSetsoverconfig.modes[modeId].inactiveBondIds— the drawn set D "阵容不完整" and the mode's static inactive bonds "本局禁用", e.g. 标准's 10; every bond bybondOrder, thenidentifier, split 核心 / 附加; the banned operators of m.publicbannedChess, known chess only, by tier; banned members per bond) and the hookless blocksMatchBondRow/MatchLegend/BannedOperators/MatchInfo(greyed disc with ✕, the red badge, thebriefingBondTiptooltip, the legend, the 本局禁用干员 grid). The briefing's right column isMatchInfo; the in-game 本局信息 tab (left 🔍) reads the same model for its compact chips and avatars (its banned list is now by tier, like the briefing's). - The draft: 查看禁用盟约与干员 under the order list opens
MatchInfoDialog—MatchInfoin the components.jsModal, read-only, closed by 关闭, a tap outside or Esc (a press on the backdrop closes it onmousedown, so the click never reaches what lies under it — e.g. 确认选择). The draft runs on underneath: the dialog's footer repeats the turn and its seconds (draftInfoStatus: 轮到你决策 / X 决策中 + the step header's number, orange at ≤ 10 s; 已选择「…」after the own pick); a turn change (a pick, a skip, a turn that runs out, an AI pick) or the own pick closes it; the end of the draft unmounts it with the screen; it never changes the highlighted band (g.bandFocus) or the buttons. - Sizes: on desktop the briefing's own sizes (1920×1080: no scrolling); every size in the dialog is max(design rem, px floor), so on phones (1rem = 40 px; 756×366, 844×390) the text stays ≥ 9–15 px, the dialog fits the safe area and its body scrolls under a fixed header and footer.
- Tests:
test/ui/match-info.test.js(the model, greying by state, badges, tips, the legend, banned by tier, real 标准 / 绝境 matches,draftInfoStatus, briefing / draft / drawer reading the one module); opt-intest/ui/match-info.e2e.test.js(SP_E2E=1: solo 标准 on desktop — dialog = briefing, the three ways to close, a confirm afterwards, the in-game tab; co-op 绝境 with a desktop and a phone — the live countdown, a turn change closing it, 844×390 / 756×366 layout and scrolling, the draft's end unmounting it).
21.29 Deliberate deviation: six 重装 skills cast with an enemy in range (GitHub issue #4, PR #12) — tools/build-data.mjs TRIGGER_DEVIATIONS / resolveTrigger / validateAll, data/chess.json skills[].trigger, kits/tier1.js (深巡, 雷蛇), kits/tier2.js (灰毫), kits/tier5.js (号角)
- Reported: public issue #4 — 深巡 (哨戒铁卫, range 2-2) never cast her default S2 行动能力剥夺 until something hit her, in practice until she blocked; PR #12 (merged on GitHub) gave 深巡 S2 and 雷蛇 S2
trigger: 'DEFAULT'in their kits. Many players asked the same of other defenders; the owner (2026-10-03): "反馈的人太多了". - Official, unchanged: the 下半 (act2autochess, 2026-03)
skillTriggerDataListrowTANK | | | 0 | TAKE_DAMAGEand PRTS 卫戍协议/帮助's 重装 strategy "不受技能范围影响,受到伤害时释放技能" (下半, 2026/3/14) — a class row, covering every MANUAL 重装 skill whatever its index (§20.2); 上半 (act1autochess, 2025-11) had no TANK row, so 重装 skills used the basic strategy then (research 03 §1.4 keeps the history). PR #12's reasons — the row as "skill 1 only" and "offensive skills activate when an enemy is in their skill range" — rested on a community summary (arknights.wiki.gg), not on the CN client or PRTS; that quote is removed from research 03 (promised on PR #12). - Decision (the owner, 2026-10-03): a deliberate, documented deviation — six offensive 重装 skills take the basic strategy (DEFAULT: SP ready, about to attack, an enemy inside the initial range or one it blocks): 深巡 S2 行动能力剥夺, 雷蛇 S2 反击电弧, 号角 S2 暴风号令 and S3 终极防线, 灰毫 S1 攻击力强化·γ型 and S2 专注轰击. Every other MANUAL 重装 skill keeps TAKE_DAMAGE — 深巡 S1 侵袭破坏应对 included.
- Where: in the data, so the server, the browser battle and the bots read one rule.
TRIGGER_DEVIATIONSis keyed by the normal chess id and the skill id (the elite record follows through itsbaseId), never by the skill id alone: 灰毫 S1 is the genericskcom_atk_up[3], which 惊蛰, 幽灵鲨, 耶拉, 莫斯提马 and 莱恩哈特 carry too.resolveTriggerapplies it last; the 12 records readtrigger { rule: 'DEFAULT', rawRule: 'TAKE_DAMAGE' }—rawRulekeeps the official row for traceability — andvalidateAllfails the build when an entry no longer meets a TAKE_DAMAGE row. PR #12's kit lines (trigger: 'DEFAULT'on 深巡 / 雷蛇 S2) stay, agreeing with the data (a test pins kit = data); their comments and the contributor's tests now give this section's reason. - Measured (a real battle: 战场#01(下半), one 萨卡兹枯朽前锋 walking the lane from the gate, the operator SP-full on (9,5) facing it): 深巡 / 雷蛇 S2 cast 6.2 s after the spawn, when the enemy steps onto the 2-2 line, and 号角 S2 / S3 and 灰毫 S1 / S2 within 1.3 s (their 要塞 range reaches the gate) — each before anything has hit it (the enemy reaches the defender after ≈ 24 s, if at all); each of the other 46 MANUAL 重装 records (normal + elite) casts on the first hit it takes, at 23.8 s.
- Players notice: these six fire sooner and more often — from range, before anything reaches the defender; 号角 S3's 过载 HP drain therefore starts sooner; 灰毫 S2 (block 0) lets an enemy pass as soon as one comes into range, not after the first hit; on a fenced tile (§21.23) 深巡's S2 fires.
- One-line flip: remove the entry from
TRIGGER_DEVIATIONS(for 深巡 / 雷蛇 also the kit line) and rebuild. - Normative lines rewritten with it: §5.6 (
TAKE_DAMAGE), §19.2 (灰毫 S2), §20.2 (players notice, the table), §21.20 (a settled row), §21.23 (深巡 on a fenced tile). - Tests:
test/sim/feedback1-tank-triggers.test.js(the data, kit = data, the real battle for the six and for every other MANUAL 重装 record),test/data.test.js,kits_t1t2/kits_alt_t1(PR #12's),kits_alt_t2(灰毫 S2),kits_t5/kits_alt_t5(号角),test/docs-consistency.test.js. - Extended 2026-10-04: 余 S2 (SKILL_RANGE on its x-1, §22.10); 14 records deviate now.
21.30 干员战斗语音: an operator's official lines, in battle only (user request, 2026-10-03) — tools/assets/plan.mjs (voiceSlots), tools/assets/audio.mjs (indexVoice / VOICE_SLOTS / VOICE_BATTLE_SLOTS / VOICE_PREP_SLOTS / VOICE_DIRS), tools/assets/cache.mjs, tools/fetch-assets.mjs --voice-lang / --voice-all, public/js/audio.js (VOICE_PRIORITY / VOICE_COOLDOWN_MS / VoiceGate / resultVoiceSlot / resultSpeaker / voice()), server/sim/ai.js performAttack 'engage', battle/runner.js emit('result'), screens/game.js onResult, ui/detailPanel.js voice, ui/settings.js, ui/gameLogic.js sanitizeSettings
User: 「给每个卫戍干员都加语音」, then, after the first build, 「整备阶段不需要干员语音,战斗开始后才需要语音,也就是只需要作战中语音」.
Where the lines come from. Every operator the mode can field already has official battle lines in the same voice dump the BGM and SFX come from — voice_cn/{charId}/cn_nn.mp3 (JP/EN/KR: voice/, voice_en/, voice_kr/, the same file names, tools/fetch-assets.mjs --voice-lang=jp|en|kr). charword_table.json's placeType says when each line is used, and indexVoice() turns it into the manifest's audio.voice[charId]: 行动出发 start (BATTLE_START), 行动开始 faceEnemy (BATTLE_FACE_ENEMY), 选中干员 select, 部署 place, 作战中1-4 skill1..skill4 (BATTLE_SKILL_N), 编入队伍 squad, 任命队长 squadFirst, 结算 resultFour/Three/Two/Lose (完成高难行动 / 3星结束行动 / 非3星结束行动 / 行动失败) and 干员报到 gacha. A slot with several lines stays an array and the client draws one. Only the base word key is planned (wordKey === charId): the dump has no folder for a skin variant's word key (char_x_ita, char_x_epoque#28), so those lines 404. Only the slots a battle can play are planned (audio.mjs VOICE_BATTLE_SLOTS): 干员报到 / 编入队伍 / 任命队长 are lines of the official client's own 养成 / 编队 UI, nothing in this mode ever requests them (test/docs-consistency.test.js checks the client's voice(...) call sites), and downloading them cost 360 files (19.3 MB) on every run — --voice-all adds them back for the complete official set. Measured: 120 of the 121 in-mode charIds have a full set — the 17 预备干员 and the mode's own 盟约·辅助干员 have no battle voice at all, so they simply get no entry — 1680 lines of the 2040 upstream, in a manifest of 5694 files / 324.2 MB (files and stats.voiceChars in data/assets.json).
Where each line plays — battle only. Every line comes from a running battle's own event stream or from its settlement, so no hook needs a phase check and the 休整期 stays silent: the shop does not say 干员报到 (a merge-completing buy neither says 任命队长), a retreat does not say 编入队伍 and dragging a piece onto the board in prep says nothing. In battle: 开战 — the first operator deployed says 行动出发 and the others 部署 (the sim's deploy events; setFieldUnits resets the flag per field — in this mode every piece enters at once, which is why the gate below exists, and a knocked-out operator's redeploy says 部署 again); 接敌 — the sim's new ['engage', id] (a unit's first attack that hits an enemy, official ENCOUNTER_ENEMY); 技能 — skillN from the unit's skillIndex (作战中1-4); 选中 — the detail panel opening on an operator while a battle runs (DetailPanel voice = the game screen's combat); 结算 — resultVoiceSlot(): 完美作战 ⇒ 3星结束行动 (绝境 / 终极 ⇒ 完成高难行动), 有漏怪 ⇒ 非3星结束行动, 一个没杀 ⇒ 行动失败, spoken by a drawn operator of that battle's own field (resultSpeaker, from the result's perPlayer.unitsEnd: the operators of that field, survivors first, a summon piece never), driven by the runner's new emit('result', …) — never the field on screen, so watching a teammate cannot put their operator's voice on the viewer's settlement line (review on #73). unitsEnd names each unit by its chess id (chess_char_*), so onResult passes the chess record's charId as resultSpeaker's charOf; as merged, the speaker looked for char_* ids only and no battle ever said its settlement line (found while porting #73 to 0.2.0; test/ui/audio.test.js runs a real battle result through it).
Why a channel, not SFX. Voices are 1–4 s long and a battle deploys eight operators, then fires dozens of skills; playing them like SFX would flood the mix. VoiceGate copies the official scheduling data (audio_data.json battleVoice.voiceTypeOptions): one line at a time, a 1.2 s global gap, a per-unit per-slot cooldown (10 s for 作战中, 3 s for 接敌, 1.5 s for 选中) and takeover when a line's priority beats the playing one by a margin (the official 0.1 s crossfade) — the priorities are the official ones (BATTLE_START 100, ENCOUNTER_ENEMY 90, SKILL_ACTIVE 70, PLACE_CHAR 20, FOCUS_CHAR 10) and the settlement lines get 85, above 作战中 but below 接敌. Every deferred step of a line — its decode, its onended, the safety timer — carries its own token and returns without touching the gate once it is stale, so a line taken over while it was still loading can never free the channel its replacement just took (review on #73). The channel has its own gain and its own slider (设置 干员语音 / VOICE; sanitizeSettings adds voice, so a saved profile without it keeps the default and players can turn voices down without losing SFX). The buffer cache also gained a decoded-PCM budget (64 MB) beside its 180-entry cap: a voice decodes to ~0.4–1.3 MB, and 180 of them would be ~100 MB.
ASSUMED. 接敌 as "the unit's first attack on an enemy" (the official trigger is the first enemy it encounters, not necessarily one it hits) and the settlement mapping (漏怪 ⇒ 非3星, 完美作战 in 绝境 / 终极 ⇒ 完成高难行动) are our readings. Lines follow the field on screen: watching a teammate's battle (前往查看) plays their operators' lines, while the settlement line fires for the player's own battle only (e.own, battle/runner.js) and is spoken by an operator of that battle's own field (resultSpeaker). 部署 and 选中干员 stay in the plan although the official groups them with the prep lines: a battle does play them (the first operator says 行动出发 and the others 部署, and the detail panel says 选中 while a battle runs), so only the three slots no call site ever asks for are dropped — VOICE_PRIORITY / VOICE_COOLDOWN_MS keep every level, so --voice-all restores 编入队伍 / 任命队长 / 干员报到 for the day the 休整期 speaks again.
- Tests:
test/ui/audio.test.js(the priority table, the settlement slot and its speaker, the gate's global gap / per-unit cooldown / takeover, the stale-callback regression, and the battle events that drive the lines against a fake Web Audio),test/ui/gameLogic.test.js(thevoicesetting),test/sim/battle.test.js(the wire format: every emitted tuple is a knownEV— the newengageincluded); the manifest's 1680 voice paths are covered bytest/assets.test.js.