feedback5 WC: a MANUAL skill whose running attack range strictly contains the own range casts as soon as an enemy is inside it (ACTIVE_RANGE, the owner's rule of 2026-10-05)

A deliberate deviation from the official 技能策略 (community report: skills with a larger range waited for the normal
range). tools/build-data.mjs resolveTrigger: a MANUAL operator skill still on the basic strategy (no class row, no
TRIGGER_DEVIATIONS entry) whose running range (its rangeId grid, or the own range grown by
ability_range_forward_extend; never 被动效果:攻击范围扩大) strictly contains the operator's range gets rule ACTIVE_RANGE,
customRangeGrid = that range, rawRule the official DEFAULT; validateAll checks it. server/sim/skills.js: ACTIVE_RANGE
checks the DEFAULT condition (a targetable enemy or a blocked one; a heal skill an injured ally) on that range, grown by
the permanent rangeExtend, every tick. Data rebuilt offline: only skills[].trigger changed (33 skills, 66 chess records,
9 stand-in form records). 深巡 S2 (§21.29), 重装 TAKE_DAMAGE, the SEARCH / CUSTOM_RANGE / SP_FULL rows, 烛煌 S2 / S3
(not containing her 3-1) and 引星棘刺 S3 keep their rule. Docs: DESIGN §5.6, DATA, SIM, PLAYING, kits README, notes.
Tests: new test/sim/feedback5-active-range.test.js; data, playtest6-skills (银灰 S3), kits_alt_t2 (蒂比 S1), kits_t5
(百炼嘉维尔 S2) updated; feedback1-transform's fixtures move 莫斯提马 one tile (her S3 now casts on its 3-15).

Golden: 27 roster, 25 bond, 21 field and 14 match scenarios moved — every one fields an operator with one of the 33
skills, which now casts when an enemy enters its running range instead of the initial range.
This commit is contained in:
sganggs committed 2026-10-05 17:44:33 +08:00
1 parent ebfda7e657
commit 01fddd3021
23 files changed
+1921 -1708

No files matched your search

+5 -3
View File
@@ -352,16 +352,18 @@ test('2_12 砾 S1 影袭: at deployment DEF +def decaying to 0 over `duration` s
}
});
test('2_13 蒂比 S1 专业喷绘技巧: DEFAULT trigger, takes off (skill range, ATK +atk, blocks flyers), single shots; hits never set it off', () => {
test('2_13 蒂比 S1 专业喷绘技巧: ACTIVE_RANGE trigger (an enemy in its 2-3 only), takes off (skill range, ATK +atk, blocks flyers), single shots; hits never set it off', () => {
for (const id of both('chess_char_2_13')) {
const bb = bbAlt(id);
const h = run({ defs: { enemies: { e: dummy('e'), f: dummy('f', { motion: 'FLY' }) } }, units: [U(id, 9, 5, { carryState: READY })], enemies: [{ key: 'e', pos: [9, 6] }] });
// (10,5) = [1,0]: inside S1's 2-3, outside her own 2-2 — the owner's rule of 2026-10-05 (data trigger ACTIVE_RANGE)
const h = run({ defs: { enemies: { e: dummy('e'), f: dummy('f', { motion: 'FLY' }) } }, units: [U(id, 9, 5, { carryState: READY })], enemies: [{ key: 'e', pos: [10, 5] }] });
const u = h.unit(id);
usesAlt(u, id);
h.step();
const range0 = u.baseRangeKeys.length;
assert.ok(!u.baseRangeKeys.includes(h.b.grid.key(10, 5)), 'the enemy is outside her own range');
h.runUntil(() => u.skill.active, 3);
assert.equal(started(h, u)[0].reason, 'DEFAULT');
assert.equal(started(h, u)[0].reason, 'ACTIVE_RANGE');
assert.equal(u.s.flags.liftoff, true, 'airborne (起飞)');
assert.equal(u.ground, true, 'still a ground unit on her low tile');
approx(u.s.atk, u.base.atk * (1 + bb.atk));
+3
View File
@@ -801,10 +801,13 @@ test('百炼嘉维尔 S2: drags unblocked enemies in front of her; T1 ATK/DEF +1
u.hp = u.s.maxHp * 0.3;
approx(h.b.heal(null, u, 100), 100 * t1.heal_scale_2);
for (const e of h.b.enemies) h.b.dealDamage(null, e, { amount: 1e9, type: 'true' });
h.run(0.3); // 战地巨斧 refreshes every 0.2 s: back to no extra blocked enemy before the cast
const near = h.spawn('enemy_dummy', { pos: [9, 5] });
const far = h.spawn('enemy_dummy', { pos: [9, 6] });
u.skill.gainSp(1000);
// S2's 2-5 strictly contains her 1-1: ACTIVE_RANGE (the owner's rule, 2026-10-05) casts it at once, no attack needed
assert.ok(h.runUntil(() => u.skill.active, 3));
assert.equal(h.hooksOf('skillStart').find((c) => c.unit === u)?.reason, 'ACTIVE_RANGE');
approx(u.s.atk, u.base.atk * (1 + t0.atk + bb.atk));
h.b.dealDamage(null, near, { amount: 1e9, type: 'true' });
h.run(1.5);