Official: PRTS 盟约记录 备注 — "复活" is 退场 + a 0-time / 0-cost redeploy, i.e. a new deployment, after which a deploy-timed
skill ("部署后…秒内") runs again; a 突袭 member counts such a running window as 技能就绪 (bonds/addon/battle.js raidPoll,
GitHub #49 / #109) and jumps when nothing is in its range. Cause: the remake revives in place (fatal hook) — her window, over
by then, stayed over, and only the idle trigger was left (measured: 缄默德克萨斯 S3 jumped 10.00 s after an M3茧甲 / 埃芒加德
revive, 0.23 s after 不屈's real redeploy). Now items/battle.js revivedInPlace also restarts an activateOnDeploy skill on the
next tick: activated anew when its window had ended, its window back to the full duration when it still ran. The SP reset
and the other 部署时 effects of PRTS's form stay unmodelled (§21.21).
Golden: 1 scenario — roster-012: the elite 缄默德克萨斯 is revived in place three times (101 s, 105 s, 113 s) and her S3 runs
again each time (checked instrumented). Tests: test/sim/feedback5-raid-revive.test.js (3; the M3茧甲 / 埃芒加德 jump and the
window cases fail on the parent, 不屈 is the unchanged control). Docs: docs/SIM.md and docs/design/engine.md (fatal hook).
80 lines
4.8 KiB
JavaScript
80 lines
4.8 KiB
JavaScript
// test/sim/feedback5-raid-revive.test.js — community report of 2026-10-06 (item 44) 「突袭盟约的干员如果带被动技能,复活之后不会
|
|
// 立马突袭出去,还要等突袭CD」. A 突袭 member counts a running deploy-timed skill ("部署后…秒内": 缄默德克萨斯 S1–S3 …) as 技能
|
|
// 就绪 (bonds/addon/battle.js raidPoll, GitHub #49 / #109), so after a redeploy it jumps at once when nothing is in its range.
|
|
// PRTS's 复活 (M3茧甲, 埃芒加德) is 退场 + a 0-time / 0-cost redeploy (PRTS 盟约记录 备注) — a new deployment — but the remake
|
|
// revives in place: her window, over by then, stayed over and she waited out the 10 s idle time. Now an in-place revive
|
|
// starts a deploy-timed skill again (content/items/battle.js revivedInPlace); 不屈's real redeploy already did.
|
|
// Run: node --test test/sim/feedback5-raid-revive.test.js
|
|
|
|
import { test } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { makeBattle, enemyRec, checkInvariants } from '../helpers/battleHarness.js';
|
|
|
|
const TEX = 'chess_char_4_16_a';
|
|
const DUMMY = { enemy_rv: enemyRec({ key: 'enemy_rv', hp: 1e9, speed: 0, mass: 0, atk: 0 }) };
|
|
const RAID = { raidShip: { count: 3, active: true, tier: 1, layers: 10 } };
|
|
|
|
/**
|
|
* 缄默德克萨斯 (S3, a 6 s deploy-timed skill) in 突袭 with `extra` (items / band): she jumps to a parked enemy, stays past her
|
|
* window, the enemy is removed, she is knocked out with nothing in range, a new enemy parks away from her. Returns how long
|
|
* after the knock-out her next jump comes (null: none within 15 s) and her skill state right after the revive.
|
|
*/
|
|
function reviveAndJump({ items, bandId = null, bonds = {} }) {
|
|
const h = makeBattle({
|
|
seed: 3, autoFinish: false, timeLimit: 120, hooks: ['deploy', 'death'], defs: { enemies: DUMMY }, bandId,
|
|
bonds: { ...RAID, ...bonds }, enemies: [{ key: 'enemy_rv', pos: [9, 9] }], units: [{ chessId: TEX, row: 12, col: 3, skillIndex: 2, items }],
|
|
});
|
|
h.step();
|
|
const u = h.unit(TEX);
|
|
const jumps = () => h.hooksOf('deploy').filter((c) => c.unit === u && !c.initial);
|
|
assert.ok(h.runUntil(() => jumps().length >= 1, 10), 'her first jump');
|
|
h.run(8);
|
|
assert.equal(u.skill.active, false, 'her 6 s window is over');
|
|
for (const e of h.b.enemies.filter((x) => x.alive)) h.b.kill(e, null);
|
|
const t0 = h.b.time;
|
|
h.b.dealDamage(null, u, { amount: 1e9, type: 'true' });
|
|
h.spawn('enemy_rv', { pos: [12, 9] });
|
|
h.step();
|
|
const state = { alive: u.alive, active: u.skill.active, timeLeft: u.skill.timeLeft };
|
|
const n = jumps().length;
|
|
const jumped = h.runUntil(() => jumps().length > n && u.tileR === 12, 15);
|
|
checkInvariants(h.b);
|
|
return { after: jumped ? h.b.time - t0 : null, state, u, h };
|
|
}
|
|
|
|
test('M3茧甲 / 埃芒加德 revive 缄默德克萨斯 in place: her deploy-timed S3 runs again and she jumps at once (until 0.2.0 only after the 10 s idle)', () => {
|
|
for (const [label, opts] of [['M3茧甲', { items: ['chess_item_4_12_e_a'] }], ['埃芒加德', { bandId: 'band_ermengard' }]]) {
|
|
const { after, state } = reviveAndJump(opts);
|
|
assert.ok(state.alive, `${label}: revived in place`);
|
|
assert.ok(state.active, `${label}: her deploy-timed S3 runs again`);
|
|
assert.ok(after != null && after < 1, `${label}: jumped ${after?.toFixed(2)} s after the knock-out`);
|
|
}
|
|
});
|
|
|
|
test('不屈\'s 立刻重新部署 (a real redeploy) already jumped at once — unchanged', () => {
|
|
const { after } = reviveAndJump({ bonds: { indomShip: { count: 2, active: true, tier: 1, layers: 300 } } });
|
|
assert.ok(after != null && after < 1, `jumped ${after?.toFixed(2)} s after the knock-out`);
|
|
});
|
|
|
|
test('an in-place revive inside the window sets it back to its full length; 砾 revived by M3茧甲 gets a fresh barrier', () => {
|
|
const h = makeBattle({ seed: 3, autoFinish: false, timeLimit: 60, units: [{ chessId: TEX, row: 12, col: 3, skillIndex: 2, items: ['chess_item_4_12_e_a'] }] });
|
|
h.step();
|
|
const u = h.unit(TEX);
|
|
h.run(3);
|
|
assert.ok(u.skill.active && u.skill.timeLeft < u.skill.duration - 2, 'half-way through the window');
|
|
h.b.dealDamage(null, u, { amount: 1e9, type: 'true' });
|
|
h.step(2);
|
|
assert.ok(u.alive && u.skill.active && u.skill.timeLeft > u.skill.duration - 0.2, `back to the full ${u.skill.duration} s (${u.skill.timeLeft.toFixed(2)})`);
|
|
checkInvariants(h.b);
|
|
|
|
const g = makeBattle({ seed: 3, autoFinish: false, timeLimit: 60, units: [{ chessId: 'chess_char_2_12_a', row: 10, col: 4, items: ['chess_item_4_12_e_a'] }] });
|
|
g.step();
|
|
const gr = g.unit('chess_char_2_12_a');
|
|
g.run(12);
|
|
assert.ok(!gr.findBuff('gravel:rats'), 'the deployment barrier has run out');
|
|
g.b.dealDamage(null, gr, { amount: 1e9, type: 'true' });
|
|
g.step(2);
|
|
assert.ok(gr.alive && gr.findBuff('gravel:rats')?.shield > 0, 'a fresh barrier after the revive');
|
|
checkInvariants(g.b);
|
|
});
|