feedback5: trigger fixes from the wave-2 kit reports — 「攻击范围向前延伸」 and 「攻击范围朝…扩大」 are attack-range changes (帕拉斯 S2 DEFAULT → ACTIVE_RANGE on her range + 1, 司霆惊蛰 S1 SEARCH → ACTIVE_RANGE on the 3-19: the owner's larger-range rule); UNIT_TRIGGER_CORRECTIONS for a live-client change the text misses — 菲亚梅塔 S1 is DEFAULT since the 2026-08-01 client (PRTS 备注: no +1 range, no flyers); data regenerated offline (only these three skills' triggers in backups.json, chess.json unchanged); golden: only diy moved (diy-060/061/068/069/075/076: those three operators)

This commit is contained in:
sganggs committed 2026-10-06 01:53:09 +08:00
1 parent faa40ca68f
commit 78251f499c
6 files changed
+120 -108

No files matched your search

+3 -3
View File
@@ -196,7 +196,7 @@ test('T2 追责: every cast strikes each low tile of her range ring by ring from
const { h, u, log } = field({ tier: 5, skill });
const e = h.spawn(pos[0] === 11 ? 'enemy_fly' : 'enemy_dummy', { pos });
u.skill.gainSp(999);
if (skill === 0) h.spawn('enemy_dummy', { pos: [11, 5] }); // S1 (SEARCH) needs a ground enemy on her 1-2
if (skill === 0) h.spawn('enemy_dummy', { pos: [11, 5] }); // S1 (ACTIVE_RANGE on the 3-19) needs a ground enemy in range
assert.ok(h.runUntil(() => u.skill.active, 2), `S${skill + 1}: cast`);
const t0 = h.b.time;
h.run(1.2);
@@ -223,11 +223,11 @@ test('T2 追责: every cast strikes each low tile of her range ring by ring from
done(h);
});
test('S1 浩气长存 (MANUAL, 3 charges, data SEARCH): a 0.5 s skill on the 3-19; 0.3 s in, a 3-2 to her left, front and right each hits every ground enemy on it for 260 % / 300 % ATK × 1.07 physical (one on her tile three times, none diagonal, no flyer)', () => {
test('S1 浩气长存 (MANUAL, 3 charges, data ACTIVE_RANGE — the SEARCH row on her larger 3-19): a 0.5 s skill on the 3-19; 0.3 s in, a 3-2 to her left, front and right each hits every ground enemy on it for 260 % / 300 % ATK × 1.07 physical (one on her tile three times, none diagonal, no flyer)', () => {
for (const [tier, elite] of [[5, false], [6, true]]) {
const sk = skillOf(tier, elite, S1);
const { h, u, log } = field({ tier, elite, skill: 0, silence: true });
assert.deepEqual([u.skill.rule, u.skill.maxCharges, sk.bb['attack@atk_scale_s1'], sk.rangeId, u.skill.spCost, sk.initSp], ['SEARCH', 3, elite ? 3 : 2.6, '3-19', elite ? 13 : 14, 17], `T${tier}`);
assert.deepEqual([u.skill.rule, u.skill.maxCharges, sk.bb['attack@atk_scale_s1'], sk.rangeId, u.skill.spCost, sk.initSp], ['ACTIVE_RANGE', 3, elite ? 3 : 2.6, '3-19', elite ? 13 : 14, 17], `T${tier}`);
const own = h.spawn('enemy_dummy', { pos: [10, 5] }), front = h.spawn('enemy_dummy', { pos: [10, 8] });
const left = h.spawn('enemy_dummy', { pos: [12, 5] }), right = h.spawn('enemy_dummy', { pos: [9, 5] });
const diag = h.spawn('enemy_dummy', { pos: [11, 6] }), fly = h.spawn('enemy_fly', { pos: [11, 5] });
+4 -6
View File
@@ -210,19 +210,17 @@ test('S1 胜利的连击 (AUTO, attack SP 3, data DEFAULT): the next attack stri
}
});
test('S2 信念的长鞭 (MANUAL, data DEFAULT): 20 / 22 s, range 2-2 + 1 tile forward, ATK +35 / 50 %, each attack 60 / 70 % to stun 0.2 s; all back after', () => {
test('S2 信念的长鞭 (MANUAL, data ACTIVE_RANGE on her range + 1 forward): 20 / 22 s, range 2-2 + 1 tile forward, ATK +35 / 50 %, each attack 60 / 70 % to stun 0.2 s; all back after', () => {
for (const [tier, elite, mod] of [[5, false, null], [6, true, INSY]]) {
const sk = skillOf(tier, elite, S2);
const { h, u } = field({ tier, elite, mod, skill: 1, seed: 9 });
// the data's DEFAULT: tools/build-data.mjs ATTACK_RANGE_CHANGE misses "攻击范围向前延伸一格" (reported, not worked around)
// 「攻击范围向前延伸一格」 is an attack-range change (tools/build-data.mjs ATTACK_RANGE_CHANGE): the owner's ACTIVE_RANGE rule
assert.deepEqual([u.skill.rule, sk.trigger.rawRule, sk.duration, sk.bb.atk, sk.bb.ability_range_forward_extend, sk.bb['attack@buff_prob'], sk.bb['attack@stun']],
['DEFAULT', 'DEFAULT', elite ? 22 : 20, elite ? 0.5 : 0.35, 1, elite ? 0.7 : 0.6, 0.2], `T${tier}`);
['ACTIVE_RANGE', 'DEFAULT', elite ? 22 : 20, elite ? 0.5 : 0.35, 1, elite ? 0.7 : 0.6, 0.2], `T${tier}`);
const far = h.spawn('enemy_dummy', { pos: [10, 8] }); // 3 tiles ahead: only in the extended range
u.skill.gainSp(999);
h.run(2);
assert.equal(u.skill.activations, 0, `T${tier}: nothing in her own 2-2: no cast (DEFAULT)`);
assert.ok(h.runUntil(() => u.skill.active, 3), `T${tier}: cast for an enemy only in the extended range (ACTIVE_RANGE)`);
const near = h.spawn('enemy_dummy', { pos: [10, 7] });
assert.ok(h.runUntil(() => u.skill.active, 3), `T${tier}: cast on her attack`);
approx(u.skill.timeLeft, sk.duration, `T${tier}: ${sk.duration} s`, 0.01);
assert.deepEqual(u.liveRangeGrid, [[0, 0], [0, 1], [0, 2], [0, 3]], `T${tier}: one tile more forward`);
const v = talentOf(u, 0)['peak_performance.atk'];
+12 -10
View File
@@ -77,7 +77,7 @@ test('菲亚梅塔 in every 自选 form: her kit (all three skills authored), th
done(h);
}
}
assert.deepEqual(formOf(6, true).skills.map((s) => [s.skillType, s.trigger.rule]), [['MANUAL', 'ACTIVE_RANGE'], ['MANUAL', 'DEFAULT'], ['MANUAL', 'DEFAULT']]);
assert.deepEqual(formOf(6, true).skills.map((s) => [s.skillType, s.trigger.rule]), [['MANUAL', 'DEFAULT'], ['MANUAL', 'DEFAULT'], ['MANUAL', 'DEFAULT']]);
assert.deepEqual([FORMS['2/1/4/0'].stats.maxHp, FORMS['2/1/4/0'].stats.atk, FORMS['2/60/7/1'].stats.maxHp, FORMS['2/60/7/1'].stats.atk], [1540, 671, 1796, 797]);
assert.deepEqual([Y, X, ISW].flatMap((id) => [modOf(5, id).attr, modOf(6, id).attr]),
[{ atk: 48, def: 26 }, { atk: 70, def: 43 }, { atk: 60, def: 17 }, { atk: 85, def: 32 }, { maxHp: 85, atk: 65 }, { maxHp: 115, atk: 90 }]);
@@ -164,28 +164,30 @@ test('T2 宣告终局: ASPD +27 outside a running skill, none while S1 / S3 run
}
});
test('S1 “你须直面” (MANUAL, attack SP 17 / 14 from 5, 30 s, data ACTIVE_RANGE on 3-10 + 1): ATK +45 % / +60 %; since the 2026-08 client her attacks pick ground enemies only (the splash still hits air) and her range stays 3-10', () => {
test('S1 “你须直面” (MANUAL, attack SP 17 / 14 from 5, 30 s, data DEFAULT — the 2026-08 client grows no range: tools/build-data.mjs UNIT_TRIGGER_CORRECTIONS): ATK +45 % / +60 %; since the 2026-08 client her attacks pick ground enemies only (the splash still hits air) and her range stays 3-10', () => {
for (const [tier, elite] of [[5, false], [6, true]]) {
const sk = skillOf(tier, elite, S1);
assert.deepEqual([sk.spType, sk.spCost, sk.initSp, sk.duration, sk.bb.atk, sk.bb.ability_range_forward_extend], ['INCREASE_WHEN_ATTACK', elite ? 14 : 17, 5, 30, elite ? 0.6 : 0.45, 1], `T${tier}`);
assert.equal(sk.trigger.customRangeGrid.length, formOf(tier, elite).rangeGrid.length + 3, `T${tier}: the data's trigger grid is the +1 range`);
assert.equal(sk.trigger.customRangeGrid, null, `T${tier}: no +1 trigger grid`);
const { h, u } = field({ tier, elite, skill: 0, pin: true });
assert.deepEqual([u.skill.kind, u.skill.rule, u.skill.spType], ['duration', 'ACTIVE_RANGE', 'attack'], `T${tier}`);
const far = h.spawn('enemy_dummy', { pos: [10, 9] }); // 5 tiles ahead: on the trigger grid, outside 3-10
assert.deepEqual([u.skill.kind, u.skill.rule, u.skill.spType], ['duration', 'DEFAULT', 'attack'], `T${tier}`);
const far = h.spawn('enemy_dummy', { pos: [10, 9] }); // 5 tiles ahead: outside 3-10
u.skill.gainSp(999);
assert.ok(h.runUntil(() => u.skill.active, 2), `T${tier}: an enemy on the +1 column casts it (the data rule)`);
approx(u.skill.timeLeft, 30, `T${tier}: 30 s`, 0.01);
h.run(2);
assert.equal(u.skill.activations, 0, `T${tier}: an enemy out of her reach casts nothing (DEFAULT)`);
const g = h.spawn('enemy_dummy', { pos: [11, 7] });
assert.ok(h.runUntil(() => u.skill.active, 3), `T${tier}: cast as she is about to attack a ground enemy in range`);
approx(u.skill.timeLeft, 30, `T${tier}: 30 s`, 0.05);
assert.deepEqual(u.findBuff(`skill:${u.id}`)?.mods, { atkPct: sk.bb.atk }, `T${tier}: ATK +${sk.bb.atk * 100} %`);
assert.deepEqual(u.liveRangeGrid, formOf(tier, elite).rangeGrid, `T${tier}: her range stays 3-10`);
h.run(6);
assert.equal(on(h, far).length, 0, `T${tier}: 5 tiles ahead stays out of reach`);
const fly = h.spawn('enemy_fly', { pos: [10, 7] });
h.run(6);
assert.equal(on(h, fly).length, 0, `T${tier}: no air target while it runs`);
const g = h.spawn('enemy_dummy', { pos: [11, 7] });
assert.equal(on(h, fly).filter((c) => !c.dmg.isSplash).length, 0, `T${tier}: no air target while it runs`);
assert.ok(h.runUntil(() => on(h, g).length > 0, 4), `T${tier}: a ground target`);
h.run(0.3);
const id = on(h, g)[0].dmg.attackId;
const id = on(h, g).at(-1).dmg.attackId; // a shell fired with the flyer on the field
assert.ok(on(h, fly).some((c) => c.dmg.attackId === id && c.dmg.isSplash), `T${tier}: her shell still splashes the flyer 1.0 away`);
for (const c of from(h, u).filter((c) => c.dmg.attackId === id)) approx(c.amount, u.base.atk * (1 + sk.bb.atk + u.findBuff('talent:phenxi:peak').mods.atkPct), `T${tier}: ATK`);
u.skill.end('test');