Commit Graph
504 Commits
Author SHA1 Message Date
sganggs fbcbfdffbc feedback7: merge fb7-voices — the 选中干员 line on every tap (prep / shop / hand, official FOCUS_CHAR: priority 10, cooldown 0, no gap restart), the Japanese voice tree (audio.voiceJp, 2674 files, setting 语音语言 中文 / 日本語, CN fallback), the full zip ships both
- Conflict: public/js/ui/settings.js imports (store from #321 + VOICE_LANGS) — both kept. test/ui/playtest6-elite.test.js's
  source pin of the onDetail line allows the new tap counter (bcee737f).
- Grok review: MERGE AFTER FIXES — the three findings fixed on the branch (bcee737f). Golden unchanged; broad node --test
  5334 (the one failure was that source pin, fixed), i18n strict clean, lint 0 errors, typecheck.
2026-10-08 20:35:26 +08:00
sganggs 7d6fefe8f5 feedback7: merge fb7-hud — HP-bar readouts (#303 / #378 / #383: ammo cells, 伺夜's wolf-shadow pips, 斩业星熊's 我执 bar), #302 renderer abort, #292 held skill cue, #334 atlas slots, #354 梓兰's per-card frost
- Grok review: MERGE (no defect). Golden unchanged (union OK); broad node --test 5327 (5326 pass, 0 fail, 1 skipped); lint 0 errors.
2026-10-08 20:29:27 +08:00
sganggs bcee737f40 feedback7 R22-VOICES: review fixes — a 选中 tap no longer restarts the battle-voice gap, and the second shop / reward card of one operator says its 选中 line too; the package switch's comment quotes the measured JP size; golden: unchanged
1. VoiceGate.start no longer moves lastAt for VOICE_TAP_SLOTS: a tap still skips the 1.2 s gap but starts none, so a
   部署 / 技能 / 接敌 the battle asks for once after a short 选中 line has ended plays where the last battle line's gap
   ends (a 部署 at 0, a tap at 500: a 作战中 at 1300 was dropped, now plays; inside the 部署 gap it still waits).
2. Shop / reward cards hand only the chess id to the detail, so two cards of one operator (the pool deals duplicates)
   had one 选中 key and the second tap said nothing nor replaced the first line: game.js numbers every card it opens
   (`tap`, cardTap), resolveDetail keeps it, and detailPanel's new selectVoiceKey() includes it (a re-render of the
   same tap keeps its key; pieces / units unchanged). Checked in the mock match: the second card of 歌蕾蒂娅 now calls
   voice() (it made no call on the parent).
3. tools/package.mjs FULL_ZIP_JP_VOICE comment: 89,388,041 bytes = 85.2 MiB on disk, about 76 MB zipped (as ASSETS /
   DEPLOY), not "~93 MB, barely compresses".
Tests: test/ui/audio.test.js (the tap-gap case; one earlier assertion that encoded the old gap after a tap replaced),
test/feedback7-voices.test.js (new: the card tap in resolveDetail / selectVoiceKey / game.js) — both fail on the parent.
§21.30's 0.2.2 note updated.
2026-10-08 20:28:38 +08:00
sganggs fc2e57ebff feedback7: merge fb7-g-small — PR #368 (AI teammates draft last, default off), #374 (double-click a teammate's avatar in prep), #321 (copy local diagnostics; whole-token / exact-value redaction) with the review's fixes
- Golden unchanged (union OK); broad node --test 5292 (5291 pass, 0 fail, 1 skipped); lint 0 errors.
2026-10-08 20:23:26 +08:00
Cloudnyco 3f8844129b feedback7 G-SMALL: settings can copy local diagnostics for a bug report (PR #321 by @Cloudnyco)
Applied 设置 → 问题反馈. The report stays in the browser: the in-memory error log, device info, and the battle on screen only while 「附上本场战斗」 is on. Nothing is uploaded.

Redaction no longer treats a long name as a bare substring. Prose replaces each secret only as a whole token, so a name does not cut a longer word or a chess id. The attached JSON replaces a string only when it equals the secret, so chess_char_1013_chen2 and a note like "charge the char" stay, while an exact "char" value becomes the placeholder. The token boundary is a consumed prefix plus a lookahead, because a lookbehind is a SyntaxError in Safari before 16.4. PLAYING section 12 stays; the other section-12 proposal is not in this batch.

Tests: node --test test/diag.test.js — 10 pass. node --test test/client-static.test.js — 315 pass. node --test test/match/runner-pending.test.js test/match/watch-bonds.test.js test/ui/playtest6-unite.test.js test/ui/spectate-effects.test.js test/ui/feedback5-hotkeys.test.js test/i18n-server.test.js test/ui/bosshit-ticker.test.js test/ui/playtest3.test.js test/docs-consistency.test.js test/docs-paths.test.js test/version.test.js test/package.test.js — 138 pass. npm run -s lint — 0 errors. npm run -s typecheck and npm run -s check:imports passed. node tools/i18n.mjs check --all --strict — 1041/1041 in en, ja, ko and zh-TW, 0 errors.
2026-10-08 20:08:32 +08:00
anAtheist987 fd791a66cb feedback7 G-SMALL: co-op rooms can make AI teammates draft last (PR #368 by @anAtheist987)
Applied the host-only room option 「AI 队友最后选择」, default off. Off, the draft order and the random streams stay as they are. On, humans draft before AI seats after the existing shuffle, and a skip stays ahead of the AI seats.

Dropped docs/history/0.2.2.md and every §27 citation. The player-facing sentence is in docs/PLAYING.md. The room-option rule is in docs/design/match.md, docs/design/client.md and docs/design/network.md. The #338 per-bot faction and ban presets are not included.

Tests: node --test test/lobby-ai-last.test.js test/match/ai-picks-last.test.js test/ui/ai-picks-last.test.js — 18 pass. node --test test/lobby.test.js test/client-static.test.js — 378 pass. node tools/i18n.mjs check --all --strict — 1032/1032 in en, ja, ko and zh-TW, 0 errors.
2026-10-08 19:57:26 +08:00
sganggs 16df0bf466 feedback7 R22-HUD: the card 梓兰's 猎头顾问 freezes shows its frost — every shop card draws its own slot's freeze, not the toggle's (GitHub #354 by @DuolaCCCCat); golden: unchanged
Seen (reproduced on the parent, notes repro-354.mjs): band_orchid's active refresh copies the top-tier operator into another
slot and freezes the copy (bands/meta.js band_shop_refresh_copy_max_lv_char, slot.frozen = true) while shop.frozen — the
toggle — stays false; m.private sends both (views.js: shop.frozen and slots[i].frozen). The shop bar passed the toggle
(`frozen = !!shop.frozen`) to every ChessCard / ItemCard, so no card showed the frost: the server kept the card to the next
prep, the player could not see which one.

Now: shopBar.js slotFrozen(slot, toggle) = the slot's own flag, or the toggle (a frame without per-slot flags — the dev
mock — and the old behaviour for the 冻结 button, which still copies the toggle onto every unsold slot); both card kinds
use it. The bar's frame (is-frozen) and the 冻结 / 解冻 button stay on the toggle. The rule is not touched (bands.test.js,
and the new test): a second manual refresh re-rolls the frozen card too — like the toggle, "再手动刷新会重抽" — and
freezes one of the new pair; with no further refresh the frozen card stays in the next prep (the flags are cleared
then, round.js startRound).

- Tests: test/ui/feedback7-orchid-freeze.test.js (4: the wire — exactly one frozen card of the pair, toggle off — and the
  cards drawn from it; the toggle still freezes every slot; the rule unchanged across two refreshes and a round; the
  ShopBar source pin); fails on the parent (no slotFrozen, the cards passed `frozen`).
- Docs: PLAYING.md (冻结 row), design/network.md (m.private slot.frozen). Golden: unchanged (client render only).
2026-10-08 19:22:08 +08:00
Cloudnyco bc11c0d61a feedback7 R22-HUD: the cached-atlas refreshes of a crowded battle take their turns from this frame's update order, not random phases — each frame refreshes ⌊n/k⌋ or ⌈n/k⌉ of n units (step 1 of GitHub #327, PR #334 by @Cloudnyco); golden: unchanged
Applied unchanged after review (Grok static review: MERGE; the diff read in full: no network, no dependency).
UnitView._updateImpostor refreshed a unit when (frame + phase) % interval === 0 with a random phase per unit, so the
refreshes of a frame were binomial: reproduced on the parent with 7 pen figures at interval 3 — 5 1 1 5 1 1 … and
2 4 1 2 4 1 … refreshes per frame instead of 2 / 3. Now render/app.js hands every impostor unit the next slot of the frame
(ctx.impostorSlot, a counter reset in frameBody), the unit refreshes when (frame + slot) % interval === 0, and a unit
whose slot keeps moving with the frame (the units before it culled on and off in step) is still refreshed after 2
intervals (imp.last); a context without slots keeps the random phase. Each unit's rate and the total work are unchanged;
no sim or pixel-as-state change.
Step 2 of #327 (a time budget by priority) is not part of this and still needs a decision.

- Tests: test/render/unitview.test.js 'impostor refresh slots' (3: slots in update order — fails on the parent, the
  starvation cap, the no-slots context); unit view / render suites unchanged.
- Docs: design/client.md. Golden: unchanged (client only).
2026-10-08 19:22:08 +08:00
LimitlessHPPK 3484178e2e feedback7 R22-HUD: a skill cue that reaches the client before its unit is held and sounds once when the unit is tracked — the first cast of a deploy-time skill was silent (PR #292 by @LimitlessHPPK, without handing the whole early buffer to the sound); golden: unchanged
Seen (Grok's review, reproduced here with the real sim): the sim's deploy emits a deployment-activated skill's
['skill', id, 1] before the unit's ['spawn', unitInfo] (宴: three events earlier) and audio.js dropped a skill event of an
unknown unit, so that one cast made no sound while every later one did.

- audio.js: pendingSkill — a 'skill' ON for an unknown unit is held (one per unit, 64 at most, the oldest go first);
  _track plays it once when the unit's spawn arrives; a skill OFF is no cast and is not held; setFieldUnits drops the holds.
  Changed from the PR: setFieldUnits clears the holds BEFORE it tracks the new field's units (the PR cleared after, so a hold
  left over from the previous field would have sounded for a unit of the new field that shares its id — ids are per battle).
- screens/game.js: NOT the PR's change. The pre-entry buffer (keepEarly: every spawn, die, deploy, status and skill since the
  field's m.field, up to 1500 of them) is handed to the sound as its 'spawn' tuples only (new early.js audioEarly): the whole
  list would play every old death, deploy line and cast at once on entering. The live path (onEv) already forwards the full
  list, so the hold alone fixes the live silent first cast; a deploy-tick cast that falls into the short pre-entry gap
  stays unheard (a deliberate gap — replaying "only the casts before their spawn" would still sound a minute-old cast when a
  field was buffered while another one was on screen).
- Tests: test/ui/audio.test.js (3: the held cast in one batch and across batches, once, no hold for a skill OFF; a hold does
  not outlive its field and is bounded; 宴's real deploy: the sim's cast comes before the spawn and sounds exactly once — all
  fail on the parent) and test/match/feedback1-runner-forms.test.js (the audio gets the spawn tuples only: silent replay that
  still teaches the units, the next live cast sounds; the whole list would have sounded old cues). Docs: design/client.md.
- Golden: unchanged (client audio only).
2026-10-08 19:22:08 +08:00
siruimei07 030d0a2938 feedback7 R22-HUD: a renderer that loses the 12 s race no longer mounts its canvas late — the attempt is aborted, a view that still comes back is destroyed, the host is no longer wiped (PR #302 by @siruimei07); golden: unchanged
Applied unchanged after review (Grok static review: MERGE; the diff read in full: no network, no new dependency, no
shipped eval). The leak was real on feedback7: mountFieldView raced createFieldView against a timeout and, on failure,
removed every child of the host while the factory kept running — Pixi finished later and appended its canvas over the
simplified view (taking its clicks); the host wipe could also delete a newer view mounted by the same hook.

- ui/fieldHost.js: mountFieldView(host, { signal }) owns an AbortController; the timeout, a failed import or an invalid
  view aborts it, a view a factory returns after that is destroyed, an outer abort (useFieldView's cleanup) rejects
  without a fallback; nothing clears the host's children any more.
- render/app.js createFieldView takes the signal: the Pixi load / manifest / font waits end on abort, the synchronous
  build registers its disposables (setupCleanup) so a throw releases the app, tiles, fx, impostors, listeners and
  observers it made, the board-art and 3D-board waits moved after the view exists (aborting them is view.destroy(),
  idempotent), destroy() also removes the canvas. render/app/host.js withTimeout(p, ms, signal).
- Tests: test/ui/fieldHost-lifecycle.test.js (5, node vm, fake timers: all fail on the parent) and the opt-in real-browser
  test/render/initialization.browser.test.js (6 scenarios in headless Chrome, 5 fail on the parent: a late Pixi load
  covering the fallback — with a factory that honours the signal and one that ignores it —, art timeout, a setup
  exception, an unmount racing a newer mount); test/ui/playtest2.test.js's source pin takes the new indentation.
  fieldview / browser suites in Chrome unchanged (the two 4× throttled fps tests flake when two Chrome files run
  at once, pass alone).
- The createFieldView body stays unindented inside its new try (the PR's choice, to keep the diff reviewable).
- Docs: design/client.md §9. Golden: unchanged (client lifecycle only).
2026-10-08 19:22:08 +08:00
IceCodeNewandConvey123 8ae891f1f3 feedback7 R22-HUD: the HP bar shows the ammo skill's rounds as yellow cells (隐现 S2: 14), 伺夜's 狼影 left as diamonds and 斩业星熊's 我执 pool as a red bar — one snapshot change for PR #303 and #378 by @IceCodeNew and PR #383 by @Convey123; golden: unchanged
The three PRs patched the same snapshot and HUD lines (Grok's review: do not merge three diffs), so this is one design
built from their code and tests; IceCodeNew is the commit author (#303, #378), Convey123 co-author (#383).

- b.snap gains three optional, display-only lists beside the unchanged 9-field tuples (snapshot.js ammoView / wolfView /
  negView, called from battle/events.js snapshot(); a living, deployed, visible ally, only when it has one):
  ammo [[id, rounds left, magazine]] (whole rounds; the magazine is the activation's real total that unitTuple's SP
  fraction runs on, extras and refills included), wolves [[id, 狼影 left, talent maximum]] (mem.shadows of the new
  mem.wolfCapacity, set by tokens.js wolfPack and the managed vigil kit), neg [[id, fill]] (unit.negFill(), a function
  op-hsgma2.js sets: the pool is read when the snapshot is taken, no per-tick sync). UnitInfo.ammoSkill (an ally whose
  skill is an ammo magazine) is known from the first appearance: fx events run before the frame's sample, so the aura
  cannot wait for the first ammo row.
- render/interp.js keeps the lists per snapshot (malformed rows / unknown ids dropped) and sample() hands them out from the
  older snapshot like flags (they step with the skill flag); render/units.js draws them: the ammo magazine replaces the
  SP bar (a cell per round, thin cuts, exact fill, no cuts under 2 px a cell), a row of diamonds under the bars for the
  wolves (the element gauge row moves below), the red bar over the drained HP bar for the pool. An ammo skill draws no
  standing aura (rings.js _aura; the activation burst stays).
- PR #378's wolf-head avatars are NOT taken: the count is data (the talent text 至多3只, mem.shadows), the HUD art of the
  official counter is in no game data we have, so the pips are plain diamonds [ASSUMED look]. The PRs' own tests are
  folded in and extended (test/sim/hud-readouts.test.js, test/render/hud-readouts.test.js, op_hsgma2.test.js).
- Docs: network.md, SIM.md §9, client.md, PLAYING.md, types/core.js.
- Golden: unchanged (283 scenarios; the canon hashes units / down / elem only, no sim state moved).
- Tests: 4 + 14 new + 1 in op_hsgma2 (12), all fail on the parent; 344 related tests pass; lint 0 errors, typecheck,
  check:imports, docs tests.

Co-authored-by: Convey123 <Convey123@users.noreply.github.com>
2026-10-08 19:22:08 +08:00
sganggs ddf8c39ed0 feedback7 R22-VOICES: the Japanese voice dub as a second tree beside the Chinese one — audio.voiceJp (the same slots and file names from ArknightsAssets2 voice/: 2674 files, 89.4 MB, for the 191 voiced operators), 设置 → 语音语言 中文 / 日本語 (default 中文, not tied to the interface language), a JP line the manifest or the host lacks plays the Chinese one; the full zip ships both dubs (one switch, tools/package.mjs FULL_ZIP_JP_VOICE), the lite zip stays art-free (the owner's request of 2026-10-08 「全套的日配语音」); golden: unchanged
- tools/assets/plan.mjs: one voiceTree() per dub — audio.voice (--voice-lang, cn by default) and audio.voiceJp
  (VOICE_JP_LANG, always the JP dub, audio/voice/jp/<charId>/cn_nnn.mp3); fetch-assets plans and downloads both
  (stats.voiceJpChars), so setup (checkAssets walks every listed URL) fetches both too.
- data/assets.json regenerated with the project's tool (node tools/fetch-assets.mjs --add-only, raw.githubusercontent.com,
  107 s): the only change is audio.voiceJp + stats (+2674 files, +89,388,041 bytes, voiceJpChars 191) + hash — the rest
  is byte-identical; the JP tree is the Chinese one with the folder swapped (191 × 14 files, every file a valid MP3).
  The audio files are not committed (public/assets/ is git-ignored).
- public/js/audio.js voiceLine(): 'jp' draws from voiceJp and falls back to the Chinese line per slot (a JP entry the
  manifest lacks) and per line at play time (the Chinese file of the same name when the host lacks the JP one —
  same token, same gate); operators without voice (stand-ins, 盟约·辅助干员, summons) stay silent. setVoiceLang().
- settings: voiceLang 'cn' | 'jp' (VOICE_LANGS, sanitizeSettings), the 语音语言 row under 干员语音 (each dub named in
  its own language, like the language switch), 语音语言 in the en / ja / ko / zh-TW packs.
- tools/package.mjs FULL_ZIP_JP_VOICE (true): false holds the JP files out of the full zip (and its update zip) —
  not missing, not orphans, never in an update's `removed`; setup then downloads them.
- Sizes measured: art download 548 MiB (was 463), full zip ≈ 505 MB (0.2.1: 428 MiB + 76 MiB of zipped JP voice),
  unzipped 711 MB (package --dry-run); lite zip unchanged. README, DEPLOY, ASSETS, setup's fallback size updated;
  PLAYING voice paragraph; §21.30 "Changed in 0.2.2" note.
Tests: test/feedback7-voices.test.js (new: plan, manifest, settings, package switch), test/ui/audio.test.js (voiceLine,
the manager's dub choice and fallback), test/ui/gameLogic.test.js, test/docs-consistency.test.js — all fail on the parent.
2026-10-08 19:09:52 +08:00
sganggs cb8865d9d8 feedback7 R22-VOICES: a tap on an operator says its 选中干员 line in every phase — the prep, the shop and the hand too, not only while a battle runs (the owner's request of 2026-10-08 「添加一下干员点击上去的语气一样的语音」, lifting 2026-10-03's 「整备阶段不需要干员语音」 for this line only; [ASSUMED] the official prep tap = the battle's FOCUS_CHAR); golden: unchanged
screens/game.js passes the detail panel voice=${true} instead of its combat flag, so the panel's existing
audio.voice(charId, 'select') runs on every opened operator: a piece on the field or in the hand, a shop /
reward card, a bond member. The gate follows the official FOCUS_CHAR row (audio_data.json battleVoice:
priority 10, cooldown 0, overlapIfSamePriority true): VOICE_TAP_SLOTS — on an idle channel a tap always
plays (no 1.2 s global gap; VOICE_COOLDOWN_MS.select 1500 → 0), a newer tap replaces the 选中 line on air,
and a higher-priority line (部署 20, 作战中 70, 开战 100 …) is never interrupted. Deploy / skill / engage /
result lines stay battle-only; stand-ins, 盟约·辅助干员 and summons stay silent (no voice entry).
Tests: test/ui/audio.test.js (new: the gate and two taps through the manager; fails on the parent),
test/docs-consistency.test.js pins updated. Docs: §21.30 "Changed in 0.2.2" note, PLAYING.md voice paragraph.
2026-10-08 18:54:02 +08:00
LimitlessHPPK 51c53de189 feedback7 G-TOOLS: listen on :: by default so one socket serves IPv6 and IPv4 (PR #291 by @LimitlessHPPK)
Ported only the dual-stack listen from PR #291 (the change accepted as #188). With HOST unset the server binds :: and, if that fails with EAFNOSUPPORT, EADDRNOTAVAIL or EINVAL, retries 0.0.0.0. An explicit HOST, including 0.0.0.0 and 127.0.0.1, is tried once. The boot banner and doctor list shareable IPv6 addresses in brackets, skip fe80::, and keep one URL per /64. An explicit IPv6 literal in the local URL is bracketed. The Windows service listing no longer drops those bracketed lines.

Not ported: /dev/grant, the settings-default migration, and the firewall scripts. The Docker image stays on 0.0.0.0; the alpine fallback was not booted.

Tests: node --test test/ipv6-bind.test.js test/doctor.test.js test/ci.test.js — 20 pass, 0 fail (8 bind, 4 doctor, 8 ci).
2026-10-08 18:53:24 +08:00
siruimei07 063408e47b feedback7 G-TOOLS: build-data --refresh fails instead of falling back to an old cache (PR #350 by @siruimei07)
Applied as-is. When --refresh is set, a download that still fails after four attempts throws instead of returning the older cached file, so one build cannot mix a new table with an old one. --offline and a warm cache without --refresh are unchanged. Files already written to the cache stay; generated data/ and the previous report are left in place.

Tests: node --test test/build-data-refresh.test.js — 11 pass, 0 fail.
2026-10-08 18:42:23 +08:00
Cloudnyco ae22335cfe feedback7: the battle sim uses engine-independent math — Chrome, Firefox, Safari and the server agree bit for bit (PR #373 by @Cloudnyco)
- server/sim/detmath.js: hypot (sqrt of the squares, rescaled outside 2^±500), powi (integer powers in double-double,
  one rounding), sin / cos (fdlibm 5.3) and atan2 (fdlibm / FreeBSD msun) built only from correctly rounded IEEE
  operations; the sim's Math.hypot / sin / cos / atan2 / pow calls go through it (65 files); eslint.config.js forbids
  the approximated Math functions under server/sim and test/sim/detmath.test.js scans for them.
- sin / cos / atan2 are the algorithms V8 runs in Node, so the server's results stay; only hypot differs from V8's.
  Golden: 2 moved — diy-027 (a hypot distance, damage +80) and coop3-ABYSS-5 (a later card pick from round 9, same end).
- Applied unchanged after review (Grok static review: MERGE; the diff read in full: no network, no new dependency).
- Tests: broad node --test 5155 / 5155 with the new file tracked (detmath 7 / 7, package 10 / 10); lint 0 errors,
  typecheck, check:imports.
2026-10-08 18:35:55 +08:00
sganggs 97152828c3 feedback6: version 0.2.1 — package.json / package-lock.json / APP_VERSION (DEV_BUILD turns false: the title-screen dev tag, the boot warning and the package guard go)
- CHANGELOG 0.2.1 (2026-10-07): the streams' lines collected (新功能: the update package and MANIFEST.json; 规则(按官方):
  联防 on the round's battlefield, 突袭 onto a teammate's half, full potential, the replace dialog for consumables,
  the 外勤医疗 Touch, 凋亡, 贾维's 拉普兰德, the 频次 器物 / leader summons; 界面: the collapse arrow, 自选 picks in the bond
  lists, flyers, enemy animations, the Touch card, Esc in the 自选 picker; 联机与大厅: battle messages while a battle
  loads; 工具与开发: the update build, .gitignore), one intro paragraph.
- README: the dev warning under the title removed, the release badge, 「当前版本 0.2.1」; DESIGN's version line names
  0.2.1 (§26) as the state described; §26's intro says when the dev line shipped.
- Tests: version, docs-consistency, docs-paths, package, i18n-packs, update-package, update-boot 80 / 80.
2026-10-07 19:35:30 +08:00
sganggs 1b75bb9229 feedback6: DESIGN §26.14 (every operator at full potential, from the R21P stream's draft) and §26.15 (Esc in the 自选 picker); the §26 intro lists both and the owner's full-potential decision (no host setting)
- docs-consistency / docs-paths 31 / 31.
2026-10-07 18:55:27 +08:00
sganggs b05845f507 feedback6: merge R21P — every operator at full potential (潜能 6; GitHub #252, PR #255 by @SpiritedAwayCN, the owner's decision of 2026-10-07)
- tools/build-data.mjs applies every potentialRanks attribute step (COST, RESPAWN_TIME, ATK, MAX_HP, DEF,
  MAGIC_RESISTANCE, ATTACK_SPEED) to the chess, elites, 补位 and 自选 units and picks talent / trait / module-talent
  candidates up to requiredPotentialRank 5; summons take the owner's potential for their talents, not its stat steps.
  data/chess.json, backups.json, tokens.json and the en / ja / ko / zh-TW data translations rebuilt; op-wang.js reads
  her stock cap (8 at full potential). 刺玫 435 ATK / cost 15, 砾 16 s, 至简 70 s.
- No conflicts outside the golden files (regenerated). Golden vs HEAD: roster 49, bonds 46, fields 22, matches 18,
  diy 133 (standins and diy-012 / diy-135…138 unchanged: no potential ranks or never hit). Union check against the
  branch and the merge base: every scenario only the branch moved equals the branch, except roster-048 and
  solo-ABYSS-2: at full potential a 频次 器物 is reached there (roster-048: one more enemy dies instead of leaking, 86 → 87
  units), so R21H's hit count (data × hpMul ÷ supplyHpMul) applies where it did not before — with only that rule put back
  (archetypes.js times as at the merge base) both equal the branch exactly.
- Tests: broad node --test 5147 / 5147; lint 0 errors.
2026-10-07 18:48:38 +08:00
sganggs 806a047717 feedback6 R21P: every operator fights at full potential (潜能 6) — the owner's decision of 2026-10-07 (GitHub #252, PR #255; no host slider): tools/build-data.mjs OPERATOR_POTENTIAL adds every character_table potentialRanks attribute step (部署费用 / 再部署时间 / ATK / HP / DEF / RES / ASPD, all ADDITION, before rounding) to every chess (normal and elite), 补位 and 自选 unit form and picks talent / trait / module-talent candidates up to requiredPotentialRank 5 (「天赋效果增强」); a summon picks its talent candidates at its owner's potential (their requiredPotentialRank mirrors the owner's — PRTS 凯尔希: Mon3tr 3.5 s / 1400) and takes none of the owner's attribute steps; the 原型干员 stand-ins have no potential ranks (unchanged). 刺玫 413 / 17 → 435 / 15 as in the tournament video, 砾 18 → 16 s, 至简 80 → 70 s. data/chess.json (256 records), backups.json (the 71 owned picks, 8 summons), tokens.json (5 summons) and the data translations en / ja / ko / zh-TW rebuilt — only potential-driven fields (JSON-compared); the 干员调配 局内数值 and detail cards read the records. op-wang.js reads her stock cap 「最多拥有8(+1)枚」 through the potential step (the pattern fell back to 7: she sat at the cap from the start). Docs: DATA §2 / §18 / tokens, PLAYING (detail card), research 03, kits README, shared/diy.js / standIn.js, the 71 自选 kit headers and the kit comments that quoted potential-0 numbers. Tests: test/full-potential.test.js (new, fails on the parent); data.test.js re-derivation adds the potential steps (+ respawnTime); every test that hard-coded a potential-0 number carries the full-potential one (from the data and the test's own formula; no check loosened); golden: moves almost everywhere — roster 49/49, bonds 46/46, fields 22/22, matches 18/18, diy 133/138 (each battle parts on a stat: roster-001 / roster-020 at tick 1 on 角峰's HP (生命上限+250), bond-arcaneShip-high at 4.73 s and diy-001 at 3.70 s on an enemy's HP from a higher ATK; diy-110 / 111 also by 望's cap; the matches through those battles); unchanged: standins 10/10 and diy-135…138 (no potential ranks), diy-012 (twelve 夜莺 never hit, never redeployed) 2026-10-07 18:35:39 +08:00
sganggs cfc149e277 feedback6: DESIGN §26.2–§26.13 assembled from the streams' drafts (联防 battlefield, 自选 picks in bond lists, consumables on a full carrier, Touch, 凋亡, 贾维, .gitignore, flyer hover, enemy animation states, runner messages, 频次 / leader summons, the collapse arrow), the §26 intro and the owner's decisions 2026-10-07 18:22:08 +08:00
sganggs a9fe794c3f feedback6: merge R21H — 器物 (频次) enemies take the 攻坚 HP share and not 补给线's (the data's exclusion list names exactly the 14 器物), leader summons take the round's enemy scaling (PR #272 by @CXUtk, re-implemented from the data); tools/golden.mjs keeps R21H's roundMods import and drops uniteStageId (removed by R21B); golden regenerated on the merge — vs the parent exactly R21H's 74 scenarios, vs the branch only the other streams' moves 2026-10-07 18:21:01 +08:00
sganggs 3f918c9f03 feedback6: merge fb6-small-fixes (see its commits) 2026-10-07 18:16:10 +08:00
sganggs 3c36cdff48 feedback6: merge R21D — with both slots full, every consumable (博士投影, 盟约之币 …) opens the replace dialog and destroys only the picked item once its effect succeeds (PRTS 卫戍协议/帮助 「达到上限强行佩戴会改为替换装备…并销毁被指定的装备」; GitHub #263, PR #264 by @SpiritedAwayCN, ported); golden as R21D's commit (10 matches: bots using a consumable on a full operator), the merged tree differs from the branch only in R21B's 联防 scenarios 2026-10-07 18:11:53 +08:00
sganggs e33ee3db8f feedback6 R21H: the round's enemy effects reach the 频次 器物 and the leaders' mid-fight summons (PR #272, 感谢 @CXUtk); golden: 74 moved
器物 (the 14 hit-count keys): hits = data × the spawn's hpMul ÷ supplyHpMul, rounded as before (setHits) — 攻坚装备 / II / III
leave out only 炎佑, 补给线 / 补给线II also exactly these 14 keys (activity_table aceffect_enemy_2 / 2_2 enemy_exclude,
data/effects.json); until now setHits(def.maxHp) overwrote the round's HP (青铜镜 30 hits at every difficulty). build-data
writes enemyScale[r].supplyHp = 1.2^(kHp − kAtk) · extra (the HP-only 补给线II steps and 补给线's 1.08; co-op 终极 R5–R15
only — the rebuild changed config.json alone), gamedata returns it as supplyHpMul and the wave / 悬赏 mods carry it
(waves.js roundMods; a 频次 enemy's death spawn inherits it). Not taken from the PR: its 1.08 / 1.2 back-calculation (it
counted the 补给线II steps as 攻坚装备) and its fractional-HP ceiling. Leader summons (死亡集群's 妖怪, 刺胄之弹,
“裂管之音”/“断弦之音”, 不祥幻影): the boss / hidden field carries the round's scale as flags.enemyScale (bossRounds.js,
golden fields, tools/balance.mjs) and bosses.js summonMods gives them HP / ATK / speed (死亡集群's summon.hp_ratio on
top; 刺胄之弹 / 余音 hit counts follow their HP); the leader's pool stays exempt; 王权号令's invulnerable equipment
unchanged; 炎佑 is an ally token, never scaled (no change). Unchanged and open: the 逐火 / 再生 husk counts (talent
prop_max_hp).

Golden — 器物 counts: roster 37 (002 003 004 005 007–020 022 023 025 026 029–034 036–041 043 045 049: each holds a 器物
extra spawn / 频次 wave / death spawn), bond-soloShip-high, diy 9 (036 040 056 062 068 073 091 114 115), boss-boss_6-solo
(俗心's 小说卷轴 escorts); matches 16 (all but solo-ABYSS-2 and coop2-NORMAL-70-diy: 频次 rounds / 悬赏 and the Final
Assault summons). Summons: boss-boss_1-pair/solo, hidden-boss_8-pair/solo (妖怪, 刺胄之弹), boss-boss_3-pair/solo,
hidden-boss_10-pair/solo (余音), boss-boss_5-pair/solo (不祥幻影). standins unchanged.
2026-10-07 18:05:42 +08:00
sganggs 8cef750e14 feedback6 R21D: a consume-on-equip item on a full carrier opens the replace dialog and replaces the picked item first (GitHub #263, PR #264); golden: matches 10 of 18 moved (a targeted consumable on a full carrier now destroys the oldest item), the rest unchanged
PRTS 卫戍协议/帮助 §装备: 「达到上限强行佩戴会改为替换装备:指定一件已佩戴装备替换为将要佩戴的装备,并销毁被指定的装备」,
no exception for the 装备时销毁 items. Before: a normal 博士投影 opened no dialog and its kept item pushed the OLDEST
equipped item out (replaceUid ignored); 盟约之币 and the other consumables kept both items.

- server PlayerState.equip (player/items.js): on a full carrier the consume path takes the pick (replaceUid, else the
  oldest) off before onEquip (_takeReplaced), destroys it (onDestroy reason replace) once the effect went through —
  the normal 博士投影 takes the freed slot —, and puts it back at its index when the effect refuses (博士投影 on an elite:
  nothing destroyed)
- client gameLogic equipReplaces: the replace dialog opens for every item on a full carrier, consumables included,
  except an attaching item that completes an item merge and a 博士投影 on an elite; itemAttaches removed
- bots (itemTarget): a consumable whose effect is the same on anyone goes to a carrier with a free slot first (PR #264)
- docs: design/match.md equipment rules, META §2.5, PLAYING 装备 row, research 04

golden (matches only; each traced equip by equip against the parent — the first change of state is a bot's, or the
AI 托管 human seat's, targeted consumable on a full carrier destroying its oldest item): solo-FUNNY-1 (r9 ai_0 随身身份牌),
solo-FUNNY-2 (r9 ai_0 寻呼模块), solo-HARD-2 (r9 ai_0 随身身份牌, r12 简易通讯机), coop2-NORMAL-3 (r9 ai_1 随身身份牌),
coop3-ABYSS-5 (r11 ai_1 随身身份牌), coop4-FUNNY-6 (r4 ai_1 随身身份牌), solo-HARD-9-boosted (r14 ai_0 随身身份牌),
coop2-ABYSS-10-boosted (r13 ai_0 随身身份牌, r14 ai_1 简易通讯机), coop2-NORMAL-14-standins (r9 ai_0 随身身份牌; now won,
14 rounds), coop2-NORMAL-70-diy (r14 h_0 随身身份牌); the bots' free-slot carrier for target-agnostic consumables moves
no scenario by itself; roster / bonds / fields / standins / diy unchanged
2026-10-07 18:03:53 +08:00
sganggs 1e58843fb4 feedback6: merge R21F — enemy flyers hover 1.3 tiles above the road instead of climbing each raised tile (GitHub #277, @FrogThai); enemy animation states from PR #275 by @xcdoge, client part: numbered skill clips, the Stun_1 / Dizzy_* spellings, run cycles (猎狗pro, 深池侦察犬), the rebirths of 巨大的丑东西 and 自在 (data/assets.json regenerated offline: enemies only); golden unchanged 2026-10-07 18:03:47 +08:00
sganggs ce2bd75b49 feedback6 R21E: a refresh runs the 刷新时 特质 of the chess that stood on the board / in the hand when it happened (EffectDispatcher.dispatch takes them before its first handler; each still runs where it stands now, one that left is skipped): the 拉普兰德 贾维 grants on every 6th refresh — or the elite that gift completes — fired in the very refresh that granted her and spent her 「本回合首次主动刷新」; now her first is the next manual refresh; round counters are still not copied on a merge (a merged elite is a new 拉普兰德, §25.13.3); the part of PR #196 0.2.0 lacked, re-implemented; golden: unchanged 2026-10-07 18:02:48 +08:00
sganggs 76a2db7dd0 feedback6 R21F: enemy animation states, the client part of PR #275 by @xcdoge re-implemented — the resolver gives an enemy every numbered skill clip (anims.skills; the caller's first index stays the primary, operators unchanged, 宴's PREFAB_SPINE_ROLES fix kept), the stun spellings Stun_1 and Dizzy_Begin/Loop/End, a separate run role (Run_Begin/Loop/End); the renderer walks an enemy faster than moveSpeed 1 on its Run cycle (猎狗pro, 深池侦察犬 ×2; [ASSUMED] threshold), swaps an enemy's skill clip on a cast event (UnitView.setSkillSlot; a form in force is kept), and plays the 重生 of 巨大的丑东西 (Revive, then the fleeing 大祭司 Idle_2 / Move_2 / Stun_2) and 自在 (Revive_01); data/assets.json regenerated offline: 18 enemies gain anims.skills, 6 anims.run, 3 a stun clip (巨大的丑东西, 斩胄之剑, 破胄之锤), nothing else; the sim's cast slot (a new event) is not part of it; golden: unchanged 2026-10-07 17:59:11 +08:00
sganggs c225559d4d feedback6 R21E: an operator's 凋亡 burst drains the official 技力 — stored charges and the ready charge included (Skill.setSpTotal(spTotal − 1) each second; PRTS 技能 可充能 「当持有者的技力流失时,充能次数也会实时降低」, PRTS 元素 「每秒损失1点技力」): the drain took only the SP towards the next charge, so a full skill kept every charge and a one-charge skill stayed ready through the 15 s; a running timed skill is left alone as before; PR #262 (#261 its closed duplicate), re-implemented; golden: 5 moved, each by a burst on an operator with SP stored (假想敌:管's 凋亡 strikes, the 萨卡兹王庭军's 凋亡 on hit) — fields hidden-boss_10-solo (泥岩 loses its stored charge: casts 1 → 0, the move PR #262 reported; the team's heals and the snapshots after it), boss-boss_3-solo / hidden-boss_10-pair and diy-058 (later snapshots and event counts only, the same results), diy-059 (黍 forms against the 萨卡兹王庭军 waves: fewer casts — 11 → 5, 6 → 2, 9 → 5, 6 → 1 — the battle goes otherwise, kills 39 → 47, leaks 57 → 49) 2026-10-07 17:58:45 +08:00
sganggs 53ccf5d8fe feedback6: merge R21G — battle control messages that arrive while the sim loads or catches up (takeover, forced end, a duplicate start, pool, pause) are held and applied in order (PR #266 by @siruimei07, ported); golden unchanged 2026-10-07 17:57:18 +08:00
sganggs c623550f29 feedback6 R21E: the 外勤医疗 Touch casts 恳切福音 on an injured ally inside its 5-2 (ACTIVE_RANGE in touchKit — the larger-range rule the Touch 补位 already runs by its data; the map character's record keeps DEFAULT), and the Touch / 预备干员-医疗 card is an operator card (干员 · 医疗 · 远程位, 特性, skill row with SP / duration, talents from the tokens.json record — it took the summon path: 召唤物, base stats only); 恳切福音 gets a line under its text that the boost is strictly below half (PRTS 修正 of 不高于, what touchGospel does; the operator card of the 补位 Touch too: SKILL_TEXT_NOTES); GitHub #260, PR #278; golden: roster-041 (its strategy Touch heals 8438 → 17072, 19 → 27 heal actions, casts unchanged at 2, the same numbers as PR #278; p1 deaths 7 → 4) and matches coop2-NORMAL-14-standins (h_0 plays 外勤医疗: defeat in round 13 → victory, teamLp 16) — both battles with that strategy's Touch, no other scenario moved 2026-10-07 17:54:17 +08:00
sganggs 26c72a6d95 feedback6: merge R21B — 联防 plays on the round's battlefield again, its terrain, crates, water, devices and runes as in 0.1.4 (the owner's report 「联防阶段地形全消失了」 and knowledge of the official mode, GitHub #244's tournament video; 0.2.0's escaped level was the placeholder road every wave-template level carries); the 联防 battle input is byte-identical to 0.1.4's in 20 cases; 乌尔比安's comment keeps both streams' sentences; golden as R21B's commit (unite-1 / unite-2 back to their stages, coop2-NORMAL-3, coop2-HARD-4, coop3-ABYSS-5, coop4-ABYSS-7, coop2-NORMAL-8-serverrun, coop2-NORMAL-14-standins), the merged tree differs from the branch only in R21A's boss pair fields 2026-10-07 17:54:16 +08:00
sganggs d84b961c80 feedback6 R21G: a b.start is registered as pending before the sim loads, so a b.end (takeover / forced, the first end counts), a second b.start of the same battle, b.pool and the solo pause that arrive during the load or the silent catch-up are applied to it in order once the Battle exists (they were dropped); a superseded authority is still built, the next prep's clear stops the preparation (PR #266 by @siruimei07, re-implemented on the current runner); docs/design/network.md §14 Client runner + b.end; golden: unchanged 2026-10-07 17:51:26 +08:00
sganggs 14df10a5a3 feedback6 R21F: an enemy flyer hovers FLY_HOVER above the road whatever tile it crosses (GitHub #277: one high-ground / forbidden block on its way lifted it 0.42 / 0.3 tiles and dropped it again like a step) — UnitView.sync keeps every enemy at z 0, operators and summons keep their tile top, the flyer's shadow lies on the tile under it (shadowZ) on the 2D and the 3D board; golden: unchanged 2026-10-07 17:49:51 +08:00
sganggs 75202776cc feedback6 R21B: 联防 plays on the round's battlefield again — its terrain, crates, water, devices and runes, as in 0.1.4 (the owner's decision of 2026-10-07, 「官服保留地形,改回去」; GitHub #244). 0.2.0's escaped-level map (8a436249, §25.6.4) is withdrawn: that map is the placeholder grid all 39 wave template levels carry, tile for tile; only the 11 stage levels have terrain. match/unitePhase.js _uniteOpts fields this.stageId in both combat modes, unite.js uniteStageId is removed, the client draws the 联防 on the board's stage again (screens/game.js: 0.1.4's ownStage / baseStage, no map switch, nothing to restore on leaving), the dev mock likewise. Kept from 0.2.0: own-board 突袭 / 乌尔比安 S3 landings (onOwnBoard, raidTile untouched), the bounty payee, the 联防 BGM, the SETTLE result box. data/stages.json keeps the two kind 'unite' records (no field uses them; engine tests use them as a plain two-halves road); tools/build-data.mjs comments only, an offline rebuild reproduces data/ byte for byte. The 联防 battle input is byte-identical to v0.1.4's in 20 cases (5 stages × 1 / 2 helpers × both combat modes); GitHub #41 item 3 (野鬃 pushing walkers back across the gate onto the other half) is back with v0.1.4's numbers, none off the map. Tests: test/match/feedback5-unite-map.test.js rewritten (6, all fail on the parent), test/e2e/coop.e2e.mjs checks the 联防 field's drawn stage; docs: DATA §12, META §4, PLAYING §5, design/geometry-time, I18N, the §25.6.4 pointer; golden: fields unite-1 (escaped_single → act1autochess_m02) and unite-2 (escaped_multi → act1autochess_m07), the stages of 0.1.4, and the co-op matches whose 联防 rounds come out differently on the round's map — coop2-NORMAL-3, coop2-HARD-4, coop3-ABYSS-5, coop4-ABYSS-7, coop2-NORMAL-8-serverrun, coop2-NORMAL-14-standins (now a victory) 2026-10-07 17:40:03 +08:00
sganggs 783bd30afd feedback6: merge R21A — 突袭 may land on a teammate's half when both halves are occupied (two-helper 联防, Final Assault / Hidden Core pair; the owner's decision of 2026-10-07), Battle.onFieldBoard; DESIGN §26 / §26.1 (docs/history/0.2.1.md); golden as R21A's commit (boss-boss_2/3/4/5/6-pair, hidden-boss_8/10-pair, coop4-ABYSS-7) 2026-10-07 17:14:51 +08:00
sganggs cfd3c67281 feedback6 R21A: a 突袭 member may land on a teammate's half when both halves of the field are taken — the two-helper 联防 field and the Final Assault / Hidden Core pair field (the owner's decision of 2026-10-07): Battle.onFieldBoard (onOwnBoard of any player of the battle) read by raidTile only; a lone 联防 helper's empty half, a solo boss field's other half and the hand / 临时整备区 rows stay closed, every other landing rule kept, 乌尔比安's S3 【移动】 keeps onOwnBoard; golden: 8 moved, each parting from the parent at a member's first landing on its teammate's half (checked tick by tick with the parent instrumented) — fields boss-boss_2 / 3 / 4 / 5 / 6-pair and hidden-boss_8 / 10-pair (the right player's 缄默德克萨斯 / 伊内丝 / 仇白 / 耀骑士临光 on the left player's half, 1–7 landings per battle, the first 0.3–39.8 s in), matches coop4-ABYSS-7 (round 6's two-helper 联防: ai_2's 斯卡蒂 on ai_1's half at 52 s; only ai_1 / ai_2 dmgDealt move, 8263 between them); unite-2 unchanged (its two landings stay on their own half)
Tests: test/sim/feedback5-raid-own-board.test.js — the pair field (the right player's member onto the left telin's board tile (2,3), the left player's onto the mirrored right (2,17), the hand rows closed) and the two-helper 联防 (each helper onto the other's half; 乌尔比安 beside a teammate stays on his own) fail on the parent; the solo boss field's hand rows and the lone helper unchanged.
Docs: docs/history/0.2.1.md (new, §26; §26.1 cites the owner's decision and PRTS 卫戍协议/帮助 §联防阶段), the DESIGN index row and versions line, a pointer from §25.18.1, docs/SIM.md (onFieldBoard; onOwnBoard now 乌尔比安's only).
2026-10-07 17:09:39 +08:00
sganggs ee43fb7c14 feedback6: merge fb5-patch-pkg — from 0.2.1 on every release also ships an update zip (only the files changed since the earlier 0.2.x releases; the server checks the install against MANIFEST.json and deletes the files the new version dropped on its first start; manual download, no auto-update — the owner's decision of 2026-10-07); README English summary: both the update-zip sentence and the Languages line kept 2026-10-07 16:47:27 +08:00
sganggs aa5502c364 feedback5: §25.22 intro — one sentence's punctuation 2026-10-07 14:32:40 +08:00
sganggs 1ca769ba16 feedback5: 阿戈尔's devour block count checked for 水月 (伏击客, base block 0) and converted members (community report of 2026-10-07) — not reproduced: he takes every marked unit's block and blocks with it, the prep card shows it; face to face the second member marks nothing back (PRTS 「不会向…“已将标记付与自身的单位”付与自身标记」); pin tests test/content/feedback5-egir-block.test.js, CHANGELOG 核实后不是问题, §25.22.10; golden unchanged (tests and docs) 2026-10-07 14:32:29 +08:00
sganggs caee1dbed9 feedback5: 宴 stands during 落地斩·破门 — her one unnumbered skill set (Skill_Start / Loop / End) is S1 分神's sit-down rest, and the by-name resolver gave it to S2 too, so she knelt through the opening seconds of every battle (the owner's playtest of 2026-10-07); tools/assets/spine.mjs PREFAB_SPINE_ROLES now also fixes operator models by character id (front and back): 宴's S2 plays her ordinary clips (via 'attack'), S1 keeps the rest; data/assets.json rebuilt offline (only her entries changed); test/feedback5-utage-anims.test.js; CHANGELOG 界面, §25.22.9; golden unchanged (client art only) 2026-10-07 13:28:12 +08:00
sganggs f24d2a9016 feedback5: DESIGN §25.22 covers the community reports too (header, intro, 25.22.1–8 in order), §25.23 the Japanese, Korean and Traditional Chinese interfaces, the §25 intro list and decisions of 2026-10-07 2026-10-07 13:09:23 +08:00
sganggs 11fd65a5a5 feedback5: merge R19E — every enemy finishes its attack clip after a strike before it moves on (PRTS 状态机: ATTACK → MOVE after the attack ends; the 鸭爵 swap's 流泪小子 no longer walks off on its strike tick, community report and GitHub #214), and the 鸭爵 strategy's swapped-in 流泪小子 hides again 1 s after a block ends instead of 0 s (the owner's decision of 2026-10-07, a deliberate deviation for that copy only); golden as R19E's commits (216 scenarios + solo-HARD-2), the merged tree differs from the branch only in R19G's three 蕾缪安 scenarios 2026-10-07 13:07:21 +08:00
sganggs dd66adfbd1 feedback5 R19E: the 鸭爵 strategy's swapped-in 流泪小子 hides again 1 s after a block ends instead of the official 0 s — a deliberate deviation, the owner's decision of 2026-10-07 (「要不等待个1秒这样再重新隐匿」); a separate commit the integrator can drop; golden: 1 scenario moves (matches solo-HARD-2)
content/enemies/archetypes.js stealth: an enemy spawned with the band's swap tag 'duck' (content/bands/meta.js
duckReplace) gets its 隐匿 back no sooner than DUCK_STEALTH_RESTORE (1 s, content/enemies/helpers.js) after a block ends
— in practice 流泪小子 (enemy_2034_sythef_2), the only one of the four swaps with 隐匿, officially 0 s (PRTS 流泪小子
「隐匿(解除阻挡0秒后恢复)」). The same enemy anywhere else keeps 0 s: the bounty card's 流泪小子 (enemy_2034_sythef), an
untagged spawn; a 3 s 隐匿 stays 3 s; a copy leaked into 联防 keeps its tag and the 1 s. After each strike (its stunned
blocker lets go) it is now revealed through its 0.8 s post-strike stand and 0.2 s of walking.

Measured, 8 seeds each — leaked / HP left / revealed s per battle; W = 3 elite melee in a lane + an elite sniper vs the
R6 wave's ground enemies with one swapped 流泪小子, B = 8 elites on 战场#01 vs the whole R6 wave with one swap: before
the clip fix W 5/8, 42 %, 2.3 / B 8/8, 47 %, 3.2; after it W 5/8, 31 %, 2.6 / B 8/8, 58 %, 2.5; with the 1 s W 4/8,
31 %, 4.0 / B 8/8, 56 %, 5.5. Options for the owner (measured on the fixed engine, not built): 2 s W 4/8, 31 %, 5.4 /
B 8/8, 46 %, 8.5; 3 s (the general 隐匿 rule) W 4/8, 31 %, 6.4 / B 8/8, 39 %, 11.5; a 1.5 s stun on the copy (below its
2 s interval, so its blocker takes it over again) W 4/8, 2 %, 4.0 / B 4/8, 6 %, 5.5; a 1 s stun: knocked out in all 16;
the 1 s delay + a 2 s stun W 4/8, 21 %, 5.1 / B 5/8, 26 %, 8.1. Worse: half speed while 隐匿; a stunned operator that
keeps blocking it (pins that operator for good).

Golden: matches solo-HARD-2 — its bot holds 鸭爵; the swapped 流泪小子 of round 13 is free and revealed for 49 ticks in
its 1 s windows (the round-12 one is never released by a block), so the match parts at round 13 (a victory instead of
a defeat); the other 282 scenarios unchanged.

Tests: test/content/feedback5-duck-restealth.test.js (3; the swap's 1 s and the band path fail on the parent, the 0 s
guard passes on both); enemies_bosses, feedback2-surtr-stealth, feedback1-ducklord, feedback5-attack-clip-rest pass.
Docs: CHANGELOG 0.2.0 有意与官方不同的调整 (and the 核实后不是问题 line points to it), docs/history/0.2.0.md §25.22.6,
docs/PLAYING.md, docs/SIM.md, design/engine.md.
2026-10-07 12:58:06 +08:00
sganggs ef25c03525 feedback5 R19E: an enemy finishes its attack clip after its block ends — the 鸭爵 swap's 流泪小子 no longer walks on the tick its strike stuns its blocker (community report of 2026-10-07 「哭狗瞬间隐匿没有后摇」, GitHub #214 by @FrogThai); golden: 216 scenarios move
ai.js enemyAttack set the post-strike stand only for an unblocked ranged enemy (`!e.blockedBy && radius > 0`), so a
melee enemy released by its own strike walked on that very tick. 流泪小子 (enemy_2034_sythef_2, the strategy's version;
「泪眼汪汪」 / 「哭狗」 in the reports) stuns its blocker with every hit, the stunned blocker lets it go and its 隐匿 is
back 0 s later: on 0255afd it walked off the tick of each strike (four non-attacking walls in a lane: 8 stuns, 19.8 s
隐匿 / 9.0 s revealed, leaked at 28.9 s). PRTS 状态机: an enemy's ATTACK state 「每帧检查异常状态…攻击结束后回退到MOVE状态」
and COMBAT 「逻辑同ATTACK」 — only a character's COMBAT 「检测到不存在阻挡对象时强行切回IDLE状态」 — so an enemy finishes its
attack when its blocker goes. Now every enemy stands for the rest of its clip after a strike (attackStand; a blocked one
never walks, so it shows only when the block ends meanwhile: its blocker stunned or knocked out by that strike, or
gone); a stun / freeze / sleep / 浮空, leaving the field and, new, a displacement (失衡, Battle.displace) end the stand.
流泪小子 now stands 0.8 s (its 1.567 s clip, strike at 0.767), 隐匿, and a blocker whose stun ended often takes it over
again: the same lane 13 stuns, 29.1 s 隐匿 / 12.8 s revealed, leaked at 42.0 s.

Checked and official, unchanged: a stunned operator blocks nothing (gamedata_const ba.stun 「无法移动、阻挡、攻击及使用技能」,
PRTS 异常效果 STUNNED 「无法攻击、释放技能、阻挡敌人类单位」 — the keeps-blocking hypothesis is wrong); 隐匿 back 0 s after a
block (PRTS 流泪小子); moveSpeed 0.8 × the level's moveMultiplier 0.5, interval 2.0 s, stun 3.5 s, MELEE (enemy_database;
PRTS 盟约记录 鸭爵 备注: 4500 HP, 3.5 s stun, 隐匿 kept); the cooldown runs on the walk, so a new blocker sees the swing at
once and the strike after the 0.767 s wind-up; a bot teammate's 鸭爵 swaps into a human's battles, the Final Assault and
the Hidden Core too (「你和队友遭遇的部分敌人」), on the replaced enemy's route.

Golden — traced in lockstep against a copy of the parent: in each of the 199 battle scenarios the first differing tick is
an enemy standing out its clip after a strike where the parent walked on; every moved match has such stands in an
earlier or the same round. roster 41 of 49 (all but -003, -005, -009, -018, -019, -025, -035, -049); bonds 21
(deputShip-low, emptyShip-low / -high, indomShip-low / -high, investShip-low, kazimierzShip-low / -high,
kjeragShip-high, lateranoShip-high, miraShip-high, raidShip-low, sargonShip-high, siracusaShip-low, soloShip-low /
-high, suntShip-low / -high, swiftShip-high, visiShip-low, yanShip-low); fields 13 (all but boss-boss_1-solo,
boss-boss_2-pair / -solo, boss-boss_6-solo, boss-boss_7-pair / -solo, hidden-boss_8-solo, hidden-boss_10-solo,
unite-1); matches 17 (all but solo-HARD-9-boosted); standins 10 of 10; diy 114 of 138 (all but -011, -012, -018, -020,
-025, -027, -029, -031, -033, -047, -053, -077, -080 … -082, -086 … -088, -091 … -093, -098, -108, -123). The
displacement rule alone moves one roster scenario.

Tests: test/sim/feedback5-attack-clip-rest.test.js (5; the four behaviour tests fail on the parent, the data pin passes
on both); test/sim (745), test/content + feedback1-ducklord (2030), test/match (593) pass. Docs: CHANGELOG 0.2.0 规则
(按官方) and 核实后不是问题, docs/history/0.2.0.md §25.22.6, docs/SIM.md, design/engine.md, DATA.md.
2026-10-07 12:56:12 +08:00
sganggs d014b9ff0b feedback5: merge R19D — 5-阿戈尔 revives: the members the battle-start devour knocks out revive by board position once every devour pass is over (community report; replaces §24.3's knock-out order for that batch; scan 「左下到右上」 = columns from the left, bottom first [ASSUMED]); bond HP before the devour and one 5000 物理 (DEF) per mark confirmed official; golden unchanged 2026-10-07 12:46:03 +08:00
sganggs 31cdd9d072 feedback5: merge R19F — 拉普兰德 / 赫默 layers in the 整备区 are official (拉普兰德: only to an active 叙拉古, once per round per copy; 赫默: deployed only, character_target_inboard) — pin tests, PLAYING §6, CHANGELOG 核实后不是问题; golden unchanged 2026-10-07 12:40:00 +08:00
sganggs 2b0dfceabd feedback5 R19D: 5-阿戈尔 revives — the members the battle-start devour knocks out revive once every devour pass is over, by board position (columns from the left, bottom first: the [ASSUMED] reading of the community report's 「左下到右上」), not in knock-out order; later knock-outs take the slots left in time order, uneaten members none (community report relayed 2026-10-07); golden: unchanged
The report's steps 1-2 were verified and hold: the bond HP is on every member before the devour (an elite 深巡,
2904 HP / DEF 655, stands its 4345 from 15 layers on), and each mark is one 5000 物理流失 less DEF (PRTS), so a member
in the middle of a chain is marked by every 阿戈尔 behind it — that is what knocked out the reporter's 蓝蒂 / 小鲨.
Golden unchanged: the corpus's only 5-阿戈尔 battle (bond-egirShip-high, 999 layers) has no devour knock-out.
DESIGN §25.22.5.
2026-10-07 12:32:33 +08:00
sganggs 522f6287bb feedback5 R19F: community report 「小拉和赫默有概率在备战区叠不上叙拉古和远见」 checked, not a bug — 拉普兰德 stacks 叙拉古 from the 整备区 whenever 叙拉古 is active (it counts deployed 叙拉古 only, her bench copy is not one of the three) at her first manual refresh of the round; 赫默's trait has no 「此干员在整备区时也有效」 and the official data marks it deployed-only (conditionkey character_target_inboard); the 临时整备区 is not the 整备区. No code change: pinned by test/match/feedback5-bench-traits.test.js (the official data rule and the report's cases through the match paths), PLAYING.md §6 says where a 特质 works, CHANGELOG 核实后不是问题, DESIGN §25.22.7; golden unchanged 2026-10-07 12:30:58 +08:00