From 205bca7a07d32b6f46b2d61d86f80652cd5bda61 Mon Sep 17 00:00:00 2001 From: sganggs Date: Tue, 6 Oct 2026 21:07:23 +0800 Subject: [PATCH] =?UTF-8?q?feedback5=20WZ:=20the=20browser=20boss-field=20?= =?UTF-8?q?check=20takes=20its=20=E7=98=AB=E7=97=AA=20from=20copies=20of?= =?UTF-8?q?=20the=20four=20captured=20specs=20with=20a=20golden=20?= =?UTF-8?q?=E5=82=AC=E6=B3=AA=E7=93=A6=E6=96=AF=20in=20every=20operator's?= =?UTF-8?q?=20second=20slot,=20compared=20Chrome=20vs=20Node=20like=20the?= =?UTF-8?q?=20originals=20(test/sim/bossfield.browser.test.js=20withTearGa?= =?UTF-8?q?s)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Seed 22's only 麻痹 source was one 5 % 催泪瓦斯 on 洛洛: a single hit at 0.1.4, and since WE1's enemy damage frame (89f730d0, server/sim/ai.js enemyAttack; it re-timed every fight) none of its rolls hits — 0 attacks in the 17 s boss fight, 12 in the hidden one. The item, the status and the fx work: forced to 100 % the hidden field shows 26 麻痹 and 8 瘫痪. Every other check (digest / pool / fx / 限伤 / drones / 奥术) still runs on the unmodified specs. With the copies, 瘫痪 occurs on every field of all 19 seeds (of 1–120) that pass the other checks (seed 22: 4 / 7 / 29 / 13); with the palsy fx removed from the sim the test fails (0,0,0,0). A test fixture fix, no product change; golden unchanged. --- test/sim/bossfield.browser.test.js | 40 +++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/test/sim/bossfield.browser.test.js b/test/sim/bossfield.browser.test.js index dd5224ee..fef482c2 100644 --- a/test/sim/bossfield.browser.test.js +++ b/test/sim/bossfield.browser.test.js @@ -8,6 +8,9 @@ // with the field's LocalBossPool, and must give the same result digest, the same pool and the same fx per kind. // The fields exercise the playtest-6b sim paths: 直接乘算 bonus sums (§20.10), one 奥术 instance per target // (applyStrongest), the drone 2 % links and the 剑 / 锤 / shell kit (§20.13), and 限伤 cancels (fx 'hitCap', §20.12). +// 瘫痪 (fx 'palsy': a 麻痹 stack interrupts an enemy attack) is checked on the same four specs with a golden 催泪瓦斯 in +// every operator's second slot (withTearGas), compared Chrome vs Node like the others: the bots' only 麻痹 had been one +// 5 % 催泪瓦斯 on one operator — a single hit at 0.1.4, none since 0.2.0's enemy damage frame (WE1) re-timed every fight. // // Opt-in (starts Chrome): SIM_E2E=1 node --test test/sim/bossfield.browser.test.js (or RENDER_E2E=1) // Run browser test files one at a time. Chrome path: $CHROME_PATH or the macOS default. @@ -59,6 +62,21 @@ function captureBossSpecs() { return specs; } +/** + * A copy of `spec` where every operator carries a golden 催泪瓦斯 (攻击时有5%概率使目标获得一层麻痹) in its second slot — + * two slots, as in the game: a 麻痹 source on every field whatever the bots drafted (seed 22's own was a single 5 % roll). + */ +const TEAR_GAS = 'chess_item_5_01_e_b'; +function withTearGas(spec) { + const s = JSON.parse(JSON.stringify(spec)); + for (const p of s.players || []) { + for (const u of p.units || []) { + if (u.kind === 'chess') u.items = [...(u.items || []).filter((id) => !String(id).startsWith('chess_item_5_01_e')).slice(0, 1), TEAR_GAS]; + } + } + return s; +} + /** * Runs a spec to the end (the same code in Node and in the page: `S` = spec module, `ds` = data source) and returns * its digest, the local pool, the fx count per kind and the leader-side checks (奥术 instances, 无来源 HP losses). @@ -100,8 +118,10 @@ describe('Final Assault / Hidden Core fields in the browser sim', { skip }, () = test('real boss specs at 999 layers: Chrome and Node give the same digest, pool and fx; 限伤, drones, 瘫痪 and 奥术 all occur', { timeout: 180000 }, async () => { const specs = captureBossSpecs(); assert.deepEqual(specs.map((s) => `${s.kind}:${s.bossId}`).sort(), ['boss:boss_1', 'boss:boss_1', 'hidden:boss_8', 'hidden:boss_8']); + // the four as captured, then the same four with a 麻痹 source on every field (withTearGas) + const runs = [...specs, ...specs.map(withTearGas)]; const ds = new DataSource(DATA, null); - const node = specs.map((spec) => runField({ createBattleFromSpec, resultDigest }, ds, spec, MAX_SECONDS)); + const node = runs.map((spec) => runField({ createBattleFromSpec, resultDigest }, ds, spec, MAX_SECONDS)); const page = await browser.newPage(); const problems = []; @@ -115,12 +135,12 @@ describe('Final Assault / Hidden Core fields in the browser sim', { skip }, () = const { spec: S, ds } = await loadBrowserSim(); const run = new Function(`return (${src})`)(); return all.map((spec) => run(S, ds, spec, maxSeconds)); - }, specs, runField.toString(), MAX_SECONDS); + }, runs, runField.toString(), MAX_SECONDS); await page.close(); assert.deepEqual(problems, []); - specs.forEach((spec, i) => { - const at = `${spec.kind} ${spec.fieldId} ${spec.bossId}`; + runs.forEach((spec, i) => { + const at = `${spec.kind} ${spec.fieldId} ${spec.bossId}${i >= specs.length ? ' +催泪瓦斯' : ''}`; assert.equal(node[i].reason, 'cleared', `${at}: the 999-layer pair clears the field`); assert.equal(chrome[i].json, node[i].json, `${at}: browser and Node results are identical`); assert.equal(chrome[i].hash, node[i].hash, at); @@ -129,10 +149,12 @@ describe('Final Assault / Hidden Core fields in the browser sim', { skip }, () = assert.equal(chrome[i].hpLoss, node[i].hpLoss, `${at}: same 无来源 HP losses on the leader`); assert.ok(node[i].maxArcane <= 1, `${at}: one 奥术 instance on the leader at a time (${node[i].maxArcane})`); }); - const sum = (k) => node.reduce((a, r) => a + (r.fx[k] || 0), 0); - assert.ok(sum('hitCap') >= 1, `a 999-layer hit reached 300000 and was cancelled (限伤: ${sum('hitCap')})`); - assert.ok(sum('palsy') >= 1, `瘫痪 occurred (${sum('palsy')})`); - assert.ok(node.every((r) => r.hpLoss >= 1), `every field cost the leader 无来源 HP (drone links / 剑 · 锤 transfers: ${node.map((r) => r.hpLoss)})`); - assert.ok(node.some((r) => r.maxArcane === 1), 'the leader carried 奥术'); + const real = node.slice(0, specs.length); + const gassed = node.slice(specs.length); + const sum = (rows, k) => rows.reduce((a, r) => a + (r.fx[k] || 0), 0); + assert.ok(sum(real, 'hitCap') >= 1, `a 999-layer hit reached 300000 and was cancelled (限伤: ${sum(real, 'hitCap')})`); + assert.ok(sum(gassed, 'palsy') >= 1, `瘫痪 occurred with the 催泪瓦斯 (${gassed.map((r) => r.fx.palsy || 0)})`); + assert.ok(real.every((r) => r.hpLoss >= 1), `every field cost the leader 无来源 HP (drone links / 剑 · 锤 transfers: ${real.map((r) => r.hpLoss)})`); + assert.ok(real.some((r) => r.maxArcane === 1), 'the leader carried 奥术'); }); });