- CHANGELOG 0.2.1 (2026-10-07): the streams' lines collected (新功能: the update package and MANIFEST.json; 规则(按官方): 联防 on the round's battlefield, 突袭 onto a teammate's half, full potential, the replace dialog for consumables, the 外勤医疗 Touch, 凋亡, 贾维's 拉普兰德, the 频次 器物 / leader summons; 界面: the collapse arrow, 自选 picks in the bond lists, flyers, enemy animations, the Touch card, Esc in the 自选 picker; 联机与大厅: battle messages while a battle loads; 工具与开发: the update build, .gitignore), one intro paragraph. - README: the dev warning under the title removed, the release badge, 「当前版本 0.2.1」; DESIGN's version line names 0.2.1 (§26) as the state described; §26's intro says when the dev line shipped. - Tests: version, docs-consistency, docs-paths, package, i18n-packs, update-package, update-boot 80 / 80.
517 lines
56 KiB
Markdown
517 lines
56 KiB
Markdown
# DESIGN §26 — 0.2.1
|
||
|
||
Part of [DESIGN.md](../DESIGN.md) (the index; section numbers are global).
|
||
|
||
## 26. 0.2.1 — after the 0.2.0 release
|
||
|
||
0.2.1 is built on `feedback6`, branched from the 0.2.0 release (tag v0.2.0) and marked `0.2.1-dev` until it shipped (2026-10-07). Its
|
||
workstreams branch off it as `fb6-*`, each merged after review with its golden scenarios named. Each rule was checked
|
||
against the official data and PRTS; anything no source backs is marked [ASSUMED]. Where each is handled: the 突袭
|
||
landing on a teammate's half of a shared field → §26.1; 联防 back on the round's battlefield → §26.2; the 自选 picks in
|
||
the bond lists → §26.3; a consumable on a full carrier → §26.4; the 外勤医疗 Touch → §26.5; 凋亡 and stored charges →
|
||
§26.6; 贾维's 拉普兰德 → §26.7; .gitignore → §26.8; enemy flyers over raised tiles → §26.9; enemy animation states →
|
||
§26.10; battle messages while the browser prepares a battle → §26.11; the 频次 器物 and leader summons → §26.12; the
|
||
bond strip's collapse arrow → §26.13; every operator at full potential → §26.14; Esc in the 自选 picker → §26.15.
|
||
|
||
Decisions of the owner: 2026-10-07 — a 突袭 member may land on a teammate's half when both halves of the field are
|
||
taken (the two-helper 联防 field, the Final Assault / Hidden Core pair field), replacing part of §25.18.1; 乌尔比安's
|
||
S3 【移动】 keeps his own board (§26.1); 联防 plays on the round's battlefield again, from the owner's knowledge of the
|
||
official mode and a tournament video (§26.2, withdrawing §25.6.4); 0.2.1 within hours of the report; every operator at
|
||
full potential, no host setting (§26.14); the 自持有 bonus, multi-language voices and the title particles are not in
|
||
0.2.1.
|
||
|
||
### 26.1 突袭 may land on a teammate's half when both halves are taken (the owner's decision of 2026-10-07) — `sim/battle/players.js onFieldBoard`, `content/bonds/addon/battle.js raidTile`
|
||
|
||
- **Seen**: 0.2.0 (§25.18.1) let a member land only on its own board, on every field. Where both halves belong to
|
||
players — the two-helper 联防 field and a Final Assault / Hidden Core pair field — a member whose target stood on the
|
||
teammate's half stayed home or took a farther tile of its own half (reproduced: the right player's member with an
|
||
enemy on the left telin (1,3) of 战场#01 stayed home; on the two-helper 联防 map the left helper's member with an enemy
|
||
at (10,15) stayed home). After the release the owner decided that such a member may land on the teammate's half.
|
||
- **Official**: the bond text 「保留技力立即再部署至一名地面敌人周围」 names no board, and where the official landing falls
|
||
among the legal tiles is not in the data (§22.2). PRTS 卫戍协议/帮助 §联防阶段: with two helpers 「两处阵地将前后拼接为一处
|
||
阵地,玩家在同一阵地作战」; a pair field is one boss map holding both players' boards (research 09 §1.2,
|
||
ConvertChessPositionInfoToBossMap). A lone helper's other half and a solo boss field's other half hold no player.
|
||
- **Now**: `Battle.onFieldBoard(r, c)` — the tile lies on the board of a player of this field (`onOwnBoard` of any of
|
||
`players`, the players whose units the battle holds; the 联防 right-hand helper's +8 columns and the mirrored right half
|
||
of a pair are mapped as before) — and `raidTile` takes only those tiles: both halves on the two-helper 联防 field and on
|
||
a pair field; the own half for a lone 联防 helper (escaped_single) and on a solo boss field (nobody stands on the other
|
||
half; a teammate eliminated before the battle or playing another field opens nothing); never a boss field's hand row 0
|
||
or 临时整备区 row 1 (no board covers them). Every other landing rule is kept: ground / ranged tiles (grid.canStand),
|
||
free tiles (isReservedTile), a blockable tile first for a melee member (§25.11.3), a target only reachable from a
|
||
forbidden tile skipped (the next candidate is tried, else the member stays); the candidates are still the player's own
|
||
enemies first (raidTargets). 乌尔比安's S3 【移动】 keeps `onOwnBoard` (§25.17.2, a separate community report): his landing
|
||
never takes the teammate's half. This replaces the own-half rule of §25.18.1 on those two fields.
|
||
- [ASSUMED]: none beyond §22.2's landing rule (which legal tile the official landing takes); with both halves open the
|
||
nearest covering tile wins, on whichever half it lies.
|
||
- Golden: 8 scenarios, each parting from the parent at the first landing on a teammate's half (checked tick by tick with
|
||
the parent instrumented: identical up to that landing): boss-boss_2 / 3 / 4 / 5 / 6-pair, hidden-boss_8 / 10-pair (the
|
||
right player's 缄默德克萨斯, 伊内丝, 仇白 or 耀骑士临光 landing on the left player's half, 1–7 times per battle, the first
|
||
0.3–39.8 s in) and the bot match coop4-ABYSS-7 (its round-6 two-helper 联防: at 52 s ai_2's 斯卡蒂 lands on ai_1's
|
||
half; of the match digest only the two players' damage totals move, 8263 from ai_1 to ai_2). unite-2 is unchanged (its
|
||
two landings stay on their own half). Tests: `test/sim/feedback5-raid-own-board.test.js` (4 cases; the pair and the
|
||
two-helper cases fail on the parent; the solo boss field's hand rows, the lone helper and 乌尔比安 beside a teammate are
|
||
unchanged). Docs: docs/SIM.md (`onFieldBoard`; `onOwnBoard` is now 乌尔比安's only).
|
||
|
||
### 26.2 「联防阶段地形全消失了」 (GitHub #244, reported by alien-zzy, official footage pointed out by RushFTK): 联防 plays on the round's battlefield again — §25.6.4 withdrawn — `match/unitePhase.js _uniteOpts`, `unite.js`, `screens/game.js`, `public/dev/game-mock.js`, `tools/golden.mjs`
|
||
|
||
- **Seen**: from 0.2.0 on, every 联防 was an empty two-part road — no walls, fences, water, crates, 活性源石, blowers or
|
||
band medic. Walkers went straight down row 9 where the round's battlefield walls them off (战场#01 cols 5–7), and the
|
||
helpers' 机变 terrain cards and the stage's devices did nothing there. In 0.1.x the 联防 was fought on the round's map.
|
||
- **Official**: the owner's first-hand knowledge of the official mode — 「官服保留地形,改回去」 (2026-10-07) — matching the
|
||
tournament footage RushFTK linked in #244 (舟戍杯#2: a 联防 on the round's map). **Why the level files misled us**:
|
||
act2autochess constData `escapedBattleTemplateMapSinglePlayer` / `MultiPlayer` name `level_act1autochess_escaped_single`
|
||
/ `_multi`, and §25.6.4 read that level's map as the 联防 map. But that map is the placeholder grid every wave
|
||
*template* level carries: the 39 template levels of the season (the round templates `01…07` and `h01…h08` with their
|
||
`_s` solo variants, the training `tr01/02/04`, both escaped levels) hold the identical 19 × 21 road grid, tile for
|
||
tile, while only the 11 stage levels (`m01…m07`, act2 `m01…m04`) have terrain and devices. Nobody plays a normal round
|
||
on its template's road: a template gives a battle its waves, routes and BGM (the 联防 BGM, PR #110, stays), the
|
||
stage gives the map.
|
||
- **Now**: `_uniteOpts` fields the 联防 battle on `this.stageId` in both combat modes, as 0.1.4 did: the match stage
|
||
opened to both halves (`GEO.UNITE_RECT`, cols 0–20) with its terrain, crates, water, devices, runes and band map
|
||
characters; the escaped templates' routes (one helper: `escaped_single` from the middle gate (9,10); two:
|
||
`escaped_multi` from col 18 through (9,10)); each helper's pieces on their prep tiles, the first of two shifted 8
|
||
columns onto the right half. `unite.js uniteStageId` is removed. The client draws the 联防 on the board's stage again
|
||
(`screens/game.js`: 0.1.4's `ownView ? ownStage : baseStage` — no map switch, nothing to restore on leaving; the
|
||
helpers see their own 机变 terrain changes, as in 0.1.4); the dev mock's 联防 likewise. Kept from 0.2.0: 突袭 and
|
||
乌尔比安's S3 land only on their own board (`Battle.onOwnBoard`; the raidTile change of another stream is independent
|
||
of this one), the payee of a bounty death no operator caused (the helper of that half — 战场#04's 活性源石 burns leaked
|
||
bounty enemies in 联防 again, §25.14.3), the 联防 BGM, the official result box at SETTLE, and everything else since
|
||
0.1.4. `data/stages.json` keeps the two `kind: 'unite'` records, which no field uses: they are the official level
|
||
data, the build checks them against `config.unite.templates`, and the engine tests of the own-board rules use them as
|
||
a plain two-halves road (乌尔比安 S3, 突袭 landing, the CB4 pins); dropping them changes no game behaviour and would
|
||
rewrite those tests (one of them belongs to the raidTile stream). `tools/build-data.mjs`: comments only — `node
|
||
tools/build-data.mjs --offline` reproduces all 15 data/*.json files byte for byte.
|
||
- **Checked against 0.1.4**: the 联防 battle input a match builds — stage id, rect, wave id, routes, spawns, helpers'
|
||
offsets, units and carried state, and the battle made from it (rows, ground / crate grid, device units, special terrain,
|
||
runes, map characters, ally tiles) — is byte-identical to v0.1.4's in 20 cases (战场#01, #04, #05(下半), #07(下半),
|
||
#08(下半) × 1 / 2 helpers × both combat modes).
|
||
- **GitHub #41 item 3** (an enemy pushed back across the spawn onto the other half): back exactly as in 0.1.4 — 野鬃 S2
|
||
beside the middle gate pushes 3–4 of `escaped_single`'s 20 walkers onto the right half (x ≤ 13.2), none off the map,
|
||
the same numbers on v0.1.4; they walk back through the left half and leak or die. The reporter accepted it 「不影响正常
|
||
运行就可以了」. The 0.2.0 road showed the same push (its right half is road too): §25.6.4 never removed it.
|
||
- [ASSUMED]: none new (the official 联防 field is the owner's knowledge, not a data reading).
|
||
- Golden: fields `unite-1` (escaped_single → 战场#02, act1autochess_m02) and `unite-2` (escaped_multi → 战场#07(上半),
|
||
act1autochess_m07) — the stages they had in 0.1.4; the matches whose 联防 rounds came out differently on the round's
|
||
map: coop2-NORMAL-3, coop2-HARD-4, coop3-ABYSS-5, coop4-ABYSS-7, coop2-NORMAL-8-serverrun, coop2-NORMAL-14-standins
|
||
(now a victory: walls and crates send the leaked walkers round through the helpers' range). coop4-FUNNY-6,
|
||
coop2-ABYSS-10-boosted and coop2-NORMAL-70-diy play 联防 rounds too, with the same digests on both fields. roster,
|
||
bonds, standins, diy unchanged.
|
||
- Tests: `test/match/feedback5-unite-map.test.js` rewritten — 6 tests: one / two helpers × both combat modes (the match
|
||
stage, its rows and walls, 战场#01's six crates on both halves, the helpers on their 围墙 prep tiles with the +8
|
||
columns, walkers going round the walls, the stage the leaker's spec / a watcher's m.field names), 战场#04's 活性源石 on
|
||
the field, and the same field with or without the escaped records — all 6 fail on the parent, and with soft asserts
|
||
every new assertion does. `test/e2e/coop.e2e.mjs`: the 联防 round's field meta and drawn stage are the match stage.
|
||
`test/data.test.js` (the records' test retitled: never the 联防 field). Docs: DATA §12, META §4, PLAYING §5,
|
||
design/geometry-time.md, I18N, the §25.6.4 pointer.
|
||
|
||
### 26.3 The 自选 picks in the bond popup's member list (the owner's report of 2026-10-07) — `ui/gameLogic/bonds.js bondMembers`, `ui/bondStrip.js BondPopup`, `ui/detailPanel.js resolveDetail`
|
||
|
||
- **Seen**: the owner's report of 2026-10-07 「自选编队的干员在局内对应盟约展开的名单里没有显示出来」. Reproduced with a
|
||
unit test of `bondMembers` (m.private.diy holding 推进之王 and 煌) and the popup in headless Chrome: the popup listed
|
||
every regular member — dimmed while not owned — but a 自选 pick only once a copy was owned (on the board, in the hand
|
||
or the 临时整备区), so a pick not bought yet, or not offered yet (the tier-5 slots from 调度中心 level 5, the tier-6
|
||
from 6), never appeared. Owned pieces did show in every case checked (board, hand, 临时整备区; normal and elite; 煌 in
|
||
both 炎 and 维多利亚; beside a 补位 stand-in of the same bond), with two smaller faults: an elite on the board drew its
|
||
row with the elite record (gold frame and golden avatar, unlike a member row, which always draws the base chess),
|
||
and in a watched teammate's popup a tap on their 自选 row opened the empty 甄选干员 slot's card (resolveDetail read no
|
||
pick for another player's member card).
|
||
- **Official**: neither the excel nor PRTS describes the popup's 自选 rows (PRTS 卫戍协议:盟约 only notes 「禁用盟约有可能
|
||
影响自选的支援干员」). The rule follows the owner's report: a 自选 pick belongs to the bonds of its operator like any
|
||
member.
|
||
- **Now**: `bondMembers` adds each pick of m.private.diy (the V and VI slots) to every bond its operator carries (the
|
||
composed record through the own getter, gameLogic/diy.js `diyGetter`): `owned: false` until a copy is on the board, in
|
||
the hand or in the 临时整备区 (then on board / in hand as before), `banned` when its slot is in m.private.diyBanned
|
||
(every bond of the operator off this match: it never shows in the shop). One row per slot, drawn as its normal form,
|
||
with the operator's name and portrait and the 「自选」 tag; a banned pick's tag sits on the portrait's bottom corner,
|
||
clear of the ✕ and the tier chip. The order rule is kept (on board, owned, tier, id: an unbought tier-5 pick comes
|
||
after the unowned tier-5 members). The 成员 header (`memberHeadCount`: the board, plus the hand for a bond that counts
|
||
it) and 在场 (the server's count) mean what they did — an unowned pick counts for nothing; the header's total includes
|
||
the picks, as it includes the unowned members. A tap opens the operator's card (the own pick, from m.private.diy).
|
||
A watched teammate: m.public sends no picks, so their popup still lists only their 自选 pieces on the field on screen
|
||
(UnitInfo `diy`, ui/watchBonds.js `ownerBoard`, as in 0.2.0); such a row now passes the unit's pick on (BondPopup
|
||
`onMember`'s fourth argument → the detail target's `diy` → `resolveDetail` composes their operator). The briefing and
|
||
the 本局信息 bond lists show no members (bond discs, banned-member counts, the 本局禁用干员 grid and the diyBanned
|
||
line), so nothing changes there.
|
||
- [ASSUMED]: how an unbought pick looks (dimmed, like an unowned member), how a pick whose bonds are all off looks (the
|
||
banned member's ✕), and the normal-form portrait for an elite copy (one row per member).
|
||
- Golden: unchanged (client only). Tests: `test/ui/feedback6-diy-bond-members.test.js` (5 cases, each failing on the
|
||
parent: the unowned pick listed and dimmed, header 0 / n + 2, its card; owned → on through the hand, the 临时整备区,
|
||
the board and the elite, one normal-form row; 煌 in 炎 and 维多利亚 beside 风笛's stand-in; the diyBanned pick; the
|
||
teammate's row opening their 煌, not the slot); `test/ui/watch-bonds.test.js` (the game screen's onMember wiring, now
|
||
with the pick).
|
||
|
||
Optional PLAYING.md sentence for the integrator (the 自选编队 paragraph, after 「和普通棋子一样计入盟约人数」 — not edited
|
||
here to avoid a conflict on that one-line paragraph): 盟约详情的「成员」一栏也列出你的自选干员(带「自选」标记;还没买到、商店
|
||
里还刷不出来时和没有的成员一样显示为灰色)。
|
||
|
||
### 26.4 A consume-on-equip item on a full carrier replaces first (GitHub #263, PR #264) — `match/player/items.js equip / _takeReplaced`, `ui/gameLogic/placement.js equipReplaces`, `match/bot.js itemTarget`
|
||
|
||
- **Seen**: with both equipment slots of an operator used, a normal 博士投影 dropped on it opened no replace dialog and
|
||
silently destroyed the OLDEST equipped item: the client's `itemAttaches` treated every `consume_on_equip*` item as
|
||
slot-free (`dropIntent` → `confirmReplace: false`), and the server ran the effect, whose `ev.keep` re-attached the item
|
||
through `_attach(holder, item)` with no `replaceUid` — the full row pushed out index 0 (a `replaceUid` sent anyway was
|
||
ignored). 盟约之币 and the other immediate consumables (随身身份牌, 紧急调度券, 精打细算玩偶, 简易通讯机, 见钱眼开玩偶, 骑士储蓄罐,
|
||
寻呼模块, 信标, 拟态物质, 人事部文档, the golden 博士投影) opened no dialog either and kept both equipped items
|
||
(reproduced on feedback6 783bd30: `scratchpad/w6-r21d-notes/repro.mjs`; reported by @SpiritedAwayCN with PR #264).
|
||
- **Official**: PRTS 卫戍协议/帮助 §装备 (fetched 2026-10-07): 「每名干员最多可佩戴2件装备」, 「达到上限强行佩戴会改为替换装备:
|
||
指定一件已佩戴装备替换为将要佩戴的装备,并销毁被指定的装备(可取消以中断替换操作)」 — no exception for the items the data
|
||
marks 装备时销毁; research 04's "never take a slot" is a [DATA] reading of `kind`, not a waiver of the dialog. The
|
||
reporter's first-hand account of the official mode: the picked item is destroyed first, then the item resolves, and the
|
||
operator is left with a free slot.
|
||
- **Now**: server — `PlayerState.equip`'s consume path takes the item `replaceUid` names (else the oldest, as for every
|
||
third item) off a full carrier before `onEquip` runs (`_takeReplaced`), so the effect sees the carrier without it (its
|
||
bonds: 随身身份牌 / 简易通讯机 / 寻呼模块 / 拟态物质); once the effect went through the item is destroyed (`onDestroy`
|
||
reason `replace`) and the slot stays free — the normal 博士投影 takes it (`ev.keep`) and no longer pushes another item
|
||
out. A refusal (博士投影 of either quality on an elite: `BAD_TARGET 'already elite'`) puts the item back at its index:
|
||
nothing is destroyed, the 博士投影 stays in the hand. 信标 destroys its carrier as before: the remaining item returns to
|
||
the hand, the picked one is gone. Client — `equipReplaces(ctx, uid, target)` (gameLogic) opens the replace dialog for
|
||
every item on a full carrier, a consumable too, except where nothing would be replaced: an attaching item that
|
||
completes an item merge (`equipMerges`; the server's consume path never merges) and a 博士投影 (its promoting effect
|
||
keys) on an elite. `itemAttaches` is gone. Bots — a consumable whose effect is the same on anyone (funds, 紧急调度券,
|
||
人事部文档 …) goes to a carrier with a free slot first (`itemTarget`, PR #264); the targeted ones (博士投影, 随身身份牌,
|
||
简易通讯机, 寻呼模块, 拟态物质, 信标) keep their carrier and, when it is full, replace its oldest item (bots send no
|
||
`replaceUid`).
|
||
- [ASSUMED]: the order inside the server is "take off → effect → destroy" rather than "destroy → effect": the effect sees
|
||
the carrier without the item (as officially), but the replaced item's `onDestroy` runs after the effect so a refusal
|
||
can restore it — no equipment item has an `onDestroy` effect for `replace`, so nothing observable differs.
|
||
- Golden: `matches` only, 10 of 18 move — in each the first point it parts from the parent is a bot's (or the AI 托管
|
||
human seat's) targeted consumable resolving on a full carrier, which now destroys the carrier's oldest item (traced
|
||
equip by equip against the parent: `scratchpad/w6-r21d-notes/trace-equips.mjs`): solo-FUNNY-1 (r9 ai_0 随身身份牌 →
|
||
突袭手雷 destroyed; bossPoolLeft 88535 → 91555), solo-FUNNY-2 (r9 ai_0 寻呼模块; bossPoolLeft 87617 → 107027),
|
||
solo-HARD-2 (r9 ai_0 随身身份牌, r12 简易通讯机; teamLp 17 → 13), coop2-NORMAL-3 (r9 ai_1 随身身份牌; teamLp 46 → 39),
|
||
coop3-ABYSS-5 (r11 ai_1 随身身份牌; ai_1's numbers only), coop4-FUNNY-6 (r4 ai_1 随身身份牌), solo-HARD-9-boosted
|
||
(r14 ai_0 随身身份牌), coop2-ABYSS-10-boosted (r13 ai_0 随身身份牌, r14 ai_1 简易通讯机; teamLp 398 → 529),
|
||
coop2-NORMAL-14-standins (r9 ai_0 随身身份牌; the match now won: 13 → 14 rounds, teamLp 0 → 12),
|
||
coop2-NORMAL-70-diy (r14 h_0 随身身份牌; numbers only). The bots' free-slot choice for target-agnostic consumables
|
||
moves no scenario by itself (six unmoved matches carry such a changed carrier: the effect is the same on anyone).
|
||
- Tests: `test/match/merge.test.js` (a full carrier with the picked item: normal 博士投影 takes the pick's slot, 盟约之币
|
||
pays and leaves a free slot, 随身身份牌 gives the carrier's bonds +3, the golden 博士投影 promotes and the other item
|
||
stays; 博士投影 of both qualities on an elite is refused with every pick and both items kept), `test/ui/leftovers.test.js`
|
||
(all 24 consumables open the dialog on a full operator and not on a free one, 博士投影 on an elite does not, an operator
|
||
in the hand the same; the real engine end to end: the dialog asks for 博士投影 and 盟约之币 and the picked item is the one
|
||
destroyed), `test/ui/gameLogic.test.js` (the consumable intent flipped), `test/match/bot-smarter.test.js` (盟约之币 to a
|
||
carrier with a free slot) — each new assertion fails on the parent; the dialog e2e `test/ui/leftovers.e2e.test.js`.
|
||
|
||
### 26.5 The 外勤医疗 Touch casts on its 5-2, and the map characters' card is an operator card (GitHub #260, PR #278) — `content/tokens.js touchKit`, `ui/detailPanel.js MapCharDetail / SKILL_TEXT_NOTES / skillTags`
|
||
|
||
- **Seen** (reported by @CurlyZhu on 0.1.4; PR #278 by @IceCodeNew): the 外勤医疗 strategy's Touch and 预备干员-医疗 showed as
|
||
「召唤物」 with base stats only — no skill, no talents. Touch waited for an injured ally inside her own 3-3 before casting
|
||
恳切福音 although the skill heals on its 5-2, so a 雷蛇 losing HP to 源石溶剂 five tiles ahead was never healed.
|
||
Cause: tools/build-data.mjs builds the map characters as tokens (kind `mapChar`, `variants: {}`; stats, skill, talents
|
||
and 特性 on the record) and `resolveTrigger` widens operators' skills only, so their records keep DEFAULT; TokenDetail
|
||
read skill and talents from an owner variant only (none) and always printed 召唤物.
|
||
- **Official**: PRTS Touch(卫戍协议) — an operator of the mode (医疗 · 医师, 远程位), talents 攫升 / 超脱, S3 恳切福音 手动
|
||
触发, 技能范围 5-2, 「攻击距离+2」 corrected by PRTS to 攻击范围扩大, 「不高于一半」 corrected to 低于 (原因 6);
|
||
aceffect_band_61: a 预备干员-医疗 appears, replaced by Touch with two elites on the field. The larger-range cast is the
|
||
owner's rule of 2026-10-05 (§25.5), which the Touch 补位 already runs by its data (backups.json S3 `ACTIVE_RANGE` on the
|
||
5-2).
|
||
- **Now**: `touchKit` sets the skill's trigger to `ACTIVE_RANGE` on the skill's own range (the running range, which
|
||
strictly contains her 3-3): an injured, healable ally inside it casts; a full-HP or 禁疗 one does not (the reserve
|
||
medic's 治疗强化·β型 has no range of its own and stays DEFAULT). The card (`MapCharDetail`, taken for `kind: 'mapChar'`):
|
||
chip 干员, the class line (医疗 icon, 远程位), the 特性, the four stats (live in battle), the skill row of the operator
|
||
card (icon, SP type, trigger, initial · cost, duration — `skillTags`, shared with ChessDetail) with its text, and the
|
||
talents — all from the record, translated by the data overlays. Checked against the kit: 恳切福音's stored sentence
|
||
matches what the sim does (ATK +40 %, ×150 %, 2 targets, the 30 % extra heal; the 5-2 = her 3-3 two tiles longer)
|
||
except 「不高于一半」 — touchGospel boosts strictly below half (PRTS 修正) — so the card keeps the official sentence and
|
||
adds a line under it (`SKILL_TEXT_NOTES`, by skill id; on the Touch 补位's S3 card too, whose text reads the same).
|
||
The triage's "+40 %" doubt does not hold: bb.atk of the map character's Lv10 skill is 0.4, which the kit applies.
|
||
Enemy targeting is unchanged (the units stay `kind: 'token'` in the sim; no enemy that skips them was named).
|
||
- [ASSUMED]: none new (the trigger is the standing owner rule).
|
||
- Golden: roster-041 (its strategy Touch: heal 8438 → 17072, heal actions 19 → 27, casts 2 — the same numbers as PR #278;
|
||
p1 deaths 7 → 4) and matches coop2-NORMAL-14-standins (h_0 plays 外勤医疗: defeat in round 13 → victory, team LP 16) —
|
||
the two battles with that strategy's Touch; nothing else moved.
|
||
- Tests: `test/content/tokens_devices.test.js` (Touch on her stage slot, an ally only in the 5-2: no cast at full HP, none
|
||
while 禁疗, then a cast and a heal — fails on the parent), `test/ui/playtest6_summons.test.js` (both cards: 干员, 医疗,
|
||
skill / talents / 特性 text, the 低于 line only under 恳切福音, a summon keeps 召唤物 — fails on the parent). i18n: one new
|
||
string in en / ja / ko / zh-TW. Docs: docs/SIM.md (ACTIVE_RANGE also set by touchKit).
|
||
|
||
### 26.6 An operator's 凋亡 drains the stored charges too (PR #262; #261 its closed duplicate, both by @siruimei07) — `sim/damage.js resolveBurst` (apoptosis, ally side)
|
||
|
||
- **Seen**: the burst's −1 SP/s took only `sk.sp`, the SP towards the next charge. A full skill keeps `sp = cost` with
|
||
every charge stored, so it kept all of them; a one-charge skill stayed ready through the whole 15 s (and cast the moment
|
||
静默 ended). Reproduced with 精锐 雪猎 (S2 可充能2次, 14 SP): two charges stored before the burst, two after it.
|
||
- **Official**: PRTS 元素 — 凋亡损伤 on a 我方单位: 「立即获得等时长的阻回、静默,该阻回、静默持续期间每秒损失1点技力并受到100点无来源
|
||
法术持续伤害」 (15 s); PRTS 技能 §特殊属性 可充能: 「当前技力上限等于该技能技力需求的X倍」 and 「当持有者的技力流失时,充能次数也
|
||
会实时降低(但因达到上限产生的额外效果会依然生效)」.
|
||
- **Now**: each second `sk.setSpTotal(max(0, spTotal − 1))` — the official 技力 (charges × cost + the partial bar),
|
||
charges rebuilt silently (no spGain hook, 阻回 untouched); a passive, a skill-less unit, a free skill (spTotal 0) and a
|
||
running duration / ammo / toggle skill are left alone as before (setSpTotal refuses those too). An effect a full charge
|
||
produced is not undone (none is modelled on this path).
|
||
- [ASSUMED]: none.
|
||
- Golden: 5 moved, each by a burst on an operator with SP stored — 假想敌:管's 凋亡 strikes and the 萨卡兹王庭军's 凋亡 on
|
||
hit: fields hidden-boss_10-solo (泥岩 loses its stored charge, casts 1 → 0 — the move PR #262 reported; team heals 73194
|
||
→ 72505), boss-boss_3-solo, hidden-boss_10-pair and diy-058 (later snapshots and event counts only, same results),
|
||
diy-059 (黍 forms: casts 11 → 5, 6 → 2, 9 → 5, 6 → 1; kills 39 → 47, leaks 57 → 49).
|
||
- Tests: `test/content/feedback6-apoptosis-sp.test.js` (2: 雪猎's two charges → one after 1 s, none after 15 s; a full
|
||
one-charge skill not ready after 1 s, −15 技力 in all, no spGain, a running timed skill unchanged — both fail on the
|
||
parent); `test/sim/combat.test.js` operator bursts unchanged. Docs: docs/SIM.md element table.
|
||
|
||
### 26.7 A 拉普兰德 贾维 grants during a refresh waits for the next one (PR #196 by @SpiritedAwayCN, the part 0.2.0 lacks) — `match/effectsMeta.js dispatch / _garrisons / refreshPieces`
|
||
|
||
- **Seen**: 贾维 【团伙行动】 grants a 叙拉古 operator from the dispatch of every 6th manual refresh (band step 2), before the
|
||
garrisons (step 4), which walked the board and hand live — so a 拉普兰德 granted (or the elite her copy completes) ran
|
||
her 刷新时 特质 in the refresh that granted her and spent her 「本回合首次主动刷新」 on it (+4 / +8 at once, nothing on the
|
||
next refresh).
|
||
- **Official**: garrison_123 「<刷新时>若为本回合首次主动刷新…此干员在整备区时也有效」; the players' reading (§21.1, after
|
||
0.1.0) 「获得该干员后该回合的首次刷新」 — a refresh she was gained in is not one she was there for. 贾维: 「主动刷新6次调度中
|
||
心后获得一名…<叙拉古>干员」.
|
||
- **Now**: `dispatch` takes the board (reading order) and hand chess before the first handler of an onRefresh; the
|
||
garrison step runs each of them that is still on the board or in the hand, where it stands now; a chess gained during
|
||
the dispatch does not run, nor one that left (sold, consumed by a merge, sent to the 临时整备区). Other hooks keep the
|
||
live walk. Not taken from PR #196: copying round counters on a merge and keying the counter by garrison id — 0.2.0
|
||
counts a merged elite as a new 拉普兰德 and keeps an in-place promotion's count (the owner's decision, §25.13.3).
|
||
- [ASSUMED]: none new.
|
||
- Golden: unchanged.
|
||
- Tests: `test/match/feedback1-meta.test.js` (2: a gifted copy and a gift that completes the elite — nothing in the 6th
|
||
refresh, +4 / +8 in the 7th, nothing in the 8th; both fail on the parent). Docs: docs/META.md (SERVER_REFRESH_SHOP row),
|
||
the effectsMeta.js header, garrisons/meta.js.
|
||
|
||
### 26.8 .gitignore lists .npm-cache/ (GitHub #258, reported by @qfmc7040) — `.gitignore`
|
||
|
||
- **Now**: a local npm cache (`npm_config_cache=./.npm-cache`, `npm ci --cache .npm-cache`) is ignored like node_modules/
|
||
and .cache/; the packages ship tracked files only, so it stays out of them too. Golden: unchanged. Test:
|
||
`test/gitignore.test.js` (the entry, and git's own check-ignore; fails on the parent).
|
||
|
||
### 26.9 An enemy flyer hovers from the road whatever tile it crosses (GitHub #277 by @FrogThai) — `render/units.js UnitView.sync / shadowZ`
|
||
|
||
- **Seen** (GitHub #277, 飞机经过一格方块时会跟走楼梯一样,有高低差): a flying enemy whose route crosses one high-ground or
|
||
forbidden block rose onto it and dropped back, like a step. Cause: `UnitView.sync` put every unit except a *ground*
|
||
enemy on the tile top under it (`groundZ`: TILE_H.wall 0.42, forbid 0.3) and added `FLY_HOVER` on top, so an enemy flyer
|
||
followed the tiles (reproduced headless, `w6-r21f-notes/flyer-277.mjs`: 妖怪 along row 9 of act2autochess_m01 over the
|
||
'#' at col 6 — body height 1.293 … 1.600 tiles on both boards before, 1.293 … 1.300 after).
|
||
- **Official** (docs/research/12, §25.13.5): the fly offset is one constant, `Vector3(0, 0.35, 0)` added while flying, ≈ 1.3
|
||
tiles over the route; nothing per tile. High ground is where units stand, not a step under a flyer.
|
||
- **Now**: every enemy keeps z 0 (ground enemies already did; a flyer hovers `FLY_HOVER` above the road); operators and
|
||
summons keep their tile top (an operator on high ground stands on it). The flyer's shadow lies on the tile under it
|
||
(`shadowZ`, eased like the body), so over a block it is drawn on the block top. The 2D and the 3D board share the
|
||
camera and the Pixi unit layer, so both are fixed.
|
||
- [ASSUMED]: the shadow on the block top under a flyer (no official reference for a shadow over a block).
|
||
- Golden: unchanged (client only). Tests: `test/render/unitview.test.js` `an enemy flyer crossing a raised tile keeps its
|
||
height (GitHub #277)` (2 cases; the body-height case fails on the parent: it rose 0.415 tiles). Headless screenshots
|
||
before / after, 2D and 3D: `w6-r21f-notes/{before,after}-{2d,3d}-{before,over,past}.png`. Docs: ASSETS (flying units),
|
||
design/client.md, research 12 §6.
|
||
|
||
### 26.10 Enemy animation states: per-slot skill clips, two stun spellings, the Run cycle, two 重生 (PR #275 by @xcdoge, client part) — `tools/assets/anim-roles.mjs`, `tools/assets/spine.mjs`, `render/spine.js _applyRoles`, `render/units.js setSkillSlot / moveFast / FORMS`, `render/app.js 'cast'`
|
||
|
||
- **Seen** (PR #275, a scan of every enemy skeleton against its manifest roles): (1) an enemy's manifest only had skill
|
||
index 0 (tools/assets/plan.mjs passes [0]), so a multi-skill boss could show one cast clip — 盐风主教昆图斯 has
|
||
Skill_01..04; (2) the stun resolver knew Stun / Stun_Begin / Stun_End only, so 巨大的丑东西 (Stun_1 / Stun_2) and 斩胄之剑 /
|
||
破胄之锤 (Dizzy_*) had `stun: null` and froze their clip; (3) 猎狗pro walked on Move_Loop (0.80 s) though its skeleton has
|
||
Run_Loop (0.53 s) and it is fast (moveSpeed 1.9, 行动速度很快); (4) 巨大的丑东西 and 自在 have a Revive clip and a 重生
|
||
in the sim but no FORMS entry, so their 重生 played nothing.
|
||
- **Official**: clip names and lengths are the skeletons' own (data/assets.json `animations`): 巨大的丑东西 Revive 8.67 s for
|
||
its 10 s `reborn.duration` (its self-destruct 2.17 s in), 自在 Revive_01 5.33 s for 5 s. The sim reports a 重生 as a stun
|
||
(archetypes.js `reborn`: flags.stun), so the 重生 form must say what the rest of it holds.
|
||
- **Now**: the resolver resolves every numbered skill clip of an **enemy** after the caller's indices (`numberedSkills`,
|
||
passed by spine.mjs for enemy models; the first index stays the primary `skill`; operators keep the pool's indices, 宴's
|
||
PREFAB_SPINE_ROLES fix of §25.22.9 unchanged); the stun loop is Stun → Stun_1 → Dizzy_Loop (with *_Begin / *_End); a
|
||
model's own Run cycle is a separate `run` role. The renderer: `SpineActor._applyRoles` composes the skill slot's clip,
|
||
the Run cycle and the form in force (a slot change keeps a form; a form ending keeps the slot); a UnitView of an enemy
|
||
faster than moveSpeed 1 moves on `run` (猎狗pro 1.9, 深池侦察犬 / pro 1.7; 宿主士兵 0.9 and 鸭爵 0.4 keep Move);
|
||
`UnitView.setSkillSlot` (render/app.js `case 'cast'`) swaps an enemy's skill clip. FORMS: 巨大的丑东西 `reborn` = Revive
|
||
as the change clip, then the 大祭司's Idle_2 held (also as the 重生's stun) → `form2` Idle_2 / Move_2 / Die / Stun_2, no
|
||
attack (不进行攻击); 自在 `reborn` = Revive_01 → `form2` its own clips. data/assets.json regenerated with
|
||
`node tools/fetch-assets.mjs --offline`: 18 enemies gain `anims.skills`, 6 `anims.run`, 3 a stun clip instead of null
|
||
(巨大的丑东西, 斩胄之剑, 破胄之锤 — the two 胄 parts are stun-immune, so only 巨大的丑东西's is ever seen); no operator or token
|
||
entry changed (`w6-r21f-notes/manifest-diff.txt`).
|
||
- **Not ported**: the sim side of (1) — `content/enemies.js castSkillSlot` emitting `['cast', id, slot]` when an ability
|
||
casts, 昆图斯's ability ids, `UnitInfo.skillIndex` for enemies — adds an event, so golden's event counts move (+cast in
|
||
the two 昆图斯 fields); it is left for the owner (patch: `w6-r21f-notes/sim-cast-slot.patch`, not applied). Until then the
|
||
per-slot clips are unused and every boss keeps its primary clip. Note: 昆图斯 has 10 data skills (Tidewater … Doom) and 4
|
||
clips, so mapping the G1 / G2 / Doom slots to Skill_04 or the primary needs a check first. The PR's research note
|
||
(docs/research/13-animation-states.md) is not ported.
|
||
- [ASSUMED]: the Run threshold (moveSpeed > 1; the client's own trigger is unknown) — only a model with a Run cycle changes;
|
||
巨大的丑东西's `_1` / `_2` clips as its first form / the 大祭司 (the 大祭司 set has no attack clip, matching 不进行攻击); 自在's
|
||
second form on its first-form clips.
|
||
- Golden: unchanged (client and asset tools only). Tests: `test/assets.test.js` (resolver: numbered enemy skills, Stun_N /
|
||
Dizzy_*, run role; the manifest: every stun / Run clip has its role, every enemy with numbered skill clips has a clip per
|
||
slot, 宴 unchanged — 6 cases, all fail on the parent), `test/render/forms.test.js` (the two 重生; every clip FORMS names
|
||
exists in its manifest entry), `test/render/loadout-skill.test.js` (an enemy cast slot swaps the clip, a form kept),
|
||
`test/render/unitview.test.js` (moveSpeed 1.9 on Run, 1 on Move, composing with the slot);
|
||
`RENDER_E2E=1 node --test --test-concurrency=1 test/render/models.browser.test.js` 7/7. Docs: ASSETS (`Roles`: skills of
|
||
an enemy, run, stun spellings).
|
||
|
||
### 26.11 A b.end, a second b.start, b.pool and the pause reach a battle the browser is still preparing (PR #266 by @siruimei07) — `public/js/battle/runner.js` (`pending`, `prepare`, `onEnd` / `endBattle`, `onPool`, `setPaused`, `clear`)
|
||
|
||
- **Seen** (PR #266; reproduced on feedback6 783bd30 by `test/match/runner-pending.test.js` — 11 of its 15 cases fail
|
||
on the parent): the client runner added a `b.start`'s battle to its map only after the sim module had loaded
|
||
(`ensureSim`) and the new Battle had been caught up silently to the field clock (one animation frame per 600-tick
|
||
slice), and everything else looked only at that map. **Cause** (runner.js at 783bd30): `onEnd` (line 688,
|
||
`entries.get`) dropped a `b.end` that came during the load or the catch-up — after a takeover the browser went on
|
||
reporting as the authority (the server ignores a non-authority's reports, server/match/match/reports.js), and a forced
|
||
end (the Final Assault's `cleared` / `forced`) never ended the battle, so the server waited out its 6 s grace and
|
||
filled the field with the stats-only stand-in; `onStart` (line 595) built a second Battle for a second `b.start` of
|
||
the same battle during the catch-up; line 624 (`seq !== startSeq`) never built an authority that another field's
|
||
`b.start` superseded during the load (the deadline then took the field over); lines 663–666 re-registered an
|
||
authoritative battle whose catch-up the next prep's `clear()` had interrupted, and it went on running and reporting
|
||
into the next round; `setPaused` (line 506) and `onPool` (line 680) skipped a battle in catch-up (the paused time was
|
||
caught up; the boss pool waited for the next `b.pool`). No wrong settlement came of it: the server re-simulates or
|
||
stands in.
|
||
- **Official**: not a game rule — the remake's own client / server protocol (§14); nothing to check in the game data.
|
||
- **Now** (§14 Client runner, `b.end`): a `b.start` is registered as pending the moment it arrives (`pending`,
|
||
`battle` null until built; `prepare()` loads, builds, catches up and registers it), so what comes for that battle
|
||
meanwhile is applied to it in arrival order: `b.end takeover` clears its authority; a forced / timeout `b.end` keeps
|
||
the first end reason and ends the Battle as soon as it exists — before any catch-up, or at once mid-catch-up — and an
|
||
authority reports it then; a second `b.start` of the same battle updates the same entry (authority, watch, the field
|
||
clock while it is not built yet) and never builds a second Battle; `b.pool` syncs a built pending boss Battle (one
|
||
not built yet takes the last pool when built, as before); the solo pause shifts a built pending battle's clock. A
|
||
`b.start` of another field supersedes it: an authority, or a battle the server already ended, is still built and
|
||
registered (a superseded authority catches up in the background with the bounded fast-forward, as before), a display
|
||
replica is dropped. `clear()` (the next prep, the match over, leaving) drops the pending entries; their preparation
|
||
finds itself gone at its next step and builds, steps, registers and reports nothing — a fresh `b.start` of the same
|
||
battleId afterwards is a new preparation the old one cannot touch.
|
||
- [ASSUMED] A forced / timeout end that arrives before the Battle exists ends it where it stands (tick 0, or wherever
|
||
the catch-up was) instead of catching up to the field clock first (as the PR does): the server sends those ends only
|
||
when the Final Assault / Hidden Core ends, and after a forced end it credits no further report (§20.5), so catching up
|
||
first would only cost time. A superseded display replica the server already ended is kept (registered, finished) so
|
||
switching back shows it ended (as the PR does).
|
||
- Not taken from the PR: its `cancelled` guards on a result request already in flight when the round moves on (a
|
||
timeout may still retry it once; the server ignores a stale battleId — not a lost control message) and its
|
||
cancellable frame wait (the preparation notices a clear or an end at its next frame). Its `docs/DESIGN.md` paragraph
|
||
became the §14 Client runner sentences (DESIGN.md is the index now).
|
||
- Golden: unchanged (client only). Tests: `test/match/runner-pending.test.js` (new, 15: a b.end takeover / forced /
|
||
takeover-then-forced, a second b.start, the next prep and another field's b.start, each during the load and during
|
||
the catch-up; a superseded replica, the pause and b.pool during the catch-up); the runner suites
|
||
`test/match/runner.test.js`, `clientCombat-review`, `feedback1-runner-forms`, `diy-shop`, `standin-ownership`,
|
||
`watch-bonds`, `test/ui/playtest6-unite.test.js`, `test/ui/spectate-effects.test.js` unchanged and green.
|
||
|
||
### 26.12 The round's enemy effects reach the 频次 器物 and the leaders' summons (PR #272, 感谢 @CXUtk) — `content/enemies/archetypes.js times`, `content/bosses.js summonMods`, config `enemyScale[r].supplyHp`
|
||
|
||
- **Seen**: a 器物 (the 14 hit-count keys: 瑞印, 卷轴, 铜镜, 镇纸, 茶器, 矛头, 灯盘 and their _2) needed the data's number
|
||
of hits at every difficulty: spawnEnemy multiplied its max HP by the round's hpMul, then the 频次 archetype's
|
||
`setHits(e, e.def.maxHp)` overwrote it with the raw count (reproduced: 青铜镜 30 hits in solo 标准 R1 — data × 0.75 —
|
||
and in co-op 终极 R15 — data × 4.30 — alike, as a 悬赏 card and as 身观's death spawn). A leader's mid-fight summons —
|
||
死亡集群's 妖怪, 刺胄之弹, “裂管之音”/“断弦之音”, 不祥幻影 — spawned with no round mods at all (800 HP / 8 hits / 10 hits /
|
||
10000 HP at every difficulty). Reported by @CXUtk in PR #272.
|
||
- **Official**: activity_table act2autochess (data/effects.json): the round effects are ENEMY effects on 所有敌人 —
|
||
攻坚装备 / II / III `enemy_attribute_mul` (ATK ×1.1 / 0.8 / 0.7, HP ×1.2 / 0.8 / 0.75) and 急行军 (speed +15 %) with
|
||
`enemy_exclude` = 炎佑 only; 补给线 / 补给线II (HP ×1.08 / ×1.2, no ATK) with `enemy_exclude` = 炎佑 + exactly the 14 器物
|
||
keys. The exclusion only makes sense if a max-HP multiplier changes a hit-count unit's count, so the 器物 take 攻坚装备's
|
||
HP and not 补给线's. The per-round table (PRTS 卫戍协议:盟约/下半 §开始模拟, user-provided; research 01 A3): `hp =
|
||
hpBase · 1.2^kHp · extra`, `atk = atkBase · 1.1^kAtk`; 攻坚装备 always brings ATK, so kAtk counts its stacks and the
|
||
HP-only steps 1.2^(kHp − kAtk) are 补给线II, `extra` 1.08 is 补给线 — only co-op 终极 has them (R5–R15). PRTS: "领袖单位于
|
||
服务器的生命值加成不受上述加成影响" — only the leader's HP (the server pool) is exempt. 炎佑 is a bond ally in this
|
||
remake (spawnYanyou), never an enemy (waves.js skips it in templates): its exclusion needed no change.
|
||
- **Now**: build-data writes `enemyScale[r].supplyHp` = 1.2^(kHp − kAtk) · extra when ≠ 1 (co-op 终极 R5 1.2, R6 1.08,
|
||
R7 1.44, R8–R11 1.728, R12 2.0736, R13 1.86624, R14–R15 1.5552; only config.json changed in the rebuild, checked by
|
||
a JSON compare); gamedata.baseEnemyScale returns it as `supplyHpMul` and the wave / 悬赏 mods carry it (waves.js
|
||
`roundMods`). The 频次 archetype sets hits = the spawn's max HP (data × hpMul) ÷ supplyHpMul, rounded (setHits); a 频次
|
||
enemy's death spawn inherits the parent's mods, so 身观's 青铜镜 counts the same. A boss / hidden field gets the round's
|
||
scale as the BattleSpec flag `flags.enemyScale` (bossRounds.js; golden fields and tools/balance.mjs likewise), and the
|
||
leaders' summons spawn with it (`summonMods`): HP / ATK / speed, 死亡集群's `summon.hp_ratio` multiplied on top; 刺胄之弹
|
||
and 余音 set their hit count from that HP like the 器物 (they are in no exclusion list). 王权号令's equipment
|
||
(invulnerable, untargetable, never fights) is left as it was. Examples: 青铜镜 solo 标准 23 hits, co-op 险境 R13 86,
|
||
co-op 终极 R6 62 (1.2⁴, not 1.2⁴ × 1.08), R14 129; co-op 终极 R14 刺胄之弹 53 hits, 余音 67, 妖怪 5350 HP.
|
||
- What the PR did and we did not take: it back-calculated 补给线 from the composed hpMul (recognising only a 1.08 / 1.2
|
||
ratio against 1.2^kHp), which counts the 补给线II steps as 攻坚装备 (co-op 终极 R5 / R7–R12 器物 too many hits), and kept
|
||
fractional HP (a ceiling); it also scaled the husk counts.
|
||
- [ASSUMED]: a fractional count rounds to the nearest whole hit (setHits' Math.round, kept; a ceiling — float HP — would
|
||
differ for 3 × 0.75 and 35 × 0.75); the summons' hit counts follow their HP (inferred from the 器物 exclusion, not
|
||
measured). Open: the 逐火 ember / 再生 傀儡 counts (talent `Revive[Trigger].prop_max_hp`) stay unscaled — whether the
|
||
effect multiplies a talent-set max HP is not in the data.
|
||
- Golden: 74 scenarios. 器物 counts (every one holds a 器物 — extra spawn, 频次 wave or a 频次 enemy's death spawn): roster
|
||
37 (002 003 004 005 007–020 022 023 025 026 029–034 036–041 043 045 049), bond-soloShip-high, diy 9 (036 040 056 062
|
||
068 073 091 114 115), boss-boss_6-solo (俗心's 小说卷轴), matches 16 (all but solo-ABYSS-2 and coop2-NORMAL-70-diy: solo-FUNNY-1/2,
|
||
solo-NORMAL-1/2, solo-HARD-1/2, solo-ABYSS-1, coop2-NORMAL-3, coop2-HARD-4, coop3-ABYSS-5, coop4-FUNNY-6,
|
||
coop4-ABYSS-7, coop2-NORMAL-8-serverrun, solo-HARD-9-boosted, coop2-ABYSS-10-boosted, coop2-NORMAL-14-standins — 频次
|
||
rounds / 悬赏 and the Final Assault summons). Summons: boss-boss_1-pair/solo and hidden-boss_8-pair/solo (妖怪 +
|
||
刺胄之弹), boss-boss_3-pair/solo and hidden-boss_10-pair/solo (余音), boss-boss_5-pair/solo (不祥幻影). standins unchanged.
|
||
- Tests: enemies_bosses — the 14 per-key 频次 tests (hits × the round's HP, was "must not change"), the data pin (the 14
|
||
kitTimes keys = 补给线 / 补给线II `enemy_exclude` minus 炎佑; 攻坚装备 / II / III / 急行军 only 炎佑), hpMul ÷ supplyHpMul with a
|
||
death spawn and the solo 标准 rounding, leader summons with / without `flags.enemyScale`; waves — supplyHpMul per co-op
|
||
终极 round (hp ÷ it = 1.2^kAtk), wave and 悬赏 mods; finalAssault — the boss fields carry `flags.enemyScale`.
|
||
|
||
### 26.13 The bond strip's collapse button is a bare arrow (the owner's request of 2026-10-07) — `screens/game.js`, `public/css/screens/game.css .bonds-toggle`
|
||
|
||
- **Seen**: the boxed 「收起」 / 「盟约」 tab in front of the bond discs looked heavy (0.1.4's fold button, PR #149, GitHub #142).
|
||
- **Now**: a bare chevron (‹ open, › folded), transparent, at 55 % opacity (80 % when folded), full opacity and mint on
|
||
hover / keyboard focus; the 44 × 44 touch target stays as an invisible hit area and the name is the aria-label
|
||
(展开盟约 / 收起盟约). Tests: `test/ui/bond-collapse.e2e.test.js` (SP_E2E, 2).
|
||
|
||
### 26.14 「当前全体干员为无潜能,与官方实现不符」 (GitHub #252 by SpiritedAwayCN, PR #255 by SpiritedAwayCN — a host slider, not taken): every operator fights at FULL potential (潜能 6) — the owner's decision of 2026-10-07 — `tools/build-data.mjs OPERATOR_POTENTIAL / withPotential`, data/chess.json / backups.json / tokens.json, data/i18n/*.json
|
||
|
||
- **Seen**: every operator fought at potential 1 (no potential): 刺玫 (tier 1, E1 Lv55) ATK 413 / cost 17 where the
|
||
official panel reads 435 / 15; 砾's redeploy 18 s instead of 16, the gift 5★ (至简 …) 80 s instead of 70; every
|
||
「天赋效果增强」 step missing (刺玫's 土壤基肥改良 108 % instead of 111 %, …). The data build picked every candidate at
|
||
potential rank 0 and added no `potentialRanks` attribute step.
|
||
- **Official**: the season's chess records (activity_table act2autochess `charChessDataDict` `status`: evolvePhase,
|
||
charLevel, skillLevel, favorPoint 0, equipLevel) carry no potential field; a tournament video (bilibili BV1FTowB2EzP,
|
||
02:36, linked in #252) shows 刺玫 at ATK 435 / cost 15 — exactly her E1 Lv55 413 / 17 plus her potentials 攻击力+22 and
|
||
two 部署费用-1. In the official game a player's own potential applies (RushFTK in #252: 0 信赖, potential from the
|
||
account; an operator not owned — potential 1; the shop rows' `backupCharPotRank` is 0 except 宴 / 野鬃 1). The owner's
|
||
decision of 2026-10-07: everyone at full potential, no host setting (PR #255's 1–6 slider is not taken).
|
||
**Summons**: a token's own character has no `potentialRanks`, but its talent candidates carry `requiredPotentialRank`
|
||
mirroring its owner's 「天赋效果增强」 (夕 潜能 5 → “小自在” 化境 15 → 18 层; 凯尔希 潜能 5 → Mon3tr 不毁重构 3.5 s /
|
||
1400 — PRTS 凯尔希 天赋 "潜能增强=5 … 晕眩3.5(+0.5)秒…1400(+200)点真实伤害", X 模组 3 级 4 s / 1700; 望 潜能 3 → 棋子
|
||
+1 持有 / 部署; 麦哲伦 → 龙腾 22 s; 鸿雪 → 打字机 −20 % / −25 % 5 s; 伺夜 → 狼群 200 DEF ignore; 风丸 → 纸偶 ×2.75; …).
|
||
So a summon picks its candidates at its owner's potential and takes none of the owner's attribute steps (ATK / cost /
|
||
redeploy belong to the owner entity).
|
||
- **Now**: `OPERATOR_POTENTIAL = 5`. Every chess (normal and elite), every 补位 / 自选 unit form (`buildUnitForm`) adds
|
||
every `potentialRanks` attribute modifier to the interpolated attributes before rounding (`withPotential`: all
|
||
ADDITION — COST, RESPAWN_TIME, ATK, MAX_HP, DEF, MAGIC_RESISTANCE, ATTACK_SPEED; another kind is reported, none
|
||
exists) and picks talent / trait / module-talent candidates at rank 5 (`bestCandidate(…, potRank)`). Summons
|
||
(`tokenVariant`, tokens.json and backups.json `tokens`, `tokenTalentDeckBonus`) pick theirs at the owner's rank; the
|
||
band's map characters and stage devices stay at 0. The 17 原型干员 stand-ins (预备干员 char_600–607, char_608–615,
|
||
领主·Sharp) have no potential ranks: unchanged. Nothing else reads potential: no skill or trait candidate of the
|
||
season's operators carries a `requiredPotentialRank` > 0, skill levels never depend on it.
|
||
JSON compare (old vs new build): only potential-driven fields moved — chess.json 256 of 266 records (all but the
|
||
four DIY slots and 盟约·辅助干员, which has no potential ranks; stats cost 256,
|
||
respawnTime 200, atk 176, def 16, maxHp 14, res 12, aspd 6 — `statsBase` the same; 275 talents and 140 talentsBase
|
||
entries, 27 module talent changes: bb + desc), backups.json (the 71 owned picks' three forms — none of the 17
|
||
stand-ins: cost, atk, respawnTime, hp / def / res / aspd, talents, module talent changes; 8 of 38 summons: Mon3tr,
|
||
龙腾.F / L / A, 打字机, 结构性原理 talents, 望's 棋子 deploy limit 6 → 7 / holding 7 → 8 / count 7, 多萝西's 共振装置
|
||
count 8 → 10), tokens.json (5 summons' talents: “小自在”, 海嗣, 纸偶, 狼群, 防护单元);
|
||
every other data file byte-identical. The data translations (data/i18n/en / ja / ko / zh-TW) are rebuilt
|
||
(`tools/build-i18n.mjs --offline`): only the talent texts that carry the new numbers. Spot checks: 刺玫 435 / 15 /
|
||
66 s and 111 %; 格雷伊 ASPD 100 → 106 (攻击速度+6), 崖心 / 伊内丝 +8; 砾 16 s, 至简 70 s, 野鬃 60 s (two 再部署时间-10秒:
|
||
#252 said 70 for the gift 5★ — true for 至简; 野鬃 has two such steps). The 干员调配 局内数值 and the detail card read
|
||
the records (statsBase + module attr, shared/loadoutRecord.js), so they show the new numbers unchanged in code.
|
||
One kit read a number from a talent text that now carries the potential step: 望's stock cap, 「最多拥有7枚」 at
|
||
potential 0, reads 「最多拥有8(+1)枚」 — `op-wang.js` matched `/最多拥有(\d+)枚/` and fell back to 7, so with 7 棋子 to
|
||
start she sat at the cap (阻回, no S1 / S2); the pattern now skips the step (8 = the 棋子's holding, deckStack 8). The
|
||
other kits that parse talent texts (多萝西 「最多拥有10个」, 娜仁图亚 「偷取触发2次」, 傀影 「攻击力各+10%」, 伺夜 「至多3只」)
|
||
read numbers that carry no step; skill texts have no potential steps.
|
||
- **Consequences worth knowing** (the data's, nothing assumed): 多萝西's 共振装置 stock at a deployment is 10 at full
|
||
potential (潜能 5 「第一天赋效果增强」) — her trap limit without TRP-X is 10, so the kit's 阻回 (no SP while the stock is
|
||
full) holds from her deployment until a trap is spent (TRP-X: limit 13, no change); 望 sends 7 棋子 (TRP-X 8); 史尔特尔's
|
||
余烬 lasts 9 s; 琳琅诗怀雅's 破财消灾 revives to 80 %. One official-data quirk surfaces: 重岳's FGT-Y stage 3 potential
|
||
candidate (battle_equip_table uniequip_003_chyue) keeps `prob` 0.23 while its text reads 「25%(+2%)」 (damage_scale
|
||
1.75 → 1.8 does move) — the sim follows the blackboard, as for 嵯峨's SOL-Y before (op-saga.js), so that one form rolls
|
||
23 % and every other form 25 %.
|
||
- [ASSUMED] none beyond the owner's decision (full potential for every player and every operator, including the
|
||
自选 picks, whose official potential would be the account's).
|
||
- Golden: moves almost everywhere, as a data change of every operator must — roster 49 / 49, bonds 46 / 46,
|
||
fields 22 / 22, matches 18 / 18, diy 133 / 138, standins 0 / 10. Unchanged: the 10 stand-in battles and the four
|
||
prototype 自选 battles diy-135…138 (the 原型干员 have no potential ranks), and diy-012 (twelve 夜莺 pieces that are
|
||
never hit, never redeploy and deal no damage, so her cost / RES / redeploy steps never show; her talents do not
|
||
move). Spot checks, the parent data against the new one tick by tick through the golden scenario path
|
||
(diy-110 / diy-111, 望's battles, also move with her cap fix): roster-001 and roster-020 part
|
||
at tick 1 on 角峰's HP (生命上限+250: 2770 → 3083 and 5961 → 6634 with the board's HP bonuses), bond-arcaneShip-high at 4.73 s and diy-001 at 3.70 s on an enemy's HP (a hit of a higher
|
||
ATK). Every moved battle's first differing snapshot hash is in its first 10 game seconds except one bond battle
|
||
(20–30 s) and ten 自选 battles (10–100 s): a later hit or redeploy; the matches part at rounds 1–8 through those
|
||
battles (battle results, then the bots' boards).
|
||
- Tests: test/full-potential.test.js (new, 6 tests — every one fails on the parent data): 刺玫 435 / 15 / 66 s and her
|
||
talent; the other potential kinds and #252's redeploy times; every chess, 自选 form and stand-in form re-derived from
|
||
the raw keyframes plus the potential steps; every chess talent = the last candidate unlocked with requiredPotentialRank
|
||
≤ 5; summons at the owner's potential (PRTS Mon3tr numbers) without the owner's attribute steps; the loadout record
|
||
(局内数值 / detail card), 自选 / 补位 compositions and a battle unit (刺玫 base ATK 435, cost 15, 66 s). data.test.js's
|
||
independent re-derivation now adds the potential steps (and re-derives respawnTime too); the spot checks and the kit
|
||
tests that hard-coded potential-0 numbers carry the full-potential numbers (each from the data and the test's own
|
||
formula; no check loosened): 205 tests in the 71 自选 kit test files (op_*), 21 elsewhere (data / backups / i18n-data,
|
||
chess kits, summons, 史尔特尔 余烬 9 s, 望's hand of 7, …); op_wang's S1 / stock / S3 tests fail on the parent kit (the
|
||
stock cap). Full `node --test`: 5114 / 5114 pass (the first run on the new data: 233 failures in 87 files, two of them
|
||
timing tests that failed under load only and pass alone).
|
||
|
||
### 26.15 Esc in the 自选 picker closes only the picker (GitHub #284 by Xinglan233, idea from PR #286) — `screens/diy.js DiyPicker`, `screens/loadout.js`
|
||
|
||
- **Seen**: in 干员调配 → 自选编队, one Esc with the operator picker open closed the whole overlay and went back to the
|
||
lobby (5阶 and 6阶 slots, also from the picker's search field), while the picker's 取消 and × close only the picker and
|
||
the 导入 dialog's Esc closes only itself. The overlay's capture-phase key handler gave way only to a `.modal`; the
|
||
picker is a `.diy-pick` section without key handling of its own.
|
||
- **Now**: the picker registers its own capture-phase Esc handler that acts as 取消 (the draft is dropped, the saved
|
||
picks are untouched) and gives way to a `.modal` over it (the 导入 dialog); the overlay's handler skips Esc while a
|
||
`.diy-pick` is open. The next Esc closes the overlay as before.
|
||
- Golden: unchanged (interface only). Tests: test/ui/diy.e2e.test.js — a real-server browser test that needs no art:
|
||
Esc closes the picker of a 5阶 slot, then of a 6阶 slot from its search field, the overlay and its four slots stay,
|
||
sp.pref.diy is unchanged, and a second Esc closes the overlay; it fails on the parent.
|