Files
ark-stronghold-protocol/test/content/feedback7-champagne-hold.test.js
T
sganggs 5f98baecc1 feedback7 R22-OSIM: 琳琅诗怀雅's 香槟炸弹 holds 禁疗, loses no HP and never leaves because of its HP (the owner's decision D2); golden: bond-yanShip-high, coop2-HARD-4 move
The owner's decision D2 of 2026-10-08, a deliberate deviation: PRTS 香槟炸弹 gives it 1000 HP and the note
"即使自身生命值未满,模型下方也不会显示生命值槽" (officially it can lose HP), and it had no 无敌 / 禁疗. Now
(tokens.js champagneHold, installed by the token kit and by 琳琅诗怀雅's own bomb):
- 禁疗: no heal reaches it, no healer picks it (flags noHeal + healFree, no status icon);
- no HP loss: every damage to it is cancelled in its `hit` / `elementHit` hooks (活性源石, 污染秽蚀, …) and a 流失
  is given back before the knock-out check, so it never leaves because of its HP;
- its 见面礼 is unchanged: the first ground enemy that touches it, then it is gone. No 无敌 flag (that would put a
  badge over it); the client side (its hidden HP bar) belongs to the render stream.

Golden: bond-yanShip-high (a healer of that team spent 27 heals — 5040 HP — on a bomb standing on 活性源石; now it
attacks nothing there) and coop2-HARD-4 (ai_1's bombs from round 6 on stood on 活性源石 / 污染秽蚀; their longer life
changes later rounds' layers).

Tests: test/content/feedback7-champagne-hold.test.js (3; 2 fail on the parent, the 见面礼 guard passes on both).
2026-10-08 20:07:06 +08:00

68 lines
3.9 KiB
JavaScript

// test/content/feedback7-champagne-hold.test.js — 琳琅诗怀雅 S2's 香槟炸弹 (token_10031_swire2_gdtrap) under the owner's
// decision D2 of 2026-10-08: it holds 禁疗, loses no HP (活性源石, damage, a 流失) and never leaves because of its HP; its
// 见面礼 is unchanged (the first ground enemy that touches it, then it is gone). Both kits: 琳琅诗怀雅's own bomb and the
// token kit (content/tokens.js champagne, a bomb spawned without her kit).
// Run: node --test test/content/feedback7-champagne-hold.test.js
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { makeBattle, enemyRec, checkInvariants } from '../helpers/battleHarness.js';
const TRAP = 'token_10031_swire2_gdtrap';
const SW = 'chess_char_3_04_a';
/** 琳琅诗怀雅 on (9,5) of a field whose only free x-6 tile is (9,6) — 活性源石 when `infected`; she throws one bomb there. */
function herBomb({ infected = false, units = [], enemies = [] } = {}) {
const row9 = infected ? '##ERRriRrrSrrrrrrrS##' : '##ERRrrRrrSrrrrrrrS##';
const h = makeBattle({
flat: { rows: { 9: row9, 10: '##hrrRrrrrfrrrrrrrf##', 11: '##hrrRrrrrfrrrrrrrf##' } },
defs: { enemies: { e_w: enemyRec({ key: 'e_w', hp: 1e6, speed: 1, def: 0, atk: 0 }) } },
timeLimit: 60, autoFinish: false, hooks: ['heal', 'death', 'damaged'], captureNoisy: true,
units: [{ uid: 1, chessId: SW, row: 9, col: 5 }, ...units], enemies,
});
h.step();
h.unit(1).mem.coins = 1;
assert.ok(h.runUntil(() => h.b.allyUnits.some((t) => t.alive && t.defId === TRAP), 6), 'she threw a bomb');
const bomb = h.b.allyUnits.find((t) => t.alive && t.defId === TRAP);
assert.deepEqual([bomb.tileR, bomb.tileC], [9, 6]);
return { h, bomb, sw: h.unit(1) };
}
test('香槟炸弹 (her kit): no HP loss on 活性源石, 禁疗, a lethal 流失 or hit leaves it at full HP and on the field', () => {
const { h, bomb } = herBomb({ infected: true });
assert.ok(bomb.s.flags.untargetable && bomb.s.flags.noHeal && bomb.s.flags.healFree, '不可选中 + 禁疗');
assert.ok(!bomb.s.flags.invulnerable, 'no 无敌 flag (no badge on the client)');
h.run(3.2);
assert.equal(bomb.hp, bomb.s.maxHp, '活性源石 takes nothing');
assert.equal(h.b.heal(h.unit(1), bomb, 500), 0, 'no heal reaches it');
assert.equal(h.b.injuredAlliesInKeys([9 * 21 + 6], h.unit(1)).length, 0, 'no healer picks it');
h.b.dealDamage(null, bomb, { amount: 1e6, type: 'true', tags: ['test'] });
h.b.loseHp(bomb, 1e6, { tags: ['test'] });
h.step();
assert.ok(bomb.alive && bomb.deployed, 'not gone because of its HP');
assert.equal(bomb.hp, bomb.s.maxHp);
assert.equal(h.hooksOf('death').filter((c) => c.unit === bomb).length, 0);
checkInvariants(h.b);
});
test('香槟炸弹 (her kit): its 见面礼 is unchanged — the first ground enemy touching it is hit and slowed, then the bomb is gone', () => {
const { h, bomb } = herBomb({ enemies: [{ key: 'e_w', route: 0, time: 1 }] });
assert.ok(h.runUntil(() => !bomb.alive, 30), 'set off');
const e = h.b.enemies.find((x) => x.alive);
assert.ok(e && e.hp < 1e6 && e.findBuff('sluggish'), 'hit + 停顿');
assert.ok(h.hooksOf('death').some((c) => c.unit === bomb && c.reason === 'expired'), 'used up');
checkInvariants(h.b);
});
test('香槟炸弹 (the token kit, spawned without her kit): the same 禁疗, no HP loss, no knock-out', () => {
const h = makeBattle({ timeLimit: 30, autoFinish: false, hooks: ['death'], units: [{ uid: 1, chessId: SW, row: 12, col: 3 }] });
h.step();
const bomb = h.b.spawnToken(h.unit(1), TRAP, 9, 6);
assert.ok(bomb && bomb.s.flags.noHeal && bomb.s.flags.healFree && bomb.s.flags.untargetable);
h.b.loseHp(bomb, 1e6, { tags: ['test'] });
h.b.dealDamage(null, bomb, { amount: 1e6, type: 'true', tags: ['test'] });
h.step();
assert.ok(bomb.alive && bomb.hp === bomb.s.maxHp);
assert.equal(h.b.heal(h.unit(1), bomb, 500), 0);
checkInvariants(h.b);
});