feedback6: merge R21H — 器物 (频次) enemies take the 攻坚 HP share and not 补给线's (the data's exclusion list names exactly the 14 器物), leader summons take the round's enemy scaling (PR #272 by @CXUtk, re-implemented from the data); tools/golden.mjs keeps R21H's roundMods import and drops uniteStageId (removed by R21B); golden regenerated on the merge — vs the parent exactly R21H's 74 scenarios, vs the branch only the other streams' moves

This commit is contained in:
sganggs committed 2026-10-07 18:21:01 +08:00
commit a9fe794c3f
20 files changed
+1493 -1326

No files matched your search

+1
View File
@@ -79,6 +79,7 @@ for (const n of [1, 2, 3, 4]) {
assert.deepEqual(f.opts.rect, GEO.BOSS_RECT);
assert.equal(f.opts.timeLimit, Infinity);
assert.equal(f.opts.flags.layerGainsEnabled, false);
assert.deepEqual(f.opts.flags.enemyScale, m.gd.enemyScale(14), 'the round\'s enemy effects for the leader\'s summons (PR #272)');
assert.ok(f.opts.players.every((p) => p.lpForBoss === m.teamLp));
if (f.opts.players.length === 2) {
assert.deepEqual(f.opts.players.map((p) => p.side), ['L', 'R']);
+24
View File
@@ -179,6 +179,30 @@ test('boss templates: leaders never scaled, pairs use the multi template, single
assert.equal(w.pick, setup.picks[9], 'solo 标准: the leader round uses slot 9');
});
test('co-op 终极: supplyHpMul = the 补给线 / 补给线II share of hp (hp ÷ it = the 攻坚装备 stacks 1.2^kAtk); wave and bounty mods carry it (PR #272)', () => {
const gd = new GameData(DATA, 'mode_multi_abyss');
for (const [r, row] of Object.entries(gd.mode.enemyScale)) {
const sc = gd.enemyScale(Number(r));
assert.ok(Math.abs(sc.hpMul / (sc.supplyHpMul ?? 1) - 1.2 ** row.kAtk) < 1e-5, `R${r}: ${sc.hpMul} / ${sc.supplyHpMul}`);
}
assert.equal(gd.enemyScale(4).supplyHpMul, undefined);
assert.equal(gd.enemyScale(5).supplyHpMul, 1.2); // 补给线II
assert.equal(gd.enemyScale(6).supplyHpMul, 1.08); // 补给线
for (const id of ['mode_single_funny', 'mode_single_abyss', 'mode_multi_hard']) {
const g = new GameData(DATA, id);
for (const r of Object.keys(g.mode.enemyScale)) assert.equal(g.enemyScale(Number(r)).supplyHpMul, undefined, `${id} R${r}`);
}
const key = 'enemy_1200_msfjin';
const picks = [];
picks[6] = { round: 6, type: 'TIMES', key, normal: key, elite: key, fly: false, firstHalf: true };
const wave = buildNormalWave(gd, createRng(1), { picks }, 6);
const tokens = wave.spawns.filter((s) => s.enemyKey === key);
assert.ok(tokens.length > 0);
for (const s of tokens) assert.deepEqual([s.mods.hpMul, s.mods.supplyHpMul], [gd.enemyScale(6).hpMul, 1.08]);
const [b] = bountySpawns(gd, 6, wave, [{ id: 'jin', card: { enemyKey: key, count: 1 } }], 'p1');
assert.deepEqual([b.mods.hpMul, b.mods.supplyHpMul], [gd.enemyScale(6).hpMul, 1.08]);
});
test('bounty spawns: joined to the template\'s first normal action of the enemy\'s class, spread over its window; no solo ×0.7', () => {
const gd = new GameData(DATA, 'mode_multi_hard');
const picks = [];