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).
This commit is contained in:
sganggs committed 2026-10-08 20:07:06 +08:00
1 parent 5a045731f1
commit 5f98baecc1
6 files changed
+106 -19

No files matched your search

@@ -0,0 +1,67 @@
// 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);
});
+6 -6
View File
@@ -1793,7 +1793,7 @@
"bossHpLeft": null,
"rngDraws": 10,
"errors": {"count":0,"kinds":[]},
"players": {"p1":{"killed":21,"total":21,"perfect":true,"leaks":0,"leaksUncounted":0,"coins":0,"dmg":263171,"bossDmg":0,"heal":6511,"deaths":0,"layerGains":{}}},
"players": {"p1":{"killed":21,"total":21,"perfect":true,"leaks":0,"leaksUncounted":0,"coins":0,"dmg":263171,"bossDmg":0,"heal":1471,"deaths":0,"layerGains":{}}},
"units": [
["p1",1,"op","chess_char_4_17_b",1,"uniequip_002_hsguma",3513,1,8,0,0,0,0,0,0],
["p1",2,"op","chess_char_6_03_b",2,"uniequip_002_yu",3700,1,6,0,0,0,0,0,0],
@@ -1803,14 +1803,14 @@
["p1",6,"op","chess_char_2_04_b",1,"uniequip_002_grabds",1270,1,5,0,0,0,0,0,0],
["p1",7,"op","chess_char_5_03_b",2,"uniequip_002_blaze2",632.96,1,12,3,51359,2310,1471,8,2],
["p1",8,"op","chess_char_5_12_b",0,"uniequip_002_dusk",1787,1,7,4,68403,0,0,4,4],
["p1",9,"op","chess_char_5_23_b",1,"uniequip_002_reckpr",1610,1,10,0,0,0,5040,27,2],
["p1",9,"op","chess_char_5_23_b",1,"uniequip_002_reckpr",1610,1,10,0,0,0,0,0,0],
["-","~1","device","trap_1105_accrate",null,null,100,1,1,0,0,0,0,0,null],
["-","~2","device","trap_1105_accrate",null,null,100,1,2,0,0,0,0,0,null],
["-","~3","device","trap_1105_accrate",null,null,100,1,3,0,0,0,0,0,null],
["p1","~1","token","enemy_9012_acloon",null,null,17300.16,1,13,4,49667,389,0,2,2],
["p1","~2","token","enemy_9012_acloon",null,null,13634.89,1,14,2,28865,4054,0,1,1],
["p1","~1","token","token_10031_swire2_gdtrap",null,null,1000,0,15,0,0,0,0,0,null],
["p1","~2","token","token_10031_swire2_gdtrap",null,null,930,1,18,0,0,5110,0,0,null],
["p1","~2","token","token_10031_swire2_gdtrap",null,null,1000,1,18,0,0,0,0,0,null],
["p1","~3","token","token_10031_swire2_gdtrap",null,null,1000,0,27,0,0,0,0,0,null],
["p1","~4","token","token_10031_swire2_gdtrap",null,null,1000,0,30,0,0,0,0,0,null],
["p1","~5","token","token_10031_swire2_gdtrap",null,null,1000,0,33,0,0,0,0,0,null],
@@ -1822,10 +1822,10 @@
["p1","~10","token","token_10031_swire2_gdtrap",null,null,1000,1,46,0,0,0,0,0,null]
],
"enemies": {"enemy_1019_jshoot_2":[7,7,0,1748,47029],"enemy_1381_winman":[9,9,0,0,129890],"enemy_1389_winbab_2":[5,5,0,5215,87091]},
"hooks": [1,46,2282,121,121,188,224,960,22,30,12,9,6,5355,1,4,21,0,1,23,0,0,0],
"events": {"atk":121,"deploy":22,"die":30,"dmg":224,"engage":6,"fx":46,"heal":27,"skill":25,"spawn":46,"status":3},
"hooks": [1,46,2282,94,94,188,151,933,22,30,10,8,6,4515,1,4,21,0,1,23,0,0,0],
"events": {"atk":94,"deploy":22,"die":30,"dmg":151,"engage":6,"fx":46,"skill":22,"spawn":46,"status":3},
"fx": {"coin":14,"downed":1,"explode":8,"revive":1,"splash":6,"summon":11,"yanyouFlame":3,"yanyouSummon":2},
"snaps": "a8dd7569 06196293 7fe10e91 ba972d4d 6d08cfcf fb599bc6 1b50192f 15529175"
"snaps": "663ec4b2 fcbf72b6 e7a436e9 b069f338 26ddf9b5 aebe36fd afb4e8de 79b62d42"
}
}
}
+9 -9
View File
@@ -680,37 +680,37 @@
{
"round": 7,
"ai_0": [1,25,10,0,0,4,8,"1ec7b320",17,"kazimierzShip 0/2, kjeragShip 0/2, siracusaShip 1/6, skillfulShip 1/6, soloShip 1/3, steadShip 2/2, swiftShip 0/8",26,28,2,49],
"ai_1": [1,26,11,0,1,4,8,"b417fbf1",19,"arcaneShip 1/8, kazimierzShip 0/2, preciShip 1/5, sargonShip 0/8, skillfulShip 1/6, swiftShip 0/8",28,28,0,54]
"ai_1": [1,26,11,0,1,4,8,"b417fbf1",20,"arcaneShip 1/8, kazimierzShip 0/2, preciShip 1/6, sargonShip 0/8, skillfulShip 1/6, swiftShip 0/8",28,28,0,54]
},
{
"round": 8,
"ai_0": [1,25,11,0,0,4,8,"24603f24",20,"kazimierzShip 0/2, kjeragShip 0/2, siracusaShip 1/6, skillfulShip 1/6, soloShip 1/3, steadShip 2/5, swiftShip 0/8",5,9,4,60],
"ai_1": [1,26,12,0,0,4,8,"33304c67",63,"arcaneShip 1/8, kazimierzShip 0/2, preciShip 1/19, sargonShip 1/25, skillfulShip 1/11, soloShip 1/0, swiftShip 0/8",9,9,0,65]
"ai_1": [1,26,12,0,0,4,8,"33304c67",64,"arcaneShip 1/8, kazimierzShip 0/2, preciShip 1/20, sargonShip 1/25, skillfulShip 1/11, soloShip 1/0, swiftShip 0/8",9,9,0,65]
},
{
"round": 9,
"ai_0": [1,19,12,0,4,4,8,"aea06001",50,"deputShip 1/2, kazimierzShip 0/2, kjeragShip 0/2, siracusaShip 1/22, skillfulShip 1/8, soloShip 1/5, steadShip 1/11, suntShip 1/2, swiftShip 0/8",14,23,9,72],
"ai_1": [1,26,12,0,2,4,8,"1bc5731a",105,"arcaneShip 2/14, kazimierzShip 0/2, preciShip 1/33, sargonShip 1/42, skillfulShip 1/16, suntShip 1/0, swiftShip 0/8",23,23,0,77]
"ai_1": [1,26,12,0,2,4,8,"1bc5731a",106,"arcaneShip 2/14, kazimierzShip 0/2, preciShip 1/34, sargonShip 1/42, skillfulShip 1/16, suntShip 1/0, swiftShip 0/8",23,23,0,77]
},
{
"round": 10,
"ai_0": [1,19,16,0,0,5,8,"9b53670a",75,"deputShip 1/4, kazimierzShip 0/2, kjeragShip 0/2, siracusaShip 1/34, skillfulShip 1/10, soloShip 1/7, steadShip 1/16, suntShip 1/4, swiftShip 0/8",5,8,3,88],
"ai_1": [1,26,14,0,4,5,8,"aa0f2565",129,"arcaneShip 2/16, kazimierzShip 0/2, preciShip 1/43, sargonShip 1/52, skillfulShip 1/18, suntShip 1/0, swiftShip 0/8",8,8,0,91]
"ai_1": [1,26,14,0,4,5,8,"aa0f2565",132,"arcaneShip 2/16, kazimierzShip 0/2, preciShip 1/45, sargonShip 1/53, skillfulShip 1/18, suntShip 1/0, swiftShip 0/8",8,8,0,91]
},
{
"round": 11,
"ai_0": [1,9,12,0,1,5,8,"9a1f89d4",88,"deputShip 1/6, emptyShip 1/2, kazimierzShip 0/2, kjeragShip 0/2, raidShip 1/3, siracusaShip 1/50, skillfulShip 1/12, soloShip 1/9, steadShip 0/16, suntShip 1/6, swiftShip 0/8",13,27,14,100],
"ai_1": [1,16,16,0,2,5,8,"b7d6fa51",183,"arcaneShip 2/26, kazimierzShip 0/2, preciShip 1/64, sargonShip 1/73, skillfulShip 1/20, suntShip 1/0, swiftShip 0/8",13,27,14,108]
"ai_1": [1,16,16,0,2,5,8,"b7d6fa51",186,"arcaneShip 2/26, kazimierzShip 0/2, preciShip 1/66, sargonShip 1/74, skillfulShip 1/20, suntShip 1/0, swiftShip 0/8",13,27,14,108]
},
{
"round": 12,
"ai_0": [0,0,13,0,0,6,0,"811c9dc5",0,"deputShip 0/6, emptyShip 0/2, kazimierzShip 0/2, kjeragShip 0/2, raidShip 0/3, siracusaShip 0/52, skillfulShip 0/12, soloShip 0/9, steadShip 0/16, suntShip 0/6, swiftShip 0/8",24,50,26,113],
"ai_1": [1,6,14,0,2,6,8,"41045310",233,"arcaneShip 2/26, kazimierzShip 0/2, preciShip 1/88, sargonShip 1/97, skillfulShip 1/22, suntShip 1/0, swiftShip 0/8",36,50,14,121]
"ai_1": [1,6,14,0,2,6,8,"41045310",236,"arcaneShip 2/26, kazimierzShip 0/2, preciShip 1/90, sargonShip 1/98, skillfulShip 1/22, suntShip 1/0, swiftShip 0/8",36,50,14,121]
},
{
"round": 13,
"ai_0": [0,0,0,0,0,6,0,"811c9dc5",0,"deputShip 0/6, emptyShip 0/2, kazimierzShip 0/2, kjeragShip 0/2, raidShip 0/3, siracusaShip 0/52, skillfulShip 0/12, soloShip 0/9, steadShip 0/16, suntShip 0/6, swiftShip 0/8",null,null,null,113],
"ai_1": [0,0,14,0,0,6,0,"811c9dc5",0,"arcaneShip 0/30, kazimierzShip 0/2, preciShip 0/93, sargonShip 0/102, skillfulShip 0/24, swiftShip 0/8",40,49,9,135]
"ai_1": [0,0,14,0,0,6,0,"811c9dc5",0,"arcaneShip 0/30, kazimierzShip 0/2, preciShip 0/98, sargonShip 0/106, skillfulShip 0/24, swiftShip 0/8",42,49,7,135]
}
],
"players": {
@@ -738,9 +738,9 @@
"title": {"id":"comment_5","name":"万事俱备","picId":"comment_icon_5","text":"装备配发数量最多"},
"trophies": 3,
"reward": 298,
"stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":3769501,"gold":135,"itemsEquipped":18,"kills":260,"leaks":38,"lpLost":29,"merges":3,"perfectRounds":9,"refreshes":16},
"stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":3796247,"gold":135,"itemsEquipped":18,"kills":262,"leaks":36,"lpLost":27,"merges":3,"perfectRounds":9,"refreshes":16},
"lineup": [],
"bonds": ["arcaneShip 0/30","kazimierzShip 0/2","preciShip 0/93","sargonShip 0/102","skillfulShip 0/24","swiftShip 0/8"]
"bonds": ["arcaneShip 0/30","kazimierzShip 0/2","preciShip 0/98","sargonShip 0/106","skillfulShip 0/24","swiftShip 0/8"]
}
}
},