feedback1f WV: a knocked-out operator keeps its tile; 突袭 never lands on it

Player report F5 after 0.1.0: "突袭盟约的干员会移动到濒死干员的尸体上".

Real. A knocked-out operator left the occupancy map at once and only its
home tile stayed reserved. A 突袭 member that fell after its jump lay on the
landing tile, which nothing reserved, so the next member's jump (same target,
same scoring) landed on it; and a member's timed redeploy came back on its home
where a teammate that had jumped there lay. Real matches (six real 突袭
members, 战场#07 / #08, R6 / R9 / R12, 96 battles): 6 placements on a body
(3 jumps, 3 home redeploys) before, 0 of 526 jumps after.

Official (PRTS 卫戍协议/帮助, 作战阶段 / 单位部署):
- "干员退场后…原地留下一个“倒地干员”…满足再部署条件时,移除场上的该倒地干员
  并自动部署至该位置"
- "倒地干员所在地块视为可部署,但所有我方单位在此处的部署行为将被阻止"
- "若干员被击倒的位置为其他干员或召唤物的初始位置,则在被击倒后,尝试返回其
  自身的初始位置"

Now (sim/Battle.js):
- A knocked-out operator lies on `unit.body`: the tile it fell on, or its
  own home when it fell on another board piece's home and its home is free
  (one attempt at the knock-out, assumed). x / y / tileR / tileC keep where
  it fell, so the kill / death handlers still act there.
- Every redeploy comes back there (restTile): the timed one, 不屈's and
  阿戈尔's. A retreated operator still comes back on its home.
- downOn(r, c): _deploy (redeploys, the 突袭 landing, summons), spawnDevice
  and relocate refuse the tile for every ally of the field; isReservedTile
  reports it, so every automatic picker skips it (突袭 raidTile, tactical
  points, summon / device pickers, 维娜's lions). The kit helpers that kept
  their own home-tile loop (tier1 summonTileFree, tier3 / tier4 freeTile) go
  through isReservedTile.
- b.snap `down` entries gain the body tile: [id, respawnAt, respawnTime,
  state, row, col]. DOWN_STATE.WAIT_TILE becomes a safeguard only.

Tests: test/sim/feedback1f-downed-tile.test.js (real Match / PlayerState /
BattleSpec runs with real 突袭 chess, the 突袭 jump, the engine refusals,
the return home); downelem and bonds_addon follow the official rule.
This commit is contained in:
sganggs committed 2026-10-03 07:16:24 +08:00
1 parent 60ed4fdab9
commit 5a3d562b75
12 files changed
+374 -71

No files matched your search

+8 -3
View File
@@ -307,7 +307,7 @@ test('突袭: idle member relocates next to a ground enemy (SP kept) with ATK/HP
checkInvariants(h2.b);
});
test('突袭: the jump is a redeployment — deploy fires (部署时 effects), full HP, SP kept; later redeploys use the board tile', () => {
test('突袭: the jump is a redeployment — deploy fires (部署时 effects), full HP, SP kept; knocked out there, it comes back there', () => {
const bb = bondBb('raidShip');
const defs = { chess: { r_m: chessRec({ id: 'r_m', bonds: ['raidShip'], skill: { spCost: 40, initSp: 0, duration: 10 } }) }, enemies: DUMMY };
let spAtJump = null;
@@ -329,10 +329,15 @@ test('突袭: the jump is a redeployment — deploy fires (部署时 effects), f
close(u.hp, u.s.maxHp, 'fresh deployment: full HP');
close(u.s.maxHp, 2000 * (1 + bb.base_max_hp), 'raid HP bonus');
assert.equal(h.result?.().perPlayer?.p1?.deaths ?? 0, 0, 'not counted as a death');
// knocked out later: the auto-redeploy goes back to its board tile, without the raid bonus
// knocked out later: it lies on its landing tile and comes back there ("原地留下一个“倒地干员”…满足再部署条件时…
// 自动部署至该位置", PRTS 卫戍协议/帮助 — player report F5 after 0.1.0), without the raid bonus; its home stays the
// board tile
const landed = [u.tileR, u.tileC];
h.b.dealDamage(null, u, { amount: 1e9, type: 'true' });
assert.deepEqual([u.tileR, u.tileC], landed, 'lies where it fell');
h.b.redeploy(u);
assert.deepEqual([u.tileR, u.tileC], [12, 3], 'home tile unchanged');
assert.deepEqual([u.tileR, u.tileC], landed, 'redeployed where it lay');
assert.deepEqual([u.homeR, u.homeC], [12, 3], 'home tile unchanged');
close(u.s.maxHp, 2000, 'bonus gone after leaving the field');
checkInvariants(h.b);
});