0.2.3: prepare follow-up fixes and client features (#407, #408, #410, #413, #431, #435, #436, #440, #444)

Add special-target activation without bypassing attack cadence, cooperative
card preferences and emotes, skill sound banks, per-operator voice choices,
text sizing, install prompts, closed-tab recovery and unanimous setup rerolls.
Retain late deployment elapsed time and resolve echo/counter pulses without
recursive hook overflow. Independent final review remains pending.

Golden: roster-016, roster-035, roster-036, roster-045;
boss-boss_1-pair, boss-boss_3-pair, boss-boss_5-pair, boss-boss_7-pair,
hidden-boss_9-pair; solo-HARD-9-boosted: Lemuen attack cadence.
diy-132: Necras extra activation targets.
coop2-NORMAL-14-standins: cooperative purchase preference and simulation fixes.
hidden-boss_10-solo: echo pulse FIFO resolution.
See DESIGN 28.13-28.24 for assumptions, sources and contributor credit.
This commit is contained in:
sganggs committed 2026-10-09 22:55:24 +08:00
1 parent 314564c139
commit f3658a0dd4
104 files changed
+3674 -741

No files matched your search

+157
View File
@@ -187,3 +187,160 @@ assertion is corrected to require actual healing, and development builds retain
新增真实子进程继承 stderr 的回归,以及关闭失败/信号退出的边界检查;验收要求原异常文件单独重跑并确认正常结束。首轮异常与复测结果分别保留在本轮验收记录中。
同一轮的版本重连测试在刻意停服时,Chrome 可能报告连接拒绝、连接重置或未连接的 socket。测试只在主动停服至恢复握手的窗口内排除这些已知传输日志;正常运行期间仍检查它们,脚本异常始终保留,所有版本指纹、重连及换局刷新断言不变。
### 28.13 Special targets respect DEFAULT attack timing — #438–440
Seen: 死芒's normal attack reaches enemies blocked by his own summons, but S1/S2 did not open for those targets.
The earlier 蕾缪安 fix also registered a per-tick trigger, allowing an early cast before her attack countdown elapsed.
The [PRTS mode help](https://prts.wiki/w/卫戍协议/帮助) describes DEFAULT activation at the next ordinary attack.
`addTriggerRange` now accepts `attackOnly`; both operators use it, while existing summon-area triggers retain their
per-tick behavior. Only live owned summons / wanted marks contribute selectable targets. S2's actual link range is
unchanged. PR #438 by @YinqiMargatroid addresses the same wanted-target symptom; the existing registration API is
extended instead of introducing a second targeting flag. SIM.md and design/engine.md contain the current contract.
Tests: real summoned-bone blocking, S1/S2 and united fields, attack countdown, sleep/ownership, wanted-target S1–S3,
plus existing 令 / 麦哲伦 trigger regressions. The combined operator checks pass 27 tests; the additional area-trigger
and champagne controls also pass. Golden movement is recorded below.
### 28.14 Interface text size — #435 / PR #446
Adapted from @7eLaryn: four persisted text sizes use `--t` without changing the rem layout root. All shipped language
packs retain their current translations. Added the missing explanatory note and fixed English XL settings overflow:
segmented controls and header actions wrap within their available width. Long briefing alliance labels size and wrap
with their cards instead of overlapping neighboring names. Default sm preserves the previous sizes.
Tests: settings / text-unit checks, phone landscape growth and unchanged board/canvas/shop geometry, reload persistence,
and XL settings in Chinese, English, Japanese, Korean and Traditional Chinese. The first multilingual check exposed
English overflow; the corrected browser suite passes. Current behavior is in design/client.md and PLAYING.md.
### 28.15 Cooperative card purchasing — PR #407
Adapted from @IceCodeNew. [ASSUMED] A tier V/VI shared-pool card matching another living teammate's active opening
bond receives one 18-point buying-score penalty. It is a soft preference, not reserved stock; normal/elite and DIY
stock semantics remain unchanged. Humans and bots receive the same cooperation. This does not add persona presets.
Tests: 14 targeted bot tests; 40-seed solo:HARD and coop4:HARD before/after benchmark is part of integration evidence.
The comparison includes the special-target timing changes of §28.13, so it must not be interpreted as an isolated
measurement of the purchasing heuristic. Current rule: design/match.md §6.6 and its follow-up contract.
### 28.16 AI teammate emotes — PR #444
Event wiring from @DDDarkstar, with corrected dispatch semantics. [ASSUMED] Cosmetic reactions are enabled only in
mixed human/AI matches, under the existing emote whitelist and cooldown; `SP_BOT_EMOTES=0` disables all hooks.
Responses skip cooling bots and stop after one reply. MVP uses this round's kills and damage, with stable order for
ties; settlement reacts once per round. Only the third chess merge reacts, then resets for the next round. A gift uses
its source playerId rather than a display-name heuristic. No RNG, gameplay decisions or timers are introduced.
Tests cover whitelist/cooldown, no human impersonation or recursive AI reply, pure-AI silence, round statistics,
settlement/merge limits, gift identities, the disable switch and actual emote/acquisition hooks. README, META.md and
design/match.md document the switch and boundary.
### 28.17 Active skill attack and impact sounds — PR #410
Adapted from @Convey123's updated PR. The current official `audio_data.json` gives 司霆惊蛰 mode attack banks and
赤刃明霄陈 S3 separate swing/impact banks. The generator adds per-slot `attacks` / `hits`, each with its own optional
`attackMix` / `hitMix`. It preserves the selected bank's chance and volume rather than applying the normal attack's
mix. A late impact keeps the mode at launch; an end before spawn cancels a pending start. Snapshot restoration and
ordinary-mode fallback remain supported. JP voices and current roster assets survive regeneration.
[ASSUMED] Where no activation sound names the mode, slot 1/2/3 is inferred as d/h/s. Bank selection prefers START to
ON and then plain/numeric order; this approximates an ability graph and is not independently established for every
operator. Mixed-letter banks are rejected. Instant effects that emit no normal attack event remain outside this
mechanism. ASSETS.md and design/client.md specify the manifest/runtime contract.
Tests: 85 audio, manifest, official-mix and JP-voice checks, including independent mode gain, delayed impacts,
end-before-spawn, late join and field reset. Regeneration is additive; no existing manifest leaf was removed. Four
unresolved upstream candidate icon/avatar leaves still make strict asset fetching report failure; they are not new
sound failures or deletions of existing entries. Packaging must retain this limitation visibly.
### 28.18 Per-operator voice language — #436
Roster details and selected DIY cards offer 中文 / 日本語 / 跟随全局. Settings persist a sanitized charId map locally;
normal/elite/DIY forms of that charId share the choice. Existing profiles migrate to an empty map, so global language
continues to apply. Deleting an override restores the current global choice. Missing Japanese slots / files keep the
existing Chinese fallback. No room protocol or exported combat-loadout data changes.
Tests cover malformed/inherited entries, persistence, changing the global default, deleting an override and fallback;
a real phone-sized browser changes the control, reloads and resets it. Current contract: design/client.md / PLAYING.md.
### 28.19 Browser installation — PR #413
Based on @Cromemadnd's proposal, with a one-use install controller. Buttons appear only after a browser install offer;
standalone/fullscreen and installed sessions hide them. Accept, dismiss, failure and duplicate clicks all consume the
same offer once. See [the prompt API](https://developer.mozilla.org/en-US/docs/Web/API/BeforeInstallPromptEvent/prompt)
and [installability requirements](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Guides/Making_PWAs_installable).
The existing original fortress favicon supplies correctly sized 192/512 px icons, shipped outside optional game art.
No service worker or offline cache is installed; the server is still required.
Tests: offer lifecycle/disposal, actual PNG dimensions, real Chrome manifest and installability checks, and a synthetic
dismissed offer through the actual button. Initial incognito rejection was a test-environment limitation; the real
installability check runs in Chrome's ordinary temporary profile. Native OS installation itself is not performed.
### 28.20 Recover a closed local match window — #431
Reproduced with an idle window retaining its own session while a second match window closes. The title/lobby now
lists local saved match seats using bounded metadata beside the existing recent-token ring. Explicit selection asks
live tabs to claim the chosen token again before adopting it and reloading. An active tab is never deliberately
replaced; names/IP are not authentication. Missing BroadcastChannel refuses recovery. A server-generated replacement
token or leaving clears obsolete metadata. No server API changes; normal automatic startup selection is preserved.
Tests: live tab protection, closed-tab recovery, simultaneous claim arbitration, no channel/unknown id, malformed
storage and expired metadata; a real two-window browser restores the identical server playerId and room after closing
the match tab. The existing identity/static suite also passes. Current contract: design/client.md and network.md.
### 28.21 Unanimous opening reroll — #77 / PR #408
Adapted from @Siornya, excluding the unrelated result-screen redesign. [ASSUMED] This remake feature is host-proposed,
requires every connected human's consent, and pauses confirmation while voting. Bots/spectators do not vote; autoplay
humans still do. Single-human matches refresh immediately. Refusal, cancellation, disconnect or leave restores the
old setup and remaining confirmation time. Success preserves room/seats/settings, resets Ready and restarts the full
normal timer; fixed stages/bans remain fixed. The random result may repeat.
A dedicated seed-derived reroll stream does not advance the original shop/draft/meta streams. A 300 ms anti-burst guard,
setup revision and vote id reject stale actions. Opening data and every player's DIY stock are prepared before any
mutation; a forced failure on the second player's stock leaves all previous state intact. Current contracts are in
META.md, design/match.md, design/network.md and PLAYING.md; all four translation packs have the new messages.
Tests: 13 match checks including real four-player sockets, permission/spectator checks, cancellation/disconnect,
reconnect, last-second and queued-Ready timers, outdated messages, one human with bots, fixed rules, fuzzed requests
and transactional failure. Final browser/integration validation supplements these targeted checks.
### 28.22 Late-loading deployment animation — part of PR #416
@Cromemadnd's proposal exposed a real missing case: onDeploy arrived before the model, so Start was never played.
The view now retains elapsed game time until the model arrives and continues the existing clip at that point. A clip
already over is not restarted; an intervening attack/death cancels the pending entrance. Existing Front/Back transfer
and single-source deployment audio remain unchanged. The proposed repeated phase/purchase sound sequence is not part
of this fix: its timing and duplication need separate visual evidence.
Tests: delayed load, simulation rate, load after clip end, attack/death supersession, destroyed view and existing
UnitView behavior, 48 checks passing. No simulation/golden change from this visual fix.
### 28.23 Follow-up golden movement
Recomputed all 285 scenarios from the integrated code; never copied PR baselines. The DEFAULT cadence correction moves
`roster-016`, `roster-035`, `roster-036`, `roster-045`; paired fields `boss-boss_1-pair`, `boss-boss_3-pair`,
`boss-boss_5-pair`, `boss-boss_7-pair`, `hidden-boss_9-pair`; and match `solo-HARD-9-boosted`. 死芒's added activation
condition moves `diy-132`. `coop2-NORMAL-14-standins` includes both cooperative purchasing and the corrected simulation.
The pulse queue correction of §28.24 additionally moves `hidden-boss_10-solo`. The bonds and standins families are unchanged. Cosmetic emotes, preferences, installation and opening rerolls that
are not requested do not intentionally alter those games. The validation record lists any outcome flips and measured
benchmark changes; the independent final review remains pending.
### 28.24 Echo / counter recursion found by the cooperative benchmark
The 40-seed benchmark exposed existing hook-depth errors in co-op seeds 3 and 29, plus seed 14 after the purchasing
change. In all three, “余音” damage pulses and 刺玫 / 泡泡 counters synchronously called each other. A scaled echo can
have 67 hit-count HP; the generic 32-hook guard cut off effects before the legitimate chain finished. The same chain
could also enter another hit while zero HP was waiting for death bookkeeping.
[PRTS “余音”](https://prts.wiki/w/“余音”) establishes the hit-count and damage-pulse behavior. [ASSUMED] The exact nested
ordering is not documented: each echo now queues recursively earned pulses and resolves them FIFO in the same call,
after the current pulse finishes. Every earned pulse retains its attack value, including the lethal hit's pulse. No
extra tick, RNG draw, global hook-depth increase or arbitrary pulse suppression is introduced. Death prevents further
counter targeting normally. This local scheduling choice needs final fidelity review.
Tests use real elite 刺玫 and 泡泡 against 67-hit echoes: all 67 pulses resolve, the echo dies, no engine error is logged,
and the queue clears. Both tests fail against the previous implementation. Together with the existing enemy/boss and
刺玫 healing suites, 273 tests pass. The benchmark now writes actual diagnostic messages beside nonzero error counts,
so failures cannot be mistaken for ordinary performance results. Recomputed golden and final benchmark results are
recorded with the candidate; do not reuse the earlier, erroneous benchmark as a clean result.