feedback4: a sleeping enemy cannot be blocked and takes no block slot — 沉睡 = 无法行动+无敌+不可阻挡 (GitHub #140; the owner's decision of 2026-10-06)
- GitHub #140: a player tested 瑕光's S2 in the official mode — she slept 4 enemies and still blocked the next ones, after the skill no more were (「被沉睡的敌人是不占用阻挡数的」). PRTS 异常效果: SLEEPING 沉睡 = 「无法行动+无敌+不可阻挡」, BLOCK_FREE 不可阻挡 = 「无法阻挡/被阻挡,自动解除阻挡」. Until now an enemy that fell asleep while blocked stayed blocked and kept its blocker's slot. - Battle.applyStatus releases an enemy that falls asleep (as for 浮空 / 恐惧 / 不可阻挡), Battle._checkBlock never blocks a sleeper, and ai.js updateEnemy lets a plain-buff sleeper go on its next update. The sleeper stays put; awake, it is blocked again only by a blocker with a free slot, else it walks on. A stun, a freeze or a refused sleep keeps the block; sleeping operators are unchanged. - Readers of the block follow: Battle.blockedTargets (blocked-first targeting, the DEFAULT trigger, hitAllBlocked) drops the sleeper — of the hitSleep attackers only 瑕光 blocks, and her 仁慈 reads her range; a 要塞 holding only a sleeper no longer stalls; “庞贝”'s self-blast clock restarts when a sleep ends its block [ASSUMED]; the snapshot's blocked bit follows. - Tests: new test/sim/feedback4-sleep-unblock.test.js (5 cases, each fails without the change: the slot frees and the next enemy is blocked, the sleeper stays put; awake with no room it walks on; never blocked while asleep, re-blocked on waking beside a free blocker; a plain-buff sleep; the real 瑕光 S2). 1274 targeted tests (blocking, statuses, 小满 / 瑕光 / 缇缇 kits, garrisons, professions, enemies, kits t1–t6), docs-consistency and GOLDEN_FULL pass. - Docs: DESIGN §24.9 (and the §24 intro, §5.5 Blocking, §22.8's release list), SIM.md (Blocking, the `sleep` row, plain buffs), the buffs.js / Battle.js / ai.js / 瑕光 kit comments. - Golden (every moved scenario sleeps blocked enemies; DESIGN §24.9 gives each move): roster-003 / roster-005 (小满 S2: 82 kills instead of 83 / cleared at 136.73 s instead of the 140 s timeout), roster-013 (缇缇 S3: only the blocked count and the snapshot hashes), roster-032 (缇缇 S1: 7 knock-outs instead of 5), roster-038 (elite 缇缇 S2: ends at 197.43 s), roster-039 (瑕光 S2 normal + elite: 41 of 52 killed instead of 47 — the sleepers walk on when they wake), roster-042 (缇缇 S2: ends at 161.17 s instead of 166.97 s), bond-maniShip-high (小满: only the blocked count and the snapshot hashes), coop4-FUNNY-6 (ai_0's 缇缇: layers and boss damage from round 9), coop4-ABYSS-7 (ai_2's 小满: rounds 11–12), coop2-NORMAL-8-serverrun (ai_1's 缇缇: one more leak in round 10, the team eliminated in round 13 instead of winning). fields unchanged.
This commit is contained in:
1 parent
30e7acea42
commit
bbe59b9a5f
10 files changed
+460
-222
No files matched your search
@@ -1117,12 +1117,14 @@ export class Battle {
|
||||
* a fenced tile blocks no ground enemy, _blockerFor). Checked every tick for every
|
||||
* unblocked enemy, moving or not, so an enemy overlapping an operator is taken over as soon as its blocker is gone or
|
||||
* the operator's capacity frees up (user playtest #5 item 4). Several blockers in contact → the nearest [ASSUMED],
|
||||
* ties → the first in row-then-column scan order.
|
||||
* ties → the first in row-then-column scan order. Never blocked: an enemy holding 不可阻挡 (PRTS 异常效果 BLOCK_FREE
|
||||
* 「无法阻挡/被阻挡,自动解除阻挡」) — the flag itself (恐惧 / 诱导 carry it), 浮空, and 沉睡 (SLEEPING = 无法行动+无敌+不可阻挡:
|
||||
* a sleeper takes no block slot, DESIGN §24.9); once it wakes it is blocked again only by a blocker with room.
|
||||
*/
|
||||
_checkBlock(e) {
|
||||
if (e.blockedBy || e.hidden || !e.alive) return !!e.blockedBy;
|
||||
const f = e.s.flags;
|
||||
if (f.unblockable || f.levitate || f.fear) return false;
|
||||
if (f.unblockable || f.levitate || f.fear || f.sleep) return false;
|
||||
const r0 = Math.round(e.y), c0 = Math.round(e.x);
|
||||
const w = e.blockWeight ?? 1;
|
||||
let u = null, bd = Infinity;
|
||||
@@ -1355,7 +1357,8 @@ export class Battle {
|
||||
* then resumes); other statuses refresh to the longer duration. 诱导 (`attract`) walks the enemy to `point`
|
||||
* ([r, c] or {x, y}; default the source's tile — a new application moves the point); 恐惧 (`fear`) stamps where it
|
||||
* was applied and from where (fear.js stampFear: the fan of 恐惧可达地块 its movement uses). A stunned/sleeping operator
|
||||
* releases the enemies it blocks; a feared/levitated/unblockable/attracted enemy is released by its blocker.
|
||||
* releases the enemies it blocks; a feared/levitated/unblockable/attracted/sleeping enemy is released by its blocker
|
||||
* (沉睡 = 无法行动+无敌+不可阻挡, PRTS 异常效果: the slot frees for the next enemy, the sleeper stays put — DESIGN §24.9).
|
||||
* `statusApplied` reports the final duration and `entered` (the target did not carry the status before) — or, with
|
||||
* `reenter`, entered anyway: a pulse whose own short status the caller re-applies as a fresh one each time (缇缇 S2's
|
||||
* sleep ward, DESIGN §24.8); the buff itself is refreshed as usual.
|
||||
@@ -1424,7 +1427,7 @@ export class Battle {
|
||||
if (key === 'fear' && b && target.side === 'enemy') stampFear(this, target, b, source);
|
||||
}
|
||||
const f = tpl.flags;
|
||||
if (f && target.side === 'enemy' && (f.levitate || f.unblockable || f.fear)) this._unblock(target);
|
||||
if (f && target.side === 'enemy' && (f.levitate || f.unblockable || f.fear || f.sleep)) this._unblock(target);
|
||||
if (f && target.side === 'ally' && f.noBlock) this.releaseBlocked(target);
|
||||
if (this._hooks.statusApplied) this.emit('statusApplied', { source, target, status: key, duration, value, entered });
|
||||
return true;
|
||||
|
||||
+11
-3
@@ -11,7 +11,9 @@
|
||||
// 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. Blocked enemies fight their blocker (ranged ones may pick anyone in
|
||||
// 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
|
||||
@@ -459,8 +461,14 @@ export function updateEnemy(b, e, dt) {
|
||||
// true: an unblocked ranged enemy in the wind-up of its next attack with a target in range (it stands)
|
||||
const winding = !e.hidden && !stunned && enemyAttack(b, e);
|
||||
if (!e.alive) return;
|
||||
// a stun / freeze / sleep cuts the attack clip short: no stand left once it ends [ASSUMED]
|
||||
if (stunned && !e.hidden) { e.atkStandUntil = -Infinity; return; }
|
||||
// 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 — Battle.applyStatus releases it at once,
|
||||
// this catches a sleep added as a plain buff — and it stays where it is until it wakes (DESIGN §24.9)
|
||||
if (stunned && !e.hidden) {
|
||||
e.atkStandUntil = -Infinity;
|
||||
if (e.blockedBy && e.s.flags.sleep) b._unblock(e);
|
||||
return;
|
||||
}
|
||||
if (e.blockedBy) {
|
||||
const bl = e.blockedBy;
|
||||
// (unblockable/levitate/fear may also arrive through a plain addBuff, which does not unblock by itself; a
|
||||
|
||||
+3
-1
@@ -58,7 +58,9 @@ export const STATUS = Object.freeze({
|
||||
// 寒冷: ASPD −30. A second cold while this one remains applies 冻结 for max(remaining, incoming)
|
||||
// (Battle.applyStatus; PRTS 术语释义 寒冷 「持续时间取双方之中最高」).
|
||||
cold: { flags: { cold: true }, mods: { aspd: COLD_ASPD } },
|
||||
// 沉睡: 无敌且无法行动 (untargetable, takes no damage — except from `hitSleep` attackers — and blocks nothing)
|
||||
// 沉睡: 无敌且无法行动 (untargetable, takes no damage — except from `hitSleep` attackers — and blocks nothing); PRTS 异常效果
|
||||
// SLEEPING = 无法行动+无敌+不可阻挡: an enemy asleep is not blocked either — its blocker lets go and the slot frees, it
|
||||
// stays put until it wakes (the `sleep` flag: Battle._checkBlock / applyStatus, ai.js updateEnemy; DESIGN §24.9)
|
||||
sleep: { flags: { sleep: true, noBlock: true }, immune: 'sleep' },
|
||||
// value = fraction of speed removed (0.8 ⇒ moveMul 0.2). Default 0.5.
|
||||
slow: { mods: (v) => ({ moveMul: 1 - clamp01(v ?? 0.5) }), valued: 0.5 },
|
||||
|
||||
@@ -1101,7 +1101,9 @@ const KITS = {
|
||||
const dur = skill.timeLeft > 0 ? skill.timeLeft : Math.max(0.1, num(s.duration, 10));
|
||||
let n = 0;
|
||||
// PRTS 备注: "技能生效对象实际为“自身这格内的所有地面敌人及自身阻挡的敌人”,即使阻挡的是飞行敌人" — a blocked
|
||||
// enemy stands at the block radius, outside her tile (Battle._checkBlock)
|
||||
// enemy stands at the block radius, outside her tile (Battle._checkBlock). Asleep they are no longer blocked
|
||||
// (沉睡 = 不可阻挡, DESIGN §24.9; GitHub #140): her slots free for the next enemies, and a sleeper that wakes
|
||||
// is held again only while she has room
|
||||
for (const e of battle.enemies) {
|
||||
if (!e.alive || e.hidden) continue;
|
||||
if (e.blockedBy !== unit && (e.isFlying || !bodyOnTile(e, unit.tileR, unit.tileC))) continue;
|
||||
|
||||
Reference in new issue
Block a user