Files
ark-stronghold-protocol/test/sim/feedback5-catapult-smog.test.js
T
sganggs fdb3ae1b46 feedback5 CB4: 自制投石机's targeting ignores its block (索敌不受阻挡影响) — blocked by an operator on the 排气格栅 with nobody else in range it does not attack, as in the official game (community report of 2026-10-06, item 24)
The 「投石车」 is 自制投石机 (enemy_10162_mnctpt), the 「蒸汽地板」 the 排气格栅 smog (隐匿 to enemy ranged attacks). PRTS
天赋 「索敌不受阻挡影响」: the blocker exception (a blocked enemy attacks its blocker whatever its selectability) does not
apply to it. New enemy profile field `blockFree` (spawns.js): attackTargets selects as if unblocked, canTargetAlly
refuses a 隐匿 / 迷彩 blocker to it, sortAllyTargets puts no blocker first. Other enemies unchanged.

Golden: standin-06 (the bounty catapult, blocked by 忍冬, now shoots by 仇恨值 — checked instrumented).
Tests: test/sim/feedback5-catapult-smog.test.js (3; 2 fail on the parent). Docs: DESIGN §25.18.7, docs/SIM.md.
2026-10-07 02:07:51 +08:00

57 lines
3.5 KiB
JavaScript

// test/sim/feedback5-catapult-smog.test.js — 自制投石机's 「索敌不受阻挡影响」 (community report of 2026-10-06, item 24, a
// player's report of the official game: 「投石车」敌人如果被蒸汽地板上的干员阻挡会不进行攻击; DESIGN §25.18).
// PRTS 自制投石机 天赋: 「普通攻击为3连击…索敌不受阻挡影响,且不会因丢失目标而结束攻击」. An operator on the 排气格栅 smog
// (战场#07 下半, tile_smog) is 隐匿 for enemy ranged attacks; any other enemy still attacks the operator that blocks it
// ("阻挡优先级最高"), but the catapult's targeting ignores its block, so that blocker is no target: alone, it is not attacked.
// Run: node --test test/sim/feedback5-catapult-smog.test.js
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { makeBattle, chessRec, enemyRec, flatStage, checkInvariants } from '../helpers/battleHarness.js';
const KEY = 'enemy_10162_mnctpt'; // 自制投石机 (RANGED, range 4.0)
const wall = chessRec({ id: 't_wall', profession: 'TANK', stats: { atk: 0, maxHp: 1e9, def: 0, blockCnt: 3 }, rangeGrid: [[0, 0]], skill: null });
const other = chessRec({ id: 't_other', profession: 'SNIPER', stats: { atk: 0, maxHp: 1e9, def: 0, blockCnt: 0 }, rangeGrid: [[0, 0]], skill: null });
const kits = { t_wall: () => ({ trait: { noAttack: true } }), t_other: () => ({ trait: { noAttack: true } }) };
const route = { motion: 'WALK', start: [9, 10], end: [9, 2], checkpoints: [] };
const shooter = enemyRec({ key: 'enemy_t_shooter', hp: 1e9, atk: 100, range: 3, speed: 1, bat: 2 }); // an ordinary ranged walker
function run({ smog, withOther = false, key = KEY }) {
const stage = flatStage();
if (smog) stage.rows[9] = stage.rows[9].slice(0, 6) + 'g' + stage.rows[9].slice(7); // the blocker's tile (9,6)
const units = [{ chessId: 't_wall', row: 9, col: 6 }];
if (withOther) units.push({ chessId: 't_other', row: 11, col: 6 });
const h = makeBattle({ stage, autoFinish: false, timeLimit: 60, defs: { chess: { t_wall: wall, t_other: other }, enemies: { enemy_t_shooter: shooter } }, kits, units, enemies: [{ key, route }] });
h.step();
const e = h.enemy(key);
assert.ok(h.runUntil(() => e.blockedBy, 30), 'blocked by the wall');
h.run(20);
checkInvariants(h.b);
assert.equal(h.b.errors.length, 0);
return { e, h, wall: h.unit('t_wall'), other: withOther ? h.unit('t_other') : null };
}
test('自制投石机 blocked by an operator on the 排气格栅, nobody else in range: no attack', () => {
const { e, wall: w } = run({ smog: true });
assert.ok(w.s.flags.stealth, 'the smog tile hides the blocker from ranged attacks');
assert.equal(e.blockedBy, w);
assert.equal(e.stats.attacks, 0);
assert.equal(w.stats.taken, 0);
});
test('自制投石机 blocked on the 排气格栅 shoots another operator in its range instead; off the smog it hits its blocker', () => {
const a = run({ smog: true, withOther: true });
assert.ok(a.e.stats.attacks >= 2);
assert.equal(a.wall.stats.taken, 0, 'the hidden blocker is never its target');
assert.ok(a.other.stats.taken > 0);
const b = run({ smog: false });
assert.ok(b.e.stats.attacks >= 2);
assert.ok(b.wall.stats.taken > 0, 'a visible blocker is a target as before');
});
test('any other ranged enemy still attacks the operator that blocks it on the 排气格栅 (阻挡优先级最高)', () => {
const { e, wall: w } = run({ smog: true, key: 'enemy_t_shooter' });
assert.ok(w.s.flags.stealth);
assert.ok(e.stats.attacks >= 2 && w.stats.taken > 0);
});