feedback5 WAA: the 孤岛风云 prisoners leave the gate confined and change model as their official prefabs do — grey while 【禁锢】, the blinking orange warning before the last confined attack, red once freed; the 4th attack already hits freed (community report of 2026-10-06 「囚徒类敌人出门就是已解放的模型」)
- Cause, client: the prisoner skeletons carry three clip sets whose collar light is grey, blinking orange and red. The official battle prefabs (local client battle/enm_pfb_6.ab: the Graphic component's anim key -> clip table, the modes' replace pairs, the clip the Spine starts on) draw mode Default (【禁锢】) on the grey set, mode R (the warning) on the orange set and mode L (【解放】) on the red one. The name-based role resolver gave 普通囚犯 / 老练囚犯 their unnumbered set — the red one — so they came out of the gate freed; no FORMS entry existed, so no prisoner ever changed (强壮囚犯 / 拳师囚犯 / 重犯 / 传奇重犯 stayed grey). - Cause, sim: buff_template_data enemy_confinement[atk_cnt] spends a charge ON_BEFORE_ATTACK (not while 麻痹) and switches to mode R when one is left; enemy_liberty_listener[warning] switches to mode L before the next attack (PRTS "进行第4次攻击前,切换至解放状态"). archetypes.js prisoner() counted on the 'attack' hook, after the damage, so the 4th attack still hit confined, and it sent no form. - Now: prisoner() counts in `before` (an attack 麻痹 interrupts never gets there, PRTS 拳师囚犯), setForm 'warning' (fx 'phase', drawn silently: kinds.js SILENT_PHASES) before attack times-1 and 'liberty' (fx 'liberate') before attack times; 重犯's free-all and 杰斯顿's KO switch the freed ones too. render/units.js FORMS maps 'warning' / 'liberty' for the six skeletons (普通 / 老练囚犯 *2 / unnumbered, 强壮囚犯 *2 / *3, 拳师囚犯 / 重犯 / 传奇重犯 *_orange / *_red). tools/assets/spine.mjs PREFAB_SPINE_ROLES gives 普通囚犯 / 老练囚犯 their grey *3 set; data/assets.json regenerated (fetch-assets --offline --add-only, JSON compare: only those two anims and the hash), data/enemies.json too (build-data --offline: only their attackAnim.clip Attack -> Attack3, same length and strike frame). The 3D board draws the same Pixi views. - Docs: ASSETS.md (PREFAB_SPINE_ROLES, the prisoner forms), SIM.md (the form fx), design/client.md (FORMS). - Tests: test/content/feedback5-prisoners.test.js (the six: warning before attack 3, attack 4 hits with ATK +50 %, the forms reach the client and UnitInfo; free-all; 麻痹), test/render/feedback5-prisoners.test.js (manifest = the prefab's grey set, FORMS sets, view clips, PREFAB_SPINE_ROLES = the committed manifest) — both fail on the parent but the 麻痹 pin; test/render/feedback5-prisoners.browser.test.js (RENDER_E2E=1: three prisoners on the 2D and the 3D board, each model's set follows its sim form). - Golden: 28 scenarios. Only the warning's new 'phase' fx (the prisoner fell before its 4th attack): roster-001, roster-003, roster-007, roster-024, roster-033, bond-miraShip-low. The 4th attack now hits freed, one attack earlier than before, so the fight after it moves: roster-004, roster-022, roster-026, bond-kazimierzShip-low, bond-raidShip-low, bond-soloShip-low, bond-victoriaShip-low, bond-visiShip-low, standin-01, diy-024, diy-061, diy-065, diy-066, diy-094, diy-097, diy-104, diy-105, diy-127; only the per-second snapshots besides the fx: roster-021, diy-017; and the bot matches whose battles field prisoners: solo-ABYSS-2, solo-HARD-9-boosted.
This commit is contained in:
1 parent
e1ffcd1b07
commit
6372f2892b
18 files changed
+788
-352
No files matched your search
@@ -85,8 +85,8 @@
|
||||
// kind, tiles?, id?, form?} (delayed strikes, charges, 'reborn' with form 'reborn') · 'beam' {from, to, kind} ·
|
||||
// 'summon' {id, key} · 'ember' {id, hits, dur, form: 'husk'} · 'revive' {id, kind?, form: 'form2' | 'revived' | 'fly'}
|
||||
// · 'stone' {id, dur, form: 'stone'} · 'blink' {id, fx, fy} · 'charge' {id, tx, ty} · 'expose' {id} · 'shieldBreak'
|
||||
// {id} · 'liberate' {id} · 'phase' {id, kind, dur?, form?} (form changes — 掠海漂移体 'crawl', 暴鸰 'bombed',
|
||||
// translator_* — and barrier / charge states) · 'lpLoss' {value, reason} · 'steal' · 'ignite'. Forms go through
|
||||
// {id} · 'liberate' {id, form: 'liberty'} · 'phase' {id, kind, dur?, form?} (form changes — 掠海漂移体 'crawl', 暴鸰
|
||||
// 'bombed', translator_*, a prisoner's 'warning' — and barrier / charge states) · 'lpLoss' {value, reason} · 'steal' · 'ignite'. Forms go through
|
||||
// setForm(): the unit keeps it (`e.form`, UnitInfo `form`) and the fx's `form` is the model's clip set from then on
|
||||
// (render/app.js → UnitView.setForm, units.js FORMS); the client keeps every fx with a `form` through catch-ups and
|
||||
// hidden tabs (shared/protocol.js fxForm).
|
||||
|
||||
@@ -232,16 +232,28 @@ const selfFear = (ab) => ({
|
||||
},
|
||||
});
|
||||
|
||||
/** Prisoners (禁锢 → 解放). */
|
||||
/**
|
||||
* Prisoners (孤岛风云: 禁锢 → 解放). The official battle prefabs (local client battle/enm_pfb_*.ab) have three modes and the
|
||||
* buff templates (buff_template_data enemy_confinement, enemy_confinement[atk_cnt], enemy_liberty_listener[warning]) move
|
||||
* between them: the 禁锢终端 starts with `confinement.times` charges; before each attack (ON_BEFORE_ATTACK — here the
|
||||
* `before` hook: an attack 麻痹 interrupts never gets there, PRTS 拳师囚犯 "在持有麻痹的情况下,攻击不计数") one is spent, and
|
||||
* the one that leaves a single charge switches it to mode R, the warning — still confined, its collar light blinking
|
||||
* orange; before the next attack it switches to mode L, 【解放】, so the times-th attack already hits freed (PRTS
|
||||
* "进行第4次攻击前,切换至解放状态"; until 0.2.0 it was freed after that attack's damage). The model follows the mode
|
||||
* (setForm: UnitInfo `form`, render/units.js FORMS): the manifest's clips are the grey confined set, 'warning' the orange
|
||||
* set, 'liberty' the red one — the 'liberate' fx carries it (a community report of 2026-10-06: they came out of the gate
|
||||
* drawn freed and never changed).
|
||||
*/
|
||||
function prisoner(ab, { freeAll = false } = {}) {
|
||||
const t = (k) => T(ab, k) ?? 0;
|
||||
const times = Math.max(1, Math.round(T(ab, 'confinement.times') ?? 4));
|
||||
const liberate = (b, e, a) => {
|
||||
if (a.free || !e.alive) return;
|
||||
a.free = true;
|
||||
b.removeBuff(e, 'ab:confined');
|
||||
b.addBuff(e, { key: 'ab:liberty', persist: true, visible: true, mods: {
|
||||
atkPct: t('liberty.atk'), defIgnorePct: t('liberty.def_penetrate'), resFlat: t('liberty.magic_resistance'), hpRegen: t('liberty.hp_recovery_per_sec') } });
|
||||
b.fx('liberate', { x: e.x, y: e.y, id: e.id });
|
||||
setForm(b, e, 'liberty', 'liberate');
|
||||
if (freeAll && !stOf(b).freedAll) {
|
||||
// "第一次解放时同时解放全场敌人"
|
||||
stOf(b).freedAll = true;
|
||||
@@ -254,7 +266,11 @@ function prisoner(ab, { freeAll = false } = {}) {
|
||||
a.n = 0; a.free = false;
|
||||
b.addBuff(e, { key: 'ab:confined', persist: true, visible: true, mods: { aspd: t('confinement.attack_speed'), defFlat: t('confinement.def') } });
|
||||
},
|
||||
attack(c, b, e, a) { if (!a.free && ++a.n >= (T(ab, 'confinement.times') ?? 4)) liberate(b, e, a); },
|
||||
before(c, b, e, a) {
|
||||
if (a.free) return;
|
||||
if (++a.n >= times) liberate(b, e, a);
|
||||
else if (a.n === times - 1) setForm(b, e, 'warning');
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user