feedback5: merge W5F — ACTIVE_RANGE also replaces SEARCH rows and covers 深巡 S2, 纯烬艾雅法拉 S1 at full SP, 伺夜 S1 only with his pack on the field
Golden regenerated on the merged code (only test/golden/matches.json conflicted): against feedback5 it moves exactly W5F's scenarios (roster-001/002/016/026/027/028/038/041/042/044, bond-egirShip-low, bond-suntShip-high, boss-boss_2-pair, boss-boss_6-pair, solo-NORMAL-1, coop2-NORMAL-3, coop4-FUNNY-6, coop2-NORMAL-8-serverrun); against fb5-bugs3 exactly W5E's (unite-1/2 and the 8 match scenarios of the 联防 map and the full-hand rule). data/ equals an offline build of the merged tools/build-data.mjs.
This commit is contained in:
commit
17d9960fb4
30 files changed
+552
-407
No files matched your search
+20
-10
@@ -460,9 +460,11 @@ test('chess: skills[] = every skill unlocked at the status, at the chess skill l
|
||||
// Triggers per skill (PRTS 卫戍协议/帮助 技能策略; tools/build-data.mjs resolveTrigger): the class rows cover every MANUAL
|
||||
// skill of the class and no AUTO one; a MANUAL skill with a 技能范围 of its own (not an attack-range change) is SKILL_RANGE.
|
||||
const rules = (id) => chess[id].skills.map((s) => s.trigger.rule);
|
||||
const raws = (id) => chess[id].skills.map((s) => s.trigger.rawRule);
|
||||
assert.deepEqual(rules('chess_char_1_02_a'), ['TAKE_DAMAGE', 'TAKE_DAMAGE']); // 角峰 (重装, both MANUAL)
|
||||
assert.deepEqual(rules('chess_char_1_10_b'), ['DEFAULT', 'TAKE_DAMAGE']); // 古米 (S1 自动触发)
|
||||
assert.deepEqual(rules('chess_char_3_08_a'), ['SEARCH', 'SEARCH']); // 薄绿 (阵法术师: the default S2 too)
|
||||
assert.deepEqual(rules('chess_char_3_08_a'), ['ACTIVE_RANGE', 'SEARCH']); // 薄绿 (阵法术师: the default S2 too; S1's x-2: the owner's rule)
|
||||
assert.deepEqual(raws('chess_char_3_08_a'), ['SEARCH', 'SEARCH']);
|
||||
assert.deepEqual(rules('chess_char_3_19_a'), ['DEFAULT', 'DEFAULT', 'SP_FULL']); // 伺夜 (战术家: S1/S2 are AUTO)
|
||||
assert.deepEqual(rules('chess_char_6_11_b'), ['MLYSS_WTRMAN', 'MLYSS_WTRMAN', 'MLYSS_WTRMAN']); // 缪尔赛思 (charId row −1)
|
||||
assert.deepEqual(rules('chess_char_1_08_a'), ['DEFAULT', 'SKILL_RANGE']); // 德克萨斯 S2 剑雨: 对周围所有敌人
|
||||
@@ -471,9 +473,8 @@ test('chess: skills[] = every skill unlocked at the status, at the chess skill l
|
||||
assert.deepEqual(rules('chess_char_3_18_a'), ['DEFAULT', 'SKILL_RANGE', 'ACTIVE_RANGE']); // 忍冬: S2 对周围…, S3 攻击距离+1
|
||||
// the deliberate deviation from the 重装 row (DESIGN §21.29, the owner's decision; tools/build-data.mjs
|
||||
// TRIGGER_DEVIATIONS, per chess): six skills cast with an enemy in range, rawRule keeps the official TAKE_DAMAGE
|
||||
const raws = (id) => chess[id].skills.map((s) => s.trigger.rawRule);
|
||||
for (const id of ['chess_char_1_04_a', 'chess_char_1_04_b']) { // 深巡: S1 keeps the row, S2 deviates
|
||||
assert.deepEqual(rules(id), ['TAKE_DAMAGE', 'DEFAULT']);
|
||||
assert.deepEqual(rules(id), ['TAKE_DAMAGE', 'ACTIVE_RANGE']); // (S2: and the owner's ACTIVE_RANGE on top)
|
||||
assert.deepEqual(raws(id), ['TAKE_DAMAGE', 'TAKE_DAMAGE']);
|
||||
}
|
||||
for (const id of ['chess_char_1_20_a', 'chess_char_1_20_b']) { // 雷蛇: S1 AUTO, S2 deviates
|
||||
@@ -495,24 +496,33 @@ test('chess: skills[] = every skill unlocked at the status, at the chess skill l
|
||||
assert.deepEqual(raws(id), ['TAKE_DAMAGE', 'TAKE_DAMAGE', 'CUSTOM_RANGE_SEARCH_ENEMY']);
|
||||
assert.deepEqual(chess[id].skills[1].trigger.customRangeGrid, chess[id].skills[1].rangeGrid);
|
||||
}
|
||||
// the owner's rule of 2026-10-05 (a deliberate deviation): a MANUAL skill on the basic strategy whose running attack
|
||||
// range strictly contains the operator's own range is ACTIVE_RANGE on that range (rawRule keeps the official DEFAULT)
|
||||
// the owner's rule of 2026-10-05 (a deliberate deviation): a MANUAL skill on the basic strategy (DEFAULT by no row or by
|
||||
// a deviation: 深巡 S2) or on the SEARCH row whose running attack range strictly contains the operator's own range is
|
||||
// ACTIVE_RANGE on that range (rawRule keeps the official row)
|
||||
const tiles = (g) => new Set((g || []).map(([r, c]) => `${r},${c}`));
|
||||
const wider = (a, b) => { const A = tiles(a), B = tiles(b); return A.size > B.size && [...B].every((k) => A.has(k)); };
|
||||
const active = Object.values(chess).flatMap((c) => (c.skills || []).filter((s) => s.trigger.rule === 'ACTIVE_RANGE').map((s) => [c, s]));
|
||||
for (const [c, s] of active) {
|
||||
assert.equal(s.skillType, 'MANUAL', `${c.chessId} ${s.skillId}`);
|
||||
assert.equal(s.trigger.rawRule, 'DEFAULT', `${c.chessId} ${s.skillId}: only the basic strategy`);
|
||||
const dev = c.baseId === 'chess_char_1_04_a' && s.skillId === 'skchr_udflow_2';
|
||||
assert.equal(s.trigger.rawRule, dev ? 'TAKE_DAMAGE' : s.trigger.rawRule === 'SEARCH' ? 'SEARCH' : 'DEFAULT', `${c.chessId} ${s.skillId}: only the basic strategy / SEARCH`);
|
||||
assert.ok(wider(s.trigger.customRangeGrid, c.rangeGrid), `${c.chessId} ${s.skillId}: its running range strictly contains ${c.rangeId}`);
|
||||
if (s.rangeGrid) assert.deepEqual(s.trigger.customRangeGrid, s.rangeGrid, `${c.chessId} ${s.skillId}: the skill's own grid`);
|
||||
}
|
||||
assert.equal(active.length, 66, '33 skills, normal + elite');
|
||||
assert.equal(new Set(active.map(([c, s]) => `${c.baseId} ${s.skillId}`)).size, 33);
|
||||
assert.equal(active.length, 78, '39 skills, normal + elite');
|
||||
assert.equal(new Set(active.map(([c, s]) => `${c.baseId} ${s.skillId}`)).size, 39);
|
||||
// from the SEARCH row (解放者 / 阵法术师 / 安洁莉娜) and 深巡 S2's deviation: the owner's decisions of 2026-10-05
|
||||
const fromSearch = active.filter(([, s]) => s.trigger.rawRule === 'SEARCH').map(([c, s]) => `${c.baseId} ${s.skillId}`);
|
||||
assert.deepEqual([...new Set(fromSearch)].sort(), ['chess_char_3_08_a skchr_mint_1', 'chess_char_4_05_a skchr_beewax_1', 'chess_char_4_24_a skchr_billro_3', 'chess_char_5_19_a skchr_mlynar_2', 'chess_char_5_20_a skchr_aglina_3']);
|
||||
assert.equal(fromSearch.length, 10);
|
||||
// the SEARCH skills whose range does not grow keep the row (薄绿 / 蜜蜡 S2, 卡涅利安 S1 / S2, 玛恩纳 S1, 安洁莉娜 S2, 圣聆初雪 S1)
|
||||
const search = Object.values(chess).flatMap((c) => (c.skills || []).filter((s) => s.trigger.rule === 'SEARCH').map((s) => `${c.baseId} ${s.skillId}`));
|
||||
assert.deepEqual([...new Set(search)].sort(), ['chess_char_3_08_a skchr_mint_2', 'chess_char_4_05_a skchr_beewax_2', 'chess_char_4_24_a skchr_billro_1', 'chess_char_4_24_a skchr_billro_2', 'chess_char_5_19_a skchr_mlynar_1', 'chess_char_5_20_a skchr_aglina_2', 'chess_char_6_02_a skchr_sbell2_1']);
|
||||
const grown = (id, sid) => chess[id].skills.find((s) => s.skillId === sid).trigger;
|
||||
assert.deepEqual(grown('chess_char_5_07_a', 'skchr_surtr_3').customRangeGrid.length, chess.chess_char_5_07_a.rangeGrid.length + 2, '史尔特尔 S3 攻击距离+2: 1-1 grown by 2');
|
||||
assert.equal(grown('chess_char_5_03_a', 'skchr_blaze2_3').rule, 'DEFAULT', '烛煌 S3: 4-11 does not contain her 3-1');
|
||||
assert.equal(grown('chess_char_1_04_a', 'skchr_udflow_2').rule, 'DEFAULT', '深巡 S2: the §21.29 deviation stays');
|
||||
assert.equal(grown('chess_char_3_08_a', 'skchr_mint_1').rule, 'SEARCH', '薄绿 S1: its official 阵法术师 row');
|
||||
assert.deepEqual(grown('chess_char_1_04_a', 'skchr_udflow_2'), { rule: 'ACTIVE_RANGE', rawRule: 'TAKE_DAMAGE', customRangeGrid: [[0, 0], [0, 1], [0, 2], [0, 3]] }, '深巡 S2: its 3-2 over her 2-2, on top of the §21.29 deviation');
|
||||
assert.equal(grown('chess_char_3_08_a', 'skchr_mint_1').rawRule, 'SEARCH', '薄绿 S1: its official 阵法术师 row stays the rawRule');
|
||||
assert.equal(grown('chess_char_5_15_a', 'skchr_thorn2_3').rule, 'DEFAULT', '引星棘刺 S3: 被动效果:攻击范围扩大 is her own range');
|
||||
const deviated = Object.values(chess).flatMap((c) => (c.skills || []).filter((s) => s.trigger.rawRule === 'TAKE_DAMAGE' && s.trigger.rule !== 'TAKE_DAMAGE').map((s) => `${c.baseId} ${s.skillId}`));
|
||||
assert.equal(deviated.length, 14, 'exactly the six skills of §21.29 and 余 S2, normal + elite');
|
||||
|
||||
Reference in new issue
Block a user