feedback3 WF: a 傀儡师 leaves every switch at full HP (PRTS: the HP is reset during the switch, 不死 throughout) — a second 阿戈尔 devour mark inside the 1-s switch no longer leaves 归溟幽灵鲨's 替身 at 1 HP (community report #2)

This commit is contained in:
sganggs committed 2026-10-04 19:27:34 +08:00
1 parent 21eb496e02
commit e376cff5a8
3 files changed
+42 -3

No files matched your search

+1 -1
View File
@@ -1143,7 +1143,7 @@ table: `hitSleep` (targets and damages sleeping enemies — "可以攻击沉睡
| agent / hookmaster | can hit FLY (ranged reach); hook displacement comes from skills (generic: `force`) |
| bearer | block 0 while the skill is active |
| alchemist | ranged lob, can hit FLY |
| dollkeeper | fatal damage (no 不死 — a running 坚固维式重锤 window comes first) ⇒ a 1 s switch animation [ASSUMED length, also on a direct switch] (无敌, 不死, 阻回, 禁疗, 孤立, 缴械, 眩晕 / 冻结 / 睡眠 immune; ends the running skill and removes the statuses), then the <替身> for 20 s (bb duration): block 0 from the switch on, 阻回, HP = its own 替身 token's (风丸 纸偶), else its own max HP (归溟幽灵鲨: PRTS resets it "至最大值", the trait's 替身 HP bonus `max_hp` is 0, 风丸's 纸偶 has her HP); then the switch back (the same animation) at full HP; dies if the 替身 dies. Kit flag `dollNoAttack` (归溟幽灵鲨): the 替身 makes no normal attack and casts no skill. Hook `dollSwitch` switches it at once (归溟幽灵鲨 S2's end). Model form `'doll'` (PRTS 分支特性信息 傀儡师, DESIGN §22.11) |
| dollkeeper | fatal damage (no 不死 — a running 坚固维式重锤 window comes first) ⇒ a 1 s switch animation [ASSUMED length, also on a direct switch] (无敌, 不死, 阻回, 禁疗, 孤立, 缴械, 眩晕 / 冻结 / 睡眠 immune; ends the running skill and removes the statuses; the HP is set to the max at its start and again at its end — PRTS "切换途中重设自身生命至最大值" —, so a lethal 流失 inside it, held at 1 HP, never outlasts it), then the <替身> for 20 s (bb duration): block 0 from the switch on, 阻回, HP = its own 替身 token's (风丸 纸偶), else its own max HP (归溟幽灵鲨: PRTS resets it "至最大值", the trait's 替身 HP bonus `max_hp` is 0, 风丸's 纸偶 has her HP); then the switch back (the same animation) at full HP; dies if the 替身 dies. Kit flag `dollNoAttack` (归溟幽灵鲨): the 替身 makes no normal attack and casts no skill. Hook `dollSwitch` switches it at once (归溟幽灵鲨 S2's end). Model form `'doll'` (PRTS 分支特性信息 傀儡师, DESIGN §22.11) |
| geek | loses 1–3 % max HP per second (bb hp_ratio), never lethal on its own |
| merchant | −3 DP every 3 s (bb cost/interval); retreats when DP runs out |
| skywalker | can block FLY enemies (蒂比's kit: only while airborne — 起飞, flag `liftoff`, which also releases the ground enemies she blocked) |
+11 -2
View File
@@ -128,15 +128,24 @@ const installDollkeeper = (battle, unit) => {
};
const at = (extra) => ({ x: unit.x, y: unit.y, id: unit.id, ...extra });
// a switch animation: "切换动画开始时会清除自身一切Buff" (header: the running skill and the statuses), then 1 s of 不死
// (the fatal hook below) 无敌 阻回 禁疗 孤立 强制缴械 and 眩晕 / 冻结 / 睡眠 immunity (beforeStatus below)
// (the fatal hook below) 无敌 阻回 禁疗 孤立 强制缴械 and 眩晕 / 冻结 / 睡眠 immunity (beforeStatus below). "切换途中重设自身
// 生命至最大值": the HP is set to the max at the start (enter / leave) and again when the switch ends, so she always
// leaves it at full HP — a lethal 流失 inside the switch (无敌 does not stop it, 不死 holds it at 1 HP: a later 阿戈尔
// devour mark on her) used to leave her the 替身's 20 s at 1 HP (community report #2)
const startSwitch = () => {
const sk = unit.skill;
if (sk && sk.active && sk.kind !== 'passive') sk.end('substitute');
for (const b of unit.buffs.slice()) if (b.status) battle.removeBuff(unit, b);
unit.trait.dollSwitching = true;
const done = () => { unit.trait.dollSwitching = false; };
const ended = () => {
done();
if (!unit.alive || !unit.deployed) return;
unit.markDirty();
unit.hp = unit.s.maxHp;
};
battle.addBuff(unit, {
key: 'trait:dollSwitching', duration: DOLL_SWITCH, onExpire: done, onRemove: done,
key: 'trait:dollSwitching', duration: DOLL_SWITCH, onExpire: ended, onRemove: done,
flags: { invulnerable: true, noSp: true, noHeal: true, isolated: true, disarm: true },
});
};
+30
View File
@@ -158,6 +158,36 @@ test('S1 / S3 running at a lethal hit end with the switch (清除自身一切Buf
assert.equal(s2.h.b.emit('dollSwitch', { unit: s2.g, reason: 'test', done: false }).done, false, 'already the 替身');
});
test('E: a lethal 流失 inside the switch (a second 阿戈尔 devour mark) — she leaves it at full HP, both ways (PRTS "切换途中重设自身生命至最大值"; community report #2)', { skip }, () => {
// 乌尔比安 → 斯卡蒂 → 归溟幽灵鲨 → 深巡 → 幽灵鲨 → 隐现 (5 阿戈尔): 乌尔比安's mark switches her, 斯卡蒂's lands inside
// the 1 s switch (无敌 does not stop a 流失, 不死 holds it at 1 HP; the switch is no knock-out, so the mark is not
// cancelled). Until 0.1.3 she then fought the 替身's 20 s at 1 HP and fell to the first hit
const ids = ['chess_char_5_05_a', 'chess_char_3_05_a', G, 'chess_char_1_04_a', 'chess_char_2_07_a', 'chess_char_1_01_a'];
const h = makeBattle({
seed: 4, timeLimit: 300, autoFinish: false, captureNoisy: true, hooks: ['damaged', 'death'],
units: ids.map((chessId, i) => ({ chessId, row: 10, col: 3 + i })),
bonds: { egirShip: { count: 5, active: true, tier: 2, layers: 0 } },
});
h.step();
const g = h.unit(G);
const marks = h.hooksOf('damaged').filter((c) => c.target === g && c.dmg?.tags?.includes('bond:egir:devour'));
assert.equal(marks.length, 2, 'two devour marks resolve on her');
assert.ok(g.alive && g.trait.doll && g.trait.dollSwitching, 'the first switched her');
assert.ok(g.hp < 2, `the second, inside the switch, left 1 HP (不死; a regen tick aside): ${g.hp}`);
h.run(DOLL_SWITCH + 0.05);
assert.ok(g.trait.doll && !g.trait.dollSwitching, 'the 替身 fights');
approx(g.hp, g.s.maxHp, 'at full HP when the switch ends');
// the switch back: a lethal 流失 inside it is held at 1 HP, then she is the 本体 at full HP
assert.ok(h.runUntil(() => !g.trait.doll && g.trait.dollSwitching, 25), 'switching back');
h.b.loseHp(g, 1e9, { source: null });
assert.ok(g.alive && g.hp < 2, `not knocked out while switching: held at 1 HP (${g.hp})`);
h.run(DOLL_SWITCH + 0.05);
assert.ok(!g.trait.doll && !g.trait.dollSwitching && g.form === null, 'the 本体');
approx(g.hp, g.s.maxHp, 'at full HP when the switch back ends');
assert.equal(h.hooksOf('death').filter((c) => c.unit === g).length, 0, 'never knocked out');
checkInvariants(h.b);
});
test('a 本体 holding 不死 (a running 坚固维式重锤 window) does not switch: held at 1 HP (PRTS "受到足以致命的伤害且未持有不死的情况下")', { skip }, () => {
const { h, g, lethal } = setup({ items: ['chess_item_3_09_e_a'] });
lethal();