0.2.6-dev: port PR #416 by Cromemadnd — at every round start the own board's pieces replay their Start clip and landing sound one after another in deployment order (the maintainer's confirmation from official play, 2026-10-13), and a purchase or a move onto / across the board plays them too ([ASSUMED], the PR author's account); one sound per landing, idle prep pieces only, the round marked once the view took the sequence, own board only, timers cleared when prep mode ends and on destroy; DESIGN §31.8; golden: unchanged

This commit is contained in:
sganggs committed 2026-10-13 04:36:56 +08:00
1 parent 281cf01ff0
commit 231da0efbc
16 files changed
+791 -22

No files matched your search

+1
View File
@@ -77,6 +77,7 @@
- 想只改朝向:把干员拖回它自己所在的格子再选方向。朝向会一直保留到下一回合。
- **点选按地上的方格**:点哪个方格就选中站在那个方格上的单位,点空方格不选任何单位(点到后一格干员画出来的头部,算作点了后面那一格)。作战中友方同样按方格;敌人在格子之间走动,点它的身体或脚下附近(0.6 格内)即可,飞行敌人点它的身体。
- 拖动时**指针 / 手指所在的格子**就是落点并高亮。这一格可以放下(棋盘或整备区)时,干员模型直接站在这一格上,和官方部署一样:手指在格子里移动时模型不动,移到下一个可放下的格子就站到那一格;指针不在可放下的格子上时(例如在商店栏上),模型跟在指针 / 手指下(脚在指针下方约半格)。鼠标和触屏一样。装备拖到哪个格子,就配发给那个格子上的干员;在商店栏上松手则放回原位。
- **入场动画与落地音效**(0.2.6,PR #416):每回合开始(「回合开始」)时,你棋盘上的干员按部署顺序(和开战时一样:逐列从左到右、每列从上到下,召唤物在干员之后)一个接一个重播入场动画,各响一声落地音效(维护者按官方实机确认)。买到干员(新干员出现在整备区)、把干员放上棋盘或换到另一格时也会播放一次(按 PR 作者对官方的描述);原地改朝向、撤退回整备区、在整备区里挪动不播放,同一次落地只响一声。音量跟随「音效」,静音时不响;只在你自己的棋盘上播放,查看队友的阵地或观战时不播放。
- 点击干员显示攻击范围和底部按钮(「撤退」回到整备区、「出售 +1」);右键 / 长按查看详细信息。作战中、查看队友的阵地时,点开场上我方干员或召唤物(包括队友的)的详情卡,棋盘上也会画出它**当前的攻击范围**(按它现在的位置、朝向和技能,例如开着扩大攻击范围的技能时就是扩大后的范围),关掉详情卡、拖动或选方向时消失;敌人、整备区和临时整备区里的单位不画。详情卡里:头像旁是盟约,下面第一项就是干员的**特质**(如「休整期结束时 …」),之后是特性、属性、攻击范围、技能、模组、装备、天赋。属性是**实时数值**:作战中每秒刷新 4 次(当前生命、攻击、防御、法抗、攻击间隔、阻挡),休整期显示棋盘上的干员**开战时**的数值(已算入装备、盟约层数、特质、策略与机变效果);高于基础值为绿色、低于为红色并标出差值(攻击间隔变短算增益),鼠标悬停可看基础值。基础值是官方数据在该阶级的等级、技能等级(和精锐的模组)下、按你在「干员调配」里给这名干员设的**潜能和练度**(默认潜能 6、精英阶段2-60级)、0 信赖的数值:潜能加成包括部署费用、再部署时间、攻击力 / 生命 / 防御 / 法抗 / 攻速,以及「天赋效果增强」,召唤物的天赋按召唤者的潜能;练度把攻击、防御、生命上限单独乘上自持有加成(精英阶段2-60级 ×1.1;召唤物不乘)。补位的替补干员和原型干员没有潜能档,也没有自持有加成(0.2.2;0.2.1 是所有干员一律满潜能、没有自持有加成,GitHub #252)。
- **召唤物手动摆放**:把带可部署召唤物的干员放上棋盘后,它的召唤物会进入整备区(赫默的医疗探机、巫恋的诅咒娃娃、凯瑟琳的 2 个支援装置、浊心斯卡蒂的海嗣、伺夜的狼群、缪尔赛思的流形;同一干员的召唤物叠在一格,不占部署位),像干员一样拖到棋盘上并选方向。凯瑟琳的支援装置给**它面朝的那一格**上的干员加屏障,所以要放在干员旁边、朝向干员。**战术家的援军**(伺夜的狼群、缪尔赛思的流形,也就是选战术点)和官方一样**只能放在召唤者的攻击范围内**:拖动时只有这些格子会亮起;之后在原地给战术家换朝向时,不在新攻击范围里的援军会退回整备区(还在范围里的保持不动;整备区和临时整备区都满、援军无处可退时,这次换朝向不生效并提示「整备区已满」)。撤退或出售干员时它的召唤物一起移除;**移动干员到别的格子时(包括把召唤物拖到干员身上交换位置),它已摆放的召唤物会退回整备区**,需要重新摆放。整备区满时退回的召唤物进入临时整备区,休整期结束还没处理的会在下一回合重新回到整备区。
- 作战中:海嗣、狼群、流形、支援装置开战时就在摆放的位置部署;医疗探机、诅咒娃娃开战时也在摆放的位置免费部署一次(持续 10 / 15 秒,和官方一样不需要先开技能),之后所属干员每次发动技能时再次在原位出现;所属干员被击倒、再部署回场时,技能给的那一个如果还没出现,会立刻出现。巫恋退场时诅咒娃娃随之撤退,赫默退场时已出现的医疗探机不受影响。医疗探机、诅咒娃娃、海嗣(以及自选干员的工程蓄水炮、沙地兽、打字机、本能的召唤)这类不会受到攻击、到时间就离场的召唤物不受任何伤害(活性源石也一样)、也不会被治疗,它们的血条就是倒计时:随时间减少,空了就离场。琳琅诗怀雅二技能的香槟炸弹也不会掉血(活性源石和各种伤害都不会)、不会被治疗,不会因为生命值而消失,只在「见面礼」炸到碰到它的第一个地面敌人后消失(维护者 2026-10-08 的决定);隐匿(没有被反隐、也没有被阻挡)或沉睡的敌人选不中,不会触发它。医疗探机、诅咒娃娃、支援装置、海嗣没有摆放就不会出现;狼群、流形(战术家的援军)没有摆放时会在战术点出现。狼群最后一只狼影被击倒后进入战术点形态:暂时离开战斗(不阻挡、不攻击、不会被攻击,格子仍然占着),经过狼影的恢复时间(25 秒)后在原地满血、带着 1 只狼影回来,之后重新开始每 25 秒加 1 只狼影;伺夜退场时狼群随之撤退,伺夜再部署回场时狼群重新带着 2 只狼影出现。
+27 -4
View File
@@ -19,7 +19,9 @@ view.setCamera(kind /* 'prep'|'normal'|'unite'|'boss' */, { rect, side, shop })
// folded shop's prep camera (official shop-collapsed framing, §21.27)
view.setPrep(privateState, { editable }) // render hand/temp/board pieces as chibis (spine or fallback); editable enables drag;
// a merge's new elite on the board plays fx.promote instead of fx.deploy (render/promote.js, §20.11),
// also after a battle (a merge between two preps: the last prep state is kept, §20.14)
// also after a battle (a merge between two preps: the last prep state is kept, §20.14);
// an operator new on the bench, a merge's elite and a piece put on another board tile land (§31.8)
view.replayDeploys(uids) // the round start: the own board's pieces land one after another, 50 ms apart (§31.8)
view.enterBattle(fieldMeta) // switch to battle rendering for m.field
view.pushSnapshot(snap) ; view.pushEvents(ev) // interpolated rendering (100 ms buffer)
view.highlightTiles(tiles, style) // legal/illegal placement, range preview
@@ -67,7 +69,7 @@ its board-space position and facing, including on a mirrored boss half. They can
from this preview. The inspection affordance does not make the preview a combat unit; the battle sim still creates
the selected variant at battle start (§30.22).
Audio: autochess BGM per phase, UI SFX (buy/sell/refresh/level/merge/ready/timer), per-unit attack/hit SFX (throttled; an impact plays for a hostile attack's phys / arts / true damage only, never an operator's skill-mode file for a normal attack, ≤ 2 copies of one battle sound — §18.4; a skill cue that reaches the client before its unit — the sim emits a deploy-tick cast ahead of the unit's `['spawn']` — is held and sounds once when the unit is tracked, `AudioManager.pendingSkill`, GitHub PR #292 by @LimitlessHPPK; entering a field late hands the sound the buffered `spawn` tuples only, `screens/game/early.js audioEarly`, so no old death, deploy or cast replays), boss music in R14/15. While the music is silent — muted, or the BGM volume at 0 — no BGM is downloaded: the track the screen wants is remembered, a different one still playing stops, and the wanted track is fetched once the music is audible again (`AudioManager.bgmSilent`; about 14 MB a match for a player who turned the music off).
Audio: autochess BGM per phase, UI SFX (buy/sell/refresh/level/merge/ready/timer), a prep piece's landing sound (its battle deploy sound, once per landing, §31.8), per-unit attack/hit SFX (throttled; an impact plays for a hostile attack's phys / arts / true damage only, never an operator's skill-mode file for a normal attack, ≤ 2 copies of one battle sound — §18.4; a skill cue that reaches the client before its unit — the sim emits a deploy-tick cast ahead of the unit's `['spawn']` — is held and sounds once when the unit is tracked, `AudioManager.pendingSkill`, GitHub PR #292 by @LimitlessHPPK; entering a field late hands the sound the buffered `spawn` tuples only, `screens/game/early.js audioEarly`, so no old death, deploy or cast replays), boss music in R14/15. While the music is silent — muted, or the BGM volume at 0 — no BGM is downloaded: the track the screen wants is remembered, a different one still playing stops, and the wanted track is fetched once the music is audible again (`AudioManager.bgmSilent`; about 14 MB a match for a player who turned the music off).
---
@@ -159,8 +161,8 @@ an unknown unit, and a late-join snapshot restores the current mode. A mode with
sound. A selected mode with no mix uses its own default volume/probability, not the ordinary bank's mix.
A model that arrives after an actual deployment continues Start at the elapsed game time; after the clip's duration it
shows the current base pose. A subsequent attack/death supersedes that pending entrance. No extra deployment sound
or per-phase animation schedule is added by this change.
shows the current base pose. A subsequent attack/death supersedes that pending entrance. That change (0.2.3) added no
deployment sound or per-phase animation schedule; the prep landings of §31.8 below add both for prep pieces.
The simplified-view notice describes a renderer load failure, rather than declaring the device incapable of 3D.
On same-battle metadata refresh, remembered once-spawned device records also seed the renderer's new entry (§29.14).
@@ -192,3 +194,24 @@ while 「显示伤害数字」 is also on, the field draws a red integer for a `
for a dodge fx whose payload is `num: 'm'` (PRTS 作战机制 「伤害红字(暴击)」). Ordinary hits and heals draw no
digits in that mode. The mark is computed in the sim; the random offset above the model is client-side. The
simplified board uses the same mark with a fixed slot. The title footer links the project repository in a new tab.
### Deploy animation replays (§31.8)
PR #416 by @Cromemadnd. A prep piece that lands plays its Start clip again with its landing sound, once per landing
(`render/units.js UnitView.land`): the sound its battle deployment makes (`audio.js deployCue` → `deploySfxUrl`, its own
ON_UNIT_BORN file else b_char_set / b_char_tokenset) through the SFX volume, mute and the battle limiter. A piece whose
Start still plays (on its model, owed to a model still loading, or cued less than a clip ago) keeps it and sounds nothing
more. Only an idle prep piece lands, never a battle unit or one attacking, dying, dragged or in another clip, and nothing
lands in a hidden tab. Battle deploys keep the silent `onDeploy`; audio.handleBattleEvents sounds them.
- Round start (the maintainer's confirmation from official play, 2026-10-13): during 回合开始 the own board's pieces land
one after another in deployment order — operators by column from the left, top to bottom within a column, then the
summons (`ui/gameLogic deployOrder`, the order of Battle.start) — `DEPLOY_REPLAY_MS` 50 ms apart [ASSUMED], chained
timeouts (`render/app.js replayDeploys`). `screens/game.js` asks once its field-view wiring has run and marks the round
only when the view has taken the sequence (`roundStartReplay`): a view or board not built yet is asked again during
回合开始, a resync or re-render of the same round replays nothing. Own board only: never a scouted or watched
teammate's board, a spectator seat, an eliminated player or the boss partner's half. The timers end with prep mode, a
fresh prep scene and `destroy()`.
- [ASSUMED] (the PR author's account): an operator that arrives on the bench (a purchase, a reward, a grant) and a merge's
elite land; so does a piece that a new state puts on a board tile it was not on (from the bench, another tile, a swap),
when that state arrives. Turning in place, a retreat and bench moves do not.
+59
View File
@@ -117,3 +117,62 @@ and its glass, which were matched to screenshots, and does not touch the scene m
lightmap page) stays [ASSUMED]. The water (#549's second half, PR #555) is unchanged here.
Tests: `test/render/board3d-scene.test.js`; the 3D board browser suite with the extracted scenes.
### 31.8 Deploy animation replays (PR #416)
PR #416 by @Cromemadnd, written against 0.2.2 and ported onto the current files.
**Seen.** A prep piece never played its Spine Start clip or a landing sound outside a battle. A piece put on the board
through the direction wheel played nothing: the state that shows the move arrives while the wheel's hold still pins the
piece on its tile, so `setPrep`'s branch for a piece entering the board was never reached. A purchase only flashed
(`fx.deploy`), and the board rebuilt after every battle stood still.
**Official / decision.** The maintainer confirmed from official play on 2026-10-13 that at the start of every round the
operators on the board replay their Start animation and landing sound one after another in deployment order. The PR also
plays them when an operator is bought and when it enters the board or swaps tiles; its author says the official client
does so. Those two triggers are [ASSUMED] on that account. The PR's late-model case (a deploy asked before the Spine model
has loaded) was fixed in 0.2.3 (§28.22: the late model continues Start at the elapsed game time) and stays as it is.
**Now.**
- `UnitView.land()` (render/units.js) plays the piece's Start clip again through `onDeploy` (its fade-in and the late-model
continuation) and asks `audio.deployCue` for the landing sound: the piece's battle deploy sound (`deploySfxUrl`: its own
ON_UNIT_BORN file at its bank gain, else b_char_set / b_char_tokenset at 0.5) through the SFX channel's volume and mute
and the battle limiter (≤ 2 copies of one sound, a 160 ms cooldown per piece). One landing makes one sound: a piece
whose Start still plays keeps it and sounds nothing more, whether the clip runs on its model, is owed to a model still
loading, or was cued less than a clip length ago. That covers a purchase followed by a move, and the round start's replay
reaching a piece just put down. Only an idle prep piece lands. A battle unit never does: the sim's `deploy` events keep
the silent `onDeploy`, and audio.handleBattleEvents makes their sound (the PR's view-side sound would have doubled every
battle deployment). Nor does a piece that is attacking, dying, lifted by a drag or in another clip, or any piece while the
tab is hidden (the clip and the suspended audio would play it stale on return).
- Round start (`ui/gameLogic roundStartReplay`, `screens/game.js`, `render/app.js replayDeploys`). During 回合开始 the own
board's pieces land one after another in deployment order, each turn chained 50 ms after the one before. The order is the
battle's (`deployOrder`, as Battle.start): operators by column from the left, top to bottom within a column, then the
summons the same way, in board coordinates. The PR used the m.private board list, which is reading order. The screen
asks after its field-view wiring effect, so the view already holds the round's pieces, and it marks the round only once
the view has taken the sequence. A view still loading or a board not built yet is asked again on the next render of
回合开始; the PR marked the round first, so a late view never replayed. A re-pushed state (a reconnect's resync) or a
re-render of the same round plays nothing more, and nothing is replayed outside 回合开始. The timers end with prep mode
(`enterBattle`, which a scouted board also uses), with a fresh prep scene and with `destroy()`; a turn whose piece is
gone or rebuilt passes over it.
- Spectators and watched boards: the replay plays on the viewer's own board only. A scouted or watched teammate's prep
board, a spectator seat (no m.private), an eliminated player and the boss partner's half in a Final Assault prep get none.
- Purchase and board triggers (`render/app.js setPrep`). An operator that has just arrived on the bench lands: a purchase,
a reward or a grant, and a merge's elite on its bench slot or on the copy's tile. A summon that arrives with its owner's
placement does not, nor does an item. A piece that a new state puts on a board tile it was not on lands when that state
arrives, whether it came from the bench, from another tile or as the partner of a swap. The PR also landed a piece at
the wheel's confirm, which sounded twice whenever the server answered after 0.3 s, and on every move on the bench and
every retreat. Here turning in place, a retreat and bench moves do not land. That is narrower than the PR's wheel hook,
which fired on every confirm. The deploy flash (`fx.deploy`) is unchanged.
**[ASSUMED]** The purchase and board triggers (the PR author's account). The 50 ms gap (`DEPLOY_REPLAY_MS`, the PR's
value; no source gives the official one). Summon pieces taking part in the replay after the operators, as in the battle's
deployment. The clip length a landing counts as playing when the model's own is unknown (`START_CLIP_FALLBACK` 1 s; the
operators' Start clips run 0.77–1.2 s). A page reloaded during 回合开始 replays once in the new page.
**Tests.** `test/render/deploy-replay.test.js` (the clip, one sound, a second trigger, a late model, the refusals, a hidden
tab; the app.js wiring), `test/ui/deploy-replay.test.js` (`deployOrder`, `roundStartReplay`, the screen wiring; `deployCue`:
the files, the limiter keys, volume and mute), `test/render/playtest6-promote.test.js` (the elite's cue), and the browser
check `test/ui/deploy-replay.e2e.test.js` (`SP_E2E=1`, mock harness: SETTLE → 回合开始 replays the board in deployment order
about 50 ms apart with one sound each, once per round and again the next round; in the 休整期 a purchase lands, a move
within its Start sounds nothing more, a later move lands, a turn in place does not). Golden: unchanged (client only).