feedback2 WA: DESIGN §22.1 (Back-facing operators play their fall, GitHub issue #25), docs/ASSETS.md model choice and die role

This commit is contained in:
sganggs committed 2026-10-03 22:58:26 +08:00
1 parent 072e1231ae
commit e08f22447b
2 files changed
+16 -4

No files matched your search

+3 -2
View File
@@ -197,7 +197,8 @@ Roles = {
attackDown: Clip|null, // _Down variants (target below the unit)
skill: SkillClip|null, // for the chess's default skill (primary index)
skills?: { [index]: SkillClip }, // when the char is used with several default skills (backups)
die: string|null, // null ⇒ use the Front model's Die (Back lacks it) or fade out 0.5 s
die: string|null, // null ⇒ a Back model gives way to the Front model's Die (DESIGN §22.1); any other
// skeleton holds its idle's first frame while it fades out
move: Clip|null, // enemies: Move_Begin|Move_Start + Move_Loop|Move + Move_End → Run_*
stun: Clip|null // null ⇒ freeze the track (timeScale 0)
}
@@ -237,7 +238,7 @@ Not mapped (clip names ambiguous): “自在”, “巨大的丑东西”, 主
is not known) keep their manifest clips.
Other renderer rules from research 07 §5.4–5.5:
- **Choosing the model:** Front when the unit faces right or down; Front mirrored when facing left; Back when facing up.
- **Choosing the model:** Front when the unit faces right or down; Front mirrored when facing left; Back when facing up — while it stands: a dead or knocked-out operator falls and lies with the Front model unless its Back skeleton has a Die clip of its own (131 of the 135 have none; DESIGN §22.1, GitHub issue #25).
- **Attack speed:** set the attack `timeScale` to `duration / attackInterval`.
- **Model size:** every skeleton is drawn at one `UNIT.modelScale` (render/style.js, 320 skeleton units per tile), which stands for the official standard. The official client also scales each enemy model in its battle prefab: the Graphic / FaceSwitcher / Spine transforms multiply to 0.27 for most enemies and for the operators' battle skins, but not for all of them. For example, 威龙 is 0.16, 妖怪 0.20 and 青铜镜 0.6. The skeletons themselves carry no such scale, because every enemy SkeletonDataAsset uses 0.01. So an enemy is drawn × data/enemies.json `modelScale` (its prefab's product ÷ 0.27, see docs/DATA.md; user playtest #6: 威龙 used to be drawn 1.35× a 妖怪 instead of 1.08×), and its HP bar sits on that model: at its setup-pose bounds' height × the same factors, or, for a skeleton without bounds, at the chibi headroom × `modelScale` (bosses 2.2 tiles). `tools/local-extract/enemy_scales.py` reads the products from a local client, and `tools/build-data.mjs MODEL_SCALES` keeps them.
- **Enemy aliases:** `enemies[id].spineAliasOf` means the model belongs to another enemy. Two cases:
+13 -2
View File
@@ -1462,9 +1462,20 @@ Two public GitHub issues after 0.1.0, both client-only. Normative lines rewritte
TBD-22-intro
### 22.1 Back-facing operators play their fall (GitHub #25)
### 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`
TBD-22.1
- **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 德克萨斯, "并非稳定出现"). **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 Roles `die: 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 (`dieClipDur` of its manifest entry), so the other 131 fall and lie with the Front model (unmirrored, as facing UP).
- `die()` and `revive()` mark the view, and its next `update()` — 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 联防 entry `FORCED_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 (`_dropActor` drops the cached tint; a swapped-in model used to keep the previous model's tint state). A withdrawn operator facing UP (no `down` entry) fades like one facing RIGHT: the Front model's Die clip times the fade (`_fallDur`).
- The redeploy (a `deploy` event: timed, 不屈, 阿戈尔 — every `Battle._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: operators facing RIGHT / DOWN / LEFT, enemies and summons (no Back models in the manifest: `_wantsBack` is false for them), the redeploy ring, the sim. Nothing changes in render/interp.js or battle/runner.js: `die` / `deploy` are state events every client stage keeps (runner `keepsState`, game.js `keepEarly`, interp `isCosmeticEvent`), and b.snap `down` / `fieldMeta` rebuild 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; 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.js` unchanged.
### 22.2 突袭's idle redeploy lands where it can attack (#51)