feedback1f WV: engine and docs tests follow the knocked-out tile rule

- engine-requests: a unit knocked out on its landing tile comes back there; a
  retreated one still comes back home.
- robustness: the busy-tile spawnToken check uses the tile the guard stands on
  after its redeploy (where it was knocked out).
- docs-consistency: SIM.md's b.snap `down` entry carries row, col; the DESIGN
  check accepts the shape before and after the integrator's §8.2 update.
This commit is contained in:
sganggs committed 2026-10-03 07:21:11 +08:00
1 parent 8de9002d1a
commit 54e131645a
3 files changed
+13 -7

No files matched your search

+3 -2
View File
@@ -301,11 +301,12 @@ test('user playtest #4 (DESIGN §18): picking by tile, timers, 机变 two taps,
assert.deepEqual(ELEMENT_ORDER.slice(0, 4), ['neural', 'erosion', 'burn', 'apoptosis']);
assert.deepEqual({ ...DOWN_STATE }, { COUNTING: 0, WAIT_DP: 1, WAIT_TILE: 2 });
assert.match(DESIGN, /\*\*爆发冷却\*\*/);
assert.match(DESIGN, /`down: \[\[id, respawnAt \(game s\), respawnTime \(s\), state\]\]`/);
// player report F5 after 0.1.0: the entries carry the tile the operator lies on (DESIGN §8.2 follows at integration)
assert.match(DESIGN, /`down: \[\[id, respawnAt \(game s\), respawnTime \(s\), state(, row, col)?\]\]`/);
assert.match(SIM, /`burstLocked\(unit\)` in damage\.js/);
assert.match(SIM, /损伤抵抗 = the target's data `epResistance`/);
assert.ok(!/800 phys; no lock/.test(SIM), 'SIM §3: operators\' 侵蚀 locks too');
assert.match(SIM, /`down: \[\[id, respawnAt, respawnTime, state\]\]`/);
assert.match(SIM, /`down: \[\[id, respawnAt, respawnTime, state, row, col\]\]`/);
// #5 / #11 / #12 data rules
const special = DATA.items.chess_item_4_09_e_a;
assert.ok(special.shopExcluded && special.shopExcludedBy, '灼燃维式重锤 is never sold');
+7 -3
View File
@@ -369,7 +369,7 @@ test('skill end: onEnd runs with the skill mods and range still applied, then th
// ---------------------------------------------------------------------------------------------------------------
// 9) redeploy { free, tile, keepSp }
test('redeploy: { tile } lands on another tile (home unchanged, later redeploys at home); { keepSp } restores SP before deploy fires', () => {
test('redeploy: { tile } lands on another tile (home unchanged; knocked out there, it comes back there); { keepSp } restores SP before deploy fires', () => {
let atDeploy = null;
const h = makeBattle({
defs: { chess: { t_gd: guard({ skill: { spCost: 10, initSp: 2 } }), t_x: guard({ id: 't_x' }) } },
@@ -391,7 +391,8 @@ test('redeploy: { tile } lands on another tile (home unchanged, later redeploys
assert.equal(h.b.unitAt(12, 8), u);
approx(atDeploy, sp, 1e-9, 'SP restored before the deploy hook');
approx(u.hp, u.s.maxHp);
// plain redeploy: initSp; knocked out later → the auto-redeploy goes home
// plain redeploy: initSp; knocked out there later → the auto-redeploy comes back on that tile (PRTS 卫戍协议/帮助
// "…原地留下一个“倒地干员”…满足再部署条件时…自动部署至该位置"; player report F5 after 0.1.0); a retreat → home
h.b.retreat(u);
h.b.redeploy(u, { tile: [12, 8] });
approx(u.skill.sp, 2, 1e-9, 'without keepSp: initial SP');
@@ -399,7 +400,10 @@ test('redeploy: { tile } lands on another tile (home unchanged, later redeploys
p.dp = 50;
h.b.dealDamage(null, u, { amount: 1e9, type: 'true' });
assert.ok(h.runUntil(() => u.alive, u.base.respawnTime + 1));
assert.deepEqual([u.tileR, u.tileC], [10, 4], 'auto-redeploy on the home tile');
assert.deepEqual([u.tileR, u.tileC], [12, 8], 'auto-redeploy on the tile it lay on');
h.b.retreat(u);
assert.equal(h.b.redeploy(u), true);
assert.deepEqual([u.tileR, u.tileC], [10, 4], 'a retreated unit comes back on its home tile');
// paid redeploy on a tile
h.b.retreat(u);
const dp = p.dp;
+3 -2
View File
@@ -340,12 +340,13 @@ test('helpers validate their inputs: spawnDevice / relocate / spawnToken / spawn
assert.ok(g.baseRangeKeys.includes(10 * 21 + 6), 'initial (DEFAULT trigger) range follows the unit');
b.kill(g);
assert.equal(b.relocate(g, 12, 6), false, 'dead units stay put');
b.redeploy(g);
b.redeploy(g); // back on the tile it was knocked out on, (10,6) (PRTS 卫戍协议/帮助: 自动部署至该位置)
// spawnToken on a busy tile (even with force) → null, no half-built token left behind
const aliveBefore = b.allyUnits.filter((u) => u.alive).length;
const hooks = hookCount(b);
assert.equal(g.alive, true);
assert.equal(b.spawnToken('p1', 'x_token', 9, 5, { def: { name: 't', stats: { maxHp: 100 } }, force: true }), null);
assert.deepEqual([g.tileR, g.tileC], [10, 6]);
assert.equal(b.spawnToken('p1', 'x_token', 10, 6, { def: { name: 't', stats: { maxHp: 100 } }, force: true }), null);
assert.equal(b.allyUnits.filter((u) => u.alive).length, aliveBefore, 'no half-built token');
assert.ok(hookCount(b) <= hooks);
const t = b.spawnToken('p1', 'x_token', 12, 8, { def: { name: 't', stats: { maxHp: 100 } }, hp: NaN, stats: { atk: NaN, def: 50 } });