Files
ark-stronghold-protocol/docs/history/v2.5-playtest6.md
T

81 KiB
Raw Blame History

DESIGN §20 — User playtest #6 (v2.5) and its follow-ups, up to 0.1.0

Part of DESIGN.md (the index; section numbers are global).

20. User playtest #6 (v2.5): summons, skill triggers, blocking, push force, enemies, boss pool, bounties, 机变 cards, element gauge; leader HP, elite to the board, official limits

Nineteen reports after playing v2.4 (#1–#3 from a friend of the user). Where each is handled: #1 凯瑟琳's device could not be placed by hand → §20.1; #2 赫默's drone could not be placed by hand → §20.1; #3 忍冬's 叙拉古 stealth never ended → §20.2; #4 the bounty enemies kept coming instead of stopping after two battles → §20.6; #5 the Final Assault leader at 0 HP kept fighting until the team LP ran out, and the result still said 胜利 → §20.5; #6 the 道具补给 cards showed no description → §20.7; #7 in 联防 the leaker's count should fall live as teammates kill (it seemed capped at 10) → §20.6; #8 新约能天使 S2's shield on teammates seemed not to work → §20.2; #9 威龙 was drawn far too large → §20.4; #10 on the phone the shop's confirm strip did not buy (only the middle of the card worked) → §20.7; #11 the element gauge seemed to fill at the wrong rate → §20.8; #12 萨卡兹枯朽战车 hit too softly → §20.4; #13 “萨科塔之翼”'s 战栗 (small random flight) was missing → §20.4; #14 enemy weight vs push force (野鬃 pushed too many enemies) → §20.3; #15 skills whose range is wider than the attack range did not fire when an enemy entered it → §20.2; #16 古米's ally heal fired only after she engaged an enemy → §20.2; #17 the front operator that took over an enemy held between two operators never attacked it → §20.3; #18 炎佑 was healed by medics → §20.3; #19 the 3-choose-1 promotion reward often offered two identical operators → §20.6.

Eight workstreams were merged into playtest6 (WH → WG → WD → WC → WB → WA → WF → WE); the reviews' last rounds and the cross-workstream notes were applied on top: a stocked skill summon deploys as soon as its owner is back (§20.1), the AUTO DP skills 焰尾 S1 / 凛御银灰 S1 fire on full SP (§20.2), 锏 S3's slashes give 无敌 and 晕眩 / 冻结免疫 (§20.3), the AUTO_OP_COOLDOWN comment and the SKILL_RANGE count (13 skills / 26 records) were corrected. The user then answered the open questions (2026-10-01); the answers are settled decisions, recorded in their sections and in §20.9, each with the one-line change that flips it: the placed skill summons deploy once at the battle start and 凯瑟琳's device stays a hand card (§20.1), every blocker hits what it blocks (§20.3), 萨卡兹枯朽战车 follows the re-research — ranged enemies reach an operator by its 0.25 collider (§20.4) — and multi-round bounties last two battles (§20.6). Normative lines rewritten with it: §2 (fear.js), §3 (the enemies' reach), §5.2 (ASPD floor 20), §5.3 (camou, fear, noHeal, isolated), §5.4 (push / pull / ally-selection helpers), §5.5 (initial deployment, blocking, operator attacks, healers, enemy AI, end), §5.6 (skill triggers, the operation cooldown), §6.1 / §6.2 (the Final Assault verdict, summon stacks, summons in the hand, the promotion reward), §7 (bounty draft, 教鞭), §8.2 / §8.3 (uniteLeft, descRaw, counterText, the elem fill, the stealth bit), §9 (gauge row, model scale), §10 (机变 card, the card tap), §14 (left, the pool floor, the verdict, uniteLeft); superseded or revised: §17.5 (the 联防 count), §18.3 (the gauge look), §18.6 #11 (skill summons), §19.2 (the melee-only blocked-first rule; 灰毫 S2), §19.5 (the gauge look).

Follow-up (playtest6b, 2026-10-02). Four more workstreams were merged into playtest6b in this order: the elite-to-board rule (§20.11), the 假想敌:胄 kit audit (§20.13), the Final Assault leader HP report (§20.10) and the official limits read from the client binary (§20.12). Reports: co-op 终极 / 绝境 leaders died within seconds although the official leader never dies that fast with such layers (§20.10); "官方就是合成精锐时,如果消耗了场上的干员,精锐会出现在场上那个位置" (§20.11, user-confirmed). The boss-HP and the limits workstreams both capped bond layers at 999 — one implementation remains (shared/constants.js BOND_LAYER_CAP / layerGainRoom, used by every writer), and the boss-HP workstream's reading of 限伤 as an engine fixed-point overflow "not modelled" is superseded by the binary evidence (MAX_BATTLE_DAMAGE 300000, the hit is cancelled). Normative lines rewritten with it: §2 (promote.js), §5.1 (layerGains capped), §5.2 (直接乘算 in Σpct), §5.3 (same-named debuffs), §5.4 (addLayers, loseHp), §5.5 (限伤 between the multipliers and the shields), §6.2 (where a merge's elite goes), §6.3 (the layer cap), §7 (leader parts, drones), §8.2 (hitCap), §9 (the promotion cue), §14 (the layer bound of client results); §20.9 records the decisions. Two QA verifiers then checked the integrated build; their residual issues are handled in §20.14. A later report after v2.5.1 — the bond strip should follow the watched teammate in every watch situation — → §20.15. The player reports after the public 0.1.0 release → §21 (v0.1.1).

20.1 Summons placed by hand (#1, #2) — tools/build-data.mjs, match/gamedata.js placeableTokens, PlayerState, match/bot.js, sim/Battle.js, content/tokens.js, kits tier2/3/4, ui/detailPanel.js, shared/constants.js SKILL_SUMMON_START_DEPLOY

  • Seen (the friend): 凯瑟琳's device and 赫默's drone could not be placed by hand. Cause: v2.3 (§18.6 #11) limited hand pieces to talent summons after the user's playtest #4 report on the drone, which dropped 医疗探机 and 诅咒娃娃; 爬行号·防护单元 has no shop-state entry, so 凯瑟琳's kit placed it automatically.
  • Official (PRTS 卫戍协议/帮助): §战斗部署 "如果部署的干员拥有可手动部署的附属召唤物,则该召唤物会立刻加入手牌区" — deploy-limit copies ("非初始持有量"), one stack per operator, no deploy slot (token pages: 部署占用数 0); withdrawing the operator removes its summons, moving it "其所属召唤物全部退场并重置至手牌区". §手牌区: summons left in temp "会于下一回合返还". §作战阶段: placed summons deploy after the operators; "所有手动部署的召唤物,无视所属干员的持有状态…作战开始时立即部署一次(例如…赫默的医疗探机)"; one that leaves "将在满足条件后立即原地再部署1个". Token 备注: 诅咒娃娃 is withdrawn when 巫恋 leaves; a deployed 医疗探机 stays when 赫默 leaves. shopStateTokenDict (55 entries): 医疗探机 / 诅咒娃娃 / 狼群 / 海嗣 / 流形 DEFAULT, 投递坐标 and the battle-only tokens HIDDEN, and token_10041_cathy_catsld the only pool summon with no entry.
  • Now (data / prep): tokens.json placeable = not HIDDEN and made by an owner's talent or skill — 医疗探机 (赫默 S2), 诅咒娃娃 (巫恋 S2), 爬行号·防护单元, 海嗣, 狼群, 流形. placeableTokens(chessId, loadout) follows the equipped skill (赫默 S1: no card) and gives the deploy limit (凯瑟琳 2). A moved or swapped operator sends its placed summons back to their stack (PlayerState._liftTokensOf; a summon dragged onto an operator stays where it was dropped when it stays legal — a range-bound summon inside its owner's range from the owner's new tile, §21.3); startRound tops the board owners' stacks back up, so a stack removed from temp returns.
  • Now (battle): talent pieces (狼群, 海嗣, 流形, devices) deploy with the board, after the operators. A skill summon's piece also deploys once with the board, free (settled below), then is docked on its tile (dockSkillSummons, the tile stays reserved): releaseSkillSummon adds one to the owner's stock per cast (≤ 1) and the piece takes its tile once its 5 s redeploy time has passed, free, never while its owner is off the field — a stocked one deploys as soon as the owner is back (review round 3: a deploy hook on the owner; it used to wait for the next cast). The drone lasts 10 s and stays when 赫默 leaves; the doll lasts 15 s and leaves with 巫恋. Not placed: 医疗探机 / 诅咒娃娃 / devices / 海嗣 never appear; 狼群 / 流形 still come as 援军 on a tactical point (inside the tactician's range, never water, §21.3). The 爬行号·防护单元 kit shields the operator on the tile it faces (range 1-1): the full shield on each new target, invulnerable, key cathy:shield. Bots place the cards (a device next to the best operator no device faces yet, facing it); every bot layout first lifts the placed summons and places them again, so a device never stays beside a tile its operator left (residual QA: 3.5–8.6 % of the bots' devices faced nothing). The summon card says how it takes the field (summonDeployHint, reading the switch) and shows the token's talent / skill with the owner's variant (golden 巫恋's doll −30 %, golden 赫默's drone ATK 114).
  • Settled by the user: (1) the placed skill summon deploys once at the battle start, the PRTS rule — SKILL_SUMMON_START_DEPLOY = true drives the sim and the card hint (PLAYING §4 and SIM.md follow; test/ui/playtest6_summons.test.js checks). The playtest #4 report ("是赫默开技能释放一次,不是开局直接就部署了") had been made against v2.2, which already deployed the drone the PRTS way. Flip: SKILL_SUMMON_START_DEPLOY = false (only with the skill). (2) 凯瑟琳's device stays a manually placed hand card (no shop-state entry read as shown). Flip: dropping token_10041_cathy_catsld from placeable in tools/build-data.mjs only hides the card — the v2.4 automatic placement in her kit would have to come back with it.
  • [ASSUMED]: skill-summon redeploys cost no DP; no deploy while the owner is off the field; a swap lifts both operators' summons; the device is invulnerable.
  • Tests: test/match/playtest6_summons.test.js, test/content/playtest6_summons.test.js (start deploy, each cast, the owner's redeploy), test/ui/playtest6_summons.test.js, test/content/{playtest4_summons,tokens_devices,kits_t3,summon_loadout_conflicts}, test/sim/{loadout-summons,docs-examples}; opt-in test/ui/playtest6-summons.e2e.test.js (SP_E2E=1).

20.2 Skill triggers and buffs (#15, #16, #8, #3) — tools/build-data.mjs resolveTrigger, sim/skills.js, sim/Battle.js, sim/targeting.js, sim/snapshot.js, kits

  • #15 seen: a skill whose range is wider than the attack range did not fire when an enemy entered it (德克萨斯 剑雨 13 tiles vs 2; 凛御银灰 S2; 锏 S3). Cause: every MANUAL skill used the basic rule, and research 03 C1 read the class rows (skillIndex 0) as "skill 1 only" (BWIKI).
  • Official (PRTS 卫戍协议/帮助 §作战阶段 技能操作): basic rule "技能就绪,且即将进行普通攻击/治疗"; a skill with its own 技能范围 (not an attack-range increase) "不通过普通攻击/治疗触发技能,仅在技能范围内存在敌人(无视其不可选中)时释放技能"; class strategies — 重装 "不受技能范围影响,受到伤害时释放技能" (下半, 2026/3/14), 战术家 / 执旗手 / 吟游者 when ready, 解放者 / 阵法术师 an enemy in the initial range, 澄闪 / 纯烬 / 荒芜 S3 "全场存在可选目标时"; "自动操作具有3s冷却,在完成一次操作或作战开始时部署的单位将进入冷却".
  • Now (§5.6): the class rows cover every MANUAL skill of the class and never an AUTO one — an inference (PRTS names whole classes; under the old reading the 阵法术师 row could never cover 薄绿's default S2, a phalanx never attacks with its skill off); counter-evidence kept: BWIKI's "技能1" and the data's skillIndex 0 / -1 sentinel. SKILL_RANGE: 13 skills (26 records with the elites; 14 / 28 in the data since 余 S2's deviation, §22.10), any living enemy on the skill range, checked every tick (attack-range changes keep the basic rule). GDGLOW_SKILL_2: a valid target anywhere. The 3 s operation cooldown after each automatic cast and after the battle-start deployment (flags.startOpCooldown; the test harness sets it to 0); kit-driven casts of MANUAL skills (波登可 S1, 雪猎 bullets, the 流形 copy) check skill.opCooling; AUTO skills are exempt; a pending "next attack" never takes a second charge. Players notice: every 重装 manual skill (号角 S3, 瑕光 S3, 灰毫 S2 — reversing §19.2 —, 古米 S2 …) now waits for the operator to be hit — partly reverted by §21.29: by the owner's deliberate deviation (2026-10-03, community feedback) 深巡 / 雷蛇 S2, 号角 S2 / S3 and 灰毫 S1 / S2 cast with an enemy in range (DEFAULT) again.
  • Integration follow-up (WE review round 2): the AUTO DP skills 焰尾 S1 迅敏直觉 and 凛御银灰 S1 周旋的谋略 ("立即获得…部署费用") are SP_FULL like 伺夜 S1 and 德克萨斯's 冲锋号令·γ型 — an AUTO skill takes no 技能策略 (they used to wait 12–16 s for the next attack).
  • #16: 古米 备用军粮 (AUTO) had picked up the 重装 row. PRTS 备注 "存在生命值不满的可治疗角色时可触发;…切换至治疗模式…直至古米完成一次普通攻击的治疗": it fires on an injured ally of its x-4 area and her next attack heals the most injured ally there (heal_scale × ATK); S2 食粮烹制 is MANUAL, so the 重装 rule. Audit: 塞雷娅 S1 急救 — PRTS 备注 "仅在周围有符合血量条件的友方单位时可触发,触发时会替换当次攻击", 修正 "血量小于等于一半" — is checked at her attack (DEFAULT + allies + hpAtMost 0.5) and that attack becomes the heal; a cast whose ally recovered first is withdrawn with its charge.
  • #8: 新约能天使 S2 gave the shield to the fastest attacker; official (PRTS 备注) "攻击范围内仇恨值最高的我方干员" — now aggroCmp (taunt, then the latest deployed), through battle.alliesInGrid (孤立 units skipped). The amounts were already right: 180 % (精锐 200 %) of each holder's own max HP, decaying by initial / 30 per s.
  • #3 (friend): the bond 隐匿 already ended on time (bondeffect_siracusa 32 + 0.4 × layers s). 忍冬 S3 "进入迷彩状态,直至下一次开启技能" was the generic stealth under the key stealth, which made 伪装服's timed 隐匿 permanent (or stripped it) and counted as 隐匿 for 叙拉古 / 家族徽章. Now a separate camou flag and status (vulpis:camou; 寒芒克洛丝 S1 too) that works like 隐匿 for targeting and on screen — only the enemy it blocks attacks it (PRTS 异常效果 "※迷彩与'阻挡时解除'没有直接关系"; §19.1 kept). What stays see-through after a kill is that official 迷彩, until her next cast. Declined (optional review UX item): a distinct 迷彩 look — the base game draws both see-through, and the detail card has no status row.
  • [ASSUMED]: enemy skills and splash selectors skip 迷彩 like target selection; every initial deployment gets the full 3 s (the remake deploys everyone at 0 s); a token skill without a skillType counts as MANUAL.
  • Tests: test/sim/playtest6-skills.test.js, kit tests (incl. kits_alt_t4 / kits_alt_t5 for the AUTO DP skills), test/data.test.js, test/sim/docs-examples.test.js.
Skill (examples) type before → after why
德克萨斯 S2 剑雨, 凛御银灰 S2, 锏 S2 / S3, 异客 S3, 焰尾 S2, 忍冬 S2, 诗怀雅 S1, 山 S3, 小满 S2, 归溟幽灵鲨 S1, 崖心 S2, 见行者 S2 MANUAL DEFAULT (kit CUSTOM_RANGE) → SKILL_RANGE own 技能范围
深巡 S2, 古米 S2, 折桠 S2, 瑕光 S2 / S3, 信仰搅拌机 S2 / S3, 泥岩 S3, 塞雷娅 S3, 余 S2, 角峰 / 雷蛇 / 泡泡 / 菲莱 / 蛇屠箱 S2, 灰毫 S2, 星熊 S3, 号角 S2 / S3 MANUAL DEFAULT → TAKE_DAMAGE (深巡 / 雷蛇 S2, 号角 S2 / S3, 灰毫 S1 / S2 back to DEFAULT, §21.29; 余 S2 now SKILL_RANGE on its x-1, §22.10) 重装 row (§21.29: a deliberate deviation for six skills)
薄绿 S2, 蜜蜡 S2, 卡涅利安 S2 / S3, 玛恩纳 S2 MANUAL DEFAULT → SEARCH 阵法术师 / 解放者 row
伺夜 S3, 魔王 S2 / S3, 浊心斯卡蒂 S3 MANUAL DEFAULT → SP_FULL 战术家 / 吟游者 row
古米 S1, 瑕光 S1, 塞雷娅 S1 AUTO TAKE_DAMAGE → own rule (古米: injured ally; 塞雷娅: at her attack, ally ≤ 50 %) AUTO: no 技能策略
焰尾 S1, 凛御银灰 S1 AUTO DEFAULT → SP_FULL AUTO DP skill (integration)
银灰 S3, 深靛 S1, 野鬃 S2, 蒂比 S1, 断崖 S2, 忍冬 S3 … MANUAL DEFAULT (unchanged) attack-range change ("非攻击距离增加")

20.3 Combat rules: blocking, ASPD floor, push / pull force vs weight, 炎佑 / 孤立 (#17, #14, #18) — targeting.js sortEnemyTargets, Battle.blockedTargets / push / pull / pullToFront / allySelectable / alliesFor, constants.js ASPD_MIN / PUSH_TILES(_EFFECT), kits, tools/build-data.mjs TOKEN_ABNORMAL

  • #17 seen: an enemy held between two operators; after the rear one died the front one took it over (§19.2), but only the enemy attacked. Workstream finding: every melee hand-over hit the enemy (3360 immediate hand-overs, 215 late ones — the enemy 0.52–0.69 tile behind the front operator's centre); the cases that did not were a ranged operator on a melee tile with the enemy behind it out of range, the playtest #5 QA rule (§19.2, PRTS 索敌的概念 "我方索敌优先级:阻挡(近战限定)"; "远程位干员…甚至如果这个目标被阻挡在该干员的身后…无法攻击到这个敌人") — the likeliest reading of the report.
  • Settled by the user — "阻挡了就一定要能打到": officially the collision pushes a blocked enemy to its blocker's front, so whatever blocks an enemy can hit it. Now every blocker — melee units, summons and a ranged operator on a melee tile alike — may always target the enemies it blocks, in range or not, whatever its facing, and targets them first (Battle.blockedTargets without the melee gate; sortEnemyTargets blocked-first for every attacker; the skills' DEFAULT trigger follows); targeting.js meleeUnit is gone. 深靛's kit (canAttack, the bound-target retarget) looked at her range only and now includes her blocked enemies; so do the skills that pick their own targets from the range (residual QA): 迷迭香 S3 (blocked enemies only — her own included), 荒芜拉普兰德 S2's drone locks, 佩佩 S2's random pick, 灵知 S3's unfrozen-first order and 远牙 S3's own-range filter. Two skill texts still win over the rule: 普罗旺斯 S2 "普通攻击不再以生命值高于80%的敌人作为目标" and 寒檀 S2's icicles on random tiles of her range. Healers (they keep healing while they block, §21.17) and 停止攻击 skills (泡泡 S2, 蛇屠箱 S2, 古米 S2, 凯瑟琳 S2, 菲莱 S2, 宴 S1, 铃兰 S3, 信仰搅拌机 S3) still never hit. Flip: gate Battle.blockedTargets and sortEnemyTargets' blocked-first on a melee position again (the v2.4 meleeUnit).
  • ASPD floor: 沼泽 −50 + 寒霜 −50 took ASPD to 10 (塞雷娅 swung every 12 s); officially "ATTACK_SPEED 默认下限 20" (PRTS 数值范围; 游戏数据基础 "实际被限制了下限为20") — ASPD_MIN 20, also in the bot's DPS estimate.
  • #14 seen: 野鬃 S2 pushed too many enemies. Cause: every push / pull moved 0.5 + 0.5·力度 tiles, cut 25 % per missing weight level. Official (PRTS 游戏数据基础 §重量公式, 推与拉): 受力等级 = 力度 (微小力 −1, 小力 0, 中力 1, 较大力 2, 大力 3) − the current 重量等级 (失重 counts). Push, 弹道 column: ≤ −3 → 0, −2 → 0.12, −1 → 0.44, 0 → 1.7, 1 → 2.14, 2 → 2.96, ≥ 3 → 3.53 tiles; 特效 column (见行者 S1 / S2, named 特效类): 0.085 / 0.374 / 1.562 / 1.987 / 2.773 / 3.331. Pushes follow the client buff template (§21.13): knockback[dir] = directional (推击手, 野鬃 S2), turning radial at −2 when the target is > 45° off or < 0.25 tile away; knockback[relative] = radial (琳琅诗怀雅 S3, 山 S3, 莫斯提马 S3); 圣聆初雪 S1 朝部署方向 = KnockBackWithCharacterDirection (no correction); 见行者 S2 waives the angle only. Pull: ≥ 0 all the way (拉力起点 0.5 tile ahead, 急停 0.6708), −1 → 35 %, −2 → 0.03, ≤ −3 → 0; 薄绿 S2's 拖拽 is a push towards her. Now: Battle.push / pull / pullToFront / forceLevel / pushDistance, displace the raw mover; every source migrated. 野鬃 S2 on weight 0 / 1 / 2 / 3 / ≥ 4: 2.14 / 1.7 / 0.44 / 0.12 / 0 tiles (light enemies now move further — official). Enemy weights equal enemy_database for all 249 enemies.
  • 锏 S3 (PRTS 备注 "※可对空…※多段斩击期间,自身获得无敌,晕眩免疫,冻结免疫"): its slashes reach air units (※可对空, added to §19.1's list) — the mode's air units are 静态刚体: hit, never moved (a non-static flyer would be pulled, §21.9) —, and during the slashes she is invulnerable (buff blkkgt:slashes) and refuses stun / freeze (integration follow-up of the WF note).
  • #18: PRTS “炎佑” 天赋 "{{特殊机制|我方单位}},{{异常效果|孤立}}"; 孤立 = ALLY_TARGET_FREE — PRTS 选择器 可选判定: a friendly selector never picks it, and selectors decide heal, buff and talent targets. 禁疗 (“小自在”, “耀阳”, 斯卡蒂的海嗣, 沙之碑, 流形, 狼群, 迷迭香的战术装备, 黄金盟誓, 保护目标(冻结状态)) blocks heals only; a 生命回复速度 attribute still applies (PRTS 安洁莉娜 备注). Now: tokens.json abnormal (TOKEN_ABNORMAL) gives noHeal, 孤立 also isolated; Battle.allySelectable / alliesFor back every ally selection (alliesInGrid(Of), auras, 全场 talents, zone / grid buffs, heal picks — a heal pick skips 禁疗 units instead of wasting the heal); enemies still target 炎佑; a summon aimed at injured allies (findSummonTile 'ally') skips them too.
  • [ASSUMED]: the 失衡 slide is instantaneous; a 静态刚体's 0.1 s 失衡硬直 is not modelled (§21.9); unclassified pushes use the 弹道 column; a puller never moves the enemy it blocks; 薄绿's inward push stops at the 急停 radius; 溯光星源's pull stops 0.3 tile from the target; 全场 talents select through a selector (skip 孤立); no unit-specific ASPD floor.
  • Tests: test/sim/playtest6-combat.test.js (the user's layout on act2 m01 × 4 facings; the late hand-over and a plain block for every blocking chess × 4 facings, ranged blockers and out-of-range enemies included; the skills that pick their own targets, 普罗旺斯 S2 the exception; ASPD; push / pull tables; 孤立 / 禁疗), test/sim/playtest5_blocking.test.js (the ranged blocker hits and casts for the enemy behind it), kits_t6 (锏 S3), expectations moved in core, engine-requests, facing and the kit suites.
Displacement source Official 力度 / kind v2.4.1 Now
野鬃 S2 中力 directional (knockback[dir], §21.13); > 45° off / < 0.25 tile → radial −2 1.0 tile, −25 % per weight above 2 弹道 along her facing: w0 2.14, w1 1.7, w2 0.44, w3 0.12, ≥ 4 0
山 S3 (2nd hit) 中力 radial (knockback[relative]) 1.0 tile, −25 % per weight above 2 弹道: w0 2.14, w1 1.7, w2 0.44, w3 0.12, ≥ 4 0
莫斯提马 S3 小力 radial 0.5 tile w0 1.7, w1 0.44, w2 0.12, ≥ 3 0
琳琅诗怀雅 S3 radial (knockback[relative], PRTS 备注), ground only (§21.13) 0.5 tile along the facing, flyers too push table, radial from her centre
圣聆初雪 S1 中力 directional, fixed (KnockBackWithCharacterDirection), ground only 1.0 tile along the facing, flyers too push table along the facing
见行者 S1 / S2 (hidden) 中力 推击手, 特效类 1.0 / 1.5 tiles 特效 column (w0 1.987 … w3 0.085)
崖心 S1 / S2, 歌蕾蒂娅 S1–S3, 百炼嘉维尔 S2, 乌尔比安 S1 pulls 至面前 ≤ 1 tile or all the way, scaled pull table (w ≤ 1 all the way, w2 35 %, w3 0.03, ≥ 4 0)
锏 S3 p_force 1 / e_force 2, 可对空 ≤ 1 / 1.5 tiles, ground only pull table, 急停 0.6708; air units hit, a 静态刚体 not moved (§21.9)
Air units of the mode 静态刚体 (PRTS 特殊机制) moved like ground ones 0 tiles from every source, still hit (§21.9)
薄绿 S2 小力 拖拽 = push towards her pull ≤ 0.5 tile push table towards her
ASPD floor 20 10 20

20.4 Enemies: 萨卡兹枯朽战车, the “萨科塔” 恐惧, 威龙's size (#12, #13, #9) — sim/fear.js, ai.js, grid.js straightClear, Battle.applyStatus, content/enemies.js (kitTank, pollution), constants.js ALLY_COLLIDER_RADIUS, tools/build-data.mjs MODEL_SCALES, render/units.js

  • #13 seen: “萨科塔之翼” below half HP should fly about at random in a small area (the user wrote 战栗) but hung still. Cause: every feared enemy was frozen. Official: the bird fears itself — PRTS "生命值首次低于50%时,对自身施加持续5s的恐惧,在5s内移动速度最终提升至150%" (之眼 / 昂首 the same); 恐惧 = "无法被阻挡并四散逃跑" (ba.fear), 战栗 = "被阻挡后无法进行普通攻击" (ba.tremble), so the behaviour described is 恐惧 — report and data agree, only the name differs. PRTS 诱发移动 §恐惧: 恐惧可达地块 = tile centres within 10 of the hit point, in the ±45° fan away from the source, passable, able to reach the goal, not the objective; next stop a random reachable tile within 5 path steps (offset in a 0.5 square), else its own tile; no source / itself as source ⇒ no reachable tile; 恐惧 outranks 诱导.
  • Now: fear.js; the birds flutter within ±0.25 of their tile for 5 s at ×1.5, then fly on. Operator fears (叙拉古 6, 妮芙 S2, 荒芜拉普兰德 S3) make enemies flee into the fan instead of freezing (a visible change). The 恐惧 and 诱导 walks re-plan from where the enemy stands after a push or an obstacle change (grid.version; PRTS "恐惧效果结束时立刻清除【恐惧移动】"); a checkpoint on an obstacle tile counts as unreachable; an off-centre ground start steps back to its tile centre when the straight line would clip a wall (grid.js straightClear, shared). They used to walk through walls after a push (4–7 % of random pushes on m01–m04).
  • #12 seen: 萨卡兹枯朽战车's damage felt low. Already right: stats (lv0 HP 12000 / 15000, ATK 750 / 850, DEF 750, RES 10, BAT 4.5, range 2.2, weight 4; no season override, no wave overwrittenData), the round multipliers, the target filter (只攻击位于低地的我方单位,且不会攻击飞行单位) and the 200 % blocked melee attack. Fixed (WG): 秽蚀轰击 timing (initSp 2 = maxSp 2 ⇒ attacks 1, 4, 7 …, not 3, 6 …); the 【污染秽蚀】 radius 1.7 (was 1 [ASSUMED]); its damage "每秒受到50/25点真实普通伤害…同名效果不叠加" — true damage through dealDamage, one tick per second per unit (also 萨卡兹枯朽战士's death zone, r 2, 8 s; zones are now absorbed by shields, scaled by damage-taken modifiers and fire TAKE_DAMAGE skills). The skill hit is 100 % (PRTS skill text), also against a blocker [ASSUMED that it fires there]. Measured: roughly unchanged overall (first 20 s +4…+42 %, a long blocked fight −7…−9 %).
  • #12 re-researched (the user's follow-up: "远程攻击射程挺远、伤害很高"): enemy_database (enemy_1272_nhtank / _2: rangeRadius 2.2, PollutedRangedAtk range_radius 2.2, atk_scale implicit 100 %, Empty.attack@chuang_atk_scale 2, spData 攻击回复 2 / 2), the PRTS pages (攻击方式 近战 远程; "仅半径2.2范围内存在位于低地的非飞行我方单位时可触发:对目标造成100%物理普通伤害"), activity_table act2autochess (DOT special, no stat override; the range-4 tank is 斗智场's 改装排污车, another mode) and the act1 / act2 autochess levels: there is no higher scale and no longer data range. The hard hits are the round multiplier (table below: 绝境 R7 998, 尖端 R13 1656 per ranged hit before DEF). What the data does support is a longer effective reach: PRTS 作战机制 §碰撞体积与位置识别 "我方干员碰撞体积基本均为以0.25格为半径的圆形…关于碰撞体积与远程索敌,碰撞箱碰撞是为最常用的方式,索敌抬手等均使用碰撞箱" — the remake measured enemy range from the operator's centre. Settled: every ranged enemy's normal attack takes an ally whose 0.25 collider touches its range circle (ALLY_COLLIDER_RADIUS, ai.js enemyAttack: centre distance ≤ rangeRadius + 0.25; content skills / zones keep their own point radius [ASSUMED]). 枯朽战车: reach 2.2 → 2.45 (13 → 21 tiles around it); a lone operator beside its lane takes 4 hits instead of 3 (绝境 R6, DEF 300: 2095 → 2793 attack damage, 2595 → 3593 with the zone); dense lineups unchanged (its 4.5 s interval is the limit). The user's memory of a long range is stronger than the data supports — said plainly in the user notes. Flip: ALLY_COLLIDER_RADIUS = 0.
  • #9 seen: 威龙 drawn far too large. Official: the client scales each enemy Spine model in its battle prefab — 0.27 for 1454 of 2147 prefabs; 威龙 0.16, 妖怪 0.20, 妖怪MKII 0.22, the 岁 relics 0.4–0.6. Now: MODEL_SCALES (122 prefabs) → enemies.json modelScale = product / 0.27 (125 keys); render/units.js scales the skeleton and the bar height (enemies without setup-pose bounds: chibi headroom × modelScale). 威龙 is 1.28 tiles wide instead of 2.15 — 1.08 × a 妖怪, as in the game. 85 enemy skeletons still have no bounds in data/assets.json (asset pipeline, not addressed).
  • Tests: test/sim/playtest6-enemies.test.js (flutter, determinism, operator fear, 恐惧 > 诱导, push / obstacle re-planning on m01–m04, the tank incl. its reach), test/render/playtest6-enemies.test.js, test/local-extract.test.js (enemy_scales), combat.test.js; opt-in test/render/playtest6-enemies.browser.test.js.
Round Tank 险境 ATK · ranged hit · blocked 绝境 终极
R1 萨卡兹枯朽战车 600 · 600 · 1200 825 · 825 · 1650 825 · 825 · 1650
R4–R7 萨卡兹枯朽战车 726 · 726 · 1452 998 · 998 · 1997 998–1208 · … · 1997–2416
R8 尖端萨卡兹枯朽战车 — (banned) 1131 · 1131 · 2263 1369 · 1369 · 2738
R13 尖端萨卡兹枯朽战车 — 1656 · 1656 · 3313 1656 · 1656 · 3313

20.5 Final Assault leader at 0 HP that would not die (#5) — sim/constants.js BOSS_POOL_MIN_HP, match/finalAssault.js SharedBossPool, sim/spec.js LocalBossPool, match/Match.js _creditBoss / _onResult / _finishFinal, ui/hud.js PhaseCapsule, ui/gameLogic.js bossPctText

  • Seen: 2 humans (AI teammates eliminated), 绝境, act2 m01, client-side combat — the boss bar read "0.0%" with 95 s left, the leader fought on until the overtime drain took the team LP, and the result said 胜利. Reproduced: real 2-human client-combat matches (pool scaled to 5 %): on v2.4.1 11 of 40 fights stalled, every boss family; about 11 minutes of drain followed, ending 胜利 or 失败.
  • Cause: float dust in the shared pool, not a boss script, hit area or part. _creditBoss subtracts each report per player and the pieces add up only in exact arithmetic (40000 − 12345.6 − 7654.3 − 20000.1 = 3.6e-12); the browser's LocalBossPool (server hp − (cum − acked)) absorbed every later hit (less than half an ulp of cum), so the leader never died, nothing new was reported, and m.public rounded the server's dust to "0 HP". When the team LP hit 0, the verdict was read from the pool after the final b.result, whose per-player damage is Math.rounded — rounding up emptied the dust (胜利).
  • Official: PRTS 卫戍协议/帮助 §领袖作战阶段 "…计时结束后战斗仍然会继续,但每超时1秒扣除1目标生命值"; 卫戍协议:盟约 下半 "…使目标生命值扣除至0,则无视倒计时直接失败" (research 01 §10). The victory after the drain contradicted it; the user was right.
  • Now: a shared pool never holds less than BOSS_POOL_MIN_HP (1) — the hit, or the per-player credit, that would leave less takes the rest (SharedBossPool, LocalBossPool), and a browser pool reads any finite rest below 1 as 0, so a pool shown at 0 is a dead leader on every field (Battle._bossSync / _checkEnd) and b.end cleared goes out at once. The first end the server registers decides (Match._finalEnding: pool 0 → victory, team LP 0 → defeat); after a forced end no report credits the pool. The boss bar prefers the live pool of the battle on screen over the ~1 Hz m.public.bossHp and never shows "0.0%" for a living leader ("<0.1%"). Checked and ruled out: the kill check reads the pool whatever the unit's own hp or invulnerability; mirrored and hidden-core copies share it; parts only pass damage; the Hidden Core, 4 humans and server-run mode end promptly.
  • [ASSUMED]: the 1-HP floor (official HP is a float shown rounded — PRTS 作战机制 "红字在显示时会就近取整"); a client emptying the pool within its latency after the server registered team LP 0 still loses; the HUD prefers the local snapshot pool.
  • Tests: test/sim/playtest6_bosspool.test.js, test/match/playtest6-bosspool.test.js, test/ui/playtest6-bosshud.test.js.

20.6 Match flow: the bounty draft, 教鞭, the 联防 leak counter, the promotion reward (#4, #7, #19) — tools/build-data.mjs bountyDraftExclusion, match/choices.js (MULTI_ROUND_BOUNTY_BATTLES), content/items/meta.js, Match.addBounty, PlayerState.pushRewardOffer / effectsView, sim/spec.js uniteLeft, Match._uniteLeft, fields.js uniteBillBounds, ui/hud.js MissTag, ui/teamPanel.js rowLp

  • #4 seen: "悬赏决策的敌人…正常只会在选择之后出现两回合,现在是一直出". Cause: a 1–2-battle card already stopped after its battles, but the draft drew from all 129 ENEMY_GAIN cards (战术特训 and the hidden 鸭爵 set included) and every card was plain text (the server's stripped desc beat the rich text), so a 每场 multi-round card looked like a 下场 one.
  • Official: PRTS 卫戍协议:盟约 下半/PRTS盟约记录 §机变阶段 "敌人轮选" lists the 1- and 2-battle bounties, 源石虫·特训 and the 7 multi-round cards and comments out the 鸭爵 set; 战术特训 sits under "※以下悬赏任务仅由法术教鞭生成"; §法术 教鞭 "于3个战术特训的悬赏任务中选择一项"; 杜宾 加练! "若自身战斗完美作战可获得资金". The card text colours the duration (<@ba.vup>下场作战</> / 两场作战, <@ba.vdown>每场</>).
  • Now: cards.bounty draft = that table (105 cards: 56 next-battle incl. 源石虫·特训, 42 two-battle, 7 multi-round), drawn uniformly, no multi-round cap — superseded by §21.2 (the official draft structure of each round); bounty cards carry descRaw; the effects column says 还剩 N 场作战 (counterText). 教鞭 draws 3 战术特训 cards and takes one at random (it used to draw the band bounties, so 战术特训 was unreachable); “神秘顾客” (granted by nothing in act2) keeps the band bounties [ASSUMED]. Not modelled: the one official draft screenshot hints at one series per draft.
  • Settled by the user — "我不记得有过多轮悬赏": until confirmed otherwise every official multi-round card (山海众头目·多轮悬赏, 多轮悬赏·假想敌 ×6, and 法术大师A2·多轮战术特训 from 教鞭) lasts two battles exactly like the "接下来两场作战" cards (no draft offers one since §21.2; 教鞭's card still lasts two battles): MULTI_ROUND_BOUNTY_BATTLES = 2 (bountyBattles; bountyText rewrites "之后的 / 后续<@ba.vdown>每场</>作战" to "接下来<@ba.vup>两场作战</>" on the card and in the effects column, 还剩 N 场作战); choices.json keeps the official rounds 99 / multiRound. Flip: MULTI_ROUND_BOUNTY_BATTLES = null restores the official red 每场 everywhere (no data rebuild).
  • #7 seen: the leaker's number never fell during 联防 and seemed capped at 10. Official: PRTS 帮助 "防卫失败的玩家可通过上方信息栏确认自身所属敌人的剩余数量"; the capsule shows an orange runner tag ×N (research 09 tag_miss, drawn with ui/battle bg_miss_enemy [ASSUMED]); the 10-LP cap per round is unchanged. Now (revising §17.5): uniteLeft per leaker = not spawned yet + alive + counted leaks — it falls on kills and rises on splits / summons (children inherit sourcePlayerId); the server takes it from b.progress left, the headless timeline [gt, killed, total, left] or the exact uniteSurvivors, clamps live values to uniteBillBounds; m.public players[] uniteLeft is uncapped and pendingLp = min(cap, uniteLeft). ×N in the capsule and in the leakers' team rows; a client with the 联防 field on screen shows its local replica's counts there; a teammate's row tooltip names the teammate.
  • #19: PRTS 帮助 "临时刷新3名高于调度中心等级1阶(最多6阶)的干员"; the official 玩法介绍 image shows three different operators at price 0. v2.4.1 rolled three times independently (14–21 % of offers had a duplicate). Now three different chess, copy-weighted; a tier short of different chess tops up from the tier below [ASSUMED]; the offer reserves no copies. The integration QA found the same repeat in the other free pick-one chess offers — 信标 (same tier), 寻呼模块 (same bond ≤ shop level, content/items/meta.js) and the engine default builtinMeta use_equip_reward_special_goods_char_chess — they now draw different operators too (信标 tops up from the tier below, 寻呼模块 shows fewer cards when the pool has no other one), and pushRewardOffer drops a repeated id from any list it is given (test/content/items.test.js).
  • Tests: test/match/playtest6-matchflow.test.js (the draft, two battles for a multi-round card end to end, 联防 counts, rewards), test/match/followups.test.js, test/ui/playtest6-unite.test.js, test/ui/playtest6-bounty.test.js, test/content/items.test.js (教鞭).

20.7 机变 card text; the shop's confirming tap on phones (#6, #10) — ui/choiceOverlay.js, css/screens/game-panels.css, ui/shopBar.js, css/screens/game-shop.css, css/devices.css

  • #6 seen (screenshot 2, Android 756×366 CSS px, 终极 co-op): the 道具补给 cards showed only icon, name and tier. Cause: the card was a centred column sized for 16:9; with the root font clamped at 40 px a 3×2 card was 105 px tall and showed 0.2 lines of text (217 of 223 texts cut), in all four families; at 1920×1080 nine long texts were cut at 4 lines. Official (research 06 §11.5; screenshots Mb2mtd9 / Wrkhtxv / SEbySVl): a framed icon at the top-left with the bold name beside it, the description below, numbers in blue, the tier chip on the icon's corner, the taker's round avatar alone at the top-right. Now: that layout; the text from cardText (the card's own descRaw, else the data descRaw while its plain form equals the server's desc, else the server text), clamped to 7 lines (8 at line height 1.3 on phones under 700 px wide: with Noto Sans SC — the page's web font, metrics of Android's CJK font — 海沟实验体 wraps to 8 lines at 640×360, residual QA); short screens get a tighter overlay header (no CONTINGENCY micro line) and .19rem text; the taker badge is the avatar alone (name in its tooltip and the card's aria-label) and the taken card's head reserves its width. All 223 texts (51 items, 129 bounty, 43 tactic) fit in full, taken or not, from 640×360 to 2560×1080, measured with Noto Sans SC.
  • #10 seen: on the phone the second tap on the 确认购买 strip did nothing; only the middle of the card worked. Cause: the card's invisible ⓘ corner had the generic 44 px touch area (its 30 px override lost on specificity: :is() scores 0,3,1 against 0,2,1), covering the bottom-right quarter of the card and the right half of the strip (120 / 144 grid points); below 768 px the left-docked detail card opened by the first tap also reached over the bar's first cards. Official (research 09 §5): first tap = detail + 确认购买, second tap buys; the card has no info corner. Now: the ⓘ is removed (a tap anywhere on a card is the card's tap), .shopbar.has-armed { z-index: 10 } keeps the armed bar above the detail card (z 9; below .uframe 12 and .spov 20), and a card that cannot be bought right now opens its detail (not aria-disabled, help cursor). 144 / 144 points on touch at 640×360 … 932×430 and with the mouse at 1920×1080; the strip centre, its right end and the bottom-right corner buy.
  • [ASSUMED]: phone text 7.6 px; the avatar inside the card's corner; the gold strip may cover the last line of the longest texts while a card is selected; the micro line hidden on phones; the armed bar hides the detail card's corner on < 768 px phones; solo 3-card sizes.
  • Tests: test/ui/playtest6-ui.test.js; opt-in test/ui/playtest6-ui.e2e.test.js (SP_E2E=1: every text at 9 viewports, each card taken once, the hit grid by tap target and real taps); mock.e2e (detail + Esc through a card tap).

20.8 Element gauge on screen (#11) — sim/damage.js elementView, render/units.js, render/textures.js

  • Seen: correcting playtest #5, the user said the 海嗣's element damage is right after all, but the gauge "fills at the wrong rate: far from used up when the burst already hit". Measured (Node sim → interp → UnitView, and headless Chrome on the real view; 14 element enemies vs 古米, the reviewer with 5 operators against a mixed 海嗣 wave): the drawn gauge follows the sim within one frame. Steady attackers burst off a 1–5 % sliver; big hits legitimately burst far up the bar (掠海漂移体 does 50 % of its ATK × the round's ATK per hit — 25 % of the gauge at base, ≈ 44 % at 绝境 R11; 萨卡兹王庭军精锐术师 死亡之眼; 昆图斯's skills).
  • Display faults, all fixed: elementView rounded the fill to the nearest 1 % (a gauge 1–4 元素值 short showed empty); the v2.3 ring right of the bars sat on the next operator's tier chip (read as the neighbour's gauge, and a shrinking ring reads like a filling one); during the 爆发冷却 the refill was the same white as the 元素值 left while the burst's stun / damage hit the unit.
  • Official (PRTS 元素): "当单位存在一项不为最大值的元素时,模型下部会显示对应的元素图标,并以白条显示剩余的元素值"; enemies get a small colour-only icon; during the 爆发冷却 "单位的元素条显示缓慢恢复至上限(动画效果)" (no colour given).
  • Now: fill rounded DOWN to 1 %, kept at 0.01–0.99 outside a burst (1 during one) — the bar only runs out when the gauge bursts; the gauge is a row under the unit's own HP / SP bars inside their span: the element disc (0.15 tile, 8–15 px; enemies ×0.8, plain colour; light rim) and a white bar of the remaining 元素值 (SP-bar height, dark track); during the 爆发冷却 the bar refills in the element's colour (ELEMENT_RING[el].tint) with the disc pulsing, white again when it ends.
  • [ASSUMED]: the refill colour and pulse; the geometry; placement under the above-head bars (official: under the model); the disc rim. The render-demo recordings carry fills made with the old rounding (display only).
  • Tests: test/sim/playtest6_elemgauge.test.js, test/render/playtest6_elembar.test.js, test/render/downring.test.js; opt-in test/render/elembar.browser.test.js, downelem.browser.test.js.

20.9 Settled decisions and what stays [ASSUMED]

The user's answers of 2026-10-01 (playtest #6 follow-up):

Decision Settled rule One-line flip
A placed skill summon at the battle start (§20.1) deploys once, free, then with each cast (PRTS §作战阶段) shared/constants.js SKILL_SUMMON_START_DEPLOY = false
凯瑟琳's 爬行号·防护单元 (§20.1) a manually placed hand card drop token_10041_cathy_catsld from placeable (tools/build-data.mjs) — and restore the v2.4 kit placement, or the device never appears
Multi-round bounty cards (§20.6) last two battles like "接下来两场作战", blue text — 教鞭's card; no draft offers one (§21.2) server/match/choices.js MULTI_ROUND_BOUNTY_BATTLES = null (official 每场)
萨卡兹枯朽战车's reach (§20.4) data range 2.2 + the operator's 0.25 collider for every ranged enemy; scales per data (100 % ranged, 200 % blocked melee, skill 100 %) server/sim/constants.js ALLY_COLLIDER_RADIUS = 0
Blocking (§20.3) every blocker hits the enemies it blocks, first, whatever its range or facing gate Battle.blockedTargets / sortEnemyTargets blocked-first on a melee position again
A merge's elite (§20.11) takes the consumed deployed copy's tile and facing, else the hand (PRTS 帮助 "若消耗已部署至作战区的干员,则发送至作战区对应位置"; the user confirmed it, 2026-10-01) none — official; restoring the v2.5 hand-first rule means reverting PlayerState._mergeChess (merge 5f3842f)

Official since the follow-up — read from the client binary (research 11, §20.12) or the official texts (§20.10, §20.13):

Rule Now One-line flip
Bond layer cap (§20.12) 999 per bond (MAX_GARRISON_STACK; AddBondCount stores min(L + n, 999)) — no longer [ASSUMED] shared/constants.js BOND_LAYER_CAP = 0
限伤 (§20.12) a leader hit with ceil(d) ≥ 300000 in boss / hidden battles is cancelled (MAX_BATTLE_DAMAGE), not an overflow shared/constants.js BOSS_HIT_LIMIT = 0
直接乘算 (§20.10) bond / strategy / item / 机变 / 特质 "+X%" are summed with each other and the skills' server/sim/constants.js DIRECT_BONUS_STACKING = 'multiply'
Ground routes (§21.15) the official flow field unless the playtest #2 route crosses strictly fewer floor tiles (a corner touch is no crossing) server/sim/grid.js _buildField always use = o (reverses 战场#01's col-8 road)
Leader parts (§20.10, §20.13) every damage a 剑 / 锤 / 碎铳之簧 takes is lost by its leader 1:1, 无来源 server/sim/content/bosses.js PART_TRANSFER = 0.5 (v2.5's share; v2.5 also passed a 剑 / 锤's damage only while grounded)

Settled by the user ("保持固定血量"): the co-op leader pool does not shrink when players are eliminated — config bossHpScale.aliveScaling is false (pool = bloodPoint whatever the number of alive players; one community note, 巴哈姆特 12294, said otherwise) (§20.10); flip: aliveScaling: true (× alive / 4).

Settled by the user (2026-10-02, after v2.5.2: "共享阵地按你说的规则就行"): on a shared field (联防, Final Assault / Hidden Core) the bond strip follows the player of the half the ‹ › pill points at; on 全景 or an empty half it shows your own bonds when you fight there, else the teammate picked with 前往查看 or the field's first player — never your own (§20.15); rule: public/js/ui/watchBonds.js bondOwnerId.

Still [ASSUMED] (each in its section): the class rows covering every MANUAL skill (§20.2); skill-summon redeploys free and never while the owner is away (§20.1); the 1-HP pool floor and the server-order verdict (§20.5); a short promotion tier topping up from below and “神秘顾客” keeping the band bounties (§20.6); the 弹道 column for unclassified pushes and the other displacement details (§20.3); 枯朽战车's skill firing on its blocker at 100 % and content enemy skills keeping a point radius (§20.4); the gauge's refill colour and placement (§20.8); the 机变 card's phone sizes (§20.7); the elite's tile among several deployed copies (the first in deploy order) and the elite's fresh summon stack (§20.11; a transformed carrier's tile no longer counts, §21.1); "strongest" rather than "latest" for same-named debuffs, which "最大生命值" the 胄 drone link reads (the pool), the 剑 / 锤 dive hits passing on, the springs' split and the solo pool × 0.25 (§20.10); the scene server clamping prep gains like the client, a cancelled hit showing nothing, a passed-on loss being one hit and the order blocks → 限伤 → HP shields (§20.12); 胄's unread max_hp 0.5, the untargetable 初始模式 and a replacement copy inheriting only the HP (§20.13); talent debuffs keyed per source unit (two copies of one operator each apply theirs, §20.14).

  • Seen: co-op 终极 / 绝境 leaders died within seconds; with the user's layer counts the official leader never dies that fast.
  • HP and pool — official, unchanged: bloodPoint[difficulty] = activity_table act2autochess bossInfoDict bloodPoint / bloodPointNormal / bloodPointHard / bloodPointAbyss of the current data (the cache equals upstream ArknightsGameData master as of 2026-10-01; test/match/bosshp.test.js pins all 40 values — e.g. 胄 247 500 / 675 000 / 1 800 000 / 3 600 000, hidden 胄 750 000 / 937 500 / 3 600 000 / 7 200 000). PRTS 盟约记录's leader table is an older (11月18日) revision: 铳 险境 and 胄 / 铳 / 萨米 绝境 differ, no 终极 column, no 卢西恩 row. The data has no per-player or per-field factor; leader HP ignores enemyScale ("领袖单位于服务器的生命值加成不受上述加成影响"). One pool for every boss field ("最终攻势中,所有人将一起对敌方领袖造成伤害"); notice 5114's "敌方领袖的总生命值不变" means the two mirrored copies of a pair field share it.
  • Pool size: co-op = bloodPoint whatever the number of alive players — bossHpScale.aliveScaling (× alive / 4, one community note: 巴哈姆特 12294) stays off — the user chose the fixed pool (§20.9); solo × 0.25 [ASSUMED].
  • Fixed — why leaders fell too fast:
    1. 直接乘算: "+X%" ATK / DEF / max HP from bonds, strategies, equipment, 机变 cards and per-layer 特质 are summed with each other and with the skills' "+X%" (PRTS 盟约记录 "…属性加成均为直接乘算"; 游戏数据基础 D_t = Σtᵢ, A = (A₀ + D_p)(1 + D_t)). v2.5 compounded them (×74 instead of ×12 at 600 精准 layers with four more bonuses). DIRECT_BONUS_STACKING 'add', content/support directMods; 炎佑 ×1.5 and the char_attribute_mul 特质 stay multipliers (§5.2).
    2. Same-named enemy debuffs: one instance per target, the strongest (PRTS 作战机制 "同名buff…只能表现出一个"; 巴哈姆特 12316 "共享型buff會跟對面搶") — 奥术, 灵知 坚冰, the 莱恩哈特 / 缄默德克萨斯 RES cuts (Battle.applyStrongest, §5.3). v2.5 kept one 奥术 per player, so a pair field multiplied it (×40 arts damage taken at +400 layers; ×7.6 now). "Strongest" rather than "latest" is [ASSUMED].
    3. Leader parts: 剑 / 锤 / 碎铳之簧 pass every damage they take 1:1 as 无来源 HP loss, credited to the attacker (PRTS "受到等量的无来源生命流失"; PART_TRANSFER 1, loseHp … sourceless); v2.5 passed ½, a 剑 / 锤 only while grounded. The dive hits counting (PRTS talent "受到伤害时" vs the handbook "被击落时") and the springs' split to the other springs are [ASSUMED] (§20.13 for the 剑 / 锤 kit).
    4. 坚守 thorns: dealt 无来源 by the member who was hit and credited to that member (statistics only; up to 19 % of a co-op pool was nobody's).
    5. Bond layers: uncapped in v2.5; now 999 per bond — official, §20.12 (this workstream first added the cap as [ASSUMED] from the community; the merge kept the limits workstream's single implementation).
  • Kept: a 胄 drone costs 2 % of the leader's shown max HP = the pool (DRONE_LINK_BASE 'pool', = v2.5; which "最大生命值" the official client reads is [ASSUMED] — the review tried the unit's data 600 000, which made drones 19–78 % of a solo 标准 fight, and went back). Under 限伤 it is one hit: 144 000 at most today (hidden 胄 终极), cancelled only above a 14 999 950 pool (§20.12).
  • Ruled out by measurement: double crediting (client / server / replica), multi-hits through the 5×3 hit area, element bursts, %-max-HP damage on leaders (only the drone link).
  • Official, not a bug: per-layer bonuses have no cap below 999 (谢拉格 vs cold ×(1.35 + 0.01 L) = ×11.34, 奥术 at low HP ×15.7, 精准 +1208.8 % ATK at 999 layers), so very high layers still kill fast — 绝境 with +400 on every active bond: median 8.6 s, fastest 3.6 s; 终极 under 20 s needs ≈ +400 on every bond (≈ 2 800 active layers per player; median 16.8 s); with two bonds per player at 999 (≈ 2 000 layers) 绝境 kills median 10 s, 7 of 28 under 5 s; with every active bond at 999 终极 median 8.3 s, 绝境 3.7 s — and there 限伤 (§20.12) cancels 6–77 hits per 12–28 fights. The community reports the same (巴哈姆特 12522, 999 谢拉格 "boss一秒死").
  • Measured (real seconds — the fights run at 2×, so half the game seconds of the QA tables). Integrated build (QA 6b on cca11e6, after the elite-to-board merge): the R14 boards of real 4-AI co-op matches, seeds 7 / 8 / 9 / 12 × boss_1…7 = 28 Final Assaults per row, both pair fields on one pool, the same boards replayed on v2.5 → now: 终极 own layers median 206 → 258 s, +200 on every active bond 24.5 → 42.1 s (fastest 8.2 → 18.9 s), +400 11.3 → 16.8 s; 绝境 own 79 → 99 s, +200 11.6 → 14.8 s (fastest 1.9 → 7.3 s), +400 6.1 → 8.6 s; each player's top bond requested at 1500 (now held at 999): 终极 53 → 95 s, 绝境 17.5 → 33 s. On each version's own bot boards the medians moved only ×1.0–1.2 (终极 own 256 → 258 s, +200 47 → 54 s; 绝境 own 81 → 99 s, +200 13 → 15 s): the elite-to-board rule (§20.11) gives the bots slightly stronger R14 boards (终极 2.12 → 2.26 elites per player, 绝境 2.14 → 2.51; 绝境 6.0 → 6.9 active bonds), so a player whose board did not change feels the same-board numbers. No landed leader hit reached 300 000 (v2.5 landed up to 18.6 M); the largest at +400 is 144 000 (a hidden 胄 drone; 114 000 from an operator), at 999 layers 299 600. The boss-HP workstream's own measurement (42 pre-merge boards, seeds 7–9: 终极 +200 30.9 → 56.0 s, …) is kept in BALANCE §3, labelled as such; BALANCE §3 has both tables.
  • Tests: test/match/bosshp.test.js (real Final Assault / Hidden Core matches, the 40 bloodPoint values; the 奥术 pair test runs seed 12 since the elite-to-board merge changed the bots' boards), test/sim/bosshp_stacking.test.js, test/match/layercap.test.js, test/sim/battle.test.js (Battle.addLayers cap), test/content/bonds_addon.test.js, kits_t4.test.js, kits_alt_t4.test.js, enemies_bosses.test.js, items.test.js, garrisons_battle.test.js, choices.test.js, test/match/unitStats.test.js, finalAssault.test.js, balance.test.js, playtest6-bosspool.test.js; opt-in SIM_E2E=1 node --test test/sim/bossfield.browser.test.js (the real boss / hidden specs of a 999-layer bot match give the same digest, pool and fx in headless Chrome as in Node; covers §20.12 and §20.13 too).

20.11 A merge's elite takes the deployed copy's tile (user-confirmed) — match/PlayerState.js _mergeChess / transformChess, match/board.js mergeTile, match/audit.js, ui/gameLogic.js mergeProgress / mergeTarget, ui/shopBar.js armedSlotOf / onArm, screens/game.js MERGE_HL, render/promote.js, render/fx.js promote, render/app.js setPrep

  • Seen (the user, first-hand, after playtest #6): "官方就是合成精锐时,如果消耗了场上的干员,精锐会出现在场上那个位置".
  • Cause: research 01 A1 row 7 read PRTS 帮助 as "1 elite goes to the hand, not to a board tile" (marked VERIFIED, repeated in 01-core-data.json promotion.eliteGoesTo), so _mergeChess put every elite in the hand and used a consumed copy's tile only when the hand and temp were both full.
  • Official (PRTS 卫戍协议/帮助): §干员的获得与精锐化 "…发送1名【精锐】状态的该干员至手牌区(若消耗已部署至作战区的干员,则发送至作战区对应位置)"; tip "干员晋级后已配发装备会回收至整备区,需要重新配置"; §战斗部署 "如果部署的干员拥有可手动部署的附属召唤物,则该召唤物会立刻加入手牌区", deploy order "从上到下>从左到右". The text does not say which tile wins among several deployed copies.
  • Now (server): when a consumed copy stood on the board the elite takes that tile and its facing, otherwise it goes to the hand, overflowing into temp (outside PREP too). Several deployed copies: the one that deploys first — col asc, then row desc since 0.1.3 (§23.23; row desc, then col asc before), the mirrored right boss half included — board.js mergeTile(tiles, legal) [ASSUMED]. A 突变细胞 transformation destroys its carrier before the gain, so the carrier's freed tile is no copy's (§21.1 — official footage; the 0.1.1 WA rule that counted it, transformChess fromKey / fromDir [ASSUMED], is withdrawn). A tile the elite may not use is skipped; with the hand and temp also full the elite stays on the first deployed copy's tile rather than being lost (as _evictIllegal). The deploy count never grows (no BOARD_FULL path). Equipment returns to the hand, overflow temp; with both full it stays on the elite up to its equipPerChess (2) slots, any further item destroyed with a log warning (unchanged). The consumed copies' summons are removed; an elite on the board gets its own fresh stack (grantTokensFor, after the items) following the §16 loadout [ASSUMED: placed summons are not carried over]. Every path (g.buy, g.reward, ctx.grantChess, 画卷, transformations), every phase (a SETTLE / Final Assault merge places the elite at once, its reward offer waits for the next prep) and the boss-field prep (legality on the player's half, §19.7). Reward offer, ticker and stats.merges unchanged; onMerge carries area ('board' | 'hand' | 'temp'). Bots plan the elite like any owned unit.
  • Now (client): gameLogic.mergeTarget(priv, chessId) mirrors mergeTile; a shop or reward card armed for a merge-completing buy lights the target tile in gold (ShopBar onArm → screens/game.js highlight group mergeTile); the 可晋升 tag says 精锐干员将出现在作战区原位置 or 精锐干员将进入整备区; an elite card never merges (completesChessMerge refuses isGolden, mergeProgress reports 0 copies: no pips, tag or tile); render/promote.js promotionsOf recognises a merge in setPrep and the elite plays fx.promote (gold pillar, shockwave, hex ring, streaks from the other copies) instead of fx.deploy (view.debug.promotions).
  • Checks: audit.js checks every merge — the tile is mergeTile of the deployed copies legal for the elite, with that copy's facing; the deploy count did not grow; otherwise the elite is not on the board, except the no-room fallback. tools/matchrun --check sweeps are clean.
  • Normative lines rewritten: §2 (promote.js), §6.2 (the economy line and the temp-overflow paragraph), §9 (the promotion cue).
  • Tests: test/match/playtest6-elite-to-board.test.js, test/match/merge.test.js, hardening.test.js, facing.test.js, test/ui/playtest6-elite.test.js, test/render/playtest6-promote.test.js, test/docs-consistency.test.js; opt-in SP_E2E=1 node --test test/ui/playtest6-elite.e2e.test.js (real server and prep: the armed card lights the deployed copy's tile, the elite stands there after the buy, 剩余可放置角色 unchanged).

20.12 Official limits: 999 layers per bond, 限伤 300000 on leaders — shared/constants.js BOND_LAYER_CAP / BOSS_HIT_LIMIT / layerGainRoom, match/PlayerState.addLayers, Match SETTLE, sim/Battle.addLayers / loseHp, sim/damage.js leaderHitCancelled, match/fields.js validateClientResult, match/invariants.js, render/fx.js, tools/matchrun.mjs, tools/balance.mjs

  • Asked: the co-op leaders as hard to kill as in the official game. Research 11 (docs/research/11-limits-official.md) found two official caps in the client (2.7.71 il2cpp) that the remake lacked.
  • Official [DATA]: AutoChessBattleConst.MAX_GARRISON_STACK = 999 — AddBondCount stores min(L + n, 999), per bond. MAX_BATTLE_DAMAGE = 300000 — in step mode (every boss battle outside training) AutoChessStepModeManager._OnBossEnemyTakeDamage runs modifier.Cancel() when ceil(value) ≥ 300000 on an IsBossEnemy unit; IsBossEnemy (disassembled, research 11 §2.3) = enemyId ∈ autoChessData.bossInfoDict[*].enemyId = data/bosses.json enemyKey. [COMM] bonds stay at 999 while fed; 「一次超过30w会被卡掉」: players avoid extra multipliers at 999 layers. 限伤 is a deliberate constant, not an engine fixed-point overflow — the boss-HP workstream's earlier reading (2³¹, "not modelled", §20.10) is superseded.
  • Now — layers: the single implementation is shared/constants.js BOND_LAYER_CAP + layerGainRoom(before, n) = min(n, 999 − before), never negative (the boss-HP workstream's duplicate sim/constants.js BOND_LAYER_CAP / layerRoom was removed in the merge). Every writer uses it: PlayerState.addLayers (特质, items, bands, 机变 cards, bonds), the SETTLE of BattleResult.layerGains, Battle.addLayers (the live copy, clamped after the layerGain hook, like the client), and the dev tools' direct writes (matchrun --layers N, balance.mjs applyBoard). A gain at the cap adds 0: no onLayers, hook or 'layer' event, so the per-N milestones (远见, 奇迹, 维多利亚 …) stop with the count. Client results: per-bond layerGains ≤ min(60 + 4·round, 999 − starting layers) (+ the lineup's layer 特质, §21.26). invariants.js flags a bond above 999; the UI shows the server's capped count.
  • Now — 限伤: kinds 'boss' / 'hidden' only; a leader is isBoss (the tag-'boss' units: those 10 keys and their mirrored copies — parts, 刺胄之弹, drones and escorts never count). A hit with ceil(final) ≥ 300000 is cancelled: checked in dealDamage after DEF/RES and every multiplier, before HP shields, and in loseHp (a part's 传递, a drone's death). A cancelled hit causes no HP or pool loss, no credit or stats, no damage number, no damaged / fatal / kill; the attack, its SP, the hit hook and separate element 损伤 hits stay. Element bursts and DoT ticks are checked, gauge fills are not. The sim emits fx hitCap {id, n}, drawn as nothing (deterministic in Node and browsers; SP_VERIFY digests agree).
  • The line vs the pool: 【死亡集群】 drone deaths cost 0.02 × the pool max (§20.10, §20.13) — at most 144 000 today (boss_8 终极 7.2M), so it lands; a pool above 14 999 950 would cancel every drone death. An alive-scaled pool (§20.9, open) can only shrink, but any larger pool must re-check it; a real-kit test pins the line.
  • [ASSUMED]: the scene server clamps prep gains the same way; a cancelled hit shows nothing; a loss passed on to a leader is one hit; the order hit-step blocks → 限伤 → HP shields — 阿利斯泰尔's 莫非王土 vest (boss:vest, 5000): a hit of 300000–304999 is cancelled and the barrier stays; 假想敌:再生's blocks on 假想敌:管 (hidden h08_03) negate a hit past the line before the limit sees it.
  • Measured: the competent and strong balance boards never reach the line (biggest leader hits 28k–72k), so BALANCE §3's model numbers stand (BALANCE §7); the co-op replays up to +400 per bond and a top bond at 999 cancel nothing either. Two bonds per player at 999 or every active bond at 999 do reach it: 6–18 cancelled hits per 28 Final Assaults and 14–77 per 12 Hidden Cores (QA 6b), and those kills take longer than with v2.5's uncapped hits (§20.10).
  • Flip: BOND_LAYER_CAP = 0 and / or BOSS_HIT_LIMIT = 0 (or Infinity).
  • Tests: test/sim/playtest6_limits.test.js (including the real-kit vest, 再生 and 【死亡集群】 cases), test/match/playtest6-limits.test.js (a 999-layer board through a real Final Assault + Hidden Core, both tools at the cap), test/render/playtest6_hitcap.test.js, test/sim/playtest6_bosspool.test.js (adapted), and the boss-HP workstream's test/match/layercap.test.js / test/sim/battle.test.js cap tests on the single API.

20.13 假想敌:胄 kit audit (PRTS) — content/bosses.js kitHelm / kitBlade

  • Audit (a separate session, 2026-10-01), against the PRTS 技能 / 天赋 / 能力修正 texts and the skill blackboards:
    • 灭顶之灾 fires one 刺胄之弹 below 20 % too (PRTS 能力修正 原因 6 "描述与游戏实际表现不符合": no extra shell; no blackboard holds a second one), at the highest-ATK operator within attack range for both copies ("触发索敌和普通攻击相同"; boss_1 used the whole field).
    • 死亡集群: any drone death costs the leader 2 % max HP (= the pool, §20.10), credited to the killing operator if one killed it; a leak costs nothing (能力修正 "该无人机单位死亡时"). Drone HP: summon.hp_ratio (隐秘核心) scales it; skill 2's max_hp 0.5 is not read [ASSUMED] — no source defines it.
    • 斩胄之剑 / 破胄之锤: 初始模式 hovers invulnerable (untargetable [ASSUMED]) and attacks everything in range (锤 150 % ATK, unblockable). 掷 → 出击模式: blink to the start of the level's <hand>_blink route, fly at the lowest-ATK operator, no normal attack. 15 hits → 【瘫痪】: stun 10 s (its end ends 瘫痪, not the 20 s), ground unit, damage taken ×1.3. Landing or 瘫痪 ending: a new 初始模式 copy with the old HP is summoned on the <hand>_origin route and the old one is killed (a part: uncounted, no bounty) [ASSUMED: only the HP is inherited, so 掷 comes back after its initial 25 / 55 s instead of the 80 s cooldown].
    • Every damage a 剑 / 锤 takes passes 100 % to 胄 (PRTS "等量", also during the dive) — merged with §20.10 into the one PART_TRANSFER 1 (BLADE_TRANSFER is its alias), 无来源 and credited to the attacker.
  • Measured (same-seed balance.mjs --boss-only, n = 3–5 per cell): boss_1 unchanged (≤ 0.5 s); boss_8 独立 绝境 44.7 → 45.2 s, 独立 终极 win 2/3 → 3/3, 同盟 绝境 23.3 → 23.7 s, 同盟 终极 72.0 → 64.2 s (BALANCE §3).
  • Tests: test/content/enemies_bosses.test.js (shell count and range, drone deaths and leaks, the pool-based link, the sortie / 瘫痪 / replacement cycle with the 1:1 无来源 transfer and its credit).

20.14 QA residuals of the integrated build (2026-10-02) — match/Match.js _onResult / heldResult, render/app.js promoBase, ui/shopBar.js mergeHint, ui/detailPanel.js, screens/game.js dSide

Two QA verifiers ran the integrated playtest6b (real 4-AI and 2-human co-op matches for every leader, Node vs headless Chrome digests, the merge UI on the user's phone profile and on desktop). Every check passed; the residual issues and what was done:

  • Fast boss kills handed over (fixed). Under client-side combat a pair that empties the pool faster than the plausibility budget (one field credits ≤ the pool per BOSS_MIN_CLEAR_GS 5 game s, §14) had its 'cleared' b.result rejected as implausible, the client demoted and the field handed to the partner or the server — seen at 绝境 with every bond at 999 (kills at 2.0–4.0 game s; official 999-layer play reports "boss一秒死"). Now a 'cleared' result whose cumulative report covers what the pool holds (1 HP slack, the pool floor) is held (f.heldResult): the boss clock credits the report at the budget's rate, the silence watchdog and handovers skip the field, a re-sent copy is ignored, and the pool's end completes the field with it (_endFinal); a team-LP defeat first still credits nothing more. The budget itself is unchanged, so a forged report gains nothing a b.progress could not already get. The verdict waits at most until the budget covers the report (≤ 5 game s from the field's start). Re-run of the QA cases (绝境 seed 7 boss_1 / boss_6 with every bond pushed to 999, seed 21 with 990 early layers, 3 more at 999 / 终极): 0 rejections, 0 takeovers, every boss field kept by its client, Σ credited boss damage = the cleared pools; in headless Chrome (2 humans + 2 AI, a scratch server with a 12× slower budget so the path runs) both boss rounds held the clearing client's result and went on to the Hidden Core and the result with no takeover and no console error.
  • No promotion cue for a merge outside the prep (fixed). A merge completed between two preps (a band / 机变 grant at ROUND_START, a SETTLE merge) placed the elite silently: enterBattle emptied the prep state the cue compares with. render/app.js keeps the last prep pieces across a battle or scouting board (promoBase), so the next setPrep plays fx.promote on the elite (streaks from the consumed copies' slots). Re-run of the QA case (松桐 grants the third 古米 at R4 ROUND_START): view.debug.promotions records the elite on the copy's tile, copies 2.
  • The 可晋升 destination on touch (fixed). The tag's text was a native title, never shown on a phone. The detail card the first tap opens now says it under the header — 「可晋升:精锐干员将出现在作战区原位置」 / 「…将进入整备区」 (shopBar.js mergeHint, gold .dhint--merge; shop and reward cards).
  • The detail card covered the gold tile (fixed). At 1920×1080 the left card covered half of a target tile in the leftmost legal column; the card now docks on the side away from the armed merge tile (screens/game.js dSide, the underframe's panelSide rule), on phones too.
  • Measurements re-taken (docs). §20.10 / §20.12 and BALANCE §3 / §7 now give the integrated build's numbers (real seconds; the earlier boss-HP numbers are labelled as pre-merge boards) and the 999-layer profiles where 限伤 cancels hits.
  • Not changed — per-source talent debuffs [ASSUMED]. Some talents key their debuff with the source unit (etlchi:dot:<id>, ines:atkSteal:<id>, ines:sentry:<id>, and similar slows / DoTs in other kits), so two copies of one operator (a pair field where both players own it) each apply theirs. PRTS 作战机制 says only that same-named buffs show one instance under the default overlap strategy; whether these talents use it across copies is not in the data the project has, and the effect on leader kill times is small (an arts DoT, an ATK steal, a slow). The rule stays applied to the debuffs that move leader kills (奥术, 灵知 坚冰, the RES cuts — §20.10).
  • Not changed — temp items behind a full hand. With the hand full, items overflowing into the 临时整备区 (row 8) stand behind the bench row's operator models and are hard to see; the row's red frame, the 「临时整备区 N 件道具待处理」 notice and the refused Ready still show them. A layout change of the temp row is left for a later pass.
  • Tests: test/match/clientCombat.test.js (the held result; a 'cleared' result not covering the pool is still handed over), test/render/playtest6-promote.test.js, opt-in RENDER_E2E=1 node --test test/render/browser.test.js (a merge between two preps is cued), test/ui/playtest6-elite.test.js (the detail hint), opt-in SP_E2E=1 node --test test/ui/playtest6-elite.e2e.test.js (the hint in the real detail card).

20.15 The bond strip follows the watched player (user report after playtest #6, 2026-10-02) — ui/watchBonds.js, ui/bondStrip.js, ui/combatHud.js, screens/game.js (watchWho, bondOpen), battle/runner.js bondLayers / ownerOps, battle/observe.js resumedWatch, match/bondsMeta.js bondsWithGains, PlayerState.pendingLayerGains / bondsView, Match.publicView / _finishCombat / settle

User: 「观看队友时(不仅是休整时候,还有自己战斗结束时,联防时,最终boss战)盟约栏应该变成当前队友的盟约以及他的层数」.

Before this change:

  • Only 前往查看 of a teammate's normal field switched the strip, and it showed the start-of-round layers.
  • An eliminated player's auto-observed field, the 联防 field and the Final Assault pair field kept the viewer's own bonds.
  • A teammate's popup listed the viewer's pieces as its members.

Whose bonds (watchBonds.screenFieldId / bondOwnerId / screenStrip): the player whose field or half the camera shows.

The field on screen is, in order of preference:

  • the watched field (g.watch);
  • else the battle the view shows. Under client-side combat this is the runner's battle on screen, which is also an eliminated player's auto-observed field; under server-run combat it is the field the view entered;
  • else home.

In SETTLE it is the field the last battle left on screen. In prep without watching, it is the own board.

  • The own board / own battle → your bonds (m.private bonds).

  • A normal field 'n:<pid>' → its owner's bonds (m.public players[].bonds). This covers:

    • a teammate's board in prep (前往查看). Research 09 §3.1: "their board, read-only, including their pen and bonds". Official clients receive every board's bonds (ChangePositionDn.boardStatus.updatedBonds);
    • a teammate's battle watched after the own one;
    • the field an eliminated player auto-observes;
    • a field picked in the legacy view switcher.
  • A shared field (联防 'u', boss / hidden 'b1' / 'b2') — settled by the user (2026-10-02: "共享阵地按你说的规则就行"; no official source shows the strip there):

    1. If the ‹ › pill points at a half that a player holds → that player. The pill's "你自己" means yours; "👁 name" means theirs.
    2. Else (全景, an empty half "无人在家", or no pill), if you fight on that field → yours.
    3. Else (you are not on the field: a 联防 leaker, an eliminated spectator) → never your own. The strip shows the teammate you picked with 前往查看 if they are on that field, else the field's first listed player (helper 1 / the pair's first seat).

    Basis: official observing targets a player (ObserveUp { obIndex }, DeadAutoObDn { obIndex }, m_leftBattleObIdx / m_rightBattleObIdx), and the official non-helper pill reads "‹ 👁 helper# ›" (research 09 §3.1).

  • The picked teammate: the game screen remembers watchWho = { fieldId, playerId } when a team row is tapped (requestWatch(fid, playerId)). The g.watch request still names only the field. watchWho counts only while that field is the watched one (watchedPlayer). It is cleared by 返回战场, the own row and a phase change, and restored if the server refuses the watch.

  • Camera: picking a teammate on a two-half shared field moves the camera to their half (playerLayer), once per pick and per field entered. The ‹ › pill is also offered on a shared field watched with 前往查看, so the pill and the strip tag always name the same player.

    • On such a field the ‹ › pill replaces the observing pill and carries 返回战场 (absent for an eliminated player).
    • On a normal field the observing pill "👁 name" takes the strip owner's name.
    • The default camera on entering a field is still 全景.
  • 返回战场 without an own field (client-side combat: a 联防 leaker, an eliminated player): no g.watch is sent, because it would be refused with BAD_TARGET 'no such field' (an error toast). The screen keeps the field it shows, and the strip falls back to the rules above.

  • A reload / reconnect while watching a teammate's battle after the own one (client-side combat): the server already resends the watched field (Match._resendBattle, b.start watch: true). But a fresh screen starts with no watched field, so before this the HUD showed 「作战结束,等待队友完成作战」 with no observing pill and no 返回战场, and the own row did nothing.

    • The screen now adopts that field as the watched one, once per battle (observe.resumedWatch, decided at the first sight of its battleId).
    • It does so only for a teammate's normal battle in COMBAT, shown to a living player who watches nothing.
    • A battle first seen while a watch is already set is only marked as seen. So a 返回战场 tapped while the own b.start is still on its way never brings the teammate back.
    • A loading battle waits until it runs (its state carries no watch yet).
    • 联防 leakers and eliminated spectators keep their own rules.

Labelling:

  • A teammate's strip leads with an amber "👁 name" pill (the observing pill's spelling) and gets amber disc rings, aria-label "name 的盟约" and data-owner; slots carry data-bond.
  • The pill is horizontal at every width, with a pixel floor: 10 px on desktop and tablet, 9 px below 768 CSS px (1rem is clamped at 40 px there).
  • An empty teammate strip reads "name 尚未激活盟约".

Detail card chips and the popup (whose bond a popup shows):

  • A unit card's bond chips use the bonds of watchBonds.detailBondOwner:
    • a unit on the field → its owner, when that owner is a known player (yours or a teammate's: a shared field shows both halves' units, whoever the strip follows);
    • your own piece (board / bench) → yours;
    • a bond-member card opened from a popup → that popup's player;
    • anything else (shop / reward / drawer cards, a unit without a known owner) → the strip's player.
  • A bond popup carries the player it was opened for: bondOpen = { id, ownerId, from: 'strip' | 'detail' } (toggleBond). From the strip, that is the strip's player; from a chip, it is the same player the chips show. The same bond of the same player closes it.
  • popupView builds the popup from that player, never simply from the strip:
    • the entry with the live layers (as the strip and the chips show it);
    • "👁 name 的盟约" for a teammate;
    • the member list: your pieces, or that teammate's operators on the field on screen (ownerBoard: the prep scouting board's or a server-run battle field's units). Under client-side combat it also uses the battle on screen's operators (battleRunner.ownerOps(ownerId, fieldId)), because the field meta the runner publishes is taken before they deploy.
    • Their hand is never sent, so a teammate's member counts as owned only when it is on the field.
  • The strip marks a disc as open only for a popup of its own player.
  • A popup opened from the strip closes when the strip changes hands: another teammate, a ‹ › half, or back to your own. A popup opened from a chip stays open, labelled with its own player.

Layers:

  • In battle: the battle runner reports state().bondLayers = { [playerId]: { [bondId]: n } }. These are absolute live counts of every bond that grew in a battle it simulates (the own battle, a teammate's display replica), capped at BOND_LAYER_CAP (999). They are published when one grows and kept through the round.
  • The client lays them over the view's counts with withLiveLayers: the higher value wins (a count only grows) and never shows above 999. They apply in the battle phases and SETTLE only.
  • 联防 and boss battles disable gains, so they report none.
  • After COMBAT (_finishCombat): until SETTLE, both m.private bonds and m.public players[].bonds add the finished normal battle's IN_BATTLE gains (PlayerState.pendingLayerGains = the result's layerGains; bondsMeta.bondsWithGains: floored, at most up to the cap, like settle()). The strip of a player, and of a teammate watching him in the 联防, therefore keeps the layers his battle reached.
  • ps.bonds / ps.layers are untouched. SETTLE clears the pending gains as it adds them (once), and the next round start clears them too. Since 0.1.3 the 联防 field fights with the same counts: the helpers' battle input takes its bonds from bondsView() (battleInput({ reached: true }), PRTS §联防阶段 "将以其阵地当前的状态" [ASSUMED] — it used to carry the round-start layers while the strip showed the reached ones); no other rule reads them.

Data:

  • m.public players[].bonds = ps.alive ? bondList(gd, ps.bondsView(), { off: offBondCounts(gd, ps) }) : []: every bond with members, layers or an active tier (since 0.1.3 also the mode-off bonds with members, off: true, last). It is the same list and order as the player's own m.private bonds, minus thresholds / countsHand (the client reads those from bonds.json).
  • An eliminated player's list is [], for these reasons:
    • nobody can watch them: g.watch refuses them and they have no field;
    • the result screen reads m.result's own bonds;
    • their remaining layers would otherwise cost every m.public for the rest of the match.
  • Measured in a 4-AI NORMAL harness match (seed 11, round 13, 3 players eliminated): players[].bonds is 771 B, against 2424 B without this rule. The review measured 705 B on v2.5.1; the difference is the living player's layers-only entries, which the strip needs.
  • There is no new message and no new field. m.public stays throttled and deduplicated, so the extra traffic is the living players' layers-only entries plus one change at the end of COMBAT.
  • Nothing is exposed that scouting (research 09 §3.1) or watching the battle does not already show.