Files
ark-stronghold-protocol/test/sim/feedback7-raid-stealth.test.js
sganggs 48a2d37e9b feedback7 R22-OSIM: 突袭 members jump to 隐匿 ground enemies too, and one in range counts for 「范围内没有敌人」 (the owner's decision, GitHub #316); golden: 5 match scenarios move
GitHub #316 (warpgenius): "Raid operator doesn't deploy to invisible enemy units, it does in official". The owner's
decision of 2026-10-08 from their official play: a 突袭 member considers 隐匿 (unrevealed) ground enemies both as landing
targets and for 「若范围内没有敌人」. Until now raidTargets and the poll used canTargetEnemy / enemiesInKeys, which leave
隐匿 out (the bond text only says 敌人 / 地面敌人).

content/bonds/addon/battle.js: raidGroundEnemy accepts what canTargetEnemy (ground) accepts, plus a 隐匿 ground enemy that
only its 隐匿 keeps out; asleep, untargetable and flying enemies stay excluded (skipEnemy too). raidStealthInRange: a 隐匿
ground enemy on the member's range stops the jump, so a ready member next to one does not hop every poll. The landing
rules are unchanged (the target in range from the landing tile; a melee member's blocking tile first).

Golden: solo-HARD-1, solo-ABYSS-1, coop2-NORMAL-3, coop4-ABYSS-7, coop2-NORMAL-70-diy — their 突袭 members (斯卡蒂, 瑕光,
宴, 伊内丝) now jump to 隐匿 enemies (潜伏者, 隐形弩手组长, …) or stay because one stands in their range.

Tests: test/sim/feedback7-raid-stealth.test.js (4; 2 fail on the parent: the jump to the only landing spot beside a 隐匿
enemy, and staying with one in range while a visible enemy waits elsewhere).
2026-10-08 21:21:44 +08:00

86 lines
4.0 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// test/sim/feedback7-raid-stealth.test.js — 突袭 and 隐匿 ground enemies: the owner's decision of 2026-10-08 (GitHub #316
// "Raid operator doesn't deploy to invisible enemy units, it does in official", confirmed by the owner's own play): a 突袭
// member jumps next to a 隐匿 (unrevealed) ground enemy, and one already in its range counts for 「若范围内没有敌人」 (no
// hop). Asleep, untargetable and flying enemies stay out, as before (bond text: 「再部署至一名地面敌人周围」).
// Run: node --test test/sim/feedback7-raid-stealth.test.js
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { makeBattle, chessRec, enemyRec, checkInvariants } from '../helpers/battleHarness.js';
import { enemyStealthed } from '../../server/sim/targeting.js';
const HOME = [12, 3];
const BONDS = { raidShip: { count: 2, active: true, tier: 1, layers: 0 } };
/**
* A field where (10,7) is the only tile a melee member may land on beside the enemy at (10,8): the rest of rows 9–12 is
* undeployable floor ('f'), its home (12,3) road.
*/
const ONE_SPOT = { rows: {
9: '##Efffffff' + 'S' + 'fffffffS##',
10: '##hffffrff' + 'f' + 'ffffffff##',
11: '##hfffffff' + 'f' + 'ffffffff##',
12: '##hrffffff' + 'S' + 'fffffffS##',
} };
/** `foe` at `pos` (with the buff flags `flags`), and optionally a visible `vis` at `visible`. */
function field({ pos = [10, 8], flags = null, fly = false, visible = null, range = [[0, 0], [0, 1]] } = {}) {
const h = makeBattle({
flat: ONE_SPOT, bonds: BONDS, autoFinish: false, timeLimit: 60, hooks: ['deploy'],
defs: {
chess: { raider: chessRec({ id: 'raider', profession: 'WARRIOR', position: 'MELEE', bonds: ['raidShip'], rangeGrid: range, skill: { spCost: 10, initSp: 10 } }) },
enemies: {
foe: enemyRec({ key: 'foe', hp: 1e7, speed: 0, atk: 0, motion: fly ? 'FLY' : 'WALK' }),
vis: enemyRec({ key: 'vis', hp: 1e7, speed: 0, atk: 0 }),
},
},
units: [{ chessId: 'raider', row: HOME[0], col: HOME[1], dir: 'RIGHT' }],
enemies: [{ key: 'foe', pos }, ...(visible ? [{ key: 'vis', pos: visible }] : [])],
setup(b) {
if (flags) b.on('enemySpawn', (c) => { if (/foe$/.test(c.enemy.defId)) b.addBuff(c.enemy, { key: 'test:mark', persist: true, flags }); });
},
});
return h;
}
const jumps = (h) => h.hooksOf('deploy').filter((c) => c.unit === h.unit('raider') && !c.initial).length;
const at = (h) => [h.unit('raider').tileR, h.unit('raider').tileC];
const foe = (h) => h.b.enemies.find((e) => e.alive && /foe$/.test(e.defId));
test('a ready 突袭 member jumps to the only landing spot beside a 隐匿 ground enemy, and does not hop again', () => {
const h = field({ flags: { stealth: true } });
h.run(1);
assert.equal(enemyStealthed(foe(h)), true, 'still 隐匿 (not blocked, not revealed)');
assert.deepEqual(at(h), [10, 7], 'landed on the one tile beside it');
assert.equal(jumps(h), 1);
h.run(3);
assert.equal(jumps(h), 1, 'the 隐匿 enemy in its range is an enemy in range: no hop');
assert.deepEqual(at(h), [10, 7]);
checkInvariants(h.b);
});
test('a visible ground enemy: the same landing (control)', () => {
const h = field();
h.run(1);
assert.deepEqual(at(h), [10, 7]);
assert.equal(jumps(h), 1);
checkInvariants(h.b);
});
test('a 隐匿 ground enemy already in its range: 「范围内没有敌人」 is false — it stays, though a visible one waits elsewhere', () => {
const h = field({ pos: [12, 4], flags: { stealth: true }, visible: [10, 8] });
h.run(3);
assert.equal(enemyStealthed(foe(h)), true);
assert.deepEqual(at(h), HOME);
assert.equal(jumps(h), 0);
checkInvariants(h.b);
});
test('asleep or flying 隐匿 enemies are still no destination', () => {
for (const opts of [{ flags: { stealth: true, sleep: true } }, { flags: { stealth: true }, fly: true }]) {
const h = field(opts);
h.run(3);
assert.deepEqual(at(h), HOME, JSON.stringify(opts));
assert.equal(jumps(h), 0);
checkInvariants(h.b);
}
});