feedback5 WF: ACTIVE_RANGE also replaces the SEARCH row and covers 深巡 S2 — a skill whose running range strictly contains the own one casts with an enemy inside it (the owner's decisions of 2026-10-05)
tools/build-data.mjs resolveTrigger: a MANUAL operator skill on DEFAULT (no row, or a DEFAULT entry of the deviation
tables) or on the SEARCH row ("在初始攻击范围内存在敌人时释放技能": 解放者 / 阵法术师 / 安洁莉娜 — ACTIVE_RANGE_OVER) whose
running range strictly contains its own is ACTIVE_RANGE on that range, rawRule the official row; validateAll accepts a
DEFAULT deviation resolved to ACTIVE_RANGE (chess and stand-in tables) and ACTIVE_RANGE over SEARCH. Data rebuilt
offline, JSON-compared: only skills[].trigger rule / customRangeGrid of 薄绿 S1 (x-2), 蜜蜡 S1 (x-2), 卡涅利安 S3 (x-2),
玛恩纳 S2 (2-3), 安洁莉娜 S3 (y-4) — rawRule SEARCH — and 深巡 S2 (3-2, rawRule TAKE_DAMAGE), normal + elite (32
leaves); backups.json unchanged. Kits: 薄绿 S1 drops its DEFAULT trigger and 深巡 S2 PR #12's DEFAULT line, both read the
data now (雷蛇 S2 keeps PR #12's line). The sim needs no change (SEARCH was the DEFAULT condition on the initial range
every tick; ACTIVE_RANGE is the same on the running range). Docs: DESIGN §5.6 and the §21.20 flip row, DATA, SIM,
PLAYING, kits README, shared tier comments, skills.js header.
Tests: data (78 records / 39 skills, the five from SEARCH, the SEARCH skills that stay), feedback5-active-range (安洁莉娜
S3 on its y-4, S2 keeps SEARCH), feedback1-tank-triggers and kits_t1t2 (深巡 S2 on its 3-2, before any hit), kits_alt_t3
(薄绿 S1 on its x-2), feedback1f-fence (the idle measure uses the interval of the cooldown still running: the S2 now casts
between two attacks), docs-consistency (one PR #12 kit line left).
Golden: roster-001 / 002 (深巡), roster-016 / 044 (玛恩纳, 安洁莉娜), roster-026 / 027 (薄绿), roster-041 / 042 (卡涅利安),
bond-egirShip-low / bond-suntShip-high (深巡), boss-boss_2-pair / boss-boss_6-pair (玛恩纳, 安洁莉娜), matches solo-NORMAL-1,
coop2-NORMAL-3, coop4-FUNNY-6, coop2-NORMAL-8-serverrun — each fields one of the six skills, which now casts when an
enemy enters its running range.
This commit is contained in:
1 parent
65ac4afbd8
commit
383fe4a49e
27 files changed
+463
-383
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