feedback1 WD: 逐火 embers can be beaten, 转译基底 changes form before it can die

Player reports after 0.1.0:
- #8 深池逐火战士 / 精锐战士 / 护卫: every knock-out is a 1 s 重生 (invulnerable,
  unblockable, immobile), then a walking, disarmed, 隐匿 hit-count ember that a
  blocker can target and beat; it stands up with full HP after 10 s (PRTS talent
  text). The old ember stood still, stealthed and unblockable, so nobody could
  ever target it and every 逐火 enemy leaked. 假想敌:再生's puppet walks on and
  stays unblockable.
- #5 转译基底·α: its original form cancels every damage instance; the 4th
  physical / arts hit or a block starts a 2 s change into 寻仇者 / 特战术师 /
  幽灵 (Mode_*_Passive, 寻仇者 rage below half HP, 特战术师 two targets). The
  armed forms attack through the engine (profile.dmgType). It used to die in its
  first form.
- Render: units.js FORMS switch the models' clip sets (A_Die_B/C/D then B/C/D,
  Die then Idle_2/Move_2/Die_2, Revive), driven by the 'phase' kind or the fx
  `form`; a noMove enemy is drawn standing.
This commit is contained in:
sganggs committed 2026-10-03 01:52:40 +08:00
1 parent 7f134737ce
commit 92282878d3
9 files changed
+676 -112

No files matched your search

+9 -6
View File
@@ -1420,12 +1420,15 @@ export async function createFieldView(host, options = {}) {
consumedIds.add(e[4].id);
if (consumedIds.size > 200) consumedIds.delete(consumedIds.values().next().value);
}
// an enemy's mode change (掠海漂移体 → 爬行模式, user playtest #5 item 1): its view switches clip set
// (UnitView.setForm); the info keeps it for a view built later
if (e[1] === 'phase' && e[4] && typeof e[4] === 'object' && e[4].id != null) {
const inf = infos.get(e[4].id);
if (inf) inf.form = typeof e[4].kind === 'string' ? e[4].kind : null;
views.get(e[4].id)?.setForm?.(e[4].kind);
// an enemy's mode change (掠海漂移体 → 爬行模式, user playtest #5 item 1; 转译基底's forms, a 逐火 ember and its
// revival — user report after 0.1.0): a 'phase' kind, or the `form` any other fx carries, switches the view's
// clip set (UnitView.setForm); the info keeps it for a view built later
const ex4 = e[4] && typeof e[4] === 'object' && e[4].id != null ? e[4] : null;
const form = !ex4 ? undefined : e[1] === 'phase' ? ex4.kind : 'form' in ex4 ? ex4.form : undefined;
if (form !== undefined) {
const inf = infos.get(ex4.id);
if (inf) inf.form = typeof form === 'string' ? form : null;
views.get(ex4.id)?.setForm?.(form);
}
fx.simFx(e[1], Number(e[2]), Number(e[3]), e[4]);
break;
+33 -12
View File
@@ -100,21 +100,42 @@ export const DOWN_LOOK = Object.freeze({
export const EL_BAR = Object.freeze({ icon: 0.15, min: 8, max: 15, enemy: 0.8, gap: 1, pulse: 0.45, pulseHz: 1.5 });
/**
* Enemy modes drawn with another clip set of the same skeleton (sim fx 'phase' kind → UnitView.setForm), per Spine
* id: 掠海漂移体 (PRTS: 受晕眩/沉睡/冻结影响后进入爬行模式 — for good) crawls on its *_02 clips after 'Change'. The mode's
* roles override the manifest's (data/assets.json anims); 吉兆飞鳞's 晕眩模式 is its Stun clip already.
* Enemy modes drawn with another clip set of the same skeleton (sim fx 'phase' kind, or the `form` of another sim fx
* → UnitView.setForm), per Spine id; the mode's roles override the manifest's (data/assets.json anims), `change` plays
* once first:
* - 掠海漂移体 (PRTS: 受晕眩/沉睡/冻结影响后进入爬行模式 — for good) crawls on its *_02 clips after 'Change';
* - 转译基底·α (user report after 0.1.0, #5): its 2 s change clip A_Die_B / _C / _D, then 寻仇者 B_*, 幽灵 C_* (no attack
* clip: it never attacks) or 特战术师 D_* — its manifest roles are the original form's A_Idle / A_Move;
* - 深池逐火战士 / 精锐战士 / 护卫 (#8): knocked out → 'Die' (the 1 s 重生), the 余烬 on Idle_2 / Move_2 and its death on
* Die_2; standing up again → 'Revive', then the warrior's manifest clips;
* - 假想敌:再生: knocked out → A_Die, the 傀儡 on B_Idle / B_Move / B_Die; back → B_Revive, then the A_* manifest clips.
* 吉兆飞鳞's 晕眩模式 is its Stun clip already.
*/
const loop = (name, via = null) => Object.freeze(via ? { begin: null, loop: name, end: null, via } : { begin: null, loop: name, end: null });
const clipSet = (idle, move, die, attack = null) => Object.freeze({
idle, deploy: idle, die, move: loop(move),
attack: attack ? loop(attack, 'attackAny') : null,
skill: attack ? Object.freeze({ begin: null, loop: attack, end: null, via: 'attack', index: 0, idle: null }) : null,
});
const EMBER = Object.freeze({
husk: Object.freeze({ change: 'Die', roles: clipSet('Idle_2', 'Move_2', 'Die_2') }),
revived: Object.freeze({ change: 'Revive', roles: Object.freeze({}) }),
});
export const FORMS = Object.freeze({
enemy_2025_syufo: Object.freeze({
crawl: Object.freeze({
change: 'Change',
roles: Object.freeze({
idle: 'Idle_02', deploy: 'Idle_02', die: 'Die_02',
move: Object.freeze({ begin: null, loop: 'Move_02', end: null }),
attack: Object.freeze({ begin: null, loop: 'Attack_02', end: null, via: 'attackAny' }),
skill: Object.freeze({ begin: null, loop: 'Attack_02', end: null, via: 'attack', index: 0, idle: null }),
}),
}),
crawl: Object.freeze({ change: 'Change', roles: clipSet('Idle_02', 'Move_02', 'Die_02', 'Attack_02') }),
}),
enemy_10081_mpplai: Object.freeze({
translator_fuchou: Object.freeze({ change: 'A_Die_B', roles: clipSet('B_Idle', 'B_Move', 'B_Die', 'B_Attack') }),
translator_youling: Object.freeze({ change: 'A_Die_C', roles: clipSet('C_Idle', 'C_Move', 'C_Die') }),
translator_shushi: Object.freeze({ change: 'A_Die_D', roles: clipSet('D_Idle', 'D_Move', 'D_Die', 'D_Attack') }),
}),
enemy_1288_duskls: EMBER,
enemy_1288_duskls_2: EMBER,
enemy_1292_duskld: EMBER,
enemy_9010_acpupp: Object.freeze({
husk: Object.freeze({ change: 'A_Die', roles: clipSet('B_Idle', 'B_Move', 'B_Die') }),
revived: Object.freeze({ change: 'B_Revive', roles: Object.freeze({}) }),
}),
});