feedback1 WD: a form change clip plays out; browser check of the forms

- render/spine.js: no attack (wind-up or 'atk') cuts a form's change clip short;
  a knocked-out 逐火 no longer reports its last attack in the snapshot.
- test/render/feedback1-forms.browser.test.js (opt-in RENDER_E2E=1): the real
  sim in headless Chrome on 战场#01 — 转译基底·α A_* → A_Die_B → B_* → B_Die,
  a 深池逐火战士 knocked out in front of its blocker: Die → the 隐匿 ember on
  Idle_2, beaten → Die_2.
- PLAYING.md: the 余烬 and 转译基底 rules for players.
This commit is contained in:
sganggs committed 2026-10-03 02:07:59 +08:00
1 parent 73e8bdcd87
commit 06be81f67e
4 files changed
+159 -3

No files matched your search

+3 -2
View File
@@ -13,6 +13,7 @@
// die() die clip once (callers fade out afterwards)
// stunned (setBase('stun')) stun clip, or the current track frozen at timeScale 0
// setForm(roles, change) another clip set of the skeleton (an enemy's mode), after a change clip
// (no attack cuts the change clip short)
// update(dt) advances the skeleton (autoUpdate is off: one clock for everything)
// Attack mode lasts until ~1.4 attack intervals without a new attack, then the end clip (if any) and base.
@@ -212,7 +213,7 @@ export class SpineActor {
* false when it is still too early (call again next frame) or there is nothing to wind up.
*/
windUp(interval, lead) {
if (this.dead || this.mode === 'stun' || this.mode === 'die' || !(lead >= 0)) return false;
if (this.dead || this.mode === 'stun' || this.mode === 'die' || this.mode === 'change' || !(lead >= 0)) return false;
const clip = this._attackClip();
if (!clip) return false;
if (this.mode === 'attack' && this.current === clip.loop) return false; // in rhythm: attack() re-phases
@@ -232,7 +233,7 @@ export class SpineActor {
/** An attack happened now. `interval` = seconds between attacks (game time already scaled to real). */
attack(interval) {
if (this.dead || this.mode === 'stun' || this.mode === 'die') return;
if (this.dead || this.mode === 'stun' || this.mode === 'die' || this.mode === 'change') return; // a form change plays out
this.interval = clampN(Number.isFinite(interval) && interval > 0 ? interval : this.interval, 0.08, 8);
const clip = this._attackClip();
if (!clip) return;