feedback5 R19A: a stunned or frozen enemy is blocked by contact where it stands — an operator redeployed beside a frozen 隐匿 enemy blocks it and so reveals it (GitHub #232, @ccyl; the repro and the cause by @kukiC); golden: 52 scenarios move

ai.js updateEnemy returned on `stunned && !e.hidden` before the contact check (Battle._checkBlock), and 冻结 / 沉睡 / 浮空
set `stun` too, so a held enemy was never checked: an operator redeployed or deployed beside it, a slot freeing up or a
push / pull into contact with a stun all waited for the status to end (0.1.4 had it too). PRTS 异常效果: STUNNED / FROZEN
stop the enemy's moves and attacks and hold no 不可阻挡 (BLOCK_FREE), which SLEEPING / LEVITATE do; 术语释义 冻结 names no
block; 作战机制 §碰撞体积与位置识别: an operator deployed onto an enemy blocks it, a standing one included. The held branch now
runs the same check before it returns (no move, no attack, as before): _checkBlock still refuses 沉睡 / 浮空 / 恐惧 /
不可阻挡, an enemy held short of contact is blocked only once it walks in (the same point as without the status), and the
3 s 隐匿 window after a block's end is unchanged. A block the held enemy already holds is still not re-checked (a reborn()
重生 keeps its blocker, as in 0.1.4: noted as open in §25.22.1).

Golden — in each scenario the first divergence is a stunned or frozen enemy blocked during its status by an operator or
summon it touched with a free slot (traced one by one with an instrumented copy): roster-003, -014, -015, -016, -017,
-018, -019, -031, -036, -038, -040, -041, -045, -046, -047; bond-indomShip-high, bond-kazimierzShip-high,
bond-raidShip-low, bond-siracusaShip-low, bond-suntShip-low; boss-boss_3-solo, boss-boss_4-pair, boss-boss_7-solo,
hidden-boss_8-pair, hidden-boss_9-pair, hidden-boss_10-pair, unite-2; matches solo-FUNNY-1, solo-NORMAL-2, solo-HARD-2,
solo-ABYSS-1, coop2-NORMAL-3, coop2-HARD-4, coop3-ABYSS-5, coop2-NORMAL-8-serverrun, coop2-NORMAL-70-diy; diy-003, -004,
-026, -034, -035, -056, -057, -065, -066, -070, -071, -094, -095, -101, -102, -103. standins unchanged.

Tests: test/sim/feedback5-frozen-reblock.test.js (9; the four #232 cases and the 晕眩 / 冻结 capacity cases fail on the
parent). Docs: CHANGELOG 0.2.0 规则(按官方), docs/history/0.2.0.md §25.22.1, docs/SIM.md (§1.2 Blocking, stun, freeze),
docs/design/engine.md, docs/PLAYING.md.
This commit is contained in:
sganggs committed 2026-10-07 11:22:27 +08:00
1 parent 0b306baa49
commit 9123e76509
13 files changed
+959 -750

No files matched your search

+11 -5
View File
@@ -10,10 +10,10 @@
// changes (obstacles) or content displaces the enemy (route.pts = null). FLY legs fly straight between checkpoints; the
// path always follows `motion` — a hovering (近地悬浮) enemy is an air unit for targeting and blocking (Unit.isFlying)
// but walks the ground. An unblocked enemy touching an ally with free block capacity — within its block radius (0.7071
// ground, 0.8944 air, devices 0.4472; Battle._checkBlock) — is blocked, moving or not, so an enemy overlapping an
// operator is taken over once its blocker is gone; never one holding 不可阻挡 (恐惧, 诱导, 浮空, 沉睡): an enemy falling
// asleep is let go by its blocker, whose slot frees, and stays where it is until it wakes (DESIGN §24.9). Blocked
// enemies fight their blocker (ranged ones may pick anyone in
// ground, 0.8944 air, devices 0.4472; Battle._checkBlock) — is blocked, moving or not (stunned or frozen too: GitHub
// #232), so an enemy overlapping an operator is taken over once its blocker is gone; never one holding 不可阻挡 (恐惧,
// 诱导, 浮空, 沉睡): an enemy falling asleep is let go by its blocker, whose slot frees, and stays where it is until it
// wakes (DESIGN §24.9). Blocked enemies fight their blocker (ranged ones may pick anyone in
// range, blocker first); every blocker whose attack hits enemies — a ranged operator on a melee tile included — may
// always target the enemies it blocks, in range or not, whatever its facing, and targets them first (acquireTargets,
// Battle.blockedTargets; user playtest #6: "阻挡了就一定要能打到"); a heal attack keeps selecting injured allies while
@@ -543,10 +543,16 @@ export function updateEnemy(b, e, dt) {
// a stun / freeze / sleep cuts the attack clip short: no stand left once it ends [ASSUMED]. 沉睡 also holds 不可阻挡
// (PRTS 异常效果 SLEEPING = 无法行动+无敌+不可阻挡): a sleeper's blocker lets go — its swing was cut above; Battle.applyStatus
// (battle/status.js) releases it at once, this catches a sleep added as a plain buff (`flags.sleep` makes it `stun` too,
// units.js) — and it stays where it is until it wakes (DESIGN §24.9)
// units.js) — and it stays where it is until it wakes (DESIGN §24.9). 晕眩 / 冻结 hold no 不可阻挡 (PRTS 异常效果: the
// STUN / FROZEN state machines stop the enemy's moves and attacks, not its being blocked; 术语释义 冻结 names no block):
// it is still blocked by contact where it stands, like a standing enemy — an operator redeployed or deployed onto it,
// another one taking it over or one whose capacity frees up blocks it, and the block lifts its 隐匿 (GitHub #232; until
// 0.2.0 the check waited for the status to end). The contact rule of a moving enemy (Battle._checkBlock, which still
// refuses 沉睡 / 浮空 / 恐惧 / 不可阻挡): one stunned short of its blocker is blocked once it walks into contact
if (stunned && !e.hidden) {
e.atkStandUntil = -Infinity;
if (e.blockedBy && e.s.flags.sleep) b._unblock(e);
b._checkBlock(e);
return;
}
if (e.blockedBy) {