feedback7 R22-GHSIM: 假想敌:骨刺 targets as PRTS writes — its normal attack ignores its block (by priority, not the blocker first), never a flying unit, and its 3 hidden targets come from its normal attack's reach (PR #365 by @Sukvii, sim part, each line checked against PRTS); golden: roster-018 moves
The sim lines are the PR's (content/enemies/invisible.js kitBoneSpike: profile blockFree and canTarget, the hidden selection over rangeRadius + the ally collider), so the contributor is the author of this commit; the maintainer side checked each rule, kept the engine's single target when the hidden list is empty, and wrote the tests. Official: PRTS 假想敌:骨刺, 级别0 天赋 (on the page since 2025-11, PRTS editors): 「自身普通攻击索敌不受阻挡影响」 「不会攻击 飞行单位,隐匿(解除阻挡0秒后恢复)」 「隐匿期间可同时攻击3个目标」; enemy_database's level description 「隐匿;隐匿状态下同时攻击 3个目标。」 (the handbook line keeps 「数个目标」) — the 3 is no longer [ASSUMED]. blockFree works as for 自制投石机 (a 隐匿 / 迷彩 blocker is no target); canTarget as the engine's other 不会攻击飞行单位 enemies; the hidden 3 are picked among the allies its normal attack could hit (ai.js attackTargets: rangeRadius + ALLY_COLLIDER_RADIUS, PRTS 作战机制 碰撞体积) — they were picked within the bare 2.0. Stealth back 0 s after a block was already so. Golden: roster-018 — the 骨刺 in it lands one more attack (on 异客; dmg 2434 → 2771), the same move PR #365 reported.
This commit is contained in:
1 parent
bfef34ffa3
commit
c250ff66f4
3 files changed
+86
-10
No files matched your search
@@ -186,6 +186,67 @@ test(`${nm('enemy_9008_acbunn')}: attacks several targets at once while stealthe
|
||||
assert.ok(atk.targets.length >= 2, `targets ${atk.targets.length}`);
|
||||
});
|
||||
|
||||
// 骨刺 — PRTS 天赋 (级别0) 「自身普通攻击索敌不受阻挡影响 / 不会攻击飞行单位,隐匿(解除阻挡0秒后恢复) / 隐匿期间可同时攻击3个目标」
|
||||
// and the level description 「隐匿;隐匿状态下同时攻击3个目标。」 (PR #365, the targeting part checked against PRTS).
|
||||
function boneArena(o = {}) {
|
||||
const h = arena({
|
||||
units: ['t_wall', 't_wall2', 't_wall3', 't_wall4'].map((chessId, i) => ({ chessId, row: 9 + (i % 2), col: 6 + Math.floor(i / 2) })),
|
||||
captureNoisy: true, hooks: ['attack'], ...o,
|
||||
});
|
||||
h.step();
|
||||
for (const u of h.allies()) h.b.addBuff(u, { key: 'test:noBlock', persist: true, flags: { noBlock: true } });
|
||||
const e = put(h, 'enemy_9008_acbunn', [9, 6]);
|
||||
return { h, e };
|
||||
}
|
||||
/** The targets of `e`'s next attack. */
|
||||
function boneNext(h, e) {
|
||||
const n = h.hooksOf('attack').filter((c) => c.attacker === e).length;
|
||||
assert.ok(h.runUntil(() => h.hooksOf('attack').filter((c) => c.attacker === e).length > n, 8), 'a new attack');
|
||||
return h.hooksOf('attack').filter((c) => c.attacker === e)[n].targets;
|
||||
}
|
||||
|
||||
test(`${nm('enemy_9008_acbunn')}: 3 targets while hidden, 1 once blocked or revealed — by priority, not its blocker first; 3 again when hidden`, () => {
|
||||
for (const cause of ['block', 'reveal']) {
|
||||
const { h, e } = boneArena();
|
||||
const wall = h.unit('t_wall'), taunter = h.unit('t_wall4');
|
||||
assert.equal(new Set(boneNext(h, e)).size, 3, `${cause}: hidden — three distinct targets of the four in reach`);
|
||||
h.b.addBuff(taunter, { key: 'test:taunt', mods: { taunt: 5 } });
|
||||
if (cause === 'block') { h.b.removeBuff(wall, 'test:noBlock'); h.step(2); assert.equal(e.blockedBy, wall); }
|
||||
else h.b.addBuff(e, { key: 'test:reveal', persist: true, flags: { reveal: true } });
|
||||
assert.deepEqual(boneNext(h, e), [taunter], `${cause}: one target, the taunt-5 one (索敌不受阻挡影响)`);
|
||||
if (cause === 'block') { h.b.addBuff(wall, { key: 'test:noBlock', persist: true, flags: { noBlock: true } }); h.b.releaseBlocked(wall); }
|
||||
else h.b.removeBuff(e, 'test:reveal');
|
||||
assert.equal(boneNext(h, e).length, 3, `${cause}: hidden again (0 s after the block) — three`);
|
||||
checkInvariants(h.b);
|
||||
}
|
||||
});
|
||||
|
||||
test(`${nm('enemy_9008_acbunn')}: never targets a flying unit (炎佑, taunt 5), hidden or revealed`, () => {
|
||||
for (const revealed of [false, true]) {
|
||||
const { h, e } = boneArena({ content: 'full', extraContent: [] });
|
||||
const [air] = spawnYanyou(h.b, 'p1', { count: 1, hp: 50000 });
|
||||
assert.ok(air?.isFlying);
|
||||
air.x = 6.5; air.y = 9;
|
||||
h.b.applyStatus(air, 'stun', { duration: 30, force: true });
|
||||
h.b.addBuff(air, { key: 'test:taunt', mods: { taunt: 5 } });
|
||||
if (revealed) h.b.addBuff(e, { key: 'test:reveal', persist: true, flags: { reveal: true } });
|
||||
const t = boneNext(h, e);
|
||||
assert.equal(t.length, revealed ? 1 : 3);
|
||||
assert.ok(!t.includes(air), `${revealed ? 'revealed' : 'hidden'}: the flyer is no target`);
|
||||
}
|
||||
});
|
||||
|
||||
test(`${nm('enemy_9008_acbunn')}: its 3 hidden targets come from its normal attack's reach (2 + the 0.25 ally collider)`, () => {
|
||||
const { h, e } = boneArena();
|
||||
const edge = h.unit('t_wall3'), outside = h.unit('t_wall4');
|
||||
edge.x = e.x + 2.1; edge.y = e.y;
|
||||
outside.x = e.x + 2.3; outside.y = e.y;
|
||||
h.b.addBuff(edge, { key: 'test:taunt', mods: { taunt: 5 } });
|
||||
const t = boneNext(h, e);
|
||||
assert.ok(t.includes(edge), 'the ally 2.1 away is one of the three');
|
||||
assert.ok(!t.includes(outside), 'the one 2.3 away is out of reach');
|
||||
});
|
||||
|
||||
test(`${nm('enemy_1175_dushdo_2')}: stealth; next to 深池伙友卫队精英 its attack interval drops by traitAbility.base_attack_time`, () => {
|
||||
const h = arena();
|
||||
h.step();
|
||||
|
||||
Reference in new issue
Block a user