feedback2: merge WC — 阿戈尔's devour cancels a knocked-out target's pending marks (revived members stay standing); 乌尔比安 S3 stays on his tile while he blocks (GitHub #33 items 1-2)
This commit is contained in:
commit
f3cb6f414e
8 files changed
+177
-21
No files matched your search
@@ -418,6 +418,86 @@ test('阿戈尔 5: the first 3 members knocked out for the first time redeploy a
|
||||
assert.ok(!h3.unit('g0_a').alive);
|
||||
});
|
||||
|
||||
test('阿戈尔 5 + the devour chain (GitHub #33): a member the devour knocks out revives, its pending marks are cancelled and it stays standing (normal and 联防)', () => {
|
||||
// one row, all facing right: 浊心斯卡蒂 → 乌尔比安 → 幽灵鲨 → 海霓 → 深巡 → 隐现. PRTS 盟约记录 阿戈尔 备注 "标记按付与顺序触发
|
||||
// 【吞噬】效果,目标首次被击倒后解除自身被付与但还未触发的【吞噬】效果"; until 0.1.1 the marks still pending on a revived member
|
||||
// knocked it out again, so all 3 revives went at t = 0 and the members stayed down
|
||||
const ids = ['chess_char_6_04_a', 'chess_char_5_05_a', 'chess_char_2_07_a', 'chess_char_3_09_a', 'chess_char_1_04_a', 'chess_char_1_01_a'];
|
||||
for (const kind of ['normal', 'unite']) {
|
||||
const h = makeBattle({
|
||||
kind, units: ids.map((chessId, i) => ({ chessId, row: 10, col: 2 + i })), bonds: { egirShip: bondOn(5, 0, null, [3, 5]) },
|
||||
hooks: ['damaged', 'death', 'deploy'], captureNoisy: true, autoFinish: false, timeLimit: 60,
|
||||
});
|
||||
h.step(1);
|
||||
const [skadi, ulpia, ghost, hn, deep, fodder] = ids.map((id) => h.unit(id));
|
||||
// 浊心斯卡蒂 marks the whole row and resolves first; every later mark is on a unit already knocked out (乌尔比安's), or
|
||||
// comes from one (幽灵鲨's, 海霓's, 深巡's)
|
||||
assert.deepEqual(tagged(h, 'bond:egir:devour').map((c) => [c.source.defId, c.target.defId]),
|
||||
[ulpia, ghost, hn, deep, fodder].map((t) => [skadi.defId, t.defId]), kind);
|
||||
for (const u of [ghost, hn, deep]) {
|
||||
assert.equal(h.hooksOf('death').filter((c) => c.unit === u && c.reason === 'killed').length, 1, `${kind}: ${u.defId} knocked out once`);
|
||||
assert.equal(h.hooksOf('deploy').filter((c) => c.unit === u && !c.initial).length, 1, `${kind}: ${u.defId} revived at once`);
|
||||
}
|
||||
h.run(3);
|
||||
for (const u of [skadi, ulpia, ghost, hn, deep]) assert.ok(u.alive && u.deployed && u.hp > 0, `${kind}: ${u.defId} standing`);
|
||||
assert.ok(!fodder.alive, `${kind}: the fodder (no member) stays down`);
|
||||
// the 3 revives went to the first knock-outs at t = 0, as officially: the next knock-out of a member is final
|
||||
h.b.dealDamage(null, ghost, { amount: 1e9, type: 'true' });
|
||||
assert.ok(!ghost.alive, `${kind}: no revive left`);
|
||||
checkInvariants(h.b);
|
||||
}
|
||||
});
|
||||
|
||||
test('阿戈尔 devour, a unit back during the pass: a marker revived by the 5-tier revive still gives its marks; a target revived in place (埃芒加德) or redeployed (不屈) takes none', () => {
|
||||
// g1 (10,3) → g2 (10,4, 3000 × 1.35 HP: knocked out by g1's mark, revived) → g3 (10,5) → fodder (10,6); g4 / g5 face empty tiles
|
||||
const list = [
|
||||
['g1_a', ['egirShip']], ['g2_a', ['egirShip'], { stats: { maxHp: 3000 } }], ['g3_a', ['egirShip'], { stats: { maxHp: 20000 } }],
|
||||
['g4_a', ['egirShip']], ['g5_a', ['egirShip']], ['fod_a', ['preciShip'], { tier: 2, stats: { maxHp: 20000 } }],
|
||||
];
|
||||
const units = [
|
||||
{ chessId: 'g1_a', row: 10, col: 3 }, { chessId: 'g2_a', row: 10, col: 4 }, { chessId: 'g3_a', row: 10, col: 5 }, { chessId: 'fod_a', row: 10, col: 6 },
|
||||
{ chessId: 'g4_a', row: 12, col: 3 }, { chessId: 'g5_a', row: 12, col: 5 },
|
||||
];
|
||||
const h = makeBattle({ defs: defsOf(list), units, bonds: { egirShip: bondOn(5, 0, null, [3, 5]) }, hooks: ['damaged', 'deploy'], captureNoisy: true });
|
||||
h.step(1);
|
||||
const [g2, f] = ['g2_a', 'fod_a'].map((id) => h.unit(id));
|
||||
assert.ok(g2.alive && h.hooksOf('deploy').some((c) => c.unit === g2 && !c.initial), 'g2 knocked out and revived');
|
||||
// only a marker off the field gives no further mark (the rule since 0.1.0): g2, back at once, still devours g3 and the fodder
|
||||
assert.deepEqual(tagged(h, 'bond:egir:devour').map((c) => [c.source.defId, c.target.defId]),
|
||||
[['g1_a', 'g2_a'], ['g1_a', 'g3_a'], ['g1_a', 'fod_a'], ['g2_a', 'g3_a'], ['g2_a', 'fod_a'], ['g3_a', 'fod_a']]);
|
||||
close(f.hp, 20000 - 3 * 5000, 1e-6, 'the fodder: g1, g2 and g3');
|
||||
close(g2.s.atk, 1000 + 1000 + 1000, 1e-6, 'g2 keeps the base ATK it gained at marking (a persistent buff) [ASSUMED]');
|
||||
assert.equal(h.b.getPlayer('p1').bonds.egirShip.layers, 1 + 1 + 2, 'each devoured unit adds its tier once');
|
||||
checkInvariants(h.b);
|
||||
// 3 members, g1 → g2 → fodder (3000 HP): g1's mark knocks the fodder out, it is back at once, g2's mark on it is cancelled
|
||||
const list2 = [
|
||||
['g1_a', ['egirShip']], ['g2_a', ['egirShip'], { stats: { maxHp: 20000 } }], ['g3_a', ['egirShip']],
|
||||
['fod_a', ['indomShip'], { tier: 2, stats: { maxHp: 3000 } }],
|
||||
];
|
||||
const units2 = [{ chessId: 'g1_a', row: 10, col: 3 }, { chessId: 'g2_a', row: 10, col: 4 }, { chessId: 'fod_a', row: 10, col: 5 }, { chessId: 'g3_a', row: 12, col: 3 }];
|
||||
// 埃芒加德 (命结之秘: the first 3 knocked-out operators revive in place — a new deployment, items revivedInPlace)
|
||||
const e = makeBattle({ defs: defsOf(list2), units: units2, bandId: 'band_ermengard', bonds: { egirShip: bondOn(3, 0, null, [3, 5]) }, hooks: ['damaged'], captureNoisy: true });
|
||||
e.step(1);
|
||||
const fe = e.unit('fod_a');
|
||||
assert.deepEqual(tagged(e, 'bond:egir:devour').map((c) => [c.source.defId, c.target.defId]), [['g1_a', 'g2_a'], ['g1_a', 'fod_a']]);
|
||||
assert.ok(fe.alive, 'revived in place');
|
||||
close(fe.hp, fe.s.maxHp, 1e-6, 'at full HP: no second mark');
|
||||
assert.deepEqual(e.eventsOf('fx').filter((x) => x[1] === 'revive').map((x) => x[4].left), [2], 'one 埃芒加德 revive spent, not two');
|
||||
checkInvariants(e.b);
|
||||
// 不屈 at p = min(1, 0.18 + 0.004 × 300) = 1: 立刻重新部署 where it lies — a knock-out and a new deployment. Until 0.1.1
|
||||
// g2's mark knocked the fodder out again within the same instant, where 不屈 does not fire twice, and it stayed down
|
||||
const u = makeBattle({
|
||||
defs: defsOf(list2), units: units2, hooks: ['damaged', 'death', 'deploy'], captureNoisy: true,
|
||||
bonds: { egirShip: bondOn(3, 0, null, [3, 5]), indomShip: bondOn(2, 300, 1, [2, 3]) },
|
||||
});
|
||||
u.step(1);
|
||||
const fu = u.unit('fod_a');
|
||||
assert.deepEqual(tagged(u, 'bond:egir:devour').map((c) => [c.source.defId, c.target.defId]), [['g1_a', 'g2_a'], ['g1_a', 'fod_a']]);
|
||||
assert.ok(fu.alive && u.hooksOf('deploy').some((c) => c.unit === fu && !c.initial), 'redeployed by 不屈');
|
||||
assert.equal(u.hooksOf('death').filter((c) => c.unit === fu && c.reason === 'killed').length, 1, 'knocked out once');
|
||||
checkInvariants(u.b);
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
// 叙拉古
|
||||
|
||||
|
||||
@@ -234,6 +234,52 @@ test('乌尔比安 S3: anchor 135 % ATK + stun on the first enemy ahead, moves t
|
||||
clean(h);
|
||||
});
|
||||
|
||||
test('乌尔比安 S3 while he blocks (e.g. after a 突袭 landing): the anchor lands on his own tile — no 【移动】, no marker (PRTS 备注; GitHub #33)', () => {
|
||||
// PRTS 乌尔比安 S3 备注: target = his own tile (only while blocking) > the nearest tile of the skill range with an enemy >
|
||||
// its farthest tile; the 从不混淆的方向 only when the 【移动】 changes his tile
|
||||
const setup = (near) => {
|
||||
const h = makeBattle({
|
||||
defs: { enemies: { enemy_dummy: dummy(), enemy_fly: dummy('enemy_fly', { motion: 'FLY' }) } },
|
||||
units: [{ chessId: 'chess_char_5_05_a', row: 9, col: 3 }],
|
||||
enemies: [{ key: near, pos: [9, 3] }, { key: 'enemy_dummy', pos: [9, 7] }], hooks: ['damaged'], captureNoisy: true, autoFinish: false, timeLimit: 120,
|
||||
});
|
||||
const u = h.unit('chess_char_5_05_a');
|
||||
h.step();
|
||||
const [e0, far] = h.b.enemies;
|
||||
const blocked = e0.blockedBy === u;
|
||||
u.skill.gainSp(1000);
|
||||
assert.ok(h.runUntil(() => u.skill.active, 2), near);
|
||||
return { h, u, e0, far, blocked, anchor: fxOf(h, 'anchor')[0] };
|
||||
};
|
||||
{ // blocking the enemy on his tile: the anchor on his own tile, he stays
|
||||
const { h, u, e0, far, blocked, anchor } = setup('enemy_dummy');
|
||||
assert.ok(blocked, 'he blocks the enemy on his tile');
|
||||
assert.deepEqual([anchor[2], anchor[3]], [3, 9], 'the anchor on his own tile');
|
||||
assert.equal(tagged(h, 'anchor', e0).length, 1, 'the blocked enemy is hit');
|
||||
assert.ok(e0.s.flags.stun);
|
||||
assert.equal(tagged(h, 'anchor', far).length, 0, '4 tiles ahead, out of the 1.8 radius');
|
||||
assert.deepEqual([u.tileR, u.tileC], [9, 3], 'no 【移动】');
|
||||
assert.ok(!h.b.allyUnits.some((x) => x.kind === 'token'), 'no 从不混淆的方向');
|
||||
assert.ok(h.runUntil(() => !u.skill.active, 30));
|
||||
assert.deepEqual([u.tileR, u.tileC], [9, 3]);
|
||||
clean(h);
|
||||
}
|
||||
{ // not blocking, a flyer over his tile: his own tile is no candidate (only while blocking) — the anchor flies to the
|
||||
// enemy 4 tiles ahead and he moves there, as in 0.1.1
|
||||
const { h, u, e0, far, blocked, anchor } = setup('enemy_fly');
|
||||
assert.ok(!blocked, 'a flyer is not blocked');
|
||||
assert.deepEqual([anchor[2], anchor[3]], [7, 9], 'the anchor on the enemy ahead');
|
||||
assert.equal(tagged(h, 'anchor', far).length, 1);
|
||||
assert.equal(tagged(h, 'anchor', e0).length, 0, 'the flyer over his tile is out of the 1.8 radius');
|
||||
assert.deepEqual([u.tileR, u.tileC], [9, 7], 'moved onto the anchor tile');
|
||||
const marker = h.b.allyUnits.find((x) => x.kind === 'token' && x.alive);
|
||||
assert.ok(marker && marker.tileR === 9 && marker.tileC === 3, 'the marker on his tile');
|
||||
assert.ok(h.runUntil(() => !u.skill.active, 30));
|
||||
assert.deepEqual([u.tileR, u.tileC], [9, 3], 'back home');
|
||||
clean(h);
|
||||
}
|
||||
});
|
||||
|
||||
test('乌尔比安 T1 本性的坚守 heals per hit taken; T2 血脉的哺养 +HP/ATK per kill (abyssal allies half); elite healing ×1.2', () => {
|
||||
const h = makeBattle({
|
||||
defs: { enemies: { enemy_dummy: dummy() }, chess: { t_abyss: ally('t_abyss', { charId: 'char_143_ghost' }) } },
|
||||
|
||||
Reference in new issue
Block a user