0.2.5-dev: 驭械术师 normal attacks keep the operator's own hit plus one drone hit, the drone multiplier and per-target map reset on redeploy, 洛洛 S2 keeps its lock until a kill or the skill ends (PRs #537 / #536 / #535 by @Xinglan233, ported); hit tiles use the official round-half-to-even with an edge inset (PR #520 by @Cloudnyco, ported); bots softly avoid living teammates' opening factions until they are eliminated (PR #518 by @IceCodeNew, ported with changes); golden: 43 more scenarios move, each named in docs/history/0.2.5.md

This commit is contained in:
sganggs committed 2026-10-12 14:45:01 +08:00
1 parent 7fd0478dc0
commit e0f3771821
41 files changed
+1939 -779

No files matched your search

+5 -5
View File
@@ -488,13 +488,13 @@ test('3_13 至简: 神工意匠 charges: ATK% arts ×2 per cast; 忽有所悟 cr
const u = h.unit(id);
assert.equal(u.skill.maxCharges, bb.ct);
h.run(60);
const normal = h.hooksOf('damaged').filter((c) => c.source === u && c.dmg?.isAttack && !c.dmg.isSkill).map((c) => c.amount / u.s.atk);
const normal = h.hooksOf('damaged').filter((c) => c.source === u && c.dmg?.tags.includes('funnel') && !c.dmg.isSkill).map((c) => c.amount / u.s.atk);
const ramp = (v) => [tb.max_atk_scale, tb.max_atk_scale * t0.atk_scale].some((x) => Math.abs(v - x) < 1e-6);
assert.ok(normal.slice(10).every(ramp), 'ramped drone hits: max or max × crit');
assert.ok(normal.some((v) => Math.abs(v - tb.max_atk_scale * t0.atk_scale) < 1e-6), 'some crits');
assert.ok(normal.some((v) => Math.abs(v - tb.max_atk_scale) < 1e-6), 'some plain hits');
const skillAtks = h.hooksOf('attack').filter((c) => c.attacker === u && c.isSkill).length;
const sk = h.hooksOf('damaged').filter((c) => c.source === u && c.dmg?.isSkill).map((c) => c.amount / u.s.atk);
const sk = h.hooksOf('damaged').filter((c) => c.source === u && c.dmg?.isSkill && c.dmg.isAttack).map((c) => c.amount / u.s.atk);
assert.ok(skillAtks >= 2);
assert.equal(sk.length, skillAtks * 2, 'two hits per cast');
for (const v of sk) assert.ok([bb.atk_scale, bb.atk_scale * t0.atk_scale].some((x) => Math.abs(v - x) < 1e-6), `skill hit ${v}`);
@@ -790,7 +790,7 @@ test('3_20 耶拉: 低眉 ATK (ground tiles in range); 心随意动 +1 drone, AT
h.step();
approx(u.s.atk, u.base.atk * (1 + t0['kjera_t_1[high].atk']));
h.run(4);
const first = h.hooksOf('damaged').filter((c) => c.source === u && c.dmg?.isAttack).map((c) => c.amount / u.s.atk);
const first = h.hooksOf('damaged').filter((c) => c.source === u && c.dmg?.tags.includes('funnel')).map((c) => c.amount / u.s.atk);
assert.ok(first[1] > first[0], 'drone ramps on the same target');
fill(u);
assert.ok(h.runUntil(() => u.skill.active, 5));
@@ -1083,7 +1083,7 @@ test('3_20 耶拉: 心随意动 with a single enemy — both drones lock it and
});
const u = h.unit(id);
h.run(12);
const plain = h.hooksOf('damaged').filter((c) => c.source === u && c.dmg?.isAttack).map((c) => c.amount / u.s.atk);
const plain = h.hooksOf('damaged').filter((c) => c.source === u && c.dmg?.tags.includes('funnel')).map((c) => c.amount / u.s.atk);
approx(plain[plain.length - 1], tb.max_atk_scale, 1e-6, 'module ramp cap');
fill(u);
assert.ok(h.runUntil(() => u.skill.active, 5));
@@ -1159,7 +1159,7 @@ test('精锐 numbers from the golden blackboards (module traits handled by the p
const ml = m.unit(e13);
assert.equal(ml.skill.maxCharges, BB(e13).ct);
m.run(25);
const ramps = m.hooksOf('damaged').filter((c) => c.source === ml && c.dmg?.isAttack && !c.dmg.isSkill).map((c) => c.amount / ml.s.atk);
const ramps = m.hooksOf('damaged').filter((c) => c.source === ml && c.dmg?.tags.includes('funnel') && !c.dmg.isSkill).map((c) => c.amount / ml.s.atk);
assert.ok(ramps.some((v) => Math.abs(v - TR(e13).max_atk_scale) < 1e-6), 'module cap 120 %');
assert.ok(ramps.every((v) => v <= TR(e13).max_atk_scale * TB(e13, 0).atk_scale + 1e-6));
done(m);