feedback3 WG: a 隐匿 blocker is hit only by the blocked enemy's attacks, not by its areas, zones or auras (GitHub #97)

This commit is contained in:
sganggs committed 2026-10-04 20:33:35 +08:00
1 parent 95b8a24d28
commit 13e71fccba
6 files changed
+39 -36

No files matched your search

+11 -9
View File
@@ -12,10 +12,11 @@
// attack; a normal attack on every operator in range skips 迷彩 too).
// Cause: the enemy side's area effects (content/enemies.js, content/bosses.js) took every ally in the area; only the
// operator side skipped an unblocked 隐匿 enemy (Battle.foesInRadius, 0.1.1).
// Now: targeting.js areaSelectable / enemies.js areaAllies, areaAlliesInTiles, fieldAllies — no 隐匿 ally unless it blocks
// the enemy, no untargetable or sleeping one, no airborne 起飞 one for a ground enemy; 迷彩 is not checked. Buff auras:
// Now: targeting.js areaSelectable / enemies.js areaAllies, areaAlliesInTiles, fieldAllies — no 隐匿 ally, even the one
// blocking the source (GitHub #97, owner 2026-10-04; the blocked enemy's own attack still lands via canTargetAlly),
// no untargetable or sleeping one, no airborne 起飞 one for a ground enemy; 迷彩 is not checked. Buff auras:
// auraSelectable / auraAllies (the same without 对地规避). A locked target is hit as a direct pick (targetAndArea).
// (DESIGN §22.12.)
// (DESIGN §22.12 recorded the 0.1.2 blocker exception; §23 withdraws it for areas and auras.)
import { test } from 'node:test';
import assert from 'node:assert/strict';
@@ -54,7 +55,7 @@ function countFx(h, kind) {
return n;
}
test('areaSelectable: no 隐匿 ally unless it blocks the source, no untargetable / sleeping one, no 起飞 one for a ground source; 迷彩 is selected', () => {
test('areaSelectable: no 隐匿 ally even when it blocks the source, no untargetable / sleeping one, no 起飞 one for a ground source; 迷彩 is selected', () => {
const h = makeBattle({
seed: 2, autoFinish: false, timeLimit: 60,
defs: { enemies: { enemy_g: enemyRec({ key: 'enemy_g', hp: 1e7, speed: 0 }), enemy_f: enemyRec({ key: 'enemy_f', hp: 1e7, speed: 0, motion: 'FLY' }) } },
@@ -68,15 +69,15 @@ test('areaSelectable: no 隐匿 ally unless it blocks the source, no untargetabl
assert.ok(f.isFlying && !g.isFlying);
for (const u of [jf, gm, hd]) assert.ok(areaSelectable(g, u) && areaSelectable(f, u) && areaSelectable(null, u), `${u.def.name}: plain ⇒ selected`);
stealth(h, jf); stealth(h, gm);
assert.equal(areaSelectable(g, jf), true, '隐匿 but blocking the source: selected');
assert.equal(areaSelectable(g, jf), false, '隐匿 blocker: an area still skips it');
assert.equal(areaSelectable(f, jf), false, '隐匿, blocking another enemy: not selected');
assert.equal(areaSelectable(g, gm), false, '隐匿, not blocking: not selected');
assert.equal(areaSelectable(null, gm), false, '隐匿 vs an effect with no selecting enemy');
assert.deepEqual(areaAllies(h.b, g, g.x, g.y, 1.01).map((u) => u.def.name).sort(), [jf, hd].map((u) => u.def.name).sort(), 'areaAllies: the 隐匿 bystander is left out');
assert.deepEqual(areaAllies(h.b, g, g.x, g.y, 1.01).map((u) => u.def.name).sort(), [hd.def.name], 'areaAllies: the 隐匿 blocker and the 隐匿 bystander are left out');
camou(h, hd);
assert.equal(areaSelectable(g, hd), true, '迷彩: still selected by an area effect ("无法躲避溅射类攻击")');
assert.equal(auraSelectable(g, gm), false, 'a buff aura: 隐匿, not blocking — not taken');
assert.equal(auraSelectable(g, jf), true, 'a buff aura: 隐匿 blocker — taken');
assert.equal(auraSelectable(g, jf), false, 'a buff aura: 隐匿 blocker — not taken');
h.b.addBuff(gm, { key: 'test:liftoff', persist: true, flags: { liftoff: true } });
h.b.removeBuff(gm, 'stealth');
assert.equal(areaSelectable(g, gm), false, '起飞 vs a ground source (对地规避)');
@@ -140,7 +141,7 @@ test('#32.6 碎骨: 伪装服\'s 隐匿 (its carrier\'s first damage) spares the
done(r.h);
});
test('#32.6 “庞贝”\'s self-blast while blocked hits its 隐匿 blocker and a 迷彩 bystander, not a 隐匿 bystander', REAL, () => {
test('#32.6 “庞贝”\'s self-blast while blocked misses its 隐匿 blocker and a 隐匿 bystander, and hits a 迷彩 bystander', REAL, () => {
const h = makeBattle({
seed: 3, timeLimit: 120, autoFinish: false,
units: [{ chessId: JF, row: 9, col: 6, dir: 'RIGHT' }, { chessId: GM, row: 10, col: 6, dir: 'RIGHT' }, { chessId: HD, row: 9, col: 7, dir: 'RIGHT' }],
@@ -155,7 +156,8 @@ test('#32.6 “庞贝”\'s self-blast while blocked hits its 隐匿 blocker and
assert.equal(p.blockedBy, jf, 'blocked by the 隐匿 角峰');
assert.equal(blasts.v, 1, 'one self-blast in 12 s');
const blast = (u) => log.filter((x) => x.target === u && x.skill && !x.attack).length;
assert.equal(blast(jf), 1, '隐匿 blocker: hit (a blocked enemy ignores its blocker\'s selectability)');
assert.equal(blast(jf), 0, '隐匿 blocker: the blocked enemy\'s blast does not select it');
assert.ok(log.some((x) => x.target === jf && x.attack), 'the blocked enemy\'s own attack still hits that 隐匿 blocker');
assert.equal(blast(hd), 1, '迷彩 bystander: hit');
assert.equal(blast(gm), 0, '隐匿 bystander (distance 1, not blocking): spared');
done(h);