105 KiB
DESIGN §22 — 0.1.2 — GitHub issues after 0.1.1
Part of DESIGN.md (the index; section numbers are global).
22. GitHub issues after 0.1.1 (v0.1.2)
Public GitHub issues filed after the 0.1.1 release (2026-10-03), each triaged against 0.1.1 first; the real ones were fixed for 0.1.2 in fix branches WA–WL, each independently reviewed before it was merged into feedback2. Where each is handled: #25 back-facing operators keep their attack pose when knocked out → §22.1; #51 突袭's idle redeploy → §22.2; #33 items 1–2 阿戈尔's devour and 乌尔比安 S3 → §22.3; #33 item 6 活性源石, with the 沼泽 and 深水区 terrain → §22.4; #42 emotes and guide pages without a local client, and the English title (#38) → §22.5; #32 item 4 钩索师 / 推击手 on 高台 → §22.6; #52 史尔特尔's 余烬 → §22.7; #43 an enemy's 隐匿 after a block, and the 深池逐火 ember window → §22.8; #32 item 7 荒芜拉普兰德 S3's drones → §22.9; #32 item 1 余 S2 → §22.10; #44 归溟幽灵鲨's 替身 → §22.11; #32 item 6 enemy splash against a 隐匿 operator → §22.12; #35 / #16 (PR #48) 要塞 against air units and 莫斯提马's slow icon → §22.13; #58 / #60 / #64 follow in §22.14–§22.16. Decisions of the owner, 2026-10-04: 荒芜拉普兰德's drones measure the distance to an enemy's position, a huge enemy's centre (§22.9); 余 S2 casts with an enemy on its x-1 — a deliberate deviation from the official TAKE_DAMAGE row, added to §21.29's six (§22.10); 乌尔比安 keeps the devour gains through his 【移动】 — the deviation stays (§22.3).
22.1 Back-facing operators play their fall (GitHub #25) — render/units.js _wantsBack / _syncModel / die / revive / _acquireSpine / _dropActor / dieClipDur, render/spine.js SpineActor.die / dieClip / deployElapsed
- Seen (public issue #25, "部分干员被击倒后在部署状态时仍有攻击动作"; the screenshot: 跃跃 seen from behind under its redeploy ring, still in its attack pose, while 拉普兰德 beside it lies down; the reporter also saw it on 拉普兰德, 异德 (缄默德克萨斯), 斯卡蒂 and 德克萨斯, "并非稳定出现" — all five have a Back skeleton without a Die clip). Real in 0.1.0 and 0.1.1, drawing only (the sim had the operator down; it never attacked): an operator deployed facing UP is drawn with its Back model (§9, research 07 §5.5), and 131 of the mode's 135 Back skeletons (data/assets.json) have no Die clip — only 薄绿, 玛恩纳, 松果 and 预备干员-重装 have one, while every operator's Front skeleton has one. Not every time: only operators facing UP.
- Official: PRTS 卫戍协议/帮助 says a knocked-out operator stays on its tile as a "倒地干员" (§21.24); which model the official client lies it down with is in no source. The project's asset plan already had the rule — research 07 §5.4 "
die:Die(if the Back model lacks it, swap to the Front model for the death animation)", docs/ASSETS.md Rolesdie: null— but the renderer never implemented it. ASSUMED the official client does the same (a Back skeleton without a Die clip cannot show a fall), and the four Back skeletons with a Die clip fall with it. - Cause:
UnitView._wantsBack()ignored whether the unit stands;SpineActor.die()found no clip, returned 0 and changed nothing, so the attack / skill / idle loop playing at the knock-out went on under the ring for the whole respawn timer. - Now:
_wantsBack(): facing UP with a Back model, while the unit stands; a dead or knocked-out one keeps the Back model only when that skeleton has a Die clip (dieClipDurof its manifest entry), so the other 131 fall and lie with the Front model (unmirrored, as facing UP).die()andrevive()mark the view, and its nextupdate()— after every event and the snapshot of that frame (render/app.js) — runs_syncModel(): the model the state wants is loaded when it is neither shown nor loading, and swapped in place like a direction change (no avatar diamond in between). A death and a redeploy handed out in the same frame load nothing: a 突袭 jump (retreat + redeploy in one tick), a 不屈 revive (from the death hook), a backlog delivered after a hidden tab.- The Front model replays the state: the Die clip where it would be (
dieT), a knocked-down one holding its end;die(true)and a view built for a unit already down start on the held end (the 联防 entryFORCED_EXIT, a field entered late, the re-entry from the field meta after an overflowed hidden-tab backlog). Its first frame gets the knocked-down grey (_dropActordrops the cached tint; a swapped-in model used to keep the previous model's tint state). A view that gets nodownentry (summons and devices) fades like one facing RIGHT — a forced-out operator facing UP lies down like a knocked-out one (§22.15): the Front model's Die clip times the fade (_fallDur). - The redeploy (a
deployevent: timed, 不屈, 阿戈尔 — everyBattle._deploy— or a revive seen only in the snapshot) brings the Back model back; a deploy clip the Front model is playing goes on on the Back model where it is (SpineActor.deployElapsed). - Safety net: until the Front model is there, or when it fails to load, the Back model holds still —
SpineActor.die()on a skeleton without a Die clip stops whatever looped and holds the first frame of its idle clip (frozen when it has none either), and nothing restarts an attack on a dead actor. The 20 summon / enemy skeletons without a Die clip (e.g. 浊心斯卡蒂's 海嗣) now fade holding still instead of looping [ASSUMED look]. - Unchanged model choice: operators facing RIGHT / DOWN / LEFT, enemies and summons (no Back models in the manifest:
_wantsBackis false for them; only the safety net above changes how their idle-only skeletons die), the redeploy ring, the sim. Nothing changes in render/interp.js or battle/runner.js:die/deployare state events every client stage keeps (runnerkeepsState, game.jskeepEarly, interpisCosmeticEvent), and b.snapdown/fieldMetarebuild the view of an operator already down; both now reach the model swap.
- ASSUMED: the Front model for the fall and the knocked-down pose (above); the Back skeleton's own Die clip when it has one; the held first idle frame of a skeleton without a Die clip.
- Tests:
test/render/issue25-downback.test.js(fake PIXI with the real manifest and asset helpers: the manifest counts; the five operators of the report lie down with their Front model's Die; an UP knock-out mid-attack — the Back model holds still, then the Front model's Die is held under the ring, greyed, every acquire paired with a release; RIGHT unchanged; the redeploy back to the Back model with the deploy clip carried over; the snapshot's DIE anim and a snapshot revive; a death and a redeploy in one frame load nothing; a view built for an operator already down and the 联防 entry lie on the held end; a withdrawn one fades like a RIGHT one; 薄绿 keeps its Back Die clip; the SpineActor safety net; enemies and summons unchanged);downring,feedback1f-down-tile,unitview,forms,test/ui/unitFacing.test.jsunchanged.
22.2 突袭's idle redeploy lands where it can attack (#51) — content/bonds/addon/battle.js raidPoll / raidTile / raidReach / raidTargets, research 02 §3.18
- Seen (GitHub issue #51, on 0.1.0): a 突袭 member idle for 10 s was redeployed even when no landing tile let it attack an enemy, so it hopped every 10 s between useless tiles; and the idle trigger always went for the most advanced enemy, even when another ground enemy could have been reached. Real (reproduced on 0.1.1): with one speed-0 enemy that no landing tile reaches (it stands on plain floor, and a RIGHT-facing melee member reaches it only from that tile or the floor tile behind it), the member jumped at 10 / 20 / 30 / 40 s between (10,9) and (10,8) beside it and never attacked; a second, reachable enemy was ignored. On the five real runs of the F5 test (
test/sim/feedback1f-downed-tile.test.js) 26 of 60 jumps landed with no enemy in range — e.g. three members jumping to one enemy whose two reachable tiles the first two took. - Official: the bond text (activity_table bondInfoDict / effectInfoDataDict; PRTS 盟约记录, no note under it) "【突袭】干员10秒内未进行攻击或技能就绪时,若范围内没有敌人,则保留技力立即再部署至一名地面敌人周围" names no tile rule; whether an official jump can land out of range is not known (the reporter: the text "按字面允许落在不能立即攻击的位置").
- Cause: the covers-the-target requirement of
raidPollbound only the 技能就绪 trigger (if (!idleOk && !tile[2]) continue;), andraidTilescored covering tiles first but still returned a non-covering one when none covered — so the idle trigger jumped to its first candidate whatever the tile. - Now (the owner's decision — the reporter's proposal; stricter than the 「优先」 of our first reply on #51: a covering tile or no jump, never a fallback landing): both triggers jump only to a landing tile from which the member's attack range (its own grid + 攻击距离 rangeExtend, turned by its direction; a huge enemy counts on any tile it occupies) covers the chosen ground enemy. The poll walks the candidates in the existing order — its own field's ground enemies (another field's only while its own has none), the most advanced (least remaining path) first, at most the 8 most advanced — and takes the first one that has such a tile (the nearest within 2 tiles, the facing-RIGHT tie-break as before). When none has one the member stays where it is: its idle time keeps counting and the 0.25 s poll looks again, so it jumps as soon as an enemy can be reached. After a jump the idle time restarts from the landing (then from each attack). Unchanged: never onto a knocked-out operator's tile (§21.24), never into the 深水区 (§21.3,
grid.canStand), SP kept, a real redeployment (部署时 effects), the ATK / HP bonus. - Cost: a search that finds nothing now repeats every 0.25 s, so it is cheap:
raidTilelooks only at the tiles a range offset leads back to from the enemy's body (before: a 25-tile scan building a range per tile), and a player's members share one sorted candidate list per poll (rebuilt after a jump). On the heavy 2-player boss field oftest/sim/robustness.test.js(≈ 130 enemies, 72 on the ground) with 18 members that never reach anything — every poll searches — the bond costs +5.8 µs per tick, as much as 0.1.1's +5.5 µs with its jumps every 10 s (the requirement alone on the old scan: +29.6 µs). Whenever a covering tile existed the landing tile is the old one (200 000 random cases: grids, 攻击距离, directions, huge bodies). - Players notice: a member with nothing it can reach waits on its tile instead of hopping; it passes over an unreachable leading enemy for one it can hit; when several members go for one enemy and its reachable tiles are taken, the rest wait. The five F5 runs: 35 jumps instead of 60, none out of range.
- ASSUMED: "never a useless landing" (the text says only 再部署至一名地面敌人周围; issue #51); the cap of 8 candidates (a member waits when the 8 most advanced ground enemies are all out of reach — the 技能就绪 path's bound since 0.1.0, kept for the cost); the target order and the 2-tile search (research 02 §3.18, now written as above). Issue #49 (a passive skill never counts as 技能就绪) is unchanged and separate: it decides when a member may jump, this section where it lands.
- Tests:
test/content/bonds_addon.test.js"突袭 #51" ×2 (no reachable landing: neither trigger jumps in 45 s, then a reachable enemy → both jump within one poll; an unreachable leading enemy is passed over for the second, which it attacks, and the next jump comes the idle time after its last attack); the other 突袭 tests (bonds_addon,feedback1-water,feedback1f-downed-tile,facing-invariance) unchanged.
22.3 阿戈尔's devour skips members it has already knocked out (#33 item 1) — content/bonds/core.js devour, content/items/battle.js deploymentOf (exported through content/items.js), kits/tier5.js (乌尔比安 S3, #33 item 2)
- Seen (GitHub #33 item 1, "阿戈尔阵营复活机制看起来不会触发"): the 5-tier revive works on its own (5 distinct 阿戈尔, one lethal hit each: the first 3 come back on their tile at full HP, normal and 联防 battles alike), but the usual food chain used it up at t = 0. One row, all facing right, 浊心斯卡蒂 → 乌尔比安 → 幽灵鲨 → 海霓 → 深巡 → 隐现: the battle-start devour knocked out 幽灵鲨, 海霓 and 深巡, the revive brought each straight back (all 3 charges), and 乌尔比安's marks still pending on them knocked them out a second time — down until their 70 / 80 s redeploy, no revive left for the fight (联防 battles alike).
- Official (PRTS 卫戍协议:盟约 下半/PRTS盟约记录, 阿戈尔 备注): "标记按付与顺序触发【吞噬】效果,目标首次被击倒后解除自身被付与但还未触发的【吞噬】效果"; the 5-tier "前3名【阿戈尔】干员首次被击倒时立刻复活" (bonds.json
max_free_respawn_cnt3), implemented as "受益者因移动之外的原因退场时下次部署的再部署时间和费用归零". So a devour knock-out is a first knock-out and spends a revive, and the revived member takes no further mark. - Cause:
devour()skipped a mark only while its target or marker was dead (!t.alive || !m.alive); a member revived in the same pass was alive again, so its pending marks fired. 埃芒加德's revives went the same way (it revives in place: the next pending mark knocked the revived unit out again and spent a second charge), and so did 不屈's 立刻重新部署 (the redeployed unit was knocked out again in the same instant, where 不屈 does not fire twice, and stayed down). - Now: when the marks are placed, the pass records each target's deployment (
items/battle.js deploymentOf, the deploy counter plus the in-place revives — exported for this). A mark is skipped once its target is off the field or in another deployment, i.e. was knocked out during the pass — whether the 5-tier revive or 不屈's 立刻重新部署 redeployed it or 埃芒加德 / M3茧甲 revived it in place. The marker side is unchanged since 0.1.0 (PRTS describes only the target): a marker off the field gives no further mark (withdrawn in 0.1.4: a marker knocked out during the pass still resolves its marks, §24.7), one knocked out and back in the same pass still gives its marks. The chain above: 浊心斯卡蒂's five marks resolve, the three members fall once each, every later mark on them is cancelled. (0.1.2 revived all three, spending the 3 revives at t = 0; since 0.1.3 they belong to the first 3 members by position, not to the first knocked out — here 幽灵鲨 comes back and 浊心斯卡蒂 / 乌尔比安 keep theirs for the fight, §23.20.) When nothing brings a devoured unit back during the pass (no 5-tier revive, 不屈 redeploy, 埃芒加德 or M3茧甲), the devour resolves exactly as before. - 联防 (#33 item 3, not changed): the members knocked out twice at t = 0 were still down at the end of the 45–60 s own combat (70 s redeploy), so they entered every 联防 knocked out (forced out, §19.3) and the chain there formed without them. After this fix that is far rarer: with the 5-tier, the members the devour knocks out (up to 3) stand again and enter 联防 down only when the fight knocks them out once more (with 3–4 members no 阿戈尔 revive brings them back, as before). The order of the 联防 forced exit and the battle-start devour is forced exit first (§23.2).
- 乌尔比安 S3 (#33 item 2) — seen: "乌尔比安3技能结束返回会清空阿戈尔阵营增益,转职突袭后疑似不会返回(未转职时三技能会在原地留下一个图标,转职后无图标)". Official (PRTS 乌尔比安 S3 备注; the chess skill is
skchr_ulpia_3, skill range 6-1 = his own tile and the 6 ahead): the anchor's target is "自身所在地块(仅阻挡敌人时)>技能范围内距离自身最近的包含敌人的地块>技能范围内距离最远的地块"; the 【移动】 goes to the landing tile > the tile beyond it > his own tile, and "若目标地块不为当前地块,会在原地部署一个从不混淆的方向"; "【移动】后仅继承下列效果:技能进度、第二天赋叠加层数". Cause: the anchor search started one tile ahead, so while he blocked — typical right after a 突袭 landing next to the enemies — he threw ahead, walked off the enemy he blocked (the blast could miss it — with an enemy one tile ahead its 1.8 radius still reaches the one he blocks) and left the marker. Now: while he blocks an enemy the anchor lands on his own tile: the blast (radius 1.8) hits what he blocks, he stays put, no marker, nothing to return from. His own tile is a candidate only while he blocks ("仅阻挡敌人时"): without a blocked enemy the search starts one tile ahead, exactly as in 0.1.1, so a flyer over his tile does not keep him there. Considered and not taken: reading the own tile of the 6-1 range as distance 0 for the second rule. Measured with the triage's batch (8 real stages × 5 round templates × 2 seeds, 突袭 through 变形同构体 + 突袭手雷), before the 突袭 landing change (#51, §22.2) is merged: with 突袭, 56 of 84 casts used to move him off his landing tile, now none does — 82 of the 84 came while he blocked, the other 2 (act2 m03, tile (10,8)) have no standable tile ahead (pre-existing); without 突袭, 64 of 80 casts still move him, as before. - 乌尔比安 S3 — not reproduced (the triage, real data): no 阿戈尔 gain is cleared on the return. The devour's base ATK / block (
bond:egir:devour, e.g. +399 ATK, +1 block), the 阿戈尔 HP and the 血脉的哺养 stacks are there before, during and after the S3 (Battle.relocatekeeps every buff); what ends is the S3's own +40–60 % HP / +200–230 % ATK. With 突袭 he does return and the marker is drawn, both on the landing tile next to the enemies, not on his board tile (8 stages × 5 round templates × 2 seeds); with this fix a cast while he blocks there leaves no marker at all, as officially. Deviation, kept: officially the 【移动】 keeps only the skill progress and the second talent's stacks, so the official game drops the devour gains on the move; the remake keeps them through the 【移动】 and the return. - ASSUMED: (1) a marker revived during the pass keeps the base ATK / block it gained at marking (a persistent buff, as through every knock-out since 0.1.0). (2) With the anchor on his own tile, 乌尔比安's 【移动】 does not try "the tile one beyond the landing" (he would otherwise step one tile forward).
- Tests:
test/content/bonds_core.test.js(the chain in a normal and a 联防 battle: one knock-out and one revive each, all five standing, the next knock-out final; a marker revived by the 5-tier revive still gives its marks; a target 埃芒加德 revived in place takes none — one revive spent, not two; a target 不屈 redeployed takes none and stays standing),test/content/kits_t5.test.js(乌尔比安 S3 while he blocks: the anchor on his own tile, no move, no marker; a flyer over his tile, not blocked: the anchor ahead and the move, as in 0.1.1).
22.4 活性源石 stays on an enemy after it leaves the tile (#33 item 6) — server/sim/content/devices.js (touchInfection, enterTerrain, terrainFor, tickMire, deepWaterDamage), content/enemies.js (rebirthCleanse, kitDeepsea), tools/build-data.mjs (special.mire)
- Seen (public issue #33, other part 3: 「红地板图敌方单位经过后,红地板附带的效果会消失」): on 战场#04 an enemy lost the red floor's effect the moment it walked off it. Measured on v0.1.1 (the real stage): an enemy on (10,6) from 15.67 s to 19.67 s lost 4 × 70 HP; 12 s later it still had 99720 / 100000 HP and no ATK / ASPD bonus.
- Official: the red floor is 活性源石
tile_infection, the only one in the season's data — 战场#04 (act1autochess_m04) at (10,6), (11,6), (10,14), (11,14) and on the boss rows at (3,6), (4,6), (3,14), (4,14); level blackboarddamage70,atk0.2,attack_speed20,duration300. PRTS 特殊地形 tile template (特殊地形/trapper/tilesformat.json): "部署于其上的我军和经过的敌军在{duration}s内每秒受到{damage}真实伤害,攻击力提升{atk×100}%,攻击速度增加{attack_speed}" (main-story stage pages: "在五分钟内"). The crisis-contract tagglobal_tile_infection_1「目标:可控感染」 (PRTS 切尔诺伯格 6区废墟/历史合约): "踏过活性源石地块的敌人不再持续损失生命值" — without it an enemy that has walked over the tile goes on losing HP. Same-name effects do not stack (PRTS 作战机制: "同名buff的默认叠加策略buff只能表现出一个"). Its damage is 环境伤害 (PRTS 自然环境 lists 活性源石). A 重生 clears every buff but a whitelist (PRTS 特殊机制 §重生 "清空自身身上除白名单外所有Buff"; its 非首次标记 row: "如无特殊说明,也默认同常规Buff一样可被重生清除"). - Cause:
devices.jsreaddurationas a lifetime of the tiles (terrainForswitched them off after 300 s of battle time) and tied the effect to standing on the tile (enterTerrainremoved it as soon as the unit's tile changed). - Now: every tick a unit is on the tile (allies by their tile, ground enemies by their rounded position; flyers, hovering / levitated and hidden units never touch it, as before),
touchInfectiongives it the timed effectterrain:infectionfordurations — 70 true damage per second, ATK +20 %, ASPD +20 — or, when it already carries it, gives it back its full time. Leaving the tile no longer removes it: an enemy that crossed it drains 70/s with ATK +20 % / ASPD +20 for 300 s from its last contact — longer than any own-field or 联防 battle lasts (≤ 230 s of battle time). All 12 ground legs of 战场#04 (own field, 联防 field, boss field) cross one of these tiles, so every ground enemy there carries it once past the tile. One effect per unit: a second pass or the next tile of a pair restarts its time and never adds a second effect (ATK +20 % once, 70/s, no extra tick). The tiles never switch off — the old lifetime only ever ran out on the boss / hidden fields (no time limit), at 300 s. An operator deployed on it is always in contact, so it drains until it is knocked out or withdrawn, past 300 s too (this follows from the ASSUMED refresh rule; v0.1.1 drained it the same way within the first 300 s). An operator moved off the tile (Battle.relocate: 乌尔比安's S3 move and return, 夕's 小自在) keeps the effect for its time — 乌尔比安 landing on (10,6) carries it home for 300 s (measured by the review); leaving the field (knock-out, withdrawal, 突袭's jump) drops it with every other buff. A 重生 (rebirthCleanse: 逐火 embers, 假想敌:再生's 傀儡, 锏, 守墓石像 …) clears it like any buff, and contact gives it again while the enemy stands on the tile. Kept through the 重生 (the first version of this fix), its 1 s ticks — each a hit of a husk's 特殊生命值 — killed a 深池逐火战士 ember that had crossed (11,6) at ~6 s instead of letting it stand up at 1 + 10 s (护卫's 火灰 at ~11 s, 傀儡 at ~16 s; found by the review). The damage itself is unchanged: true damage no unit deals (无来源), taggedterrain(环境伤害), a damage instance, not a 流失. - ASSUMED: the time counts from the last contact (it is refreshed every tick on the tile) and the per-second rhythm runs on through a refresh — hence an operator on the tile drains past 300 s; contact gives the effect again to an enemy reborn on the tile; not 持续伤害 (no
dottag: PRTS 伤害分类's list of BUFF damage names 深水区, 源石污染区 and 活性源石风暴 but not 活性源石, so 锡人's 凋敝魂灵 does not raise it). - Checked: no other terrain reads a
duration— 沼泽 (layers, cleared on leaving), 深水区 (sea_drown[enemy]has none), 烟雾 and the blowers; 沼泽's interval and 深水区's damage kind were wrong, though (below). Nothing relied on the old lifetime: the client draws the tile glow from the stage map (it never went out), terrain effects have no status icon, the enemy abilities that care about the tile (萨卡兹枯朽前锋's arts attacks, “灵幛”, 孽罪奇美拉) read the tile they stand on (onTerrain), and 联防 carries no effects (the HP ratio and the SP only, §23.1). 纠缠藤蔓 (enemy_2052_smgia, a 悬赏 enemy: "受到来自自然环境的伤害时,自身在短时间内获得高额脆弱"; content/enemies.js reads theterraintag) that has crossed the tile stays ×2 fragile for 300 s and the tile's tick hits it for 140/s — official, PRTS 自然环境 listing 活性源石 as 环境伤害. - Other terrain found while fixing #33 item 6 (the review):
- 沼泽, official (PRTS 沼泽控制 备注; skill_table
sktok_mire): a unit in the mire "每秒将会触发一次" its 【陷入沼泽】 (the skill charges inmaxChargeTime1 s): an enemy gains 1 layer of ASPD −5 % and move speed −5 %, "若其重量大于等于3,改为获得2层"; any other unit 1 layer of ASPD −5 % only; at most 10 layers (max_stack_cnt); "上述减益于单位不再位于沼泽之中时解除". The skill'svalue3 is that 重量. Cause: build-data and the devices.js fallback readvalueas the interval (3 s): 10 layers after 27 s for light and heavy enemies alike, and operators got the move part too. Now:special.mirehasintervalSec1 (the skill's charge time) andheavyWeight3 (value);tickMiretriggers on entering and then every second — 1 layer, 2 for an enemy of 重量 ≥ 3 (s.massLevelat the trigger), operators the ASPD part only, at most 10, cleared on leaving: 10 layers after 9 s (重量 ≥ 3: 4 s). ASSUMED the first trigger comes on entering (as before). A layer stays −5 ASPD (the remake's reading of the fraction,aspdOf). - 深水区, official (PRTS 涨潮控制 技能3 深水: "【水蚀】的敌方单位每秒受到40点无来源真实持续伤害(不属于环境伤害,不会触发受击回复)"; PRTS 自然环境: 涨潮's damage to enemies "并非环境伤害"; PRTS 伤害分类 lists 深水区/涨潮水蚀 as BUFF damage). Cause: the deep-water tick shared the
terraintag with 活性源石, so 纠缠藤蔓 turned ×2 fragile in deep water (360 HP lost in 5 s instead of 200) and 锡人's 凋敝魂灵 ("受到的持续伤害提高") never raised it. Now:deepWaterDamage— 无来源 true,canDodge: false,sourceless,noSp, tagsdot/periodic/deepsea, noterrain. The 免疫水蚀 swimmers (潜水员) cancel onlydeepsea/drowndamage, not 环境伤害.
- 沼泽, official (PRTS 沼泽控制 备注; skill_table
- Tests:
test/content/tokens_devices.test.js— 活性源石 (the operator and the enemy standing on it), an enemy keeps the effect after leaving the tile (20 s later: 70/s, ATK, ASPD, the time left), one effect per unit (two passes give one effect that endsdurations after the last contact, 70/s while it lasted; an operator drains for 3 ×duration, keeps the effect afterBattle.relocateand loses itdurations later), a 重生 clears it (a 深池逐火战士 that crossed the tile stands up from its ember after 1 + 10 s), 沼泽 (the data; operators ASPD only; 重量 1 vs 3: 1 vs 2 layers a second, at most 10; cleared on leaving), 深水 (the tick's tags; 纠缠藤蔓 not fragile);test/content/enemies_bosses.test.js(潜水员: the deep-water tick is cancelled, 环境伤害 is not).
22.5 Emotes and guide pages without a local client; deployment docs (#42); the English title (#38) — tools/assets/plan.mjs UI_EXTRAS / GUIDE_PAGES, data/assets.json ui['emoticon/<dir>/<picId>'] / ui['guide/<key>'], data.js artUrls / nextArtUrl, ui/emotes.js emoteArtUrls / EmoteArt, ui/guide.js guidePages / GuideHost, tools/setup.mjs LOCAL_ART_FALLBACK / LOCAL_ART_COPY_HINT, tools/doctor.mjs, README, docs/DEPLOY.md §6, server/index.js banner, package.json name
- #42 seen: "游玩时左下角的六页表情全部显示为默认图标" — a Linux server deployed from source (git clone, setup with the asset download, Node 22); copying
public/assets/local/anddata/local-assets.jsonfrom the release bundle fixed it. Real: both are extracted from a locally installed client (§13; setup extracts only when it finds one), and the emote art and the 玩法说明 pages came from there only, so a source deploy without the client drew the neutral glyph for all 36 emotes and the official tips text instead of the 19 pages. README, DEPLOY.md, setup and doctor said the local art did not matter ("不影响游戏", "其他功能不受影响") — wrong: the 3D board, part of the official HUD art and two enemy models fall back too. - Source: the mirror the pipeline already uses (ArknightsAssets2
cn, assets/dyn) has all 55 files — the six theme bundles'ui/emoticon/theme/[uc]<themeId>/icon/<picId>.png(the 36 battle sprites, keyed by display_meta_table picId) andarts/guidebookpages/[pack]autochess/autochess_{home 1–9, shop 1–6, handbook 1–4}.png. Checked 2026-10-03: every URL answers 200 on raw.githubusercontent.com and on the jsDelivr fallback; the same pictures as the local extraction, each at the size of its local copy (all 55 compared; the pages 1024² squashed — still shown at 16:9), pixel differences only from the platforms' texture compression. - Now: the asset plan downloads them like any other UI sprite (UI_EXTRAS, built from shared/constants.js EMOTE_CATALOG and GUIDE_PAGES — a test keeps GUIDE_PAGES equal to guide.js GUIDE_CHAPTERS), keyed by the data/local-assets.json group and name:
ui['emoticon/<dir>/<picId>']→/assets/ui/emoticon/<dir>/<picId>.png,ui['guide/<key>']→/assets/ui/guide/<key>.png(55 files, 21.3 MiB; the download ≈ 270 MB, was ≈ 250 MB). The committed data/assets.json lists them (regenerated by a real fetch-assets run: only these 55 entries,hashandstatschanged), sonpm run setup— which the start scripts run on every start — on an install made before them finds 55 files missing and runs fetch-assets; §21.25's conventions hold: ledger, retries, the jsDelivr fallback, and a run where they fail keeps the committed manifest (shrink guard), so the next setup tries again. The client looks both manifests up by the same names (data.js artUrls): the local-client picture first, then the mirror copy;EmoteArtand the guide viewer move to the next URL when an image fails to load (nextArtUrl: a copied data/local-assets.json without its files falls through to the mirror copy); when none is left they keep 0.1.1's fallback — the glyph, and for a 玩法说明 page the official tips text (guideStage), also when data/assets.json lists the downloaded pages but the files are not on disk (agit pulland restart without setup, an older asset folder, a failed download the shrink guard kept); the chapter tabs, dots and keys still reach the other pages.localAssetstays local-only. - Docs now: README, docs/DEPLOY.md (new §6 本地客户端素材, the 排错 rows), docs/PLAYING.md, docs/ASSETS.md and the setup / doctor messages say what still needs the client — the official 3D board (→ the 2D board with procedural tiles), part of the official HUD art (the emote button and panel frames, the pause plate, the equip-replace dialog, the loadout screen, the team status and miss tags, module type icons … → CSS look-alikes, icons or text) and 灼热 / 炽焰源石虫's models (→ the tinted web alias, §21.14) — and README, DEPLOY §6 and doctor that a server without the client can copy
public/assets/local/anddata/local-assets.jsonfrom the release bundle of the same version (both are read per request: no restart); setup's row, which the launcher prints on every start, only points to DEPLOY §6. The Docker parts are unchanged (deferred by the owner). - Not done here: the mirror also has most sprites of the three autochess UI bundles that the local
ui/battle/ui/common/ui/outergroups hold (by name 377 / 389, 137 / 160 and 659 / 659 underui/autochess/[uc]autochess{battle,common,outer}/**; the emote panel frames,matte_pauseandbg_miss_enemychecked at the local sizes), so the HUD row of DEPLOY §6 could shrink in a follow-up. - #38, the English title — official: EN client data, activity_table
basicInfo.act2autochess.name= "Stronghold Protocol: Alliance" (CN 卫戍协议:盟约; ArknightsAssets/ArknightsGamedataen/gamedata/excel/activity_table.json, 2026-09-29 — Kengxxiao/ArknightsGameData_YoStar stops at EN client 31.5.80, 2025-11-13, before this season, with act1vautochess "Stronghold Protocol" only). Now: the README title and English summary, the server start-up banner and the package name (stronghold-protocol-alliance, package.json and the lockfile) say Alliance instead of "Covenant"; the Chinese title 卫戍协议:盟约 and the repository name stay. "Covenant" remains only in 新约能天使's official appellation ("Exusiai the New Covenant") and its buff keyangel2:covenant. Only the title is settled here: #38 itself (a full English UI) stays open. - PR #27 follow-up: tools/doctor.mjs's comment now says directly that tun0 / tap0 are matched first by VPN_IF (substring
tun\d/tap) and classed as vpn — it spoke of no longer listing^tun\d, which master's VIRTUAL_IF never had; comment only. - Tests:
test/assets.test.js(the 55 plan entries and URLs, a rebuilt manifest, the committed manifest lists them),test/ui/localArt.test.js(the lookup order, a server without the client,guideStage, GUIDE_PAGES = GUIDE_CHAPTERS),test/fetch-assets-shrink.test.js(a failed mirror run keeps the manifest),test/static-local-art.test.js(the docs and messages),test/version.test.js(the English title),test/doctor.test.js.
22.6 钩索师 and 推击手 on 高台 (#32 item 4) — tools/build-data.mjs PLACE_ON_RANGED_RE / branchTraitText / validateAll, chess.json placement, match/board.js positionClass / basePositionClass, ui/gameLogic.js piecePosition, match/bot.js isBlocker / planLayoutSteps, tools/balance.mjs
Withdrawn in 0.1.3 (§23.35): only elite 歌蕾蒂娅 carrying HOK-Y may use a 高台. The paragraphs below are what 0.1.2 did.
- Seen: public issue #32 item 4 — "哥蕾蒂娅无法部署到高台". 0.1.1 refused every 高台 tile for her (server
BAD_TILE; the client lit no 高台 and said 近战单位只能部署在地面), and the same for 崖心 (钩索师) and 见行者 (推击手), normal and elite. - Official: the branch trait of both 钩索师 chess (歌蕾蒂娅
chess_char_4_12, 崖心chess_char_2_03) reads 「技能可以使敌人产生位移\n可以放置于远程位」, the 推击手 见行者's (chess_char_3_07) 「同时攻击阻挡的所有敌人\n可以放置于远程位」 — character_tabledescription(theirtraitis null: the branch's own text), position MELEE; act2autochesscharChessDataDictsets only status, bonds and garrisons for them. PRTS 新人入门 (branch table): 钩索师 and 推击手 "可部署在高台和地面". PRTS 卫戍协议/帮助 §战斗部署: "所有行动内远程干员可部署在近战位", and 部署效果 do not apply in this placement — "携带Y模组的教官仍无法部署至高台位". That 教官 ability is a module talent ("可以额外部署在远程位", battle_equip_table uniequip_003_pallas / uniequip_002_doberm / uniequip_002_rdoc, blackboardbuildable_type2), a deploy effect; the 钩索师 / 推击手 line is the branch trait itself — no talent, no blackboard, and every module of the three keeps it (HOK-X, HOK-Y, PUS-X). No Y-module 教官 is in the pool (诗怀雅 has INS-X). No source states the 钩索师 case for 卫戍协议 in so many words: the remake reads the branch trait as still applying ASSUMED; the old "melee never on ranged-only tiles" was itself an assumption (research 03 C2). - Cause: build-data copied position MELEE and nothing read 可以放置于远程位;
board.js positionClassmapped MELEE to 'melee' (melee tiles only), soPlayerState._legalrefused the 高台 (g.move→BAD_TILE), and the client mirror (gameLogic.js piecePosition/tileAllows) agreed. - Now: build-data sets
placement: 'all'on a chess whose branch trait without module (branchTraitText: the trait candidate's text, else the description) contains 可以放置于远程位 — exactly 崖心, 见行者 and 歌蕾蒂娅, normal and elite (6 records, the only data change;validateAllallows it on a MELEE record only; module-only text never counts).positionClassreturns 'all' for them, so the server takes them on every deployable tile —g.move, swaps, a merge's elite (mergeTile), the terrain re-check (_evictIllegal), invariants and audit —, and the client'spiecePosition'ALL' lights the 高台 while dragging; every other melee unit keeps the refusal and its text. The battle keeps position MELEE: on a ground tile they block as before; on a 高台 (unit.groundfalse) they block nothing and attack from there — the sim needed no change (act2 m01, one 萨卡兹枯朽前锋 on the lower lane, 40 s: 歌蕾蒂娅 on (10,4) facing it 2 hits, no block, on (9,5) 12 hits, blocking; 崖心 and 见行者 alike; no errors). - Bots read the record's own position (
basePositionClass): they still count these units as blockers (isBlocker; with the widened class they would not, and on act2 m01 the planner put 歌蕾蒂娅 and 崖心 on the 高台 as non-blockers, leaving 见行者 the only blocker on the road) and plan them on the ground tiles, as in 0.1.1 ASSUMED: offered the 高台, the planner still chose road tiles in the cases measured, but the lineup's blocker count assumes they block and a greedy 高台 pick could take a ranged dealer's tile — not benchmarked.tools/balance.mjsuses the same blocker test. Tokens are unchanged; the sim's automatic placements (the 突袭 landing tile, summon / device tiles) still treat them as melee, ground tiles only ASSUMED. - Players notice: 歌蕾蒂娅 can be dragged onto a 高台 (PLAYING §4); there she attacks but blocks nothing. 崖心 and 见行者 get the same rule but are hidden chess this season (
isHidden, never in the pool, the shop or a 突变细胞 roll), so only 歌蕾蒂娅 is reachable in play; 见行者's elite PUS-X refund (half the DP of a redeploy onto a ranged tile, kits/tier3.js!unit.ground) is therefore moot. - On a 高台 the effects that read the tile (
unit.ground: the 只攻击地面单位 enemy, 污染秽蚀's 高地 rate) treat her like a ranged operator up there; position-based effects keep her 近战 (精准 tier 2, 诗怀雅's talent) — since 0.1.3 the effects worded 地面干员 too (不屈, the 战栗维式重锤 proc, 休谟斯 回收利用: 地面干员 = the melee position on any tile, see §23.28; the tile reading 0.1.2 ASSUMED here was wrong). - Normative lines rewritten with it: §3 (placement), §6.6 (the bot); research 03 C2; DATA §2 (
placement). - Tests:
test/match/feedback1-placement.test.js(#32-4: the data rule, cross-checked against character_table when the cache exists;g.moveonto (10,4) / (11,4) / (12,4) of 战场#01(下半) for the six records; a 重装 refused with its text; ranged unchanged; client highlights = the server's tiles; swaps; the merge's elite on the 高台; the battle input and the sim on / off the 高台; bots),test/match/board.test.js(positionClass/basePositionClass),test/ui/gameLogic.test.js(the client mirror).
22.7 史尔特尔's 余烬 holds 禁疗 (GitHub #52) — kits/tier5.js (史尔特尔 余烬 / S3), sim/damage.js heal, sim/buffs.js (healFree), render/style.js STATUS_ICON, render/textures.js STATUS_KEYS / drawStatusIcon
- Seen (GitHub #52 「史尔特尔进入锁血后仍能被治疗」): during the 8 s of 余烬 medics kept healing her. v0.1.1 measured: a 赫默 beside her healed her twice for 738 HP; with a 50 % HP 深巡 also in range, 赫默 spent 1,107 of its 1,845 HP of healing on her — on a unit that leaves anyway.
- Official (checked on PRTS 2026-10-03): chess talent 余烬 (
chess_char_5_07_a/_b) 「受到致命伤害时持续使生命值不低于1,8秒后强制退出战场」 (bbsurtr_t_2[withdraw].interval8); PRTS 史尔特尔 天赋备注 "持有不死的情况下不会触发此天赋" / "触发本天赋后,获得禁疗与不死" / "强制退出战场视为撤回干员"; PRTS 异常效果 禁疗 (HEAL_FREE) "无法成为治疗类能力的目标,且受到的治疗量变为0 ※…增减生命回复速度或生命回复速度(百分比)属性的效果不会被识别为治疗类能力"; PRTS 史尔特尔 技能3 备注 "生效后立刻治疗自身当前最大生命值100%的生命值(无视禁疗)". - Cause: the talent only prevented the blow and scheduled the retreat. Nothing marked her 禁疗, so every heal pick (
Battle.injuredAlliesInKeysand the kits' own) chose her as the lowest HP ratio andheal()let the heal through. The sim'snoHeal(the 禁疗 summons, 孤立, 武者 / 收割者 / 不屈者, 艾丝黛尔 S1, 烛煌's 倒地) stops only heals from other units (!selfindamage.js heal), the "不能被其他…治疗" of those traits. - Now: the trigger adds
surtr:ember= flagsnoHeal+healFreewith the status'healFree'(a green cross struck through in the unit's status row) until she leaves; the retreat drops it.healFreeindamage.js heal: no heal reaches the unit, its own included (休眠子裔's heal on attack, …), except an HP-regen attribute tick (regen: the engine's natural regeneration fromhpRegen/hpRegenRatio, e.g. 安洁莉娜's 兼职工作) and a heal flaggedignoreHealFree(her S3's start heal, "无视禁疗": S3 starting inside the window heals her to full). Every heal pick already skipsnoHealunits, so medics heal the next injured ally. 不死 (a later lethal blow inside the window changes nothing and does not restart the 8 s) and the retreat at 8 s are unchanged. OthernoHealusers are unchanged: the summons' 禁疗 (tokens.jsonabnormalhealFree) keepsnoHealonly — no self-heal reaches them but 伺夜 S2's 恢复 of its 禁疗 狼群, which the skill text grants. - Measured (the same layout): 0 heals reach her (v0.1.1: 2 heals, 738 HP); 赫默 heals the 50 % 深巡 instead (1,107 HP in 12 s); she leaves 8.03 s after the blow.
- ASSUMED: on the same first lethal blow 余烬 (
fatal−60) runs before 坚固维式重锤's lock (PRIO_REVIVE−100), so the hammer never starts while 余烬 is unused (PRTS gives no order); the status lasts until the retreat rather than a fixed 8 s. Known deviations, outcome-neutral (she is 不死 and leaves at 8 s anyway): 余's 闲云隐市 and 天马之盔's 8 %/s usehp_recovery_per_sec_by_max_hp_ratio, the 生命回复速度(百分比) attribute that PRTS 禁疗 exempts, but they run as kit heals (battle.heal) here, so 禁疗 stops them on her (moving them to the regen attribute would also change the bleed removal and heal-bonus hooks of other units); the forced exit's 「返还部分部署费用」 DP refund is not modelled (no retreat in the sim refunds DP); a chain healer's bounces skip her (ai.jsdoHealchain filter, review of #52). - Tests:
test/sim/feedback2-surtr-stealth.test.js(the medic beside her, her own heals / regen / S3's start heal, the status events),kits_t5,items,playtest6-combat(the 禁疗 summons),render/tiles/boardart/fxproj(the status atlas).
22.8 An enemy's 隐匿 returns 3 s after a block; the 深池逐火 embers (#43 follow-up, players' "the stealth monster revives forever") — sim/Battle.js _stealthSwitch / _unblock / releaseBlocked / foesInRadius, sim/ai.js enforceBlockCapacity, sim/targeting.js enemyStealthed / stealthOffKey / canTargetEnemy, sim/snapshot.js flagsOf, sim/constants.js STEALTH_RESTORE, sim/buffs.js (stealthOff), content/enemies.js (stealth, STEALTH_RESTORE_BY_KEY, husk, kitInvisShield, kitShadowKiller, kitJazz, kitBoneSpike)
- Seen: §21.4 left "隐匿 returns at once after a block (PRTS: 3 s)" ASSUMED; v0.1.1 hid an enemy again one tick after its block ended (a 潜伏者 whose blocker falls: untargetable 0.03 s later). Players reported (via the owner) that the 深池逐火 "stealth monster revives forever and can't be killed": a warrior knocked out while blocked releases its block through the 重生's 无法阻挡, its blocker takes the next warrior at once, and when the 重生 ends the ember is not blocked again — 隐匿, out of reach of ranged operators and operator splash — walks on and stands up with full HP 10 s later.
- Official (checked on PRTS 2026-10-03): 作战机制 §隐匿 "对于绝大部分可隐匿的敌人而言,在被我方单位阻挡后会解除隐匿,不被阻挡的3秒后重新进入隐匿。这是敌方单位的隐匿所拥有的特殊机制,我方干员并不会因为阻挡而解除隐匿" and "少部分的隐匿表现上与我方干员相同…可能不具有这样的特殊机制"; §隐匿与Buff的关系 "在被阻挡时开关会被关掉从而失去隐匿,阻挡状态解除后3s开关重新被开启而恢复隐匿"; 异常效果 隐匿 "与'阻挡时解除'没有直接关系,该机制通常由其他效果实现". The enemy pages' 天赋 "隐匿(解除阻挡N秒后恢复)" (PRTS text search): 0 s on 21 enemies — here 业余竞演者, 节日爵士乐手, 假想敌:骨刺, 流泪小子 (both), 访问团强攻冠军, and the veil of 清明 / 堂皇 ("获得隐匿(解除阻挡0秒后恢复)"); 1 s on 家族灭迹人 / 家族暗影灭迹人; no page names another time. Every other stealthy enemy here has a plain 隐匿: 潜伏者, 隐形弩手 (+组长), 隐形术师, 山海众头目 / 秘使, 重弩突袭者, 架桥船工 / 扶桥老手, 深池伙友影刃精英, 潜水员 (in water), 锏's second form and the 深池逐火 embers (PRTS 深池逐火战士 / 精锐战士 / 护卫 "获得隐匿、缴械"). 山海众头目 / 秘使 天赋 "该隐匿每次生效后自身获得强击标记(不可叠加)", 技能 破隐一击 "仅持有强击标记且被阻挡时可触发…技能开始时消耗强击标记".
- Cause:
targeting.js canTargetEnemy,snapshot.js flagsOfandBattle.foesInRadiusreadblockedBylive, so the 隐匿 came back with the block's end. - Now: every block release —
Battle._unblock,releaseBlocked,ai.js enforceBlockCapacity(a blocker's death, retreat or stun, a block-count drop, a push, 无法阻挡 / 浮空 / 恐惧 / 沉睡 (since §24.9), a teleport) — callsBattle._stealthSwitch(e): each 隐匿 source buff the enemy holds gets astealthOff:<source key>buff for its restore time,constants.js STEALTH_RESTORE(3 s) or the source'sdata.stealthRestore(content/enemies.js STEALTH_RESTORE_BY_KEY: the 0 s / 1 s enemies above; the veil 0 s).targeting.js enemyStealthed(e)— 隐匿 flag, not revealed (反隐), not blocked, and some source not switched off — is the one test for ally targeting (canTargetEnemy), operator area damage (foesInRadius), the b.snap stealth bit (drawn solid meanwhile), 节日爵士乐手 (attacks only without its 隐匿), 假想敌:骨刺 (3 targets only in 隐匿) and 山海众头目 (the mark comes at the spawn and whenever its 隐匿 turns on again, the next blocked attack spends it: a new block inside the 3 s gives no second ×2 hit — until 0.1.2 every block did). A new block inside the window lifts the 隐匿 again and its end restarts the window; a 反隐 that ends outside a window hides it at once, as before; a veiled 潜伏者 hides as soon as its block ends (the veil) and, if the veil runs out inside its own 3 s, is revealed until they end. Our operators' 隐匿 / 迷彩 are untouched (allies get no switch). - The 深池逐火 embers (all three,
husk()withstealthy): the knock-out releases the warrior's block at once (_unblockright after the 重生's 无法阻挡, asapplyStatusdoes for a status) once the ember's 隐匿 is on, so the ember is revealed until 3 s after the knock-out — the 1 s 重生 stays 无敌 and untargetable, then about 2 s in which ranged operators and operator splash can hit it even though its blocker took the next warrior. An ember knocked out while not blocked is 隐匿 as soon as its 重生 ends (the switch only follows a block). - Measured (before = the v0.1.1 sim, after = this change; the flat stage with its row-9 lane, and real rounds on 战场#01 built like the match — an official wave with the TIMES pick, full content): the integrator's seven lineups (艾丝黛尔 / 角峰 / 隐现 / 幽灵鲨 against 1–4 战士 or 护卫): identical — 艾丝黛尔 alone against four 战士 2 s apart stays 0 / 4 because only one warrior is ever knocked out, and no lineup there knocks out a 护卫. 28 runs of a blocker + 2–3 ranged / splash operators against 4–6 战士 / 精锐战士 0.5–3 s apart: 81 → 84 of 126 killed, stand-ups 12 → 7 (23 runs unchanged, 4 one more kill, 1 one fewer — its blocker fell in the changed fight). Real rounds (R3 / R5 精锐战士 pick, R9 / R11 护卫 pick, three lineups up to the tier-4 four of §21.4's test): unchanged (R3 7 / 7, R5 5 / 16, R9 0 / 18, R11 0 / 28 for the tier-4 lineup; 0–1 for the weak ones). Why so little: in real rounds most knock-outs happen while the warrior is NOT blocked — ranged operators finish it on the way after it passed a full blocker (R5, tier-4 lineup: 10 of its 18 knock-outs, and 10 of the 13 embers that stood up again) — so its ember is 隐匿 at once, which is official; an ember freed by its own knock-out gets about 2 s after the 重生 and still needs 5 separate hits (火灰 10). What beats embers, officially: a free block slot where they walk (a second blocker behind the first), 反隐 (银灰 鹰眼视觉, 伊内丝 影哨, the SPT-X modules, 信仰搅拌机's module), or many fast hits.
- ASSUMED: at the knock-out the 重生's cleanse comes first, then the ember's 隐匿 with the block-end switch, which outlasts the 重生 (PRTS documents neither the order nor whether the switch survives a 重生); 深池伙友影刃精英 and 锏's second form (PRTS writes their 隐匿 as the plain 异常效果 with no restore note) follow the general 3 s, not the "表现上与我方干员相同" kind; one switch per 隐匿 source — with several sources the enemy hides as soon as any is back on; a push or a teleport (DISAPPEAR) ends a block like any other release; a warrior released by its blocker less than 3 s before its knock-out leaves an ember that is 隐匿 straight after its 重生 (the window is opened by the knock-out's own release only — 4 of 55 knock-outs in the measured rounds, no outcome changed when prototyped).
- Tests:
test/sim/feedback2-surtr-stealth.test.js(the 3 s window and its consumers, a new block inside it, the release paths, the 0 s / 1 s enemies and the veil, our operators, 山海众头目's mark, the three embers' window, an ember knocked out unblocked),enemies_bosses,feedback1-transform,feedback1-followups,combat,playtest5-targeting.
22.9 荒芜拉普兰德 S3's drones attack (#32 item 7) — kits/tier6.js whitw2 (WHITW2_SPREAD / WHITW2_CHASE / WHITW2_REAPPEAR_SIDE / WHITW2_ORBIT, releaseDrones / pickTarget / flyDrone / circle)
- Seen (GitHub #32, item 7): 「易拉三技能浮游单元无法造成伤害,只能造成三技能自带dot伤害」 — 荒芜拉普兰德's S3 终幕·浩劫 dealt only its once-per-second area damage.
- Official: PRTS 荒芜拉普兰德 S3 备注, 技能流程: ① for 1.3 s after the cast or after a drone is added ("开启技能/补充浮游单元1.3s内") the drones spread evenly outward from her, one along her facing (初速度 0.1, 加速度 1.9, 最大速度 2.0); ② each then locks the target "距离自身最近>距离本体最近" anywhere on the field and flies at it (初速度 2.0, 加速度 1.0, 最大速度 4.0, 转向速度 1/6 per frame; a target that leaves or turns unselectable on the way → a new pick, the speed back to 2.0); ③ at the target it "锁定目标持续进行攻击,此状态下的攻击行为同正常浮游单元"; when that target leaves or turns unselectable it reappears at a random point of the 1.5-side square around it and picks again; with nothing selectable it circles (radius 0.9, 1.0 线速度, counter-clockwise, the circle on its left). Also: "该技能释放的浮游单元造成的伤害不属于普通攻击/技能直接伤害"; the area damage / slow has radius 0.9, does not stack and carries 头狼's 沉默. PRTS 分支特性信息 驭械术师: a drone starts at 20 % of the operator's ATK, +15 per hit on the target it hit last, up to 110 %, back to the start on a different target; drones released by a skill "不受缴械类效果制约" — the same note says they "造成技能直接伤害", which S3's own 备注 overrides for this skill. skill_table skchr_whitw2_3 at the chess levels (Lv4 / Lv7): atk +0.45 / +0.6, attack@cnt 2, attack@times 1.3 (PRTS ①'s 1.3 s), attack@move_speed −0.3 / −0.4, attack@magic_atk_scale 1, attack@fear 2, attack@range_radius 0.9, attack@projectile_turn_speed 5 (× 1/30 s = PRTS's 1/6 per frame); trait init 0.2 / 0.35 (elite: module 狼时), delta 0.15, max 1.1; 头狼 every 20 s: cap × 1.1 (PRTS 备注: 121 %), 2 s 沉默, 数量+1.
- Cause: the S3 spec switched her attack off for the whole skill (
attack: { noAttack: true }) and its tick only moved the drones (at attack@projectile_move_speed, 1 tile/s), feared a target the drone came within 0.9 of and dealt the area hit; no drone ever attacked. The kit's header said so ("she stops attacking while they roam"). The drones also preferred enemies no other drone held — an ASSUMED rule PRTS does not have. - Now: her own attack stays off — every drone is out (浮游单元 +attack@cnt with her own one; +1 from 头狼 stage 3, and a drone added when stage 3 comes during the skill spreads along her facing). The drones fly ① ② ③ with PRTS's numbers (spread for attack@times s; the distance covered at the mean of the old and new speed, exact under constant acceleration; the reappearance point is drawn in her facing frame, so a battle turned with her direction plays the same). A drone that reaches its target fears it (attack@fear s) and stays on it; it attacks every attack interval of hers (her live ASPD) for ATK × its own funnel ramp (trait init, +delta per hit on the same target, the cap — raised by 头狼 stage 1 —, back to init on a new target), arts. That damage is neither a normal attack nor skill damage: no
attackhook,isAttack/isSkillfalse, tagdroneAttack— the on-attack items skip it (the 叙拉古 6 chance rolls on it since 0.1.3: 普通伤害, see the §23 「叙拉古」 subsection), 头狼's 沉默 ("造成伤害时") applies; 缴械 does not stop it. The once-per-second area hit and the slow are unchanged (around every drone, one hit per enemy; tagsskill+drone, +dotsince 0.1.3: PRTS 备注 "持续法术伤害"). A wolf knocked out or withdrawn in the middle of a tick (a kill hook, say) deals nothing more in that tick. fx:droneLock(the wolf-eye mark) when a drone picks its target,droneat a drone for each of its attacks and once per second for drones still roaming; the client draws them already. - Distance to a huge enemy ASSUMED — the owner's decision of 2026-10-04: a drone measures every enemy's distance to its position (a huge enemy's centre, its 判定中心), so leader rounds stay close to 0.1.1. Considered and set aside: the sim's general convention for operator-side distance picks, the hit rectangle (body.js
bodyDist:targeting.js:135sortEnemyTargets'nearest' / 'farthest',kits/tier3.js:86enemiesAround,kits/tier6.js:1108异客's storm chain,kits/tier6.js:2525/:2549溯光星源's bounces and links,kits/tier4.js:526/:995; only the profession chain bounce,ai.js:257, takes the nearest by position) — with it a huge leader (胄, 管) was the nearest enemy of every drone around it and drew them all for the whole skill (胄 h07_01: her damage on the leader 106 309, on the other enemies 3 036, 2 deaths). A drone on a huge enemy sits at its centre; it reaches it on touching the hit rectangle. - Players notice: once a drone reaches a target that stays alive, its hits outweigh the area damage (3 parked 重装防御者, RES 0, the full 40 s: drone hits 38 411 of 54 640); against streams of weak enemies the area hit still does most of it (the flat stage's two lanes, 10 + 10 walkers, 40 s: drones 11 049, area 50 188 of 61 236). All drones may lock the same enemy — each takes the one nearest to itself — so against a cluster the area hit covers fewer enemies than before. Measured, old → new: parked, first 12 s normal 12 901 → 12 089 (drone hits 7 512 + area 4 578), elite 17 658 → 18 085; the whole 40 s 47 857 → 54 640 and 65 504 → 77 369; the lanes 15 481 → 61 236 (kills 1 → 14), elite 49 213 → 72 224 (9 → 18). Leader rounds (one elite 荒芜拉普兰德 per side, S3 at once, kept alive, 60 s; her damage on the leader / on the other enemies / enemy deaths, 0.1.1 → now): 假想敌:胄 (act1 h07_01) 32 467 / 80 186 / 9 → 84 880 / 44 391 / 6; 假想敌:管 (h07_03) 19 936 / 62 176 / 5 → 37 520 / 69 927 / 5; 假想敌:铳 (h07_02) 31 260 / 49 982 / 4 → 62 794 / 30 221 / 0; 卢西恩 (act2 h07_05) 37 957 / 58 512 / 6 → 75 094 / 40 881 / 2 (both of normal size: a drone that picks the leader because it is nearest keeps the lock, and a leader never falls); the heavy two-player 胄 scene of robustness.test.js (the review bench: 4 wolves per side, 120 extra enemies at ×20 HP, 60 s): the wolves' damage 1 315 038 → 1 291 567, on the leader 50 170 → 42 907, enemies killed 12 → 13. With the hit-rectangle distance it would have been 胄 106 309 / 3 036 / 2, 管 57 202 / 36 427 / 2 and the heavy scene 406 075 / 125 271 / 2.
- ASSUMED: the centre distance above (the owner's decision); reading ① "补充浮游单元" as: after an addition only the added drone spreads, along her facing, while the others carry on; the turn rate (转向速度) is not modelled — a chasing drone heads straight at its target; a drone's attack clock runs all the time and its first hit lands on arrival when the clock is ready; the drones keep flying and attacking while she is stunned, frozen or silenced (PRTS only names 缴械); a drone reaches a huge enemy when it touches the hit rectangle and then sits at its position; the fear's source is her (they flee from her, as before); the area hit keeps
isSkill(a skill DoT; PRTS's line speaks of 直接伤害). - Tests:
test/sim/feedback2-whitw2-yu.test.js(spread and chase numbers; per-drone hits, ramps and intervals, no normal attack, the damage flags, 缴械; nearest-target picks, the reappearance square and the ramp restart, the circle; 头狼 stage 3 mid-skill; a wolf knocked out mid-tick; facing UP / LEFT / DOWN = RIGHT with a reappearance),test/content/kits_t6.test.js(the S3 test now has one enemy: a nearer one would draw every drone).
22.10 余 S2: leaders teleported, no pull effect without a pull, cast with an enemy on its x-1 (#32 item 1) — kits/tier6.js teleportEnemy / yu (skchr_yu_2), tools/build-data.mjs TRIGGER_DEVIATIONS / resolveTrigger / validateAll, data/chess.json skills[].trigger
- Seen (GitHub #32, item 1): 「余二技能有伤害和特效,但不能拉怪」.
- Official: PRTS 余 S2 厚礼上宾 (技能范围 x-1): "立即对周围所有敌人造成相当于攻击力100%的法术伤害并将其中的地面可达目标传送至自身位置"; 备注: "可达目标" = a target that can reach his tile by ground pathing, not through impassable tiles — "如果余或目标所在位置已经是一个不可通行地块(例如围栏、高台等),传送效果会因此失效"; "处于消失状态的/持有自缚的单位不视为可达目标"; 免疫传送 / 传送免疫 units are not teleported; the teleport is judged 0.13 s after the damage, the target is unblocked first and lands at a random point near one of the four corners of his tile. PRTS 卢西恩,“猩红血钻” and 假想敌:铳 (both sections): 传送抗性 无. Trigger: the 下半 TANK class row
TANK | | | 0 | TAKE_DAMAGEand PRTS 卫戍协议/帮助's 重装 strategy "不受技能范围影响,受到伤害时释放技能" (§20.2, §21.29). - Mostly not a bug (triage on the 0.1.1 sim): ordinary ground enemies inside x-1 are teleported — 余 in a lane of the flat stage pulled all 3 enemies inside x-1, on 战场#01(下半) all 3; on a 围墙 tile none (the official rule). The usual first hit came from a ranged enemy outside x-1 (a 深池暗影术师 2.7 tiles away): under TAKE_DAMAGE the skill fired with nobody to pull, so only its damage and effect showed.
- Cause (two real faults):
teleportEnemyskipped every leader (isBoss) — no PRTS basis; and S2 played itspullfx even when nobody was teleported (n = 0). - Now: a leader is teleported like any ground enemy unless it is 自缚 — the 7 huge leaders (
SELF_BOUND), 守墓石像's 转换模式 and the 胄 parts' anchor keepselfBound(§21.19), so 卢西恩 and 假想敌:铳 are the leaders it can pull. A teleported leader re-plans from his tile: on the real rounds 假想敌:铳 (不可阻挡) walks on to its next patrol checkpoint and 卢西恩 is blocked by 余 (block +2), blinks past and resumes its loop. 静态刚体 is no exemption (the 备注 names 消失 / 自缚 / 免疫传送; PRTS 静态刚体 forbids physical movement) and every 静态刚体 of the mode flies or is 自缚 anyway (§21.9). Thepullfx plays only when at least one enemy was teleported (each still gets its ownteleportfx). - Deliberate deviation — S2 casts with an enemy on its x-1 (the owner's decision of 2026-10-04; it joins §21.29's six): players saw it fire from a ranged hit with nobody to pull, so instead of the official row's TAKE_DAMAGE it casts once its SP is ready and an enemy stands on its own 技能范围. Not the basic strategy (
DEFAULT, §21.29's rule): his attack range is his own tile (0-1), so "about to attack with an enemy in the initial range, or one he blocks" fires only once an enemy is already on his tile — measured, an enemy parked two tiles away never set it off, under DEFAULT as under TAKE_DAMAGE. It takesSKILL_RANGE— the mode's official strategy for a MANUAL skill with a 技能范围 when no class row applies ("仅在技能范围内存在敌人(无视其不可选中)时释放技能", §20.2) — on x-1:TRIGGER_DEVIATIONSchess_char_6_03_a: { skchr_yu_2: 'SKILL_RANGE' }(per chess, the elite through its baseId); a SKILL_RANGE deviation carries the skill's own range ascustomRangeGrid, andvalidateAllchecks both records. The two records read{ rule: 'SKILL_RANGE', rawRule: 'TAKE_DAMAGE', customRangeGrid: <x-1> }(rebuilt with--offline: nothing else in data/ changed). His S1 keeps TAKE_DAMAGE, his S3 its official charId row (CUSTOM_RANGE). - Players notice: S2 never fires from a hit outside x-1 any more; it fires as soon as an enemy steps onto x-1 — any enemy, a flyer or a 隐匿 one too (a flyer alone: the burst hits it, nobody is pulled) — so in a stream it often pulls only the leading enemy where the hit used to come with several bunched up. Measured on the triage lanes (SP full from the start; cast / enemies pulled): flat stage beside the lane 5.90 s by the 深池暗影术师's hit, 0 → 6.33 s, 1; in the lane 8.13 s, 3 → 4.83 s, 1; 战场#01(下半) 11.87 s, 3 → 8.57 s, 1. One-line flip: remove the entry from
TRIGGER_DEVIATIONSand rebuild. - Checked: no enemy of the mode is immune to the teleport — every enemy name of data/enemies.json was looked up on PRTS: all list 传送抗性 无 (the four 悬赏 bosses on their "(敌方)" pages, “余音” on its own) except “斩胄之剑”, “破胄之锤” and 刺胄之弹, whose value is empty — they fly (the 剑 / 锤 also stay 自缚-anchored when shot down), so they are never teleported anyway; 碎铳之簧's page has no such row and “炎佑” is an ally-side unit.
- Not modelled: the 0.13 s between the damage and the teleport (same tick) and the corner landing points (the tile centre).
- Tests:
test/sim/feedback2-whitw2-yu.test.js(S2 fires with a ground enemy two tiles away and nobody hitting him, the enemy on his tile; a ranged hit from outside x-1 no longer casts it; 卢西恩 and 铳 teleported, “萨米的意志” (自缚) stays, onepullfx for two; a flyer alone on x-1 → noteleport/pullfx; the real 铳 and 卢西恩 rounds),test/sim/feedback1-tank-triggers.test.js(the data and the real battle for 余 S2 beside the six),test/data.test.js(14 deviated records),kits_alt_t6(6_03 S2 now asserts the reason SKILL_RANGE);feedback1-residuals(守墓石像) andplaytest6-skills(#15 audit, 余 S2 now among its SKILL_RANGE skills) pass unchanged.
22.11 归溟幽灵鲨's 替身 is drawn and sits the fight out (#44, 「归鲨死了没替身」) — professions.js installDollkeeper (DOLL_SWITCH, hook dollSwitch), kits/tier5.js (归溟幽灵鲨: trait dollNoAttack, S2 onEnd, 拥抱自我), content/items/battle.js (PRIO_UNDYING_HELD), render/units.js FORMS (doll, leave), UnitView (_dieForm), render/spine.js (baseRoles), render/style.js / textures.js (icon doll), render/fx.js (dollEnd)
- Seen (GitHub #44): 「归鲨死掉后没有替身」. The owner's triage: in 0.1.1 the sim did switch her to the 替身, but the client kept drawing her 本体; the 替身 also attacked and could cast S2 生存的渴望, whose end then knocked her out.
- Official: the trait (character_table / chess
trait) 「受到致命伤时不撤退,切换成<替身>作战(替身阻挡数为0),持续20秒后自身再次替换<替身>」 (bb duration 20;atk/max_hpare the 替身's bonuses — 0 on both 傀儡师 but for the elite modules). PRTS 分支特性信息/data §傀儡师: "受到足以致命的伤害且未持有不死的情况下" → "一段切换动画", then the <替身> fights for 20 s holding 阻回; at its end a switch animation back; "切换动画开始时会清除自身一切Buff;切换途中重设自身生命至最大值,随后再次清除自身一切Buff"; during a switch animation 不死, 无敌, 阻回, 禁疗, 孤立, 强制缴械, 眩晕 / 冻结 / 睡眠 immunity; block ×0 "从切换至<替身>开始时持续至切换回非<替身>开始"; "部分机制也可以直接让干员切换为<替身>形态". PRTS 归溟幽灵鲨: 特性备注 「<替身>不进行普通攻击」 (and 天赋备注: 伤害与减速不可对空); S2's text corrected to 「技能结束后立刻切换为<替身>」 (原文 视为被击倒); S2 备注 「技能期间持有不死,因此技能期间不会因致命伤而切换为<替身>」. PRTS 风丸: 特性备注 「<替身>状态下可对空」 — her 替身 attacks; her 纸偶 leaves "持有者切换替身时". The 替身's HP: PRTS resets it "至最大值", the trait's 替身 HP bonus is 0, character_table has no 替身 of 归溟幽灵鲨 and 风丸's 纸偶 token (token_10022_kazema_shadow) has her own max HP at every level — so the operator's own max HP. Her skeleton (char_1023_ghost2.skel, read with the client's own parser): the*_Bclips draw the 替身's own slots (*_T) and hide the 本体's — Start_B (1 s, fades in), Idle_B, Die_B (1 s, breaks apart and has faded out at its end), Die_B_2 (1 s, collapses and stays); Start_2 (1 s) brings the 本体 back; her Back skeleton has only Idle_B and Start_2. 风丸's: Start_B (its OnAttack at 0.267 s is 折纸生花), Idle_B, Attack_B, Die_B; her Back skeleton Start_B, Idle_B and Attack_B. Neither Back skeleton has the 替身's death clip. - Cause: the 替身 was only a buff (block −99, HP × 0.5): nothing kept it from attacking or casting; S2's end emitted 'knockout' and
loseHp(hp + 1)— a lethal HP loss, so the 替身, or the knock-out when she already was one (a cast as the 替身 killed her at its end). On the client an operator had no form: the 'substitute' / 'swap' fx carried noform, UnitInfo none, FORMS had no entry for her skeleton, and 'trait:substitute' had no icon. Already in 0.1.1: a running 坚固维式重锤 不死 window did not stop the switch (its battle-level hook and the 傀儡师's sat at the samefatalpriority, the 傀儡师's registered first). - Now (every 傀儡师): a lethal hit on the 本体 that nothing prevented before it — a skill's or talent's own 不死 (10 … −60) or a running 坚固 window (
PRIO_UNDYING_HELD−99; the hammer's lock, a new window, stays at −100 after the switch, as in 0.1.1) — switches her: the running skill ends (reasonsubstitute: S1 / S3, 风丸's S2 and its 纸偶) and her statuses are removed; forDOLL_SWITCH(1 s) she holdstrait:dollSwitching(flagu.trait.dollSwitching) — 无敌, 阻回, 禁疗, 孤立, 缴械, 眩晕 / 冻结 / 睡眠 refused, a lethal 流失 stops at 1 HP;trait:substitutelasts the switch + 20 s: block 0 from the switch on, 阻回, max HP = her 替身 token's (风丸) or her own (归溟幽灵鲨, was 50 %), HP full;u.form = 'doll'and fx 'substitute'{ form: 'doll', dur }. When it ends, the switch back: the same 1 s animation and clearing, block and HP back, fx 'swap'{ form: null }. A lethal hit on the 替身 (after its switch) knocks her out; the form is then reset by fx 'dollEnd'{ form: null }after the 'die' event (drawn as nothing), so she redeploys as the 本体. Content switches her at once with the hookdollSwitch{ unit, reason, done }. 归溟幽灵鲨's kit setsdollNoAttack: her 替身 carries 缴械 and 沉默 — no normal attack and no cast (S2 / S3's basic strategy casts on an attack the 替身 never makes). Her S2 end emitsdollSwitch(no lethal HP loss — so no 不死 / 复活 item takes it — and no 'knockout' fx, gone from the client's table); nothing when it ended with her, by her switch, or she already is the 替身. 拥抱自我 works while the 替身 fights (not during the switch). Client: FORMSchar_1023_ghost2.doll— Start_B (change), Idle_B, Die_B as its closing clip over its last second (timed from the fxdur), Die_B_2 to die on, no attack or skill clip, and the newleaveclip Start_2, played when a form ends (UnitView.setForm; not on a knocked-out view);char_4016_kazema.doll— Start_B, Idle_B / Attack_B, Die_B,leaveStart. A form now ends in the unit's own roles (SpineActor.baseRoles: the equipped skill's clip — an operator came back on the manifest's default skill clip). 'trait:substitute' shows the puppet icondoll. The fx carryform, so no client stage drops them (keepsState/keepEarly/isCosmeticEvent) and a view built later starts from UnitInfoform. Facing UP, the Back model shows the 替身 and skips the clips it lacks; knocked out, she lies on the Front model like every operator facing UP (§22.1) — the view keeps the form it died in (_dieForm, set bydie(), cleared when it stands up), so that model, built a frame after the 'dollEnd' reset, and any model rebuilt while she lies down show the 替身's Die_B_2 (风丸: Die_B); she stands up as the 本体 (facing UP: on the Back model, its deploy clip); a revive in the same frame as the knock-out (阿戈尔: die → deploy → 'dollEnd') brings the 本体 back on Start_2 and loads no model. - Players notice: she turns into her 替身 (a puppet of her, with an icon) for 21 s; it no longer attacks and her SP waits until she is back; S2's end no longer kills her — she becomes the 替身; the 替身 has her full HP; after each switch she cannot be hurt and does not act for 1 s; S1 / S3 running at a lethal hit end; knocked out as the 替身 she lies as the collapsed 替身, facing UP too. With a 坚固维式重锤 whose 8 s window still runs (its lock set off by the 替身's lethal hit), a lethal hit on the 本体 leaves her at 1 HP instead of switching her again. 风丸 likewise (her 替身 drawn, the 1 s switches, 阻回, S2 and its 纸偶 end with the switch). Measured (the report's repro: a harmless enemy parked in her range, lethal hits by hand): 0.1.1 — the 替身 at 1 328 / 2 657 HP made 17 of her 19 attacks and her SP kept filling; with 38 / 40 SP she cast S2 as the 替身 (at 2.4 s, 1.9 s after the hit) and was knocked out at its end (17.4 s); now — 2 657 / 2 657, no attack and the SP held 22 s; S2 cast as the 本体 at 24.9 s, alive.
- ASSUMED: the switch animation lasts 1 s each way (the skeleton's Start_B / Start_2) and the 20 s run after it; the direct switch at S2's end (立刻切换) plays the same 1 s animation; "清除一切Buff" = the running skill and the catalogue statuses — every other buff survives, the remake's bond / item / talent effects (buffs too) and other units' timed buffs and shields alike, and element gauges and 爆发冷却 stay; 无敌 is modelled as damage immunity only, like every ally 无敌 in the code (PRTS also gives "无法被不同阵营选中": enemies may still target her during a switch); no S1 (技能范围) cast as her 替身 either; the 替身 can be healed (PRTS names 禁疗 for the switch only); 风丸's 替身 may still cast a skill that was ready at the switch (it attacks); Die_B over the 替身's last second is visual only; a view built from UnitInfo while she lies down (a field opened later: the sim has reset the form) shows the 本体's fall.
- Open: 风丸's 替身 attack range (PRTS x-4) and 对空 are not modelled — it keeps her own range and ground-only attack.
- Normative lines rewritten with it: §5.2 (
form), §5.4 (fatal:PRIO_UNDYING_HELD), §8.2 (fxform, UnitInfoform). - Tests:
test/sim/feedback2-doll.test.js(cases A–D of the repro: the 替身's form, HP, block, 阻回, no attack, 拥抱自我, the switch back; S2's 不死 and its end; a knock-out as the 替身, the event order, the redeploy; no cast as the 替身 for S2 at 38 / 40 SP and a ready S1; S3 ending with the switch; the hook; a running 坚固 window stopping the switch; 风丸's 替身 attacks),test/render/feedback2-doll.test.js(the clips in the manifest, Start_B → Idle_B → Die_B → Start_2 with her S3 clip after it, Die_B_2 held through 'dollEnd' and the redeploy, UnitInfoform, the Back model, a late fx; knocked out facing UP — the Front model's Die_B_2 with the reset in the same frame or the next, 风丸's Die_B —, a model rebuilt while down, the redeploy on the Back model, a same-frame revive on Start_2 without a model load, a view built while down; 风丸, sim → view, the skeleton's clip meanings),test/sim/professions.test.js(the switch windows),test/sim/fidelity.test.js(her own max HP).
22.12 Enemy splash spares a 隐匿 operator that does not block the enemy (#32 item 6) — sim/targeting.js areaSelectable / auraSelectable, content/enemies.js areaAllies / areaAlliesInTiles / fieldAllies / auraAllies / targetAndArea / zone / dmgZone, content/bosses.js, sim/Battle.js alliesInRadius
- Seen: 「隐蔽单位仍会受到溅射伤害」. Real on the enemy side (the operator side was fixed in 0.1.1, §21.19): every enemy area effect took every ally in its area. Measured on v0.1.1: 碎骨 grenades 角峰 on (10,6); 古米 on (10,7) under a 999 s 隐匿, not blocking, took 4 splashes (2650 damage) and was knocked out. §21.22's audit had listed it ("enemy splash still hits 隐匿 allies").
- Official:
- PRTS 作战机制 §隐匿 "隐匿效果使得获得该效果的单位无法被任何敌方的能力索敌选中", "隐匿的我方干员阻挡敌人时,因为“阻挡优先级最高”的效果,敌人会无视一切可选性对该干员进行攻击"; §AOE伤害判定 "AOE的判定是对攻击范围内的每个可以被选中的敌人进行判定"; §隐匿与Buff的关系 "隐匿状态下的单位一般无法被敌方的索敌机制和Buff选择器选中为目标", "目前明日方舟中使用能选中隐匿状态单位的Buff效果一定是无视隐匿状态起作用的" — its one enemy example being 寒霜's 攻速下降 Debuff.
- PRTS 异常效果 §无法选择: with 隐匿, 不可选中, 无敌, 塔不可选中 or 对地规避, "常见的、来自不同阵营的“选择”行为将无视这些单位进行(如同范围内不存在这个单位)"; the abilities PRTS marks "无视可选性" skip that check; "'直接选中'的能力不会进行具体的目标选择,故同样不受这些仅在选择时生效的异常效果制约".
- gamedata_const ba.invisible 隐匿 "不阻挡时不成为敌方攻击的目标" vs ba.camou 迷彩 "不阻挡时不成为敌方普通攻击的目标(无法躲避溅射类攻击)"; PRTS 异常效果 迷彩 "所有光环类能力、以及涉及中点判定/格子判定的效果均不受迷彩制约,但存在主动排除受迷彩影响单位的情况(此时站内会特别标注)"; PRTS 选择器 "非弹道类型的溅射攻击会自动无视迷彩", "所有触发选择器通常不无视迷彩".
- The exceptions are the notes on each PRTS enemy page ("无视无法选择", "无视(目标)可选性", "无视迷彩"); every area site below was checked against its page.
- Cause:
content/enemies.jsandcontent/bosses.jspicked their area victims withbattle.alliesInRadius/alliesInTiles/allies(); §21.22 had added only a 对地规避 filter, site by site. - Now: one area selector,
targeting.js areaSelectable(src, a)(helpersareaAllies,areaAlliesInTiles,fieldAllies): no 隐匿 ally unless it blockssrc, no untargetable or sleeping (沉睡 = 无敌 + 无法行动) ally, no airborne 起飞 ally for a groundsrc(the damage pipeline's refusal,evadesGround; the per-site filters are gone). 迷彩 is not checked. A deadsrc(death blasts) blocks nobody;srcnull (no selecting enemy) keeps 隐匿 out but not 起飞. Buff auras useauraSelectable/auraAllies: the same without 对地规避. A target the ability locked beforehand — an attack's target, 死亡之眼's channel, a C4, an 爆炸箭 — is a direct pick (targetAndArea): a 隐匿 it gained meanwhile does not save it; only the units around it are an area selection. Per site:- Selects (a 隐匿 operator that does not block the enemy is spared):
- splash — 碎骨's grenades (their DEF down too; its target keeps it), 自制投石机's stones, the 周围四格 of 控潮术师 / 领潮员 / 术师快艇 and 腐败骑士, 暴鸰's 8-tile bomb, 灼藤's first 3×3, the shells of 帝国炮火先兆者 / 中枢先兆者 (their damage stays 无来源);
- death and self blasts — 高能源石虫 / 冰爆源石虫 / 卷心籽 (PRTS 死亡爆炸 "…无视迷彩,不可对空", "来源无效" included), 烹泉 / 沏虹 (blast and steam), “巨大的丑东西”'s self-destruct, “庞贝”'s blast while blocked;
- area skills and statuses — W's C4, 鼠王's 唱沙 and 沙狱, 凋零骑士's 爆炸箭, 腐败骑士's 蓄力锤, 萨卡兹王庭军精锐术师's 死亡之眼 burst, “自在”'s 纬地经天 and 破桎而出, 锏's 速杀 (those on its way; its former blocker is still hit), 肆虐风雪 and 瞬息杀机, “萨科塔昂首”'s 祈祷邀约, “帝国的甲胄”'s barrage, the 自行炮's shots (each picks inside its 9 tiles — a 迷彩 operator can be shot now);
- pulses — the 神经毒素 of 元核孽生者 / 异光体孽生者, 鼎沸, 深溟巢涌者 (its pulse rides on its engine attack, which needs a target: a 迷彩 operator alone in its range is never hit), 水遁忍者's spin; zones — 集团军重型火炮's 燃烧区域 and 假想敌:蚀裂's sourceless 毒雾 (
srcnull: a 隐匿 operator is spared, an airborne 蒂比 still reached — §21.22; until 0.1.2 it took everyone inside); jumps — 陷落雪祀's chain, the 碎铳之簧 element bounce; - leaders and parts — the 刺胄之弹 / 斩胄之剑 / 破胄之锤 blasts (the flyer selects, so 对地规避 still does not apply), 铳's 最终之罚 and the springs' 末日布道 tramples, “余音”'s pulse and 合奏, 管's 裂管之奏, 弦's 断弦之奏, 昆图斯's 大潮 and tentacles, 卢西恩 / 不祥幻影's 【aoe】, 阿利斯泰尔's 王权号令 and 斥退, “萨米的意志”'s 冰凌 column and doom.
- Normal attacks on every operator in range take the targets of a normal attack (
canTargetAlly: no 隐匿 or 迷彩 operator unless it blocks the enemy — PRTS 选择器: a normal attack does not ignore 迷彩): 斩胄之剑 / 破胄之锤's hover attack ("普通攻击对攻击范围内的所有我方单位…") also took 隐匿 and 迷彩 operators until 0.1.2 (targetsNear … ranged: false); “灵幛” already did not. - Cast conditions of area skills count the targets of their trigger selection (
targetsNear): 卢西恩 / 不祥幻影's 【aoe】 ("需要目标"), 锏's CircleAttack — a lone 隐匿 or 迷彩 operator no longer sets them off, as a lone airborne 蒂比 has not since §21.22. - Buff auras (
auraAllies): 深池伙友卫队's field and 扎罗's 远古威慑 no longer slow a 隐匿 operator that does not block them (their PRTS pages carry no 无视 note); an airborne 蒂比 is still slowed [ASSUMED, §21.20 / §21.22's ground-enemy auras]. - Still reach every ally there: 【污染秽蚀】, 【盲信之誓】, 萨卡兹悖谬暴虐兵长's 暴击, 假想敌:淤困's burst spread (PRTS "无视目标可选性" — now
ignoreSelect, so it also reaches an airborne 蒂比, which the pipeline used to refuse), 远眺's death 暴露 and 圆仔's facing count ("无视其可选性"); 寒霜's aura (PRTS: "无视隐匿"); map, tile and terrain effects — “墓碑”'s halving (PRTS: "实际属于地图效果"), 【国度】, a chimera's 源石污染区 aura, 深水区 / 活性源石 / 源石污染区 tiles; the ticks of a debuff already on a unit. - 起飞: 锏's 【瞬息杀机】 no longer reaches an airborne 蒂比 — it used to set her SP to 0 directly, past the pipeline; its area selection now skips her (对地规避: 锏 walks).
- The operators' 隐匿 sources all set the flag the selectors read (
stealth): 伪装服 (applyStatus('stealth')on the first damage), 叙拉古 6, 伊内丝 S2 and the 排气格栅 tile (PRTS 特殊地形: "即隐匿"); 迷彩 (camou): 忍冬 S3, 寒芒克洛丝 S1.
- Selects (a 隐匿 operator that does not block the enemy is spared):
- ASSUMED:
- A 隐匿 operator that blocks the enemy stays selectable for that enemy's area abilities and auras (PRTS states the forced selection for its attacks).
- 迷彩 is hit where PRTS gives no basis — no splash type, no 中点判定 / 格子判定 or aura reading, no "无视迷彩" note — as since 0.1.1: 锏's 肆虐风雪, 瞬息杀机 and the bystanders of 速杀, 昆图斯's 大潮 (whole field) and tentacles, “萨米的意志”'s doom and 冰凌 column (a 格子判定 reading), “余音”'s pulse and 合奏, 铳's and the springs' tramples, “巨大的丑东西”'s self-destruct, W's C4 blast, “帝国的甲胄”'s barrage and the 自行炮's zone (格子判定 readings), 陷落雪祀's chain, the 碎铳之簧 bounce.
- 【国度】 and the chimera's aura reach a 隐匿 operator (tile / terrain readings).
- An airborne 蒂比 is still reached by ground-enemy buff auras and by 蚀裂's 毒雾 (§21.22).
- An invulnerable (无敌) operator is still selectable by areas, as by attacks (§21.22's open deviation; its damage is 0).
- Fixed in 0.1.3 (§23.6) — these were the enemy-number gaps left open here:
- 碎骨's grenade: PRTS's page gives 3×3, 26 % ATK, DEF −50 % for 5 s; the code has radius 1, 100 % ATK, 3 s.
- 扎罗: 远古威慑 radius 1.5 / ASPD −50 (code 2.5 / −30), the second form's range 1.25 (code 2.5), 溶血骇惧's loss curve (up to 30 % max HP/s at 40 s; code 5 %).
- “复仇者”'s 冲锋 trigger counts 隐匿 / 迷彩 operators (
targetsNear … ranged: false). - “庞贝”'s blast radius: PRTS 1.4, code 1.
- “巨大的丑东西”: its unblocked ranged 3×3 splash attack is missing; self-destruct radius 3.0 (code 2.5).
- “墓碑”, 烹泉 / 沏虹 and 集团军重型火炮: the splash of their normal attacks (3×3, radius 1.0, radius 1.0) is missing.
- 碎铳之簧: bounce radius 1.7 and ×0.8 per bounce (code 2 / 0.85), chase trample radius 0.35 (code 0.5).
- 锏's 速杀: PRTS hits only its former blocker, 0.3 s after the blink.
- W's C4: PRTS installs it on the unit in a 3.2 s cast, 180 % on it (code: 1 s fuse, radius 1).
- 管's 裂管之奏: the extra 0.8-radius 凋亡 hit "(无视其可选性)" is not modelled.
- “萨科塔昂首”'s 祈祷邀约 "不可对空" does not filter flying allies.
- Tests:
test/sim/feedback2-stealth-splash.test.js(9 — the selectors; 碎骨 next to a 隐匿, a 迷彩 and a 伪装服 operator; “庞贝”'s blast on its 隐匿 blocker; 卢西恩's 【aoe】; the 无视无法选择 exceptions; 蚀裂's 毒雾 and the 深池伙友卫队 / 扎罗 auras vs 隐匿 and 起飞; 死亡之眼's locked target; the blade / hammer hover attack, the 自行炮 and 深溟巢涌者 vs 迷彩, 祈祷邀约 vs 隐匿 — every battle case fails on 0.1.1),test/content/enemies_bosses.test.js,test/sim/feedback1f-liftoff.test.js,test/docs-consistency.test.js(卢西恩's / 锏's cast conditions).
22.13 要塞 cannot hit air units; 莫斯提马's slow shows its icon (GitHub #35, #16; PR #48) — tools/build-data.mjs classifyAttack (fortress no longer in the can-hit-flying ranged set), data/chess.json canHitFly false for chess_char_2_18_a/_b (灰毫) and chess_char_5_08_a/_b (号角), sim/professions.js (fortress, resolveProfile), kits/tier4.js (莫斯提马's slow pulse status: 'slow', visible: true)
- Seen: GitHub #35 — the 要塞 operators 号角 and 灰毫 shot at flying enemies and splashed them; #16 — "莫斯提马三技能没有减速".
- Official: PRTS 新人入门 marks the 要塞 branch 不可对空; the 要塞 trait text never mentions air, and no 号角 or 灰毫 skill adds anti-air. 莫斯提马's slow was not broken: her talent's move_speed −0.15, ×3 during S3 (
talent_scale), is 85 % / 55 % of the movement speed per PRTS, and it already applied in 0.1.1 — only no status icon was drawn on the slowed enemy, which is what the reporter saw. - Cause:
classifyAttackgavecanHitFlyto every ranged attacker unless the trait says 地面敌人 (投掷手); a 要塞 attacks from the melee position with a ranged normal attack (MELEE_RANGED_SUBPROFS), so it counted as ranged and thefortressprofile did not setgroundOnly. 莫斯提马'spulsecarried no status. - Now:
canHitFlyis false for the sub-professionfortress— the only data change is those 4 records of data/chess.json (normal + elite); thefortressprofile readscanHitFly: false, groundOnly: trueandresolveProfileforces both for any fortress orgroundOnlyprofile (the data's generic ranged default cannot re-enable it), so the four records neither target nor splash a flying enemy (targeting.canTargetEnemy, the splash loop ofai.js resolveHit); ground enemies are still attacked and take the radius-1.0 splash. 莫斯提马's slow pulse passes{ status: 'slow', visible: true }, so the existing slow status icon shows on every enemy in her range; the slow itself is unchanged. §21.29's skill-trigger deviation for the six 重装 skills (号角, 灰毫) is untouched. - Players notice: 号角 and 灰毫 (and their elites) ignore flying enemies completely — no shot, no splash — so flyers need another operator; against ground enemies they play as in 0.1.1. Enemies slowed by 莫斯提马 carry the slow icon.
- Contributed by the PR #48 author (Paper-Yuan), squash-merged 2026-10-04. Normative lines rewritten with it: docs/SIM.md (the profession table: 要塞 ground only, by the PR), docs/DATA.md §2.1 (
canHitFly). - Tests: the PR's —
test/sim/professions.test.js(the four records readcanHitFlyfalse /groundOnly; a 要塞 beside a FLY enemy makes no attack and leaves it untouched, beside a ground enemy it attacks and splashes) andtest/content/kits_t4.test.js(莫斯提马's slow buff carries theslowstatus).
22.14 Ranged enemies stand for each attack (#58) — sim/ai.js attackStand / enemyAttack / updateEnemy / advanceRoute, sim/simdata.js normalizeEnemy, sim/Battle.js spawnEnemy, tools/build-data.mjs enemyAttackAnim / attacksOnTheMove / loadManifest, enemies.json attackAnim / attackMoves, render/spine.js SpineActor.windUp / attack (clipPerAttack), render/units.js
- Seen: 「1.1中敌方远程攻击单位在攻击时任会移动」. Real in the sim and the client: an unblocked ranged enemy stood only
ATTACK_PAUSE= 0.35 s after each strike (no source for the number), while every ranged attacker's attack clip is longer (median 1.33 s, 0.37–3.5 s); the client stretched the clip over the whole attack interval and kept the attack pose for 1.4 intervals, so the clip looped while the enemy walked about 90 % of the time. Measured on feedback26b634fe(能天使 on (10,6), one enemy walking row 9): 隐形弩手 (interval 2.70 s, clip 1.0 s, strike frame 0.533 s) walked 0.24 tiles during the wind-up, stood 0.37 s and walked 2.33 s per attack; 深池暗影术师 (3.0 s, 1.2 s) stood 0.37 s and walked 2.67 s. - Official: no source gives the length of the stop. The owner's decision (2026-10-04, from first-hand memory of the official game): a ranged enemy stops for each attack's animation and walks on between attacks. The enemy handbook treats attacking on the move as a special ability — “十字路口”量产型 / 试做型 「不停止移动的四向攻击」 (not an enemy of this mode).
- Cause:
ai.js enemyAttacksetpauseUntil = now + ATTACK_PAUSEafter the strike and nothing before it; the data had no clip lengths (they lived only in the client's asset manifest);SpineActor.attack/windUpplayed the clip atclip / intervalspeed in a loop. - Now:
- Data:
tools/build-data.mjs enemyAttackAnimcopies each enemy model's attack clip from the committed asset manifest (data/assets.jsonanims.attack.loop— the clip the client plays, not an Idle stand-in —, its length and firsthitstime) into enemies.jsonattackAnim{ clip, dur, hit } (213 of 249 keys; the rest have no attack clip). An ability text with 「不停止移动」 givesattackMoves: true(no key of the mode). The sim reads both from its data, never from client files. - Sim (
ai.js attackStand): the stand is the clip —hitof it before the strike anddur − hitafter it, both divided bydur / intervalwhen the attacks come quicker than the clip. Before the strike:enemyAttackreports the wind-up (cooldown ≤ the wind-up, an ally it may target within its reach) andupdateEnemyholds the enemy; after the strikee.atkStandUntilholds it. Only the walking is held (advanceRoute(…, standing)holds a MOVE leg, and a 诱导 walk): a checkpoint's WAIT keeps running and DISAPPEAR / APPEAR legs still happen — leaving the field ends the stand — so a wait is not lengthened by the clips (data/waves.json has 122 WAIT and 99 DISAPPEAR checkpoints; review finding). It is drawn standing (movingfalse, so the client's base clip is Idle until it walks). The first attack of an enemy that walks into range with its cooldown long over still lands at once (no wind-up to stand through; strike times are unchanged). No clip known:ATTACK_PAUSEafter the strike, as before;attackMoves(data, or content throughe.profile.attackMoves): never stops. A stun / freeze / sleep ends the stand, and a 恐惧 is not held by it. Blocked and melee enemies are unaffected; content holds are separate (暴鸰 has no normal attack —noAttack— and hovers through its drop on its ownpauseUntil). - Client (
SpineActor.clipPerAttack, set for every enemy model byrender/units.js): each attack plays the clip once, wound up so its strike frame meets the attack, at its own speed (faster only when the attack interval is shorter than the clip), then the resting clip — Move once the sim walks it again. An attack seen late starts on its strike frame. Operators keep the clip looping over their attack rhythm.
- Data:
- Measured:
- Same layout as above (
attackAnimin, this branch): 隐形弩手 stands 1.00 s per attack (0.50 s before the strike — one tick short of 0.533 — and 0.47 s after) and walks 1.70 s; 深池暗影术师 stands 1.23 s and walks 1.80 s; 萨卡兹百夫长 (5.0 s, clip 3.5 s) stands 3.53 s and walks 1.50 s (it used to stand 0.40 s). Over the 75 ranged attackers of the mode, the median share of an attack cycle spent walking went from 91 % to 63 %; three (clip ≥ interval: 深溟巢涌者, 富营养的巢涌者, 节日爵士乐手) no longer walk while they have a target. - Real rounds: four seeded 4-bot co-op matches (HARD seeds 1–2, ABYSS seeds 1–2, rounds 1–12 / 14) saved every normal field as its BattleSpec. The 58 fields with ranged enemies were replayed on
50c9b5band on this branch. Leaks 750 → 764 (+1.9 %), target LP 753 → 767, kills 886 → 876. The 423 ranged enemies: leaked 222 → 223, mean time on the field 24.6 → 27.2 s (+11 %), their attacks 2725 → 2948 (+8 %), mean tiles walked 6.67 → 6.65. Per field the change goes both ways (HARD s1 R9 one field 0 → 6 leaks, ABYSS s1 R9 another 14 → 8). The same numbers after the route-leg fix (none of these fights has a ranged enemy on a WAIT / DISAPPEAR leg while it attacks). The ranged enemies cross the field more slowly, but they also stand in range of the operators longer and hit them more often, so the net effect on leaks is small.
- Same layout as above (
- ASSUMED: the stand lasts exactly the attack clip and is split at its strike frame; the clip's speed-up when the attack interval is shorter than the clip (the client's rule); no wind-up stand before an attack that lands as the target comes into range; a stun / freeze / sleep ends the stand, and 恐惧 is not held by it; an enemy in another form (掠海漂移体's crawl, 转译基底's forms, 杰斯顿's second form) stands for its base clip — known mismatches: 扎罗's second form stands 1.433 s (A_Attack) though its B_Attack lasts 2.0 s (2.5 s interval: about 0.4 s of walking during the recovery), and 转译基底's 特战术师 stands 2.0 s (B_Attack) though its D_Attack lasts 1.667 s; a per-form clip would need the client's FORMS mapping in shared data (later); an attack interrupted by 麻痹 keeps the old 0.35 s stand (PRTS 异常效果 麻痹: a 0.5 s 麻痹震颤 that also stops movement — not modelled); a model without an attack clip keeps
ATTACK_PAUSE. - Normative lines rewritten with it: §5.5 Enemy AI (the stand replaces the fixed 0.35 s pause); docs/SIM.md, docs/DATA.md (
attackAnim), docs/ASSETS.md, docs/PLAYING.md. - Tests:
test/sim/feedback2-enemy-attack-stand.test.js(7 —attackAnimmatches the manifest for every enemy; 隐形弩手 with an operator in range stands for its clip at each attack and walks between attacks; a 1 s attacker with a 2 s clip never walks while it has a target; anattackMovesenemy keeps walking while the same enemy without it stands; the fallback, and a stun ending the stand; on a 3 s WAIT checkpoint the wait ends at the same time with or without a target, and the enemy walks on once its current clip is over; DISAPPEAR / APPEAR legs proceed during the stands, and hiding ends the stand),test/render/feedback2-enemy-attack-clip.test.js(4, fake PIXI with the real manifest: wind-up, one clip per attack at its own speed, then Move; a quick attacker's clip at 2×, an attack seen late starts on its strike frame; an operator's loop unchanged; enemy views use a clip per attack, operator views do not);test/sim/combat.test.jskeeps the 0.35 s fallback.
22.15 An operator forced off the field lies down and comes back there (#60) — sim/Battle.js isDown / retreat (with _remove, _layBody, restTile, downOn, _checkRedeploys, snapshot unchanged), content/kits/tier5.js (余烬)
- Seen: 「42突袭死后会消失」 (42 = 史尔特尔, a community nickname). Real: her talent 余烬 keeps her at 1 HP after a lethal blow and forces her out 8 s later (
battle.retreat, reason'retreat').Battle.isDowntook only'killed'andFORCED_EXIT, so she left no body: no b.snapdownentry, so the client played Die and faded her out, and no redeploy ring.restTilesent her home, so 70 s later she came back on her board tile, not where she fell. Measured on feedback26b634fe: on (10,3), out at 9 s, back at 79 s on (10,3), with nothing on the field in between; after a 突袭 jump to (12,8), out at 21 s, back at 90 s on (10,3), then she jumped again. The same path was used by 耀骑士临光 S2 (tier6.js), the 骑士戒律 + 竞技旗 combo (items/battle.js), 伊内丝 S3's first deployment (tier4.js) and a 商人 that cannot pay (professions.js,tier3.js琳琅诗怀雅). - Official: PRTS 卫戍协议/帮助 §作战阶段 单位部署: "干员退场后,将返回隐藏的待部署区,并原地留下一个“倒地干员”以供查看信息,满足再部署条件时,移除场上的该倒地干员并自动部署至该位置。" This covers every 退场, not only a knock-out (the owner's decision, 2026-10-04: every operator that leaves the field leaves a 倒地干员 on that tile and is redeployed there). PRTS 史尔特尔 天赋 余烬: 「受到致命伤害时持续使生命值不低于1,8秒后强制退出战场」. Her page's 备注 "强制退出战场视为撤回干员" describes the regular game, where a withdrawal refunds part of the cost; 卫戍协议 has no withdrawal in battle.
- Cause: §21.20 / §21.24 ASSUMED "a withdrawn operator leaves no body":
isDownlisted two removal reasons. - Now:
isDowntakes every removal of an operator that may come back, except the 突袭 retreat ('raid', which redeploys at once on its landing tile):'killed',FORCED_EXIT(联防),'retreat'(余烬, 耀骑士临光 S2, 骑士戒律 + 竞技旗, 伊内丝 S3) and'merchant'. The §21.24 rules then apply unchanged:_removelays the body where the operator stood (its own home when that tile is another board piece's home and its home is free).- b.snap
downand the field meta list it, so the client draws the body and the ring. The client needed no change: it draws whateverdownlists. _checkRedeploys/redeploy()bring it back on that tile, anddownOn/isReservedTilekeep allies off it.- A forced exit is still no kill. Its death reason stays
'retreat'/'merchant': nokillhook, no 被击倒 effects, no 不屈 or 阿戈尔 redeploy, no knock-down count (perPlayer.deaths) and no knock-down sound. - 联防 is unchanged: any operator not alive at the end of its own combat already entered 联防 down (
carryState.down,FORCED_EXIT). - After the fix, the same layouts: down on (10,3) —
down[1, 79, 70, 0, 10, 3] — and back there at 79 s; after the jump, down on (12,8) and back on (12,8) at 90 s; 耀骑士临光 S2 lies on her tile with her ×1.25 redeploy ring.
- ASSUMED:
'merchant'counts as a 退场 too (the PRTS sentence and the owner's "every operator that leaves the field"; the triage had left it out); the regular game's partial refund for a 强制退出 (视为撤回干员) is not modelled; the 突袭 retreat leaves no body, since it is redeployed on its landing tile in the same tick (should both tiles refuse it, it waits for its timer and comes back home). - Normative lines rewritten with it: §5.4 DP / redeploy, §8.2
down, §21.20 (ASSUMED list), §21.24 (Now), §22.1 (the withdrawn-operator sentence); docs/SIM.md, docs/PLAYING.md. - Tests:
test/sim/feedback2-forced-exit-down.test.js(4 — 史尔特尔 after 余烬:downon her tile, the field meta, no kill / knock-down count, back on that tile after 70 s; after a 突袭 jump: down on the landing tile, the tile reserved, back there with her home unchanged; 耀骑士临光 S2: down on her tile with the ×1.25 ring and back there; a real knock-out unchanged);test/sim/downelem.test.js(a forced exit isdown; the 突袭 retreat, permanent removals, summons, devices and enemies never are) andtest/sim/engine-requests.test.js(a retreated operator lies where it stood and comes back there) follow the rule.
22.16 干员调配 shows the stats, 攻击范围, 特性 and 天赋 the chosen skill / module fight with (#64) — screens/loadout.js statsPreview / LoadoutStats, ui/detailPanel.js chessStatsBlock / chessTalents / traitText, css/screens/loadout.css .lo-sec--stats
- Seen (GitHub #64): the 干员调配 detail had only 技能 and 模组 — no 生命上限 / 攻击 / 防御 / 法术抗性 / 攻击间隔 / 阻挡数 / 部署费用 / 再部署, no 攻击范围, no 特性 or 天赋 of the operator (headless 1920 × 1080: block titles
["技能 SKILL", "模组 MODULE"], 0.dstats, 0.rgrid). A module card lists only its own overrides (ModuleInfo: 属性 +80 …), so a player could not tell what a module or a skill choice does to the operator without leaving for the board and opening its card. - Official: no rule to look up — the screen adds none. The rule it serves is unchanged (§16, "开始游戏前无法调整干员的等级,但可调整其所携带的技能和模组"); the numbers shown are the ones the sim fights with, from the same composition (
shared/loadoutRecord.js, §16): an elite's stats =statsBase+ the module'sattr(不装备:statsBase), its 特性 = the module'straitOverride(不装备:traitBase), its talents = the base ones + the module'stalentChanges, its range =attackRangeGrid(a selected "被动效果:攻击范围扩大" skill, a module's own grid, the 特性's permanent 攻击距离 — §21.16). - Cause:
Detailrendered the two choice sections only; the card's stats block was inline inChessDetailand its 特性 helpertraitTextprivate, so there was nothing to reuse. - Now: a 局内数值 section between 技能 and 模组 (next to both choices; the first
.lo-segstays the skill-level toggle).statsPreview(base, golden, entries, level, getChess)picks the variant — the 精锐 record when asked for and the chess has one, else the normal one — and resolves it withchessLoadout(variant, entries, getChess).record(gameLogic.js, the call the shop card and the detail card make); no stat arithmetic of its own.LoadoutStatsthen drawschessStatsBlock(the card's eight-stat grid and 攻击范围 mini-map — the card now calls the same function,blocks.stats, so the two cannot drift), a 特性 / 天赋 card in the module-info card style (traitTextandchessTalents, the card's own rules: the 特性 line exists when the chess has one and follows the chosen module; named, not hidden talents) and a caption. A 普通 / 精锐 toggle has its own state (statLevel, default 精锐: the module exists only there, so a module click changes the numbers at once; it does not move the skill descriptions' toggle); 精锐 is disabled for a chess without an elite record. The numbers are the record's — no live entry, so none of the card's 实时 / 开战时 tag or green / red; the caption says what they leave out: "数值含所选模组;不含技能发动、装备、盟约等局内加成" (in 普通: "普通干员没有模组,所选模组在「精锐」中生效;…"). Measured over the data: 信仰搅拌机 SPT-Y 3 → 4 tiles, 莫斯提马 SPC-X 9 → 10, 引星棘刺 S3 13 → 19 (both variants); 隐现 elite 1,421 / 508 with MAR-X, 1,341 / 486 on 不装备, 1,123 / 399 as the normal chess; 异客's other module rewrites 机理分析 (80 % → 70 %). - Now (css): the card's
.dstats/.dstat/.drange/.rgrid(game-panels.css, loaded on every page) at this screen's reading sizes — every text size keeps a px floor (labels 9, values 12, range cells 7 px at the phones' 1 rem = 40 px); the stats grid takesflex: 1 1 max(3.2rem, 190px)and the range box wraps under it when the column is narrower than about 260 px. Headless Chrome (test/ui/loadout.e2e.test.js, screenshotstest/e2e/out/loadout-stats-*.png): 1920 × 1080 the grid with the range box beside it; 844 × 390 (a 240 px detail column) the range box under the numbers, no sideways overflow, no label cut off; 667 × 375 (the detail slid over the roster) beside again. Portrait phones get the app's "rotate device" screen, so they are no target. - ASSUMED: the section's place (the issue offers before 技能 or after 模组 — between them keeps both choices within a screen of the numbers on a 1080p display), 精锐 as the default variant, the caption wording, no difference colouring against 不装备, and that the 特质 (garrison) block is not repeated (the issue names 特性 and 天赋 only).
- Tests:
test/ui/loadout-stats.test.js(9: the variant and the loadout resolution, the sim's range changes, the section againstChessDetailfor 11 chess / loadout / level cases, the eight values, 特性 / 天赋 following the module, the toggle, the section's place, the css floors),test/ui/feedback1e-range.test.js(the source contract now points atchessStatsBlock),test/ui/loadout.e2e.test.js(+2 opt-in browser tests: desktop and 844 × 390 touch). - Review (Grok, 2026-10-04): the 特性 line fell back to the class trait on a record cloned for a non-default skill or module (e.g. 隐现 on S1 showed 优先攻击空中单位 instead of MAR-X's 攻击空中单位时攻击力提升至110%; 信仰搅拌机 SPT-Y showed the class trait instead of 攻击距离+1) — the in-match card shared the bug since 0.1.1.
traitTextnow returns the record's own module line whenever there is one (a 不装备 record carries none: checked on all 133 elite records); test intest/ui/loadout-stats.test.js.