Files
ark-stronghold-protocol/test/content/kits_pinecn.test.js
T

85 lines
4.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 松果 S2: the ATK ramp belongs to a deployment, while Skill.activations stays cumulative.
// Sources: PRTS 松果 S2 备注; data/chess.json rank-4 / rank-7 blackboards.
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { makeBattle, enemyRec, checkInvariants } from '../helpers/battleHarness.js';
const FORMS = [
{ label: 'normal', id: 'chess_char_3_10_a', ramp: [0.25, 0.45, 0.65, 0.85, 0.85] },
{ label: 'elite without module', id: 'chess_char_3_10_b', moduleId: 'none', ramp: [0.4, 0.6, 0.8, 1, 1] },
{ label: 'elite RPR-X', id: 'chess_char_3_10_b', ramp: [0.4, 0.6, 0.8, 1, 1] },
];
const close = (actual, expected, msg) => assert.ok(Math.abs(actual - expected) < 1e-6, `${msg}: ${actual} ≈ ${expected}`);
const done = (h) => { checkInvariants(h.b); assert.deepEqual(h.b.errors, []); };
function field(form, { atk = 0, units = null } = {}) {
return makeBattle({
seed: 5, timeLimit: 600, autoFinish: false,
flags: { startOpCooldown: 3, dpInit: 99, dpPerSec: 10, dpMax: 999 },
units: units ?? [{ chessId: form.id, moduleId: form.moduleId, uid: 1, row: 10, col: 4 }],
defs: { enemies: { enemy_dummy: enemyRec({ key: 'enemy_dummy', hp: 1e7, speed: 0, atk, range: 5, bat: 2 }) } },
enemies: [{ key: 'enemy_dummy', pos: [10, 5] }],
});
}
for (const form of FORMS) {
test(`松果 S2 ${form.label}: natural casts grow within one deployment and stop at the fourth step`, () => {
const h = field(form), u = h.unit(1);
h.step();
assert.equal(u.skill.id, 'skchr_pinecn_2');
const wide = u.rangeKeys.length;
for (const [i, atkPct] of form.ramp.entries()) {
assert.ok(h.runUntil(() => u.skill.activations === i + 1, 60), `cast ${i + 1}`);
close(u.s.atk, u.base.atk * (1 + atkPct), `cast ${i + 1} ATK`);
assert.ok(u.rangeKeys.length < wide, 'the skill still shortens her range');
assert.ok(h.runUntil(() => !u.skill.active, 21), 'the duration ends');
close(u.s.atk, u.base.atk, 'ATK restored');
assert.equal(u.rangeKeys.length, wide, 'range restored');
}
done(h);
});
test(`松果 S2 ${form.label}: a knock-out during the fourth cast restarts the ramp on redeployment`, () => {
const h = field(form), u = h.unit(1);
assert.ok(h.runUntil(() => u.skill.activations === 4, 240), 'four natural casts');
close(u.s.atk, u.base.atk * (1 + form.ramp[3]), 'at the cap before the knock-out');
h.b.kill(u, h.enemy('enemy_dummy'));
assert.equal(u.findBuff('skill:pinecn_atk'), null, 'the active skill buff leaves with her');
assert.ok(h.runUntil(() => u.alive && u.deployed, 90), 'automatic paid redeployment');
assert.equal(u.skill.activations, 4, 'the engine activation sequence is still cumulative');
assert.ok(h.runUntil(() => u.skill.activations === 5, 30), 'first natural cast after redeployment');
close(u.s.atk, u.base.atk * (1 + form.ramp[0]), 'first ATK step again');
assert.ok(h.runUntil(() => u.skill.activations === 6, 60), 'second natural cast after redeployment');
close(u.s.atk, u.base.atk * (1 + form.ramp[1]), 'second ATK step again');
done(h);
});
}
test('松果 S2 normal: natural enemy damage, knock-out and redeployment give +25% on both first casts', () => {
const h = field(FORMS[0], { atk: 225 }), u = h.unit(1);
assert.ok(h.runUntil(() => u.skill.activations === 1, 30), 'first cast');
close(u.s.atk, 735, '588 × 1.25');
assert.ok(h.runUntil(() => !u.alive, 40), 'enemy attacks knock her out');
assert.ok(h.runUntil(() => u.alive && u.deployed, 90), 'automatically redeployed');
assert.equal(u.skill.activations, 1, 'the earlier cast remains in the engine sequence');
assert.ok(h.runUntil(() => u.skill.activations === 2, 30), 'next natural cast');
close(u.s.atk, 735, 'new deployment: 588 × 1.25');
done(h);
});
test('松果 S2: redeploying one piece does not restart another piece\'s ramp', () => {
const h = field(FORMS[0], { units: [
{ chessId: FORMS[0].id, uid: 1, row: 10, col: 4 },
{ chessId: FORMS[0].id, uid: 2, row: 11, col: 4 },
] });
const a = h.unit(1), b = h.unit(2);
assert.ok(h.runUntil(() => a.skill.activations === 2 && b.skill.activations === 2, 90));
h.b.retreat(a, { reason: 'raid' });
assert.ok(h.b.redeploy(a, { free: true }), 'only the first piece redeployed');
close(b.s.atk, b.base.atk * 1.45, 'the second piece keeps its running second step');
assert.ok(h.runUntil(() => a.skill.activations === 3, 30));
close(a.s.atk, a.base.atk * 1.25, 'the first piece starts again');
assert.ok(h.runUntil(() => b.skill.activations === 3, 60));
close(b.s.atk, b.base.atk * 1.65, 'the second piece progresses to its third step');
done(h);
});