feedback2: merge WB — 突袭 lands only where it can attack a ground enemy, on both triggers (GitHub #51)
This commit is contained in:
commit
4298815bf2
6 files changed
+160
-45
No files matched your search
@@ -2,7 +2,7 @@
|
||||
// 绝技 and the prep side of 助力 远见 奇迹 投资人 调和 (numbers from data/bonds.json, research 02 §3.9–§3.23).
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { makeBattle, chessRec, enemyRec, checkInvariants } from '../helpers/battleHarness.js';
|
||||
import { makeBattle, chessRec, enemyRec, checkInvariants, flatStage } from '../helpers/battleHarness.js';
|
||||
import { gainLayers, inRange } from '../../server/sim/content/support/index.js';
|
||||
import { bondBb, procChance } from '../../server/sim/content/bonds/addon/battle.js';
|
||||
import { registerMeta } from '../../server/sim/content/bonds/addon.js';
|
||||
@@ -342,6 +342,76 @@ test('突袭: the jump is a redeployment — deploy fires (部署时 effects), f
|
||||
checkInvariants(h.b);
|
||||
});
|
||||
|
||||
// GitHub issue #51 (reported on 0.1.0, still so in 0.1.1): the 10 s idle jump landed where no enemy was in range and
|
||||
// then hopped between such tiles every 10 s, and it ignored a second enemy it could have reached. Either trigger now jumps only to a landing tile
|
||||
// with its target in range — the first candidate that has one — else the member stays and the next poll looks again
|
||||
// [ASSUMED: the text says only "再部署至一名地面敌人周围"]. Row 9 here: cols 8–9 plain floor (not deployable); a
|
||||
// RIGHT-facing melee member (its own tile + the tile in front) reaches a speed-0 enemy on (9,9) only from (9,9) or
|
||||
// (9,8) — neither deployable.
|
||||
const RAID51_STAGE = flatStage({ rows: { 9: '##Errrrrff' + 'S' + 'rrrrrrr' + 'S##' } });
|
||||
const RAID51_ENEMY = { enemy_raid51: enemyRec({ key: 'enemy_raid51', hp: 1e7, speed: 0 }) };
|
||||
const raidJumps = (h) => h.hooksOf('deploy').filter((c) => c.unit.kind === 'op' && !c.initial); // (an enemy spawn is a deploy too)
|
||||
|
||||
test('突袭 #51: no landing tile reaches the enemy → neither trigger jumps (no hopping); once one can, the jump comes at once', () => {
|
||||
const defs = {
|
||||
chess: { r_m: op('r_m', ['raidShip']), r_s: chessRec({ id: 'r_s', bonds: ['raidShip'], skill: { spCost: 10, initSp: 10 } }) },
|
||||
enemies: RAID51_ENEMY,
|
||||
};
|
||||
const h = makeBattle({
|
||||
stage: RAID51_STAGE, defs, bonds: { raidShip: bond(1, 10) }, enemies: [{ key: 'enemy_raid51', pos: [9, 9] }],
|
||||
units: [{ chessId: 'r_m', row: 12, col: 3 }, { chessId: 'r_s', row: 11, col: 3 }], hooks: ['deploy', 'death'],
|
||||
autoFinish: false, timeLimit: 120,
|
||||
});
|
||||
const m = h.unit('r_m'), s = h.unit('r_s');
|
||||
h.run(45);
|
||||
assert.equal(m.lastAttackAt, -Infinity, 'r_m idle the whole time (the 10 s trigger, four times over)');
|
||||
assert.ok(s.skill.ready, 'r_s: its skill stays ready the whole time (the 技能就绪 trigger)');
|
||||
assert.deepEqual(raidJumps(h), [], 'no jump: no landing tile has the enemy in range');
|
||||
assert.deepEqual(h.hooksOf('death').filter((c) => c.reason === 'raid'), [], 'no 突袭 retreat');
|
||||
assert.deepEqual([m.tileR, m.tileC, s.tileR, s.tileC], [12, 3, 11, 3], 'both stay where they are');
|
||||
// a ground enemy that can be reached: both jump at the next poll (the idle time kept counting) and land with it in range
|
||||
const t0 = h.b.time;
|
||||
const e2 = h.spawn('enemy_raid51', { pos: [12, 7] });
|
||||
h.run(0.3);
|
||||
assert.equal(raidJumps(h).length, 2, 'both members jumped');
|
||||
for (const c of raidJumps(h)) assert.ok(c.t - t0 <= 0.25 + 1e-6, `within one poll (${(c.t - t0).toFixed(2)} s)`);
|
||||
for (const u of [m, s]) {
|
||||
assert.ok(inRange(u, e2), `${u.defId}: the new enemy in range after the jump (${u.tileR},${u.tileC})`);
|
||||
assert.ok(Math.max(Math.abs(u.tileR - 12), Math.abs(u.tileC - 7)) <= 2, `${u.defId}: next to it`);
|
||||
}
|
||||
checkInvariants(h.b);
|
||||
});
|
||||
|
||||
test('突袭 #51: the most advanced enemy out of reach → the jump goes to the next one it can reach and fights there; idle time restarts', () => {
|
||||
const defs = { chess: { r_m: op('r_m', ['raidShip']) }, enemies: RAID51_ENEMY };
|
||||
const idle = bondBb('raidShip').no_attack_duration;
|
||||
const h = makeBattle({
|
||||
stage: RAID51_STAGE, defs, bonds: { raidShip: bond(1, 10) }, hooks: ['deploy', 'death'], autoFinish: false, timeLimit: 120,
|
||||
enemies: [{ key: 'enemy_raid51', pos: [9, 9] }, { key: 'enemy_raid51', pos: [12, 9] }], units: [{ chessId: 'r_m', row: 12, col: 3 }],
|
||||
});
|
||||
h.step();
|
||||
const [e1, e2] = h.enemies();
|
||||
assert.deepEqual([e1.y, e1.x, e2.y, e2.x], [9, 9, 12, 9]);
|
||||
assert.ok(h.b.remainingDistance(e1) < h.b.remainingDistance(e2), 'the one out of reach is the more advanced (first candidate)');
|
||||
const u = h.unit('r_m');
|
||||
assert.ok(h.runUntil(() => raidJumps(h).length > 0, idle + 1), 'jumped after the idle time');
|
||||
const t1 = h.b.time;
|
||||
assert.ok(inRange(u, e2) && !inRange(u, e1), `landed on ${u.tileR},${u.tileC} with the second enemy in range`);
|
||||
h.run(5);
|
||||
assert.ok(u.lastAttackAt > t1 && e2.hp < e2.s.maxHp, 'it attacks the enemy it jumped to');
|
||||
assert.equal(raidJumps(h).length, 1, 'busy there: no further jump');
|
||||
// its enemy gone, a reachable one elsewhere: the next jump comes the idle time after its last attack, not at once
|
||||
h.b.dealDamage(null, e2, { amount: 1e9, type: 'true' });
|
||||
const last = u.lastAttackAt;
|
||||
const e3 = h.spawn('enemy_raid51', { pos: [10, 5] });
|
||||
assert.ok(!inRange(u, e3));
|
||||
assert.ok(h.runUntil(() => raidJumps(h).length === 2, idle + 1), 'jumped again');
|
||||
const dt = h.b.time - last;
|
||||
assert.ok(dt >= idle - 1e-6 && dt <= idle + 0.25 + 1e-6, `${dt.toFixed(2)} s after its last attack`);
|
||||
assert.ok(inRange(u, e3), 'the new enemy in range');
|
||||
checkInvariants(h.b);
|
||||
});
|
||||
|
||||
test('不屈: knocked-out ground operator redeploys (p=1 at high L); tier 2 every operator +5 SP; inactive / elevated → no', () => {
|
||||
const bb = bondBb('indomShip');
|
||||
const sk = { spCost: 50, initSp: 0 };
|
||||
|
||||
Reference in new issue
Block a user