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.
This commit is contained in:
1 parent
d40cb4e79e
commit
fdb3ae1b46
9 files changed
+127
-52
No files matched your search
+28
-40
@@ -306,58 +306,46 @@
|
||||
"time": 183.7,
|
||||
"ticks": 5511,
|
||||
"reason": "cleared",
|
||||
"killed": 50,
|
||||
"killed": 25,
|
||||
"total": 98,
|
||||
"unspawned": 0,
|
||||
"bossHpLeft": null,
|
||||
"rngDraws": 345,
|
||||
"rngDraws": 272,
|
||||
"errors": {"count":0,"kinds":[]},
|
||||
"players": {
|
||||
"p1": {
|
||||
"killed": 50,
|
||||
"total": 98,
|
||||
"perfect": false,
|
||||
"leaks": 48,
|
||||
"leaksUncounted": 0,
|
||||
"coins": 8,
|
||||
"dmg": 676478,
|
||||
"bossDmg": 0,
|
||||
"heal": 30426,
|
||||
"deaths": 31,
|
||||
"layerGains": {"raidShip":26,"visiShip":12}
|
||||
}
|
||||
"p1": {"killed":25,"total":98,"perfect":false,"leaks":73,"leaksUncounted":0,"coins":4,"dmg":397046,"bossDmg":0,"heal":12225,"deaths":31,"layerGains":{"raidShip":24}}
|
||||
},
|
||||
"units": [
|
||||
["p1",1,"op","chess_char_3_12_b",2,"none",1904,1,140,10,155395,5504,915,21,3],
|
||||
["p1",2,"op","chess_char_4_09_b",0,"none",0,0,6,10,119787,3510,0,39,1],
|
||||
["p1",3,"op","chess_char_3_05_b",2,"none",0,0,90,2,23950,8566,0,18,2],
|
||||
["p1",4,"op","chess_char_3_18_b",2,"none",0,0,93,3,28745,9190,2808,27,2],
|
||||
["p1",5,"op","chess_char_4_04_b",1,"none",0,0,137,0,25476,10224,0,40,9],
|
||||
["p1",6,"op","chess_char_4_07_b",1,"none",0,0,101,3,28031,10981,0,27,1],
|
||||
["p1",7,"op","chess_char_4_16_b",1,"none",0,0,139,7,42175,16104,0,70,12],
|
||||
["p1",8,"op","chess_char_3_01_b",2,"none",0,0,122,1,16716,3020,0,24,1],
|
||||
["p1",9,"op","chess_char_3_04_b",2,"none",0,0,4,0,0,15932,26703,25,2],
|
||||
["p1",10,"op","chess_char_3_21_b",2,"none",0,0,118,12,175716,7911,0,55,1],
|
||||
["p1",11,"op","chess_char_4_02_b",1,"none",0,0,124,2,41362,2966,0,27,1],
|
||||
["p1",12,"op","chess_char_4_13_b",1,"none",0,0,103,0,19124,2704,0,26,2]
|
||||
["p1",1,"op","chess_char_3_12_b",2,"none",1904,1,138,4,33527,4810,222,6,1],
|
||||
["p1",2,"op","chess_char_4_09_b",0,"none",0,0,6,2,12272,3510,0,5,1],
|
||||
["p1",3,"op","chess_char_3_05_b",2,"none",0,0,90,1,29963,11344,0,19,2],
|
||||
["p1",4,"op","chess_char_3_18_b",2,"none",0,0,12,1,32236,5744,2553,22,1],
|
||||
["p1",5,"op","chess_char_4_04_b",1,"none",0,0,137,0,21284,11506,0,43,9],
|
||||
["p1",6,"op","chess_char_4_07_b",1,"none",0,0,93,0,13096,7538,0,19,1],
|
||||
["p1",7,"op","chess_char_4_16_b",1,"none",0,0,135,2,26045,13645,0,38,12],
|
||||
["p1",8,"op","chess_char_3_01_b",2,"none",0,0,128,1,13435,3020,0,21,1],
|
||||
["p1",9,"op","chess_char_3_04_b",2,"none",0,0,4,0,0,1374,9451,11,1],
|
||||
["p1",10,"op","chess_char_3_21_b",2,"none",0,0,112,11,185272,6695,0,50,1],
|
||||
["p1",11,"op","chess_char_4_02_b",1,"none",0,0,2,0,16202,1483,0,17,1],
|
||||
["p1",12,"op","chess_char_4_13_b",1,"none",0,0,101,3,13715,3085,0,23,1]
|
||||
],
|
||||
"enemies": {
|
||||
"enemy_10162_mnctpt": [1,1,0,7378,39813],
|
||||
"enemy_1019_jshoot": [1,1,0,295,4479],
|
||||
"enemy_1019_jshoot_2": [1,1,0,837,6718],
|
||||
"enemy_1021_bslime": [1,1,0,1648,6121],
|
||||
"enemy_1023_jmage": [1,1,0,2497,5474],
|
||||
"enemy_10162_mnctpt": [1,0,1,13739,31746],
|
||||
"enemy_1019_jshoot": [1,1,0,769,4479],
|
||||
"enemy_1019_jshoot_2": [1,1,0,1779,6718],
|
||||
"enemy_1021_bslime": [1,1,0,2095,6121],
|
||||
"enemy_1023_jmage": [1,0,1,3326,1518],
|
||||
"enemy_1025_reveng": [1,1,0,0,12442],
|
||||
"enemy_1026_aghost": [1,1,0,0,9456],
|
||||
"enemy_1043_zomsbr": [1,1,0,0,6285],
|
||||
"enemy_1251_lysyta_2": [57,33,24,13447,289829],
|
||||
"enemy_1252_lysytb_2": [21,8,13,29953,226804],
|
||||
"enemy_1329_cbshld_2": [12,1,11,40556,69057]
|
||||
"enemy_1043_zomsbr": [1,0,1,0,0],
|
||||
"enemy_1251_lysyta_2": [57,15,42,6671,148451],
|
||||
"enemy_1252_lysytb_2": [21,4,17,25366,127156],
|
||||
"enemy_1329_cbshld_2": [12,1,11,20011,48959]
|
||||
},
|
||||
"hooks": [1,140,5511,596,596,875,906,94,81,139,37,37,0,7322,36,60,98,48,1,89,7,0,4],
|
||||
"events": {"atk":684,"bounty":4,"deploy":42,"die":91,"dmg":906,"engage":11,"fx":62,"heal":67,"layer":7,"leak":48,"skill":74,"spawn":110,"status":117},
|
||||
"fx": {"blink":10,"camouflage":1,"dodge":6,"dp":13,"explode":1,"garrison":7,"slash":24},
|
||||
"snaps": "be6d1821 fa43a30d 52099f77 a860ccb6 ea311a6e 44affa90 9dabf4d8 83adbc77 77dfae35 f02fd736 470fba6a a9871a60 b7e054aa ae8c4b73 55df9303 1c374baa 0d780d78 e03a2b60 b7879d42"
|
||||
"hooks": [1,138,5511,428,428,618,635,38,56,137,32,32,0,5767,24,40,98,73,1,59,3,0,6],
|
||||
"events": {"atk":493,"bounty":2,"deploy":40,"die":64,"dmg":635,"engage":11,"fx":32,"heal":36,"layer":3,"leak":73,"skill":64,"spawn":110,"status":83},
|
||||
"fx": {"blink":8,"camouflage":1,"dodge":6,"dp":5,"explode":1,"garrison":3,"slash":8},
|
||||
"snaps": "be6d1821 69cb5865 7bea5cb3 02fefb75 5eacb174 5bc1814c a70bea6d 0de1251c 172a35f2 f9001627 db7b6b38 a6083013 e2198a24 0adb9d99 8eed5e8b b756dcfe 538dc28e 876d2f95 835a73d1"
|
||||
},
|
||||
"standin-07": {
|
||||
"about": "elite band 6 as stand-ins: chess_char_4_17_b→char_610_acfend chess_char_4_18_b→char_602_cdfend chess_char_5_05_b→char_602_cdfend chess_char_4_19_b→char_608_acpion chess_char_5_01_b→char_600_cpione chess_char_4_22_b→char_609_acguad chess_char_4_23_b→char_609_acguad chess_char_5_06_b→char_609_acguad chess_char_4_20_b→char_603_csnipe chess_char_4_24_b→char_604_ccast chess_char_5_02_b→char_604_ccast chess_char_5_03_b→char_612_accast",
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
// 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);
|
||||
});
|
||||
Reference in new issue
Block a user