- tools/build-data.mjs applies every potentialRanks attribute step (COST, RESPAWN_TIME, ATK, MAX_HP, DEF,
MAGIC_RESISTANCE, ATTACK_SPEED) to the chess, elites, 补位 and 自选 units and picks talent / trait / module-talent
candidates up to requiredPotentialRank 5; summons take the owner's potential for their talents, not its stat steps.
data/chess.json, backups.json, tokens.json and the en / ja / ko / zh-TW data translations rebuilt; op-wang.js reads
her stock cap (8 at full potential). 刺玫 435 ATK / cost 15, 砾 16 s, 至简 70 s.
- No conflicts outside the golden files (regenerated). Golden vs HEAD: roster 49, bonds 46, fields 22, matches 18,
diy 133 (standins and diy-012 / diy-135…138 unchanged: no potential ranks or never hit). Union check against the
branch and the merge base: every scenario only the branch moved equals the branch, except roster-048 and
solo-ABYSS-2: at full potential a 频次 器物 is reached there (roster-048: one more enemy dies instead of leaking, 86 → 87
units), so R21H's hit count (data × hpMul ÷ supplyHpMul) applies where it did not before — with only that rule put back
(archetypes.js times as at the merge base) both equal the branch exactly.
- Tests: broad node --test 5147 / 5147; lint 0 errors.
器物 (the 14 hit-count keys): hits = data × the spawn's hpMul ÷ supplyHpMul, rounded as before (setHits) — 攻坚装备 / II / III
leave out only 炎佑, 补给线 / 补给线II also exactly these 14 keys (activity_table aceffect_enemy_2 / 2_2 enemy_exclude,
data/effects.json); until now setHits(def.maxHp) overwrote the round's HP (青铜镜 30 hits at every difficulty). build-data
writes enemyScale[r].supplyHp = 1.2^(kHp − kAtk) · extra (the HP-only 补给线II steps and 补给线's 1.08; co-op 终极 R5–R15
only — the rebuild changed config.json alone), gamedata returns it as supplyHpMul and the wave / 悬赏 mods carry it
(waves.js roundMods; a 频次 enemy's death spawn inherits it). Not taken from the PR: its 1.08 / 1.2 back-calculation (it
counted the 补给线II steps as 攻坚装备) and its fractional-HP ceiling. Leader summons (死亡集群's 妖怪, 刺胄之弹,
“裂管之音”/“断弦之音”, 不祥幻影): the boss / hidden field carries the round's scale as flags.enemyScale (bossRounds.js,
golden fields, tools/balance.mjs) and bosses.js summonMods gives them HP / ATK / speed (死亡集群's summon.hp_ratio on
top; 刺胄之弹 / 余音 hit counts follow their HP); the leader's pool stays exempt; 王权号令's invulnerable equipment
unchanged; 炎佑 is an ally token, never scaled (no change). Unchanged and open: the 逐火 / 再生 husk counts (talent
prop_max_hp).
Golden — 器物 counts: roster 37 (002 003 004 005 007–020 022 023 025 026 029–034 036–041 043 045 049: each holds a 器物
extra spawn / 频次 wave / death spawn), bond-soloShip-high, diy 9 (036 040 056 062 068 073 091 114 115), boss-boss_6-solo
(俗心's 小说卷轴 escorts); matches 16 (all but solo-ABYSS-2 and coop2-NORMAL-70-diy: 频次 rounds / 悬赏 and the Final
Assault summons). Summons: boss-boss_1-pair/solo, hidden-boss_8-pair/solo (妖怪, 刺胄之弹), boss-boss_3-pair/solo,
hidden-boss_10-pair/solo (余音), boss-boss_5-pair/solo (不祥幻影). standins unchanged.
ai.js enemyAttack set the post-strike stand only for an unblocked ranged enemy (`!e.blockedBy && radius > 0`), so a
melee enemy released by its own strike walked on that very tick. 流泪小子 (enemy_2034_sythef_2, the strategy's version;
「泪眼汪汪」 / 「哭狗」 in the reports) stuns its blocker with every hit, the stunned blocker lets it go and its 隐匿 is
back 0 s later: on 0255afd it walked off the tick of each strike (four non-attacking walls in a lane: 8 stuns, 19.8 s
隐匿 / 9.0 s revealed, leaked at 28.9 s). PRTS 状态机: an enemy's ATTACK state 「每帧检查异常状态…攻击结束后回退到MOVE状态」
and COMBAT 「逻辑同ATTACK」 — only a character's COMBAT 「检测到不存在阻挡对象时强行切回IDLE状态」 — so an enemy finishes its
attack when its blocker goes. Now every enemy stands for the rest of its clip after a strike (attackStand; a blocked one
never walks, so it shows only when the block ends meanwhile: its blocker stunned or knocked out by that strike, or
gone); a stun / freeze / sleep / 浮空, leaving the field and, new, a displacement (失衡, Battle.displace) end the stand.
流泪小子 now stands 0.8 s (its 1.567 s clip, strike at 0.767), 隐匿, and a blocker whose stun ended often takes it over
again: the same lane 13 stuns, 29.1 s 隐匿 / 12.8 s revealed, leaked at 42.0 s.
Checked and official, unchanged: a stunned operator blocks nothing (gamedata_const ba.stun 「无法移动、阻挡、攻击及使用技能」,
PRTS 异常效果 STUNNED 「无法攻击、释放技能、阻挡敌人类单位」 — the keeps-blocking hypothesis is wrong); 隐匿 back 0 s after a
block (PRTS 流泪小子); moveSpeed 0.8 × the level's moveMultiplier 0.5, interval 2.0 s, stun 3.5 s, MELEE (enemy_database;
PRTS 盟约记录 鸭爵 备注: 4500 HP, 3.5 s stun, 隐匿 kept); the cooldown runs on the walk, so a new blocker sees the swing at
once and the strike after the 0.767 s wind-up; a bot teammate's 鸭爵 swaps into a human's battles, the Final Assault and
the Hidden Core too (「你和队友遭遇的部分敌人」), on the replaced enemy's route.
Golden — traced in lockstep against a copy of the parent: in each of the 199 battle scenarios the first differing tick is
an enemy standing out its clip after a strike where the parent walked on; every moved match has such stands in an
earlier or the same round. roster 41 of 49 (all but -003, -005, -009, -018, -019, -025, -035, -049); bonds 21
(deputShip-low, emptyShip-low / -high, indomShip-low / -high, investShip-low, kazimierzShip-low / -high,
kjeragShip-high, lateranoShip-high, miraShip-high, raidShip-low, sargonShip-high, siracusaShip-low, soloShip-low /
-high, suntShip-low / -high, swiftShip-high, visiShip-low, yanShip-low); fields 13 (all but boss-boss_1-solo,
boss-boss_2-pair / -solo, boss-boss_6-solo, boss-boss_7-pair / -solo, hidden-boss_8-solo, hidden-boss_10-solo,
unite-1); matches 17 (all but solo-HARD-9-boosted); standins 10 of 10; diy 114 of 138 (all but -011, -012, -018, -020,
-025, -027, -029, -031, -033, -047, -053, -077, -080 … -082, -086 … -088, -091 … -093, -098, -108, -123). The
displacement rule alone moves one roster scenario.
Tests: test/sim/feedback5-attack-clip-rest.test.js (5; the four behaviour tests fail on the parent, the data pin passes
on both); test/sim (745), test/content + feedback1-ducklord (2030), test/match (593) pass. Docs: CHANGELOG 0.2.0 规则
(按官方) and 核实后不是问题, docs/history/0.2.0.md §25.22.6, docs/SIM.md, design/engine.md, DATA.md.
Text only: data/chess.json keeps both lines (traitOverride.desc = the class trait or the module's rewrite,
moduleDesc = the added line) and the battle unit keeps the class trait with the module on — the same trait
text, targeting and attack shape as 不装备 for every added-line module (圣约送葬人 REA-Y also rewrites it).
New helpers ui/loadoutModel.js traitLines / fullTraitText; traitText (ui/detailPanel.js) composes the two.
Golden unchanged (no sim change). Tests: test/ui/feedback5-module-trait.test.js (new), loadout-stats updated.
Ported by hand from PR #211 by @xcdoge:
- render/units.js: FLY_HOVER 0.32 → 1.3, flat (the official fly offset is one model-independent Vector3(0, 0.35, 0) in
the client's character space, whose unit is the standard battle-prefab scale 0.27; an official screenshot measures
1.2–1.4 tiles); enemyModelScaleY multiplies the skeleton's Y scale and the head (bar) height, also in the impostor
path; a mirrorX model flips on top of the direction flip. The body, its HP bar, damage numbers, projectile hits and
skill rings ride `hover`, the shadow stays on the ground; one camera drives the units over the 2D and the 3D board.
- tools/build-data.mjs MODEL_STRETCH_Y / MIRRORED_PREFABS → enemies.json modelScaleY / mirrorX; data/enemies.json
regenerated offline here (the PR's one-line blob was not taken) — JSON compare: only enemy_1112_emppnt /
enemy_1112_emppnt_2 modelScaleY 1.263 and enemy_1196_msfyin mirrorX true.
- tools/local-extract/enemy_model_offsets.py (optional, host-side prefab reader) and
docs/research/12-flying-visuals-official.md (without the claim that the owner said 「都要」 on 2026-10-05 — the owner's
decision of 2026-10-06 is cited; where the quirk enemies appear corrected against 0.2.0's data: 帝国炮火先兆者 is a FLY
special enemy of the mode, 帝国炮火中枢先兆者 comes with the bounty 战术特训·飞行III, 木制瑞印 is in no wave). No
screenshot or extracted art committed.
Tests (from PR #211): test/render/unitview.test.js (FLY_HOVER ≈ 0.35 / 0.27; the same lift for every flyer; a flying
view lifts by FLY_HOVER — added here: its body and HP bar ride the lift, its shadow stays on the ground),
test/render/playtest6-enemies.test.js (the data keys; the 1.263 aspect and bar height; the mirrored model's x sign);
both fail without the renderer change; the 425 render unit tests pass. Docs: DATA §9, ASSETS, design client.md,
research 00 / 12, DESIGN §25.13.5 and the §25.10 table.
Golden unchanged (drawing only; the sim reads neither field).
Ported by hand from PR #209 by @qingjingshenghuo (its one-line data/config.json was not taken):
- server/match/gamedata.js bossPoolShareOf (used by GameData.bossPoolShare and by finalAssault.bossPoolHp for a plain
game-data object): solo × `solo` (1); co-op with `perPlayer` true × `coop` (1) × the players alive at the boss
round's start (bots and AI 托管 seats count, eliminated / departed seats do not; at most aliveFull 4; a count left
out = a full team). The fixed pool stays restorable through config: bossHpScale perPlayer false + solo 0.25 (and its
optional aliveScaling).
- tools/build-data.mjs: the global bossHpScale carries the rule (perPlayer true, solo 1); the modes' entries keep only
bloodPointKey / unaffectedByEnemyScale. data/config.json regenerated offline — JSON compare: only bossHpScale (global
and the 9 mode entries) changed.
- The problem #209 documented: a 胄 drone death is 2 % of the pool and 限伤 cancels a hit of ceil ≥ 300000, so the
hidden 胄 终极 pool at 3 / 4 players (21.6M / 28.8M) would cancel every drone (432000 / 576000). The drone link is
a share of the pool, no hit: Battle.loseHp takes noHitLimit and the link passes the limit [ASSUMED: the official
client checks every damage modifier and PRTS calls it 真实伤害, but which max HP it reads is undocumented]; a part's
传递 and every hit stay under the limit.
Golden (regenerated; why: the pool size):
- fields: boss-boss_1..7-pair/-solo and hidden-boss_8..10-pair/-solo (all 20 boss / hidden scenarios) — a pair
field's pool × 2 (two players), a solo template's × 4 (solo × 1, was × 0.25); boss-boss_1-pair, boss-boss_1-solo,
boss-boss_3-pair, boss-boss_3-solo, boss-boss_5-solo, boss-boss_7-pair and hidden-boss_9-solo now end at the
200 s cap instead of clearing; the two 联防 scenarios are unchanged.
- matches: solo-FUNNY-2, solo-NORMAL-1, solo-HARD-1, solo-HARD-2 now lose the Final Assault (solo leaders have 4× the
HP: team LP 0 with 93088–176395 of the pool left); solo-FUNNY-1, solo-NORMAL-2, solo-HARD-9-boosted (its Hidden Core
too), coop2-NORMAL-3, coop4-FUNNY-6, coop2-ABYSS-10-boosted, coop2-NORMAL-70-diy still win with more leader damage
(pool × players alive; solo-FUNNY-1 / solo-HARD-9-boosted end with 6 / 1 team LP less). Unchanged:
coop2-NORMAL-8-serverrun and coop2-NORMAL-14-standins (one player left at R14: × 1 as before) and the matches that
end before R14; roster / bonds / standins / diy unchanged.
Tests: test/match/boss-hp-scaling.test.js (from PR #209, adapted — every leader and difficulty × 1–4 players and solo;
R14 / R15 leave out two departed players; AI 托管 and bots count, an eliminated player does not; config restores the
fixed pool, the opposite of the PR's test; the plain-object fallback agrees; only the leader's HP scales);
test/content/enemies_bosses.test.js (hidden 胄 终极 at 3 / 4 players: the drone share lands, a hit of the same size is
cancelled — fails without the change). Updated on purpose: finalAssault, balance, bosshp, connection, docs-consistency
(the §20.9 record points to §25.13.4; the data carries perPlayer true), sim/playtest6_limits (the drone share lands
past the line), match/playtest6-bosspool (the 5 % knob halved to keep the 2-player pool's size: that test is about the
pool's arithmetic). Docs: PLAYING §7, DATA, META, BALANCE, SIM, design engine.md / network.md, the §20.9 / §20.10
records, research 00 / 01 / 08, the code comments, DESIGN §25.13.4 and the §25.10 table.
Mon3tr T1 "可以在攻击范围内的地面使用…重构体"; PRTS 战术锚点 特性 "仅可以部署在凯尔希·思衡托攻击
范围外的远程位". tools/build-data.mjs summonRecord: ownerRange also from an owner talent
naming the token, ownerRangeOutside / rangedTilesOnly from the token text. Regenerated
offline: 3 paths (重构体 ownerRange true; 战术锚点 ownerRangeOutside, rangedTilesOnly).
Match: board.js placement class 'high'; PlayerState.summonExcluded in _legal, the
re-orientation lift (+ toast), _roomForOutOfRange, the invariants and the bot; the
client mirror (piecePosition HIGH, summonExcluded, reasons; English strings added).
Golden: no scenario moved.
PRTS 状态机: ATTACK / COMBAT "每帧检查异常状态,若有则切换到异常状态的状态"; PRTS 异常效果 STUNNED (the state machine
leaves the attack), DISARMED "正在进行的普通攻击将被中断". The strike frame is data/enemies.json attackAnim.hit.
- ai.js: attackWindup = the clip's strike frame (half the clip when none is named), shortened with the clip like the
stand; the cooldown counts down to the strike and the swing (enemy.swing) starts when it reaches the wind-up with a
target in reach (attackTargets, shared with the frame) — steady-state strikes stay one interval apart. An enemy whose
cooldown ran out before it had a target (walking, just blocked, a cut swing) swings from the start: the whole wind-up.
- A stun / freeze / sleep / 浮空 / hiding, 缴械, 恐惧, 战栗 while blocked or losing every target cuts a swing short of its
frame: no strike, the next swing starts from its wind-up. A stun in the cooldown only pauses it; a strike already
made stays made. No clip known (36 of 249 kinds): no wind-up, the old rule. [ASSUMED] targets re-taken at the frame,
a cut swing does not spend the cooldown, enemy heals keep no wind-up.
Golden: 275 of 283 scenarios moved — roster all 49; bonds 41 of 46 (unchanged: bond-arcaneShip-high,
bond-kjeragShip-low, bond-maniShip-low, bond-skillfulShip-high, bond-swiftShip-low); fields all 22; matches all 18;
standins all 10; diy 135 of 138 (unchanged: diy-027, diy-029, diy-087) — every battle where a clip-carrying enemy meets an
operator: the first strike of a contact waits its wind-up, stuns cut swings (the cut alone moves 55). With every
attackAnim stripped from the data the corpus is byte-identical to the previous commit (nothing else moved). Bot
matches: solo-HARD-2 now wins, coop2-NORMAL-8-serverrun now loses; the other 16 keep their result.
Tests: test/sim/feedback5-enemy-swing.test.js (new, 6). Updated on purpose: feedback2-enemy-attack-stand.test.js (it
locked the instant first strike), enemies_bosses.test.js (沉沙 / 新硎's cleared interval, 帝国的甲胄, 斩胄之剑 / 破胄之锤 wait
for the frame). test/sim, test/content, test/match pass. Docs: SIM.md, DATA.md (attackAnim), ASSETS.md.
shared/diy.js DIY_EXCLUDED_MODULE_TYPE = /^(?:ISW|SO|RA)-/: modules whose own effect works in
another game mode only ("在【岁的界园志异】中", "在【沉沦者的黑流树海】中", "在生息演算中"; the
excel gates those parts roguelike / sandbox) — 电弧 SO-A / SO-B, 机械师 SO-A, 森蚺 RA-A
[ASSUMED, the ISW-A reason]. validateDiyPicks refuses them, checkDiyPicks drops such a pick,
the picker hides them; the record and the kits still field them. Docs: DATA, PLAYING,
DESIGN §8. Golden: no scenario moved.
tools/build-data.mjs classifyAttack: a trait whose 法术伤害 comes only "技能开启时" keeps
the normal attack's phys (display / AI only — her kit already attacks physically
off-skill). Regenerated offline: 3 paths, the three forms of char_1044_hsgma2 dmgType
arts -> phys. Golden: no scenario moved.
tools/build-data.mjs TRIGGER_ALLY_RULES (PRTS 特殊策略 "技能范围内存在可治疗的我方单位时释放技能")
writes the row as the engine's SKILL_RANGE on the skill range with allies: true; simdata
normalizeSkill passes allies into the def's trigger (skills.js reads trigger.allies).
Regenerated offline: 9 paths, the S3 trigger of 黍's three forms. op-shu.js no longer sets
the trigger. Golden: no scenario moved.
tools/build-data.mjs summonRecord: placeable also needs an owner that shows the summon
(the variants' display source, the owner's displayTokenDict). The S3 marker is the skill's
own object (PRTS 予愿安洁莉娜 S3 备注: the skill deploys it at her initial tile); every other
undisplayed summon is already HIDDEN. Regenerated offline: one path changed,
backups.tokens.token_10071_aglna2_agairp.placeable true -> false. The kit drops its
placed-marker branch (the skill always summons it). Golden: no scenario moved.
tools/build-data.mjs TOKEN_POSITION_CORRECTIONS (PRTS 棋子 部署位置 "全部位", 备注
"游戏内召唤物信息与实际不符(显示为仅部署在近战位)") sets the 棋子 record's position to ALL;
validateAll checks every entry. Regenerated offline: one path changed,
backups.tokens.token_10064_wang_stone1.position MELEE -> ALL. Her stock / 阻回 unchanged
(an owner question). Golden: no scenario moved (none places 棋子).
tools/build-data.mjs tokenTalentDeckBonus adds a summon's talent max_deploy_count /
max_deck_stack_cnt (the hidden "TOKEN数" talent; a module token part of the same talent
replaces it, matched by prefabKey) to its variant deployLimit / deckStack. Regenerated
offline: only data/backups.json tokens of 麦哲伦 (drones), 令 (souls), 白铁 (devices) and
夜莺 (幻影) changed — deployLimit 3 / 4 with SUM-Y stage 3, 2, 3; deckStack 5 (8), 3 (4).
The hand count (player/diy.js placeableTokens) now matches the official one for every
自选 summoner (PRTS 卫戍协议/帮助 "根据召唤物部署数量上限", the owners' talents, PRTS 幻影 备注).
The summoner kits read deckStack / deployLimit as they are (same decks as before).
Golden: no scenario moved (the diy family places no summon pieces).
Conflicts: server/match/bot.js (W4A's comment + W5K's player data view pgd), test/golden/diy.json (regenerated). data/ equals an offline build of the merged tools/build-data.mjs. Golden regenerated on the merged code: against feedback5 the moved roster / bonds / fields / matches / standins scenarios are exactly W5M's per-PR attribution (#160 skill events, #109 deploy-timed lifecycle, #66 灵巧 sources and hidden layers, 83fc408 歌蕾蒂娅 placement), plus coop2-NORMAL-70-diy (fields operators those PRs touch) and diy-051 / diy-052 (events.skill only: Misery's deploy passives, #160); against fb5-merge4 only feedback5's own additions (the diy kits, coop2-NORMAL-70-diy).
Ported by hand into the split layout:
- 83fc408 board.js placeClass: the player's loadout no longer counts, as in 83fc408. feedback5's 补位 rule stays: a
chess the player fields as its stand-in is placed by the stand-in's record (ps.fieldRecord). positionClass(rec)
takes no module.
- 83fc408 client piecePosition → public/js/ui/gameLogic/placement.js: meleeOnHighGround(rec) on the stand-in or
chess record, no module. The gameLogic.js shim header and deployedModuleId's comment follow; deployedModuleId is
kept, still exported and tested.
- 83fc408 PlayerState._placementOf comment → server/match/player/placement.js.
- 83fc408 tools/golden.mjs layout: positionClass without the module, on feedback5's stand-in body (recOf) and in
diyLayout.
- bonds/core.js (6ab9a06, 8a2e0c7) applied cleanly; it equals 13808e3's plus feedback5's i18n toast. shared/highGround.js
equals 13808e3's.
- data/: only bonds.json (the 阿戈尔 spec text). node tools/build-data.mjs --offline on the merged tree rewrites all
15 files byte-identical.
Golden regenerated: 16 scenarios moved, all by 83fc408. Each fields 歌蕾蒂娅 (normal, or elite with HOK-X / no
module), whom tools/golden.mjs now lays out beside the enemy path like a ranged operator instead of on the busiest
melee tile: roster-010, roster-012, roster-029, roster-040, roster-041; bond-deputShip-high, bond-indomShip-low,
bond-preciShip-low, bond-preciShip-high, bond-suntShip-low, bond-swiftShip-high; boss-boss_2-pair, boss-boss_4-pair,
boss-boss_6-pair, hidden-boss_8-pair, hidden-boss_10-pair. This is the list in 83fc408's own message. With the
阿戈尔 commits reverted the digests are the same, so those two move nothing in this corpus. The standins, diy and
matches families are unchanged: no stand-in or 自选 piece there is 歌蕾蒂娅, 崖心 or 见行者.
The owner's decision of 2026-10-05 reverses the one of 2026-10-04 (elite 歌蕾蒂娅 with HOK-Y only) and follows PRTS:
the line is the 钩索师 / 推击手 branch trait (歌蕾蒂娅, 崖心, 见行者, normal and elite) and every module keeps it, so
the loadout no longer matters. Raised with the PRTS source in PR #69 by @sunstricken (its own change covered only
歌蕾蒂娅; its test page is not taken). GitHub #153.
- shared/highGround.js: meleeOnHighGround(rec) reads the trait without a module (traitBase, else trait); no module id.
- board.js positionClass(rec) widens those six records; placeClass(ps, rec) = positionClass(rec) for every server
check (g.move, swaps, merge tile, evictions, audit, invariants). The client's piecePosition mirrors it.
- bot.js: the existing 高台 preference (a 高台 whose range covers the road) now applies to all six; comments.
- Docs: DESIGN §3 and §6.6, DATA (placement), PLAYING §4, META, research 03 C2, code comments. data/ unchanged
(build-data --offline gives identical files).
- Tests: board, feedback1-placement (each holder with default / no module / every module on each 高台, a 重装
refused, client highlights, swap, battle, bots), gameLogic.
Golden: 16 scenarios moved, every one because tools/golden.mjs lays operators out by positionClass and now places
歌蕾蒂娅 (normal, or elite with HOK-X) beside the enemy path like a ranged operator instead of on the busiest melee
tile, which shifts the others: roster-010, roster-012, roster-029, roster-040, roster-041; bond-deputShip-high,
bond-indomShip-low, bond-preciShip-low, bond-preciShip-high, bond-suntShip-low, bond-swiftShip-high; boss-boss_2-pair,
boss-boss_4-pair, boss-boss_6-pair, hidden-boss_8-pair, hidden-boss_10-pair. roster-031 (elite with HOK-Y, already
widened) and all matches are unchanged.
Conflict: server/match/player/basics.js imports (W5G's checkNotOwned + W5I's msg / dn). data/i18n/en.json rebuilt on the merged data (tools/build-i18n.mjs --offline): the 自选 data adds texts of CN-only operators and modules the EN client lacks, so coverage is 97.6 % (skills 95.9, talents 95.1); the coverage floors in test/i18n-data.test.js and docs/I18N.md say so. Golden unchanged; i18n / lobby / match / ui suites 1144/1144 after the rebuild, i18n e2e 1/1.
Conflicts: tools/golden.mjs / test/golden/README.md take both new families (standins from W5G, diy from W5H); DATA.md / SIM.md keep both texts. data/ equals an offline build of the merged tools/build-data.mjs. Golden regenerated on the merged code: against feedback5 nothing moved, diy.json is new; against fb5-diy the W5G / W5J / W5L scenarios moved, and diy-004 / diy-006 (the prototype battles with Touch, changed by W5L) — diy-001/002/003/005 identical.
NOTICE.md and THIRD-PARTY-NOTICES.md credit @YuriRestia (PR #70) for the English UI strings and fallback
translations, note that PR #70's ak-spa-database.pages.dev texts are not used and that arknights.wiki.gg is
CC BY-SA 4.0, and name the EN data mirrors (ArknightsAssets/ArknightsGamedata, Kengxxiao/ArknightsGameData_YoStar).
DATA.md: data/i18n/en.json is written by tools/build-i18n.mjs (rerun after a text-changing build).
PLAYING.md: the 中文 / English switch.
Golden regenerated on the merged code (the textual merge had no conflict and equals the regenerated files): against feedback5 only the new match coop2-NORMAL-14-standins and the new standins.json (10 battles); against fb5-standin-game exactly the W5F and W5J scenarios (roster-001/002/007/009/016/026/027/028/038/040/041/042/044, bond-egirShip-low / siracusaShip-high / suntShip-high, boss-boss_2-pair / boss_6-pair, solo-NORMAL-1, coop2-NORMAL-3, coop3-ABYSS-5, coop4-FUNNY-6, coop2-NORMAL-8-serverrun); standins unchanged. test/match 536/536, the stand-in / golden / docs suites 175/175.
Golden regenerated on the merged code (only test/golden/matches.json conflicted): against feedback5 it moves exactly W5F's scenarios (roster-001/002/016/026/027/028/038/041/042/044, bond-egirShip-low, bond-suntShip-high, boss-boss_2-pair, boss-boss_6-pair, solo-NORMAL-1, coop2-NORMAL-3, coop4-FUNNY-6, coop2-NORMAL-8-serverrun); against fb5-bugs3 exactly W5E's (unite-1/2 and the 8 match scenarios of the 联防 map and the full-hand rule). data/ equals an offline build of the merged tools/build-data.mjs.
tools/build-data.mjs resolveTrigger: a MANUAL operator skill on DEFAULT (no row, or a DEFAULT entry of the deviation
tables) or on the SEARCH row ("在初始攻击范围内存在敌人时释放技能": 解放者 / 阵法术师 / 安洁莉娜 — ACTIVE_RANGE_OVER) whose
running range strictly contains its own is ACTIVE_RANGE on that range, rawRule the official row; validateAll accepts a
DEFAULT deviation resolved to ACTIVE_RANGE (chess and stand-in tables) and ACTIVE_RANGE over SEARCH. Data rebuilt
offline, JSON-compared: only skills[].trigger rule / customRangeGrid of 薄绿 S1 (x-2), 蜜蜡 S1 (x-2), 卡涅利安 S3 (x-2),
玛恩纳 S2 (2-3), 安洁莉娜 S3 (y-4) — rawRule SEARCH — and 深巡 S2 (3-2, rawRule TAKE_DAMAGE), normal + elite (32
leaves); backups.json unchanged. Kits: 薄绿 S1 drops its DEFAULT trigger and 深巡 S2 PR #12's DEFAULT line, both read the
data now (雷蛇 S2 keeps PR #12's line). The sim needs no change (SEARCH was the DEFAULT condition on the initial range
every tick; ACTIVE_RANGE is the same on the running range). Docs: DESIGN §5.6 and the §21.20 flip row, DATA, SIM,
PLAYING, kits README, shared tier comments, skills.js header.
Tests: data (78 records / 39 skills, the five from SEARCH, the SEARCH skills that stay), feedback5-active-range (安洁莉娜
S3 on its y-4, S2 keeps SEARCH), feedback1-tank-triggers and kits_t1t2 (深巡 S2 on its 3-2, before any hit), kits_alt_t3
(薄绿 S1 on its x-2), feedback1f-fence (the idle measure uses the interval of the cooldown still running: the S2 now casts
between two attacks), docs-consistency (one PR #12 kit line left).
Golden: roster-001 / 002 (深巡), roster-016 / 044 (玛恩纳, 安洁莉娜), roster-026 / 027 (薄绿), roster-041 / 042 (卡涅利安),
bond-egirShip-low / bond-suntShip-high (深巡), boss-boss_2-pair / boss-boss_6-pair (玛恩纳, 安洁莉娜), matches solo-NORMAL-1,
coop2-NORMAL-3, coop4-FUNNY-6, coop2-NORMAL-8-serverrun — each fields one of the six skills, which now casts when an
enemy enters its running range.
- simdata: DataSource keeps `backups` (rawBackups follows the fallback chain); getStandIn = shared/standIn.js
standInRecord normalised (the chess's ids / bonds / 特质 / tier, the stand-in's body, def.standInFor); a PRESET / DIY
chess or a source without backups fields itself; the per-battle view maps id-only lookups.
- spec.js keeps `standIn` only when exactly true (never on a token); Battle passes it to getChess; syncUnitLoadout
carries it; UnitInfo `standInFor`.
- content/index.js kitOf: a stand-in's kit is KITS[def.charId] only; no default skill (a stand-in kit writes `skills`);
without a kit the generic kit plus genericTalents. kits/index.js STANDIN_KIT_FILES / STANDIN_KITS, merged after the
tier groups (old keys keep their order; golden unchanged).
- server/data.js and tools/setup.mjs expect backups.json.
- test/content/standin.test.js: the plumbing and the eight 预备干员 on all 28 chess records they replace.
A deliberate deviation from the official 技能策略 (community report: skills with a larger range waited for the normal
range). tools/build-data.mjs resolveTrigger: a MANUAL operator skill still on the basic strategy (no class row, no
TRIGGER_DEVIATIONS entry) whose running range (its rangeId grid, or the own range grown by
ability_range_forward_extend; never 被动效果:攻击范围扩大) strictly contains the operator's range gets rule ACTIVE_RANGE,
customRangeGrid = that range, rawRule the official DEFAULT; validateAll checks it. server/sim/skills.js: ACTIVE_RANGE
checks the DEFAULT condition (a targetable enemy or a blocked one; a heal skill an injured ally) on that range, grown by
the permanent rangeExtend, every tick. Data rebuilt offline: only skills[].trigger changed (33 skills, 66 chess records,
9 stand-in form records). 深巡 S2 (§21.29), 重装 TAKE_DAMAGE, the SEARCH / CUSTOM_RANGE / SP_FULL rows, 烛煌 S2 / S3
(not containing her 3-1) and 引星棘刺 S3 keep their rule. Docs: DESIGN §5.6, DATA, SIM, PLAYING, kits README, notes.
Tests: new test/sim/feedback5-active-range.test.js; data, playtest6-skills (银灰 S3), kits_alt_t2 (蒂比 S1), kits_t5
(百炼嘉维尔 S2) updated; feedback1-transform's fixtures move 莫斯提马 one tile (her S3 now casts on its 3-15).
Golden: 27 roster, 25 bond, 21 field and 14 match scenarios moved — every one fields an operator with one of the 33
skills, which now casts when an enemy enters its running range instead of the initial range.
The trait line is on every hookmaster and is not trusted. Placement is the loadout at place time: golden 歌蕾蒂娅 carrying uniequip_003_glady may use a ranged tile; everyone else stays on the ground.
The §22 intro maps GitHub #25 … #35 / #16 to §22.1–§22.13 and records the owner's decisions of 2026-10-04.
§22.7–§22.12 are the wave-2 subsections verbatim; §22.13 is PR #48 (要塞 cannot hit air units, 莫斯提马's slow shows its icon).
One blank line before every §22 subsection, as in §21.
余 S2 (SKILL_RANGE on its x-1, §22.10) is appended wherever the six-skill deviation is listed: DESIGN §5.6, §20.2, §21.20, §21.29,
DATA §2 item 6, research 00 and 03 (SKILL_RANGE counts: 13 / 26 official, 14 / 28 in the data). DATA §2.1 names 要塞 in canHitFly.