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:
sganggs committed 2026-10-05 18:45:13 +08:00
1 parent 65ac4afbd8
commit 383fe4a49e
27 files changed
+463 -383

No files matched your search

+13 -9
View File
@@ -132,12 +132,12 @@ test('1_04 深巡: fin darts pierce attack@max_target enemies on the line + 1 s
});
const u = h.unit(id);
h.step();
// 技能策略 (issue #4; the deliberate deviation of DESIGN §21.29): an enemy inside her initial 2-2 range is enough — the
// basic strategy casts it, no hit required
assert.equal(u.skill.rule, 'DEFAULT', '行动能力剥夺 is an offensive ranged skill, not a 重装 TAKE_DAMAGE one');
// 技能策略 (issue #4; the deliberate deviation of DESIGN §21.29, and since 0.2.0 the owner's ACTIVE_RANGE on top): an
// enemy inside the 3-2 she attacks with is enough — no hit required
assert.equal(u.skill.rule, 'ACTIVE_RANGE', '行动能力剥夺 is an offensive ranged skill, not a 重装 TAKE_DAMAGE one');
h.run(3.5);
assert.ok(u.skill.active);
assert.equal(h.hooksOf('skillStart').find((c) => c.unit === u).reason, 'DEFAULT', 'cast by the basic strategy');
assert.equal(h.hooksOf('skillStart').find((c) => c.unit === u).reason, 'ACTIVE_RANGE', 'cast on its running range');
approx(u.s.atk, u.base.atk * (1 + bb.atk));
const firstAtk = h.hooksOf('attack').find((c) => c.attacker === u && c.isSkill);
assert.equal(firstAtk.targets.length, bb['attack@max_target'], 'hits every enemy of the line');
@@ -150,16 +150,20 @@ test('1_04 深巡: fin darts pierce attack@max_target enemies on the line + 1 s
done(h);
});
test('1_04 深巡 / 1_20 雷蛇 S2 技能策略: both 哨戒铁卫 S2s cast with an enemy in range, no hit needed (issue #4)', () => {
test('1_04 深巡 / 1_20 雷蛇 S2 技能策略: both 哨戒铁卫 S2s cast with an enemy in range, no hit needed (issue #4); 深巡 on her 3-2', () => {
// both S2s are offensive (深巡: range up + ATK/ASPD + piercing darts; 雷蛇: ATK +125 % + arts on 3 + stun); the official
// 下半 重装 row (TAKE_DAMAGE for every MANUAL 重装 skill) made both wait for a hit, and the owner's deliberate deviation
// from it (DESIGN §21.29, after community feedback) gives them the basic strategy — the data and the kit say DEFAULT.
// from it (DESIGN §21.29, after community feedback) gives them the basic strategy — 雷蛇's data and kit say DEFAULT;
// 深巡's 3-2 strictly contains her 2-2, so since 0.2.0 the owner's ACTIVE_RANGE rule casts it on the 3-2 (2026-10-05).
for (const id of ['chess_char_1_04_a', 'chess_char_1_04_b', 'chess_char_1_20_a', 'chess_char_1_20_b']) {
const h = run({ units: [{ chessId: id, row: 9, col: 4, carryState: READY }], enemies: [{ key: 'e', pos: [9, 6] }] });
const udflow = id.startsWith('chess_char_1_04');
// (9,7) = [0,3]: on 深巡's 3-2, not on her 2-2
const h = run({ units: [{ chessId: id, row: 9, col: 4, carryState: READY }], enemies: [{ key: 'e', pos: [9, udflow ? 7 : 6] }] });
const u = h.unit(id);
assert.equal(u.skill.rule, 'DEFAULT', `${id}: an offensive skill, not the 重装 TAKE_DAMAGE row`);
const rule = udflow ? 'ACTIVE_RANGE' : 'DEFAULT';
assert.equal(u.skill.rule, rule, `${id}: an offensive skill, not the 重装 TAKE_DAMAGE row`);
assert.ok(h.runUntil(() => u.skill.activations >= 1, 5), `${id}: casts while an enemy is in range and untouched`);
assert.equal(h.hooksOf('skillStart').find((c) => c.unit === u).reason, 'DEFAULT', `${id}: cast by the basic strategy`);
assert.equal(h.hooksOf('skillStart').find((c) => c.unit === u).reason, rule, `${id}: cast by ${rule}`);
done(h);
}
});