feedback5: merge CB4 — community reports (enemies / bosses): a 突袭 member lands only on its own player's board (never a boss field's hand / 临时整备区 rows nor the other half of a 联防 or boss field), a silenced 萨卡兹枯朽战士 leaves no 污染秽蚀, 萨卡兹穿刺手's acceleration ends with a stun or 束缚 and its first hit after a block adds move speed × 600 as its own instance, 自制投石机 ignores its block when it picks a target (no attack on the 排气格栅's hidden blocker), the briefing types' 7–9 waves, 新硎's 断刃, a freed prisoner in 联防, 鸭爵's 0–2 swaps and the moving leaders' routes pinned, four reports not reproduced; §25.18; golden: vs feedback5 CB4's own rules — roster-016, boss-boss_2/3/4/5/6-pair, hidden-boss_8/10-pair, standin-04/06/08, diy-028, diy-124, matches solo-NORMAL-1, solo-HARD-1, solo-ABYSS-1, coop2-HARD-4, coop4-ABYSS-7, coop2-NORMAL-8-serverrun; vs CB4 exactly what feedback5 changed since its base (CB2's roster 13 / bonds 7 / fields 6, CB5's 18 bot matches)

Merge fix: test/sim/facing-invariance.test.js — the 突袭 rotation check put its enemy on the board's top row (9, 9),
so the facing-RIGHT landing tile it expected, (8, 8), lies above the player's board, where CB4's onOwnBoard rule (the
official 再部署 goes where the player deploys; community reports 40 / 16.3) now refuses it; the member landed on
(10, 8). The enemy stands at (10, 6), inside rows 9–12 / cols 2–10, so all four diagonal tiles are candidates in every
direction and the test checks the facing tie-break as before. It failed on fb5-cb4 itself (the stream did not run the
file). The §25 intro's stream lines joined into one sentence again.

Golden moves outside CB4's commit list are its rules on feedback5's paths (instrumented): solo-HARD-1 and solo-ABYSS-1
now have silenced 枯朽战士 deaths (8 / 15) and 突袭 candidates the own-board filter refuses; coop2-ABYSS-10-boosted,
coop2-NORMAL-3 and coop2-NORMAL-14-standins (in CB4's list) have no silenced 枯朽战士 death on this path, and their
digests are identical with the own-board filter switched off (the refused tiles were never the chosen ones).

Gate: non-browser suites 4968 / 4968, npm run golden ok, test/golden.test.js 9 / 9, lint 0 errors, check:imports ok,
typecheck ok, docs-consistency + docs-paths 31 / 31.
This commit is contained in:
sganggs committed 2026-10-07 03:21:57 +08:00
commit 6ed3aed335
23 files changed
+1364 -598

No files matched your search

+1 -1
View File
@@ -16,7 +16,7 @@
| 阶段 | 说明 |
|---|---|
| 确认本局信息 | 本局地图(标准模拟固定为「战场#01」;险境从 8 张地图中随机,绝境 / 终极从不含战场#01 的 7 张中随机)、敌方派系、**被禁用的盟约及其干员**、领袖预告。所有人确认后继续;同盟模拟最多 25 秒;只有你一名玩家时(独立模拟,或同盟模拟只带 AI 队友)不限时。 |
| 确认本局信息 | 本局地图(标准模拟固定为「战场#01」;险境从 8 张地图中随机,绝境 / 终极从不含战场#01 的 7 张中随机)、敌方派系(三种特训敌人类型,如折射、飞行:和官方一样,每种占全局 15 个回合位中的 3 个,第 14 回合领袖战和第 15 回合隐秘核心也各占一个位,所以险境 / 绝境 / 终极第 1–13 回合中有 7–9 波是这三种类型、每种最多 3 波,其余各波是「特异」类型;每波只有一种类型)、**被禁用的盟约及其干员**、领袖预告。所有人确认后继续;同盟模拟最多 25 秒;只有你一名玩家时(独立模拟,或同盟模拟只带 AI 队友)不限时。 |
| 选择策略 | 40 名策略(分队长)决定**初始目标生命值(20–45)**和一条永久规则。同盟:随机顺序轮流选,**每人 30 秒**,右上角和「决策中」显示同一个倒计时;AI 队友立即选完;队友已选的不能重复,每人可**跳过一次**(排到最后);超时自动选你**当前选中**的策略(已被队友选走则选华法琳)。独立:自由选择、不限时;同盟模拟只有你一名玩家时也不限时。选的时候可以点左侧决策顺序下方的「查看禁用盟约与干员」,再看一遍「确认本局信息」里的盟约(置灰、禁用人数)和本局禁用干员:只能查看,倒计时照常走,底部也显示轮到谁和剩余秒数;点「关闭」、点窗口外面或按 Esc 关闭,轮到下一位时会自动关闭。 |
| 机变 | 部分回合开始前出现:同盟共 6 张卡轮流各取 1 张(首位 30 秒,其余 16 秒;只有你一名玩家时不限时),独立 3 选 1。卡片包括装备、资金、干员、盟约层数、悬赏等;**悬赏决策**为你的作战添加悬赏敌人,卡上用蓝字标出持续多久:「下场作战」/「接下来两场作战」的只出现一场或两场,之后不再出现;官方的「多轮悬赏」(原文写「之后的每场作战」)在这里也按「接下来两场作战」处理,只出现两场(如教鞭的法术大师A2·多轮战术特训)。各回合的悬赏决策按官方对局截图来出:第 3 回合是 6 张「接下来两场作战」的悬赏(3 张 1 资金、2 张 2 资金、1 张 3 资金);第 9 回合是各首领的「下场作战」悬赏和「源石虫·特训」;绝境 / 终极的第 11 回合多半是悬赏决策(也可能是机密商店或战术决策),其中的悬赏通常有 1 只特异巨兽,其余是各阵营的「下场战斗」悬赏。击倒悬赏敌人的人(联防中的队友也算)获得资金;右侧效果栏会写「还剩 N 场作战」。**机密商店**的 6 件装备都免费,和官方一样可能出现两件相同的装备(各算一张卡,可以分别被选走);第 11 回合(绝境 / 终极)的机密商店按官方:2 件 VI 阶、至少 1 件 V 阶和 1 枚盟约之币,标准 / 险境较早回合的机密商店各阶装备都可能出现。**战术决策**也和官方一样可能出现两张相同的卡(官方截图里出现过两张「补给」;各算一张卡,可以分别被选走,每张只生效一次,两人各选一张全队卡则效果叠加);第 11 回合(绝境 / 终极)的战术决策按官方截图只出盟誓、驰援、列装 / 财富 / 补给 / 整备 / 升华等增益卡,不出削弱敌人的卡和战场演变卡。每张卡左上是图标和名称,下面写着完整效果(与详情卡相同的官方描述,手机上也显示);已被选走的卡变暗,右上角显示选走它的玩家头像(悬停可看名字)。选卡要**点两次**:第一次只选中(金框 +「确认选择 · 再次点击」),再点同一张或点右上的「确认选择」才生效;点别的卡换选,按 Esc 或点空白处取消。 |
| 休整期 | 购买、摆阵、配装备、冻结 / 刷新、升级调度中心;同盟有倒计时(65–215 秒,随回合增加);独立模拟、以及只有你一名玩家(只带 AI 队友)的同盟模拟不限时。所有人「准备就绪」即提前开战,全员就绪前可取消。 |
+6 -3
View File
@@ -274,7 +274,8 @@ MELEE enemy only ever hits its blocker — data/enemies.json already zeroes thei
for any source; content may set `enemy.profile.melee = false`). Target order
(targeting.js `sortAllyTargets`, PRTS 作战机制 索敌 "阻挡→特殊优先级→仇恨值(更容易被攻击→…→最后部署的目标→不容易被攻击)"):
its blocker → highest taunt level → latest deployed (`aggroSeq` = the deploy order: a redeploy or a mid-battle summon is the
latest). `enemy.profile.canTarget(ally)` (content: 萨卡兹枯朽战车 "只攻击位于低地的我方单位,且不会攻击飞行单位", 掠海漂移体 / “萨科塔之眼” 不会攻击飞行单位 …) filters the candidates before the order;
latest) — except an enemy whose 索敌不受阻挡影响 (`enemy.profile.blockFree`: 自制投石机), which selects among the allies in
reach as if unblocked, no blocker first (DESIGN §25.18). `enemy.profile.canTarget(ally)` (content: 萨卡兹枯朽战车 "只攻击位于低地的我方单位,且不会攻击飞行单位", 掠海漂移体 / “萨科塔之眼” 不会攻击飞行单位 …) filters the candidates before the order;
a special priority (假想敌:铳 / 昆图斯 highest DEF, 假想敌:胄 highest / lowest ATK, “自在” nearest …) sorts by its key and
breaks ties by taunt, then latest deployed (`aggroCmp`); `untargetable` / sleeping allies and devices are never targets;
an airborne ally (起飞, flag `liftoff`: 蒂比's skills) never for a ground enemy (对地规避 — `targeting.js evadesGround`,
@@ -288,7 +289,8 @@ took off (出血, 沙狱, burning DoTs, 淤困, 【自然涌动】 — a tick se
sourceless 毒雾 of 假想敌:蚀裂 still reach it [ASSUMED]; a ground enemy's area skill whose cast depends on allies nearby counts only the targets of its trigger
selection — 卢西恩's 【aoe】 ("需要目标"), 锏's CircleAttack (`targetsNear`, PRTS 选择器 "所有触发选择器通常不无视迷彩");
a stealthed ally (隐匿, 排气格栅) only for the enemy it blocks — our operators keep 隐匿 while blocking (PRTS 作战机制
§隐匿; 索敌的概念: a blocked enemy "强行无视对方可选性" attacks its blocker); a camouflaged one (迷彩, flag `camou`: ba.camou
§隐匿; 索敌的概念: a blocked enemy "强行无视对方可选性" attacks its blocker) — except an enemy whose 索敌不受阻挡影响
(profile `blockFree`: 自制投石机 — it selects as if unblocked, by 仇恨值, so a 隐匿 blocker is no target; DESIGN §25.18); a camouflaged one (迷彩, flag `camou`: ba.camou
"不阻挡时不成为敌方普通攻击的目标") likewise (PRTS 异常效果: neither anomaly is "阻挡时解除") — for every target selection
(attacks, skill picks, cast conditions, a normal attack on every operator in range — 斩胄之剑 / 破胄之锤's hover attack,
“灵幛”). **Enemy area effects** — splash, death and self blasts, area skills and statuses, pulses, the zones an enemy
@@ -338,7 +340,7 @@ enemy: 隐匿 kept out, 起飞 not), 【污染秽蚀】 on every ally inside (fl
`alliesInRadius`) — through `dealDamage`, so shields absorb a damage
tick, damage-taken modifiers scale it and it counts for 受击回复 SP and TAKE_DAMAGE skills like any hit (§4; element fills
excepted). 【污染秽蚀】 (萨卡兹枯朽战车's 秽蚀轰击,
萨卡兹枯朽战士's death) is **true** damage, 50 / 25 per second on low / high ground (PRTS "每秒受到50/25点真实普通伤害 …
萨卡兹枯朽战士's death — none when it dies silenced, its client template checks 沉默, DESIGN §25.18) is **true** damage, 50 / 25 per second on low / high ground (PRTS "每秒受到50/25点真实普通伤害 …
同名效果不叠加", user playtest #6): a unit covered by several zones takes one tick per second (`unit.mem.pollutedAt`), so
a crowd of dying 萨卡兹枯朽战士 totals 50 / s, not 50 × n. It is "可对空,无视无法选择": it also burns an airborne 起飞 ally
(`ignoreSelect`), at the low-ground rate on a low tile. The other zones are no exception to 对地规避: a ground enemy's
@@ -829,6 +831,7 @@ or guard with a per-unit flag while dealing it. When the guard trips, the logged
| `addLayers(playerId, bondId, n, reason, {source})`, `addCoins(playerId, n)` | layers are a no-op when `flags.layerGainsEnabled` is false (unite/boss); a gain adds at most the room left under `BOND_LAYER_CAP` (999, shared/constants.js `layerGainRoom`: the client's `AddBondCount` min(L + n, 999)) on the live copy — or, without one, on the battle's own gains — and returns what it added (0 at the cap: no hook, no event) |
| `getPlayer(playerId)` | `{ playerId, seat, side, colOffset, mirror, dir (default unit direction: RIGHT, mirrored side LEFT), facing (its sign), bonds (live copy, layers updated by addLayers), bandId, playerEffects, lpForBoss, dp, units }` |
| `mapTile(ps, row, col, abs?)` / `mapDir(ps, dir, abs?)` | board → field tile / direction of a player (the FA right-side mirror) |
| `onOwnBoard(ps, r, c)` | whether a field tile lies on the player's own board — GEO.FIELD (rows 9–12, cols 2–10) mapped onto this field: the 联防 right-hand helper's +8 columns, the boss field's rows 2–5 (never its hand / 临时整备区 rows 0–1), the mirrored right half; the 突袭 landing takes only such tiles (DESIGN §25.18) |
| `addDp(playerId, n)`, `retreat(unit, {reason, permanent, dying})`, `relocate(unit, r, c)` | `relocate` only changes the tile (state kept, no event) |
| `moveRedeploy(unit, r, c, { clearSp })` | a 【移动】 (PRTS 术语释义: "不退场,以当前血量在目标位置部署", a special retreat + redeploy): `relocate`'s checks (false = refused, nothing changed), then a new deployment (`deploySeq` / `aggroSeq` / `deployedAt`) and `deploy {initial:false, move:true}` (deploy effects fire again); no `die` / `death`, timer or cost (不屈 / 阿戈尔's revive never see it); HP, buffs and a running skill are kept (owner's deviation, DESIGN §22.3); `clearSp` empties the SP before the deploy handlers run — 乌尔比安 S3's move (kits/ops/chess_char_5_05-ulpia.js) and 【返回】 (`clearSp`; also the 从不混淆的方向 fallback, content/tokens.js) |
| `redeploy(unit, { free=true, tile, keepSp })` | immediate (re)deployment of a dead/retreated ally (full HP, `deploy {initial:false}`); `free: false` pays `base.cost` DP (refused without it); without `tile` it lands on the unit's rest tile (`restTile`: where a knocked-out operator lies, else home); `tile: [r, c]` lands on that tile once (home unchanged; refused when off-rect, occupied or a knocked-out operator's tile, no fallback); `keepSp` keeps SP/charges (保留技力), restored before `deploy` fires — 突袭 raids, 阿戈尔 / 不屈 revives where the unit lies |
+1 -1
View File
@@ -122,7 +122,7 @@ Engine helpers (content must use these, never touch internals): `battle.dealDama
- **Blocking** (contact radius, PRTS 游戏数据基础 §阻挡半径 / 作战机制 "中点判定…案例:阻挡"; `constants.js BLOCK_RADIUS`, `Battle._checkBlock`, §19.2): an operator with free capacity blocks an unblocked, blockable enemy whose position lies within its block radius of its centre — ground 0.7071 (squared 0.49999037), 0.8944 × the blocker's 阻挡半径倍率 for blockFly units vs flyers (起飞; mod `blockRadiusScale` = 倍率 − 1 — PRTS 游戏数据基础 "飞行阻挡半径 = 0.8944 × 阻挡半径倍率", 凯尔希·思衡托 +0.23), devices 0.4472 (阻隔工事 / 障碍物); air units (FLY, 近地悬浮, 浮空) only by blockFly units. A ground enemy is never blocked by a unit standing on a tile ground units cannot pass (围墙 / 围栏: PRTS 地形机制 "部署在其中的单位,若当前阻挡类型为'地面阻挡'则无法阻挡敌人" — an enemy pushed or pulled against the fence walks on; air blocking there stays [ASSUMED], §21.23) nor by an airborne (起飞) unit, which blocks flyers only (§21.22). An enemy holding 不可阻挡 (PRTS 异常效果 BLOCK_FREE 「无法阻挡/被阻挡,自动解除阻挡」) — `unblockable` (恐惧, 诱导, enemy abilities), 浮空, and 沉睡 (SLEEPING = 无法行动+无敌+不可阻挡) — is never blocked and is released at once: a sleeping enemy frees its blocker's slot, stays where it is and, once awake, is blocked again only by a blocker with room (§24.9). The nearest such operator wins (ties: scan order [ASSUMED]). It is re-checked every tick, moving or not, so another operator in contact with room takes the enemy over when its blocker dies, is withdrawn or is stunned, and an operator whose capacity frees up grabs an overlapping enemy; one that finds no room walks on. A head-on enemy stops at contact (≈ 0.71 tile), outside the blocker's tile. Enemy uses `blockCnt` capacity (default 1). Blocked enemies stop moving and attack their blocker if melee (or anyone in range if ranged, blocker first). **Every blocker** — melee units (重装要塞 / 领主 / 哨戒铁卫 included), summons and a ranged operator on a melee tile alike — whose attack hits enemies may always target the enemies it blocks, in range or not, whatever its facing, and targets them first ("可以选择且优先选择阻挡单位", PRTS 选择器; the user's rule after playtest #6, "阻挡了就一定要能打到": officially the collision pushes a blocked enemy to its blocker's front; `Battle.blockedTargets`, `targeting.js sortEnemyTargets`; §20.3 — it replaces the playtest #5 QA's melee-only restriction of §19.2). A healer (dmgType `heal`, or a skill attack turned into a heal) selects injured allies only and keeps healing while it blocks (PRTS 卫戍协议/帮助 "对于医疗干员(咒愈师分支除外),攻击目标为需要治疗的单位"; §21.17); 咒愈师 follow the blocker rule. The huge leaders are 自缚 + unblockable (§7).
- **Operator attacks**: every `interval` s if a valid target exists; targets = up to `maxTargets` (default 1) from `unitsInGrid(rangeGrid)` (a `rangeAoe` profile — 阵法术师, 轰击术师 — takes every selectable enemy in range, §21.18; 「同时攻击阻挡的所有敌人」 — 强攻手 / 重剑手 / 推击手 and the skills worded so, `hitAllBlocked` — up to the block count, never fewer than 1, since 0.2.0: PRTS 分支特性信息 "普通攻击最大目标数等于阻挡数(不会低于1)"), priority: (1) the enemies it blocks (every blocker, in range or not, §20.3), (2) profession/trait priorities (e.g. fly-first snipers, `trait` text parsed by build-data into `targetPriority`), (3) enemy with the **least remaining path distance** to its goal, ties → earliest spawned. Melee (no ranged trait) and ground-only attacks cannot hit air units (`Unit.isFlying`: FLY, or a 近地悬浮 / 浮空 enemy — PRTS "算作空中单位", §19.1). Stealthed enemies can't be targeted unless blocked or revealed — or within 3 s of a block's end (§22.8). Ranged ops fire projectiles (speed ~12 tiles/s, instant for melee/arts-beam classes as data indicates); damage applies on impact; a 锁定攻击范围 AoE (`rangeAoe`) strikes every selectable enemy on its range at once with instant hits (§21.18).
- **Healers** (`dmgType heal`): target the ally in range with the lowest HP% below 100%, never a 禁疗 / 孤立 unit (tokens.json `abnormal`, `noHeal`; §20.3; 史尔特尔 during 余烬, §22.7), and never the enemies they block — also while blocking (§21.17); do nothing otherwise (SP still regenerates).
- **Enemy AI**: follow the route (checkpoints; WALK paths recomputed on the grid when devices change); when blocked stop & fight (`dmgType 'none'` enemies never attack unless content arms them through `e.profile` — 转译基底's armed forms, §21.4); ranged enemies (`rangeRadius > 0`) attack ops whose 0.25 collider touches their radius (centre distance ≤ `rangeRadius` + 0.25, §3, §20.4): the enemy's own target rule filters the candidates first (`profile.canTarget`: 只攻击地面单位 …), then priority blocker → special priority → highest taunt → **latest deployed** (`aggroSeq`) — PRTS 索敌 "阻挡→特殊优先级→仇恨值→最早出现"; a stealthed ally is attacked only by the enemy it blocks (§19.1), and an enemy's area effects — splash, blasts, area skills, zones, jumps — and buff auras skip it unless it blocks that enemy, while 迷彩 does not protect from splash and the other area effects (`targeting.js areaSelectable` / `auraSelectable`, §22.12); an airborne (起飞) ally is never a ground enemy's selection — picks, area damage, zones and statuses skip it, while 无视无法选择 abilities, direct picks, debuff ticks and flying units still reach it (§21.22); unblocked, they stand for each attack's clip (enemies.json `attackAnim`: its wind-up before the strike and the rest of the clip after it, shortened when the attacks come quicker than the clip; `ATTACK_PAUSE = 0.35 s` after the strike when no clip is known; a 「不停止移动」 attacker never stops; §22.14). Every enemy attack strikes at its clip's damage frame after its swing starts — the first one of a contact too —, and a stun / freeze / sleep / 浮空, hiding, 缴械, 恐惧 or 战栗 (blocked) before that frame cuts the swing: no strike, the next swing starts from its wind-up (0.2.0, `ai.js attackWindup`; PRTS 状态机 / 异常效果 STUNNED; no clip known: no wind-up). FLY enemies ignore ground obstacles (only blockFly units block them) and follow their checkpoint route. A feared enemy runs between random 恐惧可达地块 away from the source (PRTS 诱发移动; `fear.js`); 恐惧 outranks 诱导; the 恐惧 / 诱导 walks re-plan from where the enemy stands after a push or an obstacle change, and the route re-plans when they end (§20.4).
- **Enemy AI**: follow the route (checkpoints; WALK paths recomputed on the grid when devices change); when blocked stop & fight (`dmgType 'none'` enemies never attack unless content arms them through `e.profile` — 转译基底's armed forms, §21.4); ranged enemies (`rangeRadius > 0`) attack ops whose 0.25 collider touches their radius (centre distance ≤ `rangeRadius` + 0.25, §3, §20.4): the enemy's own target rule filters the candidates first (`profile.canTarget`: 只攻击地面单位 …), then priority blocker → special priority → highest taunt → **latest deployed** (`aggroSeq`) — PRTS 索敌 "阻挡→特殊优先级→仇恨值→最早出现"; a stealthed ally is attacked only by the enemy it blocks (§19.1) — and not even by that one when its 索敌不受阻挡影响 (自制投石机, `profile.blockFree`: it selects as if unblocked, §25.18) —, and an enemy's area effects — splash, blasts, area skills, zones, jumps — and buff auras skip it unless it blocks that enemy, while 迷彩 does not protect from splash and the other area effects (`targeting.js areaSelectable` / `auraSelectable`, §22.12); an airborne (起飞) ally is never a ground enemy's selection — picks, area damage, zones and statuses skip it, while 无视无法选择 abilities, direct picks, debuff ticks and flying units still reach it (§21.22); unblocked, they stand for each attack's clip (enemies.json `attackAnim`: its wind-up before the strike and the rest of the clip after it, shortened when the attacks come quicker than the clip; `ATTACK_PAUSE = 0.35 s` after the strike when no clip is known; a 「不停止移动」 attacker never stops; §22.14). Every enemy attack strikes at its clip's damage frame after its swing starts — the first one of a contact too —, and a stun / freeze / sleep / 浮空, hiding, 缴械, 恐惧 or 战栗 (blocked) before that frame cuts the swing: no strike, the next swing starts from its wind-up (0.2.0, `ai.js attackWindup`; PRTS 状态机 / 异常效果 STUNNED; no clip known: no wind-up). FLY enemies ignore ground obstacles (only blockFly units block them) and follow their checkpoint route. A feared enemy runs between random 恐惧可达地块 away from the source (PRTS 诱发移动; `fear.js`); 恐惧 outranks 诱导; the 恐惧 / 诱导 walks re-plan from where the enemy stands after a push or an obstacle change, and the route re-plans when they end (§20.4).
- **Damage**: phys `max(A − max(0, D×(1−defIgnorePct) − defIgnoreFlat), 0.05·A)`; arts `max(A × (1 − R'/100), 0.05·A)` with `R' = max(0, R×(1−resIgnorePct) − resIgnoreFlat)`; true = A; 元素伤害 ('elemental') `max(A × (1 − 元素抗性/100), 0.05·A)`. Then × `dmgDealtMul` (source) × `dmgTakenMul` & type-specific taken multipliers (target) × `dmg.mul` — 元素伤害 takes 元素脆弱 (`elementalTakenMul`) alone, not `dmgTakenMul` (脆弱 = "受到的物理、法术、真实伤害提升"). Then **限伤**: on a leader (`isBoss` — the 10 bossInfoDict enemyIds and their mirrored copies; never parts, drones or escorts) in a `'boss'` / `'hidden'` battle a hit with `ceil(final) ≥ 300000` is cancelled — no HP or pool loss, no credit, no damage number, no `damaged` / kill (`shared/constants.js BOSS_HIT_LIMIT`, `damage.js leaderHitCancelled`, after every multiplier and the hit-step blocks, before HP shields; `loseHp` too — a part's 传递, but not 胄's drone link, a share of the pool that is no hit (`noHitLimit`, §25.13.4); the client's `MAX_BATTLE_DAMAGE`, §20.12). Then shields absorb; dodge checks for phys/arts. Element damage fills a gauge (1000; enemy leaders 2000) × `elemTakenMul` × max(0.05, 1 − 损伤抵抗/100), 损伤抵抗 = data `epResistance` (PRTS 元素; the 5 % floor of DMG_e, PRTS 游戏数据基础). An element fill on a target with no HP left is refused (damage.js `hasHp`: no burst on a corpse). A full gauge bursts with the side-specific official effects, as **无来源** damage (no damage-dealt multiplier or penetration of the unit that filled the gauge, which keeps the kill and the stats; §19.5) (sim/constants.js `ELEMENT`; operators: 灼燃 1200 arts + RES −20, 神经 stun 10 s then 1000 true, 凋亡 15 s of 阻回 (`noSp`: no SP gain of any kind) + 静默 with −1 SP/s and 100 arts/s, 侵蚀 DEF −100 then 800 phys; enemies: the "·我方" terms). Every burst starts a **爆发冷却** = its duration (灼燃 / 神经 / 侵蚀 10 s — enemies' 侵蚀 8 s — 凋亡 15 s) during which NO element of the unit fills or can be recovered; when it ends every gauge of the unit resets (§18.3).
- **Leak**: an enemy reaching its goal is removed, counted as leaked for its field owner (`sourcePlayerId` for unite). LP accounting is done by the match, not the battle.
- **Results**: `damageDealt` / a unit's `dmg` count only HP removed from the other side — a unit's own drain or 流失 and friendly damage are the target's `taken` only (§21.12).
+198 -1
View File
@@ -31,7 +31,10 @@ upgrade, an eliminated player following a player through every round, the boss f
prep, the pair together in the boss prep with the official green frames; PRs #189 / #191 reused, not merged; the
battle-history and hotkey requests left to the owner) → §25.19; the community reports of 2026-10-06, stream CB2 (healers, summons, timers: 刺玫's S2 heals, 琳琅诗怀雅 healing herself and her 破财消灾 cost after a
redeploy, 赫默's drone at full SP, the countdown summons' 无敌 / 禁疗 / timer bar, stacked alchemy zones; the chain heal,
DoT damage, 伺夜's 狼影 and the 吟游者 trait answered) → §25.16.
DoT damage, 伺夜's 狼影 and the 吟游者 trait answered) → §25.16; the community reports of 2026-10-06, stream CB4 (enemies, bosses, waves, movement: the 突袭 landing on the 临时整备区 row and the other half of the
联防 map, a silenced 萨卡兹枯朽战士's death pollution, 萨卡兹穿刺手's speed after a stun and its first hit, 自制投石机 blocked on the 排气格栅; the briefing types'
7–9 waves, 新硎's 断刃, a freed prisoner in 联防 and 鸭爵's two swaps in one wave already answered; the moving leaders' routes
checked; 野鬃, 锏's pulls, 昆图斯's footprint and the leaders' attack rate not reproduced) → §25.18.
Decisions of the owner: 2026-10-04 — the server stays on plain `node:http` + `ws`, no web framework (§25.1);
2026-10-05 — the 补位 plan (§25.3); the 自选编队 rules, the collab operators out of the pool, ISW-A refused (§25.4);
@@ -1308,3 +1311,197 @@ the evidence.
trait is 生命回复速度, which 禁疗 and 无法被友方治疗 do not stop (§25.6.8): 狼群 440 → 532 and 斥罪 1480 → 1618 HP in 3 s
under 魔王 (pinned by `test/content/feedback5-regen.test.js`); 孤立 units stay out (PRTS 孤立). The countdown summons
cannot be refilled since §25.16.5.
### 25.18 Community reports of 2026-10-06, stream CB4: enemies, bosses, waves, movement — `sim/battle/players.js onOwnBoard`, `content/bonds/addon/battle.js raidTile`, `content/enemies/archetypes.js kitPolluted`, `content/enemies/special.js kitRush`, `targeting.js canTargetAlly`
The owner's second collection of community reports (2026-10-06/07) was split over five streams; CB4 took the items about
enemies, bosses, waves and movement (items 6, 10, 16.3, 24, 29, 35, 36, 38, 39, 40, 47, 52, 53, 57 of the list). Each was
reproduced on the 0.2.0 tree with a script first, then checked against the official data (enemy_database,
enemy_handbook_table, activity_table → act2autochess, the level files) and PRTS (备注 included) and the earlier decisions.
The real ones are fixed, one commit each with its golden scenarios named; the rest are answered with the evidence.
Normative text touched: §5.5 (design/engine.md: 自制投石机's exception to "a stealthed ally is attacked only by the enemy
it blocks"); docs/SIM.md (`onOwnBoard`, the 索敌不受阻挡影响 target rule, the silenced 枯朽战士); docs/PLAYING.md (the
briefing types' share of the waves).
#### 25.18.1 「…新约能天使在开了突袭之后蹦到了下方…临时放置溢出的干员和道具的那一栏高台」, 「单人联防时突袭也可以跑到对面的场地上」 (items 40, 16.3): 突袭 lands only on its player's own board — `sim/battle/players.js onOwnBoard`, `content/bonds/addon/battle.js raidTile`
- **Seen**: in a solo leader round a ranged 突袭 member (新约能天使 with the 转职球 and the 突袭手雷) jumped onto the
boss field's 临时整备区 row; in a 联防 with one helper a member jumped onto the right half of the map.
- **Cause**: `raidTile` took any tile of the battle rect its position may stand on (grid.canStand). BOSS_RECT holds rows
0–5, so the boss field's hand row 0 and 临时整备区 row 1 (tile_achand: high ground, buildable ALL) were landing tiles
for a ranged member (reproduced: an enemy on the left telin (1,3) of 战场#01 drew the member onto the hand tile (0,3));
UNITE_RECT spans both halves of the 联防 map (cols 0–20) while a lone helper's board is the left half (escaped_single's
deploy tiles: cols 3–9), and on a pair field a member jumped onto its partner's half.
- **Official**: the bond text 「保留技力立即再部署至一名地面敌人周围」 — a 再部署 goes where its player deploys; the stage data
lists each player's deploy tiles per half (data/stages.json `deployTiles`: normal, bossLeft, bossRight).
- **Now**: `Battle.onOwnBoard(ps, r, c)` — the board region (rows 9–12, cols 2–10) mapped onto the field as `mapTile`
maps the player's pieces (the right-hand helper's +8 columns, the boss rows 2–5, the mirrored right half) — and
`raidTile` takes only those tiles; a target only reachable from elsewhere is skipped (the next candidate is tried, else
the member stays and the next poll looks again, as before).
- [ASSUMED]: none beyond §22.2's landing rule (the official landing tile among the legal ones is not known).
- Golden: 8 scenarios, each a member that used to land on the other half (checked with the parent instrumented):
boss-boss_2 / 3 / 4 / 5 / 6-pair, hidden-boss_8 / 10-pair (members landing on the partner's half, 1–7 per battle) and
the bot match coop2-ABYSS-10-boosted (ai_0's members on the right half of its leader fields). Tests:
`test/sim/feedback5-raid-own-board.test.js` (4 cases; each fails on the parent). Docs: docs/SIM.md (`onOwnBoard`).
#### 25.18.2 「沉默效果没有沉默枯朽战士敌人,枯朽战士敌人被沉默之后死亡依然留下了毒」 (item 10): silenced at its death, 萨卡兹枯朽战士 releases no 污染秽蚀 — `content/enemies/archetypes.js kitPolluted`
- **Seen**: a silenced 萨卡兹枯朽战士 (组长) still left its poison zone when it died (reproduced: 150 HP in 3 s to its
blocker, silenced or not).
- **Official**: PRTS 萨卡兹枯朽战士 天赋 「{{特殊机制|死亡爆炸}}(释放半径2.0,持续8s的【污染秽蚀】)」; PRTS 特殊机制 死亡爆炸
「如无特殊说明,此能力默认可沉默…若不处于沉默状态,将会…释放」. The client agrees: the battle prefab's PollutedDie
ability is a buff of template `projectile_on_killed`, whose ON_OWNER_KILLED checks `CheckAbnormalFlag SILENCED`
(unset) before `EmitProjectile` — the same template as 高能源石虫's blast. Its handbook line alone is NORMAL (高能源石虫's is
SILENCE), so the rule "`sil` = the handbook's SILENCE lines" (content/enemies.js) missed it. An audit of all 243 enemy
prefabs of the local client (every ability's buff templates; those whose ON_OWNER_KILLED checks SILENCED: 11 enemies)
found one more NORMAL line of that kind — 刺胄之弹's blast, a part of 假想敌:胄 (not changed, for later: its blast is
triggered by its arrival); the other nine are SILENCE lines our kits already silence. 萨卡兹枯朽战车's 秽蚀轰击, also
NORMAL, stays unsilenceable (its skill node has `_ignoreSilence` 1).
- **Now**: kitPolluted's death ability is `sil`; the silence test of every authored enemy names the two keys as the
template exceptions.
- [ASSUMED]: none.
- Golden: 5 bot matches, each with silenced 枯朽战士 deaths (14–83 per match, counted instrumented): solo-NORMAL-1,
coop2-NORMAL-3, coop4-ABYSS-7, coop2-NORMAL-8-serverrun, coop2-NORMAL-14-standins. Tests:
`test/content/enemies_bosses.test.js` (the silence rule, the two keys silenced and after a silence ran out; 3 fail on
the parent).
#### 25.18.3 「每局游戏开始展示的敌人类型(折射、飞行等等),实际出现波次在7到9波,而且每个最多三波,其余波次用特异类型填充」 (item 6): already the official schedule — no change
- **Checked**: `setupMatchWaves` (research 08 §2.1, decoded from the client's `RandomEnemyGenerater`): each of the three
briefing types owns 3 of the 15 round slots, SPECIAL (特异) the other 6, shuffled, one type per round. R14 (the leader)
and R15 (the hidden core) take slots too, so the 13 normal rounds of 险境 / 绝境 / 终极 hold 7–9 type waves — 400 seeds
per mode: 7 in 141 matches, 8 in 192, 9 in 67; per type 1–3 — and every other wave is SPECIAL (solo 标准: 8 normal
rounds, 3–7). Exactly what the player describes.
- **Official**: act2autochess `specialEnemyRandomTypeDict` (count 3 per random type, SPECIAL −1), constData
`maxLevelCnt` 15, `specialEnemyNum` 3, `enemyTypeIdentifierToFillRandom` 1 (SPECIAL).
- **Now**: docs/PLAYING.md states it at 确认本局信息 (the report: 「更新日志和机制描述也没写」).
- Golden: unchanged. Pinned: `test/sim/feedback5-cb4-pins.test.js` (the 7–9 and the 1–3), `test/match/waves.test.js` (3 of
the 15 slots each).
#### 25.18.4 「敌人新硎提升攻击力的断刃好像无限消耗,实际应当攻击一次消耗一个,具体有几个复核」 (item 53): already fixed in 0.2.0 (§25.6.2, GitHub #107) — no change
- **Checked**: on v0.1.4 新硎's ATK went 2430 → 3510 → 4590 → 5670 and stayed (a layer per attack, none removed) — the
report. On this tree: 4 attacks at 2430 (+80 %), the 4th spends the last 断刃 and ends the bonus, the next swing comes at
once at 1350; killed, max(1, 4 − spent) 铁矛头 (沉沙: +70 %, 铜矛头).
- **Official**: PRTS 天赋 (4 【断刃】, one spent per successful attack); the client's template `enemy_dead_spawn[sfji]`
(battle prefab enemy_1207_sfji_2): ON_OUTPUT_ATK_OR_HEAL — while `cnt` ≥ 1, `cnt` += `cnt_add` (−1) and one blade marker
goes; ON_ABILITY_FINISH — `cnt` ≤ 0 ends the ATK buff and switches to mode 1 (无断刃模式); ON_OWNER_KILLED (not 坠落) —
summons `cnt` (at least 1).
- [ASSUMED]: the client spends a blade per damage output; here every attack spends one, so an attack an operator dodges
spends one too.
- Golden: unchanged. Pinned: `test/content/enemies_bosses.test.js` (the #107 test).
#### 25.18.5 「囚犯敌人的解放状态联防时不应继承」 (item 57): already answered (§25.14.1) — no change
- **Checked**: a leak re-enters 联防 as a new spawn — `planUnite` passes on its key, spawn mods, LP and bounty only, and
`buildUniteWave` spawns it — so `prisoner()` starts it confined (the 禁锢 buff, no form) and only its own attacks there
free it; the client drops every view when a battle starts. The freed look was 普通 / 老练囚犯's red clip set, drawn from
the gate until §25.14.1.
- Golden: unchanged. Pinned: `test/sim/feedback5-cb4-pins.test.js` (freed in its own combat, leaked, confined in 联防,
freed at its 4th attack there).
#### 25.18.6 「萨卡兹穿刺手被眩晕或者减速恢复之后移速不会重置(降低),伤害也异常」 (item 36): a stun or 束缚 ends its acceleration; its first hit adds speed × 600 — `content/enemies/special.js kitRush`
- **Seen**: after a stun it walked on at the speed it had built up (it even kept accelerating while stunned: 1 → 1.5 in a
2 s stun); its first hit after a block was one multiplied attack (×(1 + 6 × layers / 25), 2286 at 17 layers), also
after a stun that came in between.
- **Official**: PRTS 天赋 「每0.1秒检测一次:①若受晕眩/束缚影响,停止加速 ②若未被阻挡,开始加速…每0.5秒尝试获得1层移动速度+50%
的增益,最多尝试25次(增益于停止加速时解除…),同时普通攻击附加的Buff会额外造成一次当前移动速度×600的…物理伤害并停止加速」. The
battle prefab agrees: listeners every 0.1 s end `dlancer_t[trigger]` on STUNNED (a) or UNMOVABLE (b) and start it when
not blocked (c); the trigger stacks a MULTIPLIER move-speed layer every 0.5 s, blocked or not; the FirstAttack buff
`dlancer_t_atk` deals `DamageViaAttr` MOVE_SPEED × atk_scale (physical, melee, ignoreForSp) while the trigger exists,
then ends it. A slow (减速) is no reset — the report's slow is the official behaviour; it only lowers the speed while it
lasts, and so the extra damage.
- **Now**: kitRush follows the templates: a 0.1 s listener (晕眩 / 束缚 statuses end the acceleration and drop its layers;
not blocked starts it, from the spawn), a layer every 0.5 s up to 25 tries, the extra damage = its current move speed
× 600 as a second, non-attack physical instance with no 受击回复, ending the acceleration (so a block's first hit only).
Reproduced after the fix: 2 s stun → base speed, a fresh build-up; blocked at 2.375 → 450 + 1500 (2.5 × 600, one more
layer before the hit).
- [ASSUMED]: 冻结, 沉睡 and 浮空 do not stop it (they set FROZEN / SLEEPING / LEVITATE, not the two flags the listeners
read); the client's 0.1 s listener order is folded into one check.
- Golden: 6 scenarios, each with 萨卡兹穿刺手 in it: roster-016, standin-04, standin-08, diy-028, diy-124 and the bot match
coop2-HARD-4 (160 spawned). Tests: `test/content/enemies_bosses.test.js` (3: the layers and the cap, the resets, the
first hit; all fail on the parent).
#### 25.18.7 「玩家的官方原版反馈:「投石车」敌人如果被蒸汽地板上的干员阻挡会不进行攻击」 (item 24): 自制投石机's 索敌不受阻挡影响 — `targeting.js canTargetAlly / sortAllyTargets`, `ai.js attackTargets`, `content/enemies/special.js` (enemy_10162_mnctpt)
- **The enemy and the terrain**: 「投石车」 = 自制投石机 (enemy_10162_mnctpt, ranged 4.0, the 悬赏·特异III bounty); the 「蒸汽
地板」 = 战场#07 (下半)'s 排气格栅 (tile_smog: an operator on it is 隐匿 to enemy ranged attacks).
- **Seen here**: blocked by an operator on the smog, it attacked that operator — every enemy does (the engine's rule: a
blocked enemy attacks its blocker whatever its selectability, PRTS 索敌的概念 「…强行无视对方可选性发动攻击」).
- **Official**: PRTS 自制投石机 天赋 「…索敌不受阻挡影响,且不会因丢失目标而结束攻击」 — the blocker exception does not apply to
it, so a 隐匿 blocker is no target and, with nobody else in its range, it does not attack: the players' report.
- **Now**: the catapult's profile is `blockFree` (spawns.js gives every enemy the field): `attackTargets` takes the
allies in reach as if it were unblocked, `canTargetAlly` refuses a 隐匿 / 迷彩 blocker to it, `sortAllyTargets` puts no
blocker first (仇恨值: taunt, then the latest deployed). Every other enemy keeps attacking a 隐匿 blocker.
- [ASSUMED]: none for the rule. Not changed, for later: its stones' splash radius (PRTS 「目标半径1.5范围」, the data's
`projectile_range` 0.5 × 3; the kit uses 0.5), the 0.3 s between its three stones, and 「不会因丢失目标而结束攻击」.
- Golden: 1 battle — standin-06 (the bounty catapult, blocked by 忍冬, now shot the 斯卡蒂 / 瑕光 pieces by 仇恨值, its
blocker no longer first). Tests: `test/sim/feedback5-catapult-smog.test.js` (3: no attack on a hidden blocker alone, another operator
instead, every other ranged enemy still attacks its hidden blocker; the first two fail on the parent). Docs:
docs/SIM.md.
#### 25.18.8 「鸭爵策略的悬赏现在有时候会一波出现多只,比如多只鸭子」 (item 35): the official 0–2 swaps per wave — no change
- **Checked**: 鸭爵's “神秘顾客” (`round_start_all_player_change_enemy_2`: min 0, max 2, from round 5, the late part of the
wave — minweight 0.6 / maxweight 0.99) swaps 0–2 ground enemies of each player's wave, each into one of 鸭爵, 高普尼克,
流泪小子, 圆仔 at random (bands/meta.js duckReplace, once per player per battle — no second handler). 1000 real waves
(`m._normalOpts`, solo and co-op, rounds 5–12): 0 swaps 388, 1 swap 312, 2 swaps 300, never more; the two of a 2-swap
wave were the same kind in 81 of 300 (two of one kind in ≈ 1 wave in 12, two ducks in ≈ 1 in 50). A pair leader field holds
each player's 0–2 on its own half, and 联防 re-enters every leaker's leaked swaps (both as before).
- **Official**: PRTS 卫戍协议:盟约 下半/PRTS盟约记录 鸭爵 备注 「每回合将有0~2名敌人被替换为上述敌人之一」 — two ducks in one
wave is the official rule.
- Golden: unchanged. Pinned: `test/sim/feedback5-cb4-pins.test.js` (≤ 2 per wave, 0 / 1 / 2 and two of a kind all occur,
1 fund each).
#### 25.18.9 「会移动的关底boss在各个地图的移动路线复核下是否正确」 (item 29): checked on every boss field — no change
- **Checked**: the leaders that walk are 铳 (boss_2 / 9) and 卢西恩 (boss_5); the 巨型单位 (胄, 管, 昆图斯, 阿利斯泰尔, 萨米的意志)
are 自缚 (content/bosses.js SELF_BOUND). Their routes are the level files' (level_act1autochess_h07_02 route 6 and act2
h07_05: PATROL_MOVE (3,9) → (2,3) → (5,9), 卢西恩's list four such rounds with a MOVE (5,9) between them), walked with the
official SPFA flow field and looped by `patrolLoop` (back to the first leg instead of the goal). On all 8 boss fields
of the season, solo and pair, 240 s each: every sample on a walkable tile, the targets in the official order, the pair's
mirrored copy on (3,11) → (2,17) → (5,11) (its branch route), 卢西恩 restarting after its 19th checkpoint and never reaching
the goal (its lpr 30).
- Found, not changed: without the match's `waveId` (tests, tools) `templateOf` infers the template from the route list
only and can take another leader's; 卢西恩 then misses its patrol loop and walks to the goal. The match, the server's
headless fields and the browser battles always pass it (BattleSpec `waveId`).
- Golden: unchanged. Pinned: `test/sim/feedback5-cb4-pins.test.js`.
#### 25.18.10 「干员野鬃放在红门前两格的时候有概率会把怪顶出地图显示漏怪」 (item 38): not reproduced — no change
- **Checked**: 野鬃 (elite, S2 夹枪冲锋 kept running) two tiles before either red gate, facing it, on 战场#01–#08 against real
waves (912 battles, normal fields; 480 more with the solo leader fields), also on v0.1.4: her pushes (up to 30 a battle)
never left a ground enemy on a tile it cannot walk, and every leak happened at its route's end. `Battle.displace`
moves a ground enemy only over walkable tiles of the battle rect, so it stops on the gate tile at the field's edge;
planLeg then walks it back. What the rule does allow: an enemy she keeps pushing back onto the gate until the time
limit is counted as leaked (时间到仍存活的敌人算漏怪), as in the official game. A recording or the map and round would
settle what the player saw.
- Golden: unchanged. Pinned: `test/sim/feedback5-cb4-pins.test.js` (two real waves, three tiles before the gates).
#### 25.18.11 「怪物的重量以及干员的推拉力度还是有点不对,比如锏能拉动10重的关底boss」 (item 39): not reproduced — no change
- **Checked**: a leader (tag boss) is never displaced (`Battle._displaceable`); 锏's S3 pulls with 力度 1 (中力) and, at
its end, 2 (较大力): 受力等级 = 力度 − 重量 ≤ −3 moves nothing (PRTS 推与拉), so no weight ≥ 5 enemy moves either. 40 S3
casts next to each of the 10 leaders (weights 6–10): never moved; 自在 as a bounty (weight 10) not even by 特大力. The
one thing that looks like it: 卢西恩's 不祥幻影 has his model and weight 2, so 锏 does pull it (all the way, 受力等级 0).
- Golden: unchanged. Pinned: `test/sim/feedback5-cb4-pins.test.js`.
#### 25.18.12 「昆图斯占地少最左边和最右边两排,似乎变成3x3了」 (item 47): not reproduced — no change
- **Checked**: 昆图斯's hit area is PRTS' 「受击判定区域为长4.95宽2.95的长方形,向上偏移1.0」 (tools/build-data.mjs HIT_AREAS):
standing on (3,10) it occupies cols 8–12 × rows 3–5 (body.js bodyKeys); an operator whose range reaches only its
leftmost column (艾丝黛尔 on (3,7)) attacks it; the Final Assault prep lights the same 15 tiles in red on both boards
(headless screenshots of the render demo's Final Assault prep; render/pick.js hitTiles = bodyKeys, pinned by
`test/render/feedback3-leader-prep.test.js`). Its model is drawn at the official prefab scale.
- Golden: unchanged. Pinned: `test/sim/feedback5-cb4-pins.test.js`.
#### 25.18.13 「最终boss跟原版相比攻击欲望感觉低了,不怎么打人,复核」 (item 52): every leader attacks at its data interval — no change
- **Checked**: solo leader fields with eight walls on the board, 150 s: 胄 38 normal attacks (interval 4 s), 昆图斯 43 (3.5 s,
two targets each), 阿利斯泰尔 19 (8.2 s), 萨米的意志 25 (6 s) — each at its official interval from its first target on —,
their skills at their cooldowns; v0.1.4 gives the same counts (only the first strike now comes at the damage frame,
§25.7.2). 铳 opens at the PRTS minimum attack speed 20 (22.5 s) and gains +80 per attack on the same target (5 times),
so a fight that keeps switching its target sees few of its attacks; 管's normal attacks strike its 余音, not operators;
卢西恩 attacks only its blocker and blinks past it. A leader and a situation from the reporter would show what differs.
- Golden: unchanged. Pinned: `test/sim/feedback5-cb4-pins.test.js` (the four interval leaders at ≥ max − 1 attacks).