feedback7: merge fb7-hud — HP-bar readouts (#303 / #378 / #383: ammo cells, 伺夜's wolf-shadow pips, 斩业星熊's 我执 bar), #302 renderer abort, #292 held skill cue, #334 atlas slots, #354 梓兰's per-card frost
- Grok review: MERGE (no defect). Golden unchanged (union OK); broad node --test 5327 (5326 pass, 0 fail, 1 skipped); lint 0 errors.
This commit is contained in:
commit
7d6fefe8f5
33 files changed
+1751
-77
No files matched your search
@@ -17,6 +17,8 @@ import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { createBattleRunner, keepsState, compactHeld, HELD_MAX, EV_SLICE } from '../../public/js/battle/runner.js';
|
||||
import { SnapshotBuffer } from '../../public/js/render/interp.js';
|
||||
import { keepEarly, audioEarly } from '../../public/js/screens/game/early.js';
|
||||
import { AudioManager } from '../../public/js/audio.js';
|
||||
import { createStore, initialState } from '../../public/js/store.js';
|
||||
import * as specMod from '../../server/sim/spec.js';
|
||||
import { DataSource } from '../../server/sim/simdata.js';
|
||||
@@ -243,6 +245,56 @@ test('screens/game.js buffers the form fx with the state-bearing events it repla
|
||||
assert.match(src, /lastFieldRef\.current = field\.fieldId;\s+reentryRef\.current = null;/);
|
||||
});
|
||||
|
||||
// GitHub PR #292 (by @LimitlessHPPK) wanted the whole pre-entry buffer handed to the sound as well, so that a deploy-tick cast
|
||||
// ahead of its unit's spawn would be heard. The review kept the hold of such a cast (audio.js `pendingSkill`, which the live
|
||||
// path needs: `onEv` already forwards every list) and refused the replay: the buffer is every spawn, die, deploy, status and
|
||||
// skill since the field's m.field (up to 1500 of them), so the sound would play the field's old deaths, deploy lines and casts
|
||||
// all at once on entering.
|
||||
test('entering a field late: the view replays every buffered state event, the sound only the spawn tuples that teach it who is there', () => {
|
||||
const src = readFileSync(path.join(ROOT, 'public/js/screens/game.js'), 'utf8');
|
||||
assert.match(src, /view\.pushEvents\(early\);/, 'the view gets the buffered list');
|
||||
assert.match(src, /audio\.handleBattleEvents\(audioEarly\(early\)\);/, 'the sound gets audioEarly of it');
|
||||
assert.ok(!/audio\.handleBattleEvents\(early\)/.test(src), 'never the whole list');
|
||||
const stream = [
|
||||
['atk', 1, 2, 'none'], // stale cosmetics: dropped by keepEarly
|
||||
['dmg', 2, 40, 'phys'],
|
||||
['skill', 1, 1], // a deploy-tick cast, ahead of its unit's spawn
|
||||
['spawn', { id: 1, side: 'ally', kind: 'op', spine: 'char_a', skillIndex: 2 }],
|
||||
['spawn', { id: 2, side: 'enemy', kind: 'enemy', spine: 'enemy_x' }],
|
||||
['deploy', 1],
|
||||
['skill', 1, 0],
|
||||
['fx', 'form', 2, 3, { id: 2, form: 'translator_youling' }],
|
||||
['die', 2, 'killed'],
|
||||
];
|
||||
const early = stream.filter(keepEarly);
|
||||
assert.deepEqual(early.map((e) => e[0]), ['skill', 'spawn', 'spawn', 'deploy', 'skill', 'fx', 'die'], 'the pre-entry buffer keeps every state-bearing kind');
|
||||
assert.deepEqual(audioEarly(early).map((e) => e[0]), ['spawn', 'spawn'], 'the sound is told who is on the field, nothing else');
|
||||
assert.deepEqual(audioEarly(null), [], 'no buffer, nothing');
|
||||
// through a real AudioManager: the replay makes no sound at all (no old death, deploy line or cast) but tracks the units,
|
||||
// so the first live cast after entering sounds
|
||||
const vm = { audio: { sfx: { ui: {}, battle: {}, units: { char_a: { skill: '/s/a_skill.mp3', born: '/s/a_born.mp3' }, enemy_x: { die: '/s/x_die.mp3' } } }, voice: {} } };
|
||||
const a = new AudioManager({ win: null, getManifest: () => vm });
|
||||
a.ctx = {};
|
||||
const played = [];
|
||||
a._play = (url) => { played.push(url); };
|
||||
a.voice = () => { played.push('voice'); return true; };
|
||||
a.setFieldUnits([]);
|
||||
a.handleBattleEvents(audioEarly(early));
|
||||
assert.deepEqual(played, [], 'the replay is silent');
|
||||
assert.equal(a.units.size, 2, 'and has taught the sound both units');
|
||||
a.handleBattleEvents([['skill', 1, 1]]);
|
||||
assert.ok(played.includes('/s/a_skill.mp3'), 'the next live cast of a known unit sounds');
|
||||
// (the contrast: the whole list would have played the buffered death, the deploy and the cast on entering)
|
||||
const b = new AudioManager({ win: null, getManifest: () => vm });
|
||||
b.ctx = {};
|
||||
const heard = [];
|
||||
b._play = (url) => { heard.push(url); };
|
||||
b.voice = () => true;
|
||||
b.setFieldUnits([]);
|
||||
b.handleBattleEvents(early);
|
||||
assert.ok(heard.length >= 2, `the whole list would sound ${heard.length} old cues at once`);
|
||||
});
|
||||
|
||||
/**
|
||||
* The view's side too (render/app.js): the runner's frames go into the render engine's SnapshotBuffer, whose clock
|
||||
* follows the snapshots, and processEvents takes the due events with the 1.5 game s stale-cosmetic window — a form fx
|
||||
|
||||
Reference in new issue
Block a user