38 KiB
DESIGN §19 — User playtest #5 (v2.4)
Part of DESIGN.md (the index; section numbers are global).
19. User playtest #5 (v2.4): hovering enemies, target order, blocking, 联防, elements, huge bosses, maps, phone HUD
Ten reports after playing v2.3 (绝境 on desktop, 标准 on an Android phone in landscape). Where each is handled: #1 operators that only attack ground units hit 近地悬浮 enemies → §19.1; #2 an operator lying knocked out (濒死) vanished when 联防 began instead of counting down and redeploying → §19.3; #3 element damage against the 海嗣 felt weak, audit every operator's and enemy's element amounts and gauges → §19.5 (enemies, the gauge pipeline) and §19.6 (the player side); #4 an enemy held between two operators walked off when the rear one died instead of the front one taking it over → §19.2; #5 the enemies' target order differed from the official → §19.1; #6 act2 m01's normal field had its two wind lanes but no blower machines, which showed in the boss round instead → §19.7; #7 the tiles framed by the 1×3 pipes on the boss map are deployable officially, check every map → §19.7; #8 the bottom-left settings icon was deformed → §19.8; #9 on a long phone screen the shop bar covered the bench → §19.8; #10 the huge bosses could only be hit around one tile → §19.4. A follow-up message added #11 至简 should cost 1 (his 特质 says 购买价格为1) → §19.9. Normative lines rewritten with it: §3 range test, §5.1 carryState, §5.5 (initial deployment, DP / down, blocking, operator attacks, enemy AI, damage), §6.1 UNITE, §7 bosses; §18.3's isDown now includes FORCED_EXIT, and §18.1's notched-phone figures are superseded by §19.8. The integration also applied the workstreams' cross-checks (hasHp in the pipeline, 脆弱 vs 元素伤害, element healing, 淤困, 塑心 S2, splash as a 中点判定); they are listed in the sections they belong to. Three QA passes over the integrated branch then left residuals, fixed in their sections: 掠海漂移体's model crawls after its drop (§19.1); blocked-first and out-of-range selection are melee-only (§19.2 — superseded by §20.3); a forced-out operator's timer takes the battle-start redeploy effects (§19.3); the gauge intake's 5 % floor (§19.5); the fallback board draws the boss half in the boss prep (§19.7). The user's follow-up then settled two [ASSUMED] choices: the boss field's row-6 blowers do stand under the bench in the normal views (§19.7), and a forced-out operator's redeploy timer does restart in full (§19.3).
19.1 Hovering enemies and the enemies' target order (#1, #5) — sim/units.js, targeting.js, ai.js, Battle.js, content/enemies.js, bosses.js, kits, match/bot.js, render/units.js FORMS
- #1 cause:
float()only cancelled hits from non-'ranged'profiles and otherwise left the floater a ground unit, so 迷迭香 (攻击对小范围的地面敌人) shot and splashed it (5.2k damage in a sim run) and terrain, traps and 浮空 treated it as ground. Official (ba.float "无法被阻挡或近战攻击"; PRTS 术语释义 / 行动方式): the unit is unblocked and counts as a real air unit (算作空中单位) that keeps ground pathing. - Now
Unit.isFlyingis the one air-unit test:motionFLY, or an enemy flagfloat(近地悬浮) orlevitate(浮空); pathing, displacement tiles and 诱导 readmotion. canHitFly / groundOnly, splash, chain, the kits' "地面敌人" filters, ground terrain and traps skip air units; 失衡免疫 is the flagnoDisplace; 静态刚体 (every air unit of the mode except “炎佑”, plus 昆图斯) is datastaticBodyand never displaced; 守墓石像 (statue and flight) and 喷气人 (flight) are 失衡免疫 (§21.9); 浮空 is refused only on data flyers and already-levitated units (PRTS 行动方式), so 阿罗玛 can lift a floater. Blocking follows (§19.2): only blockFly units block air units. - Enemy kits: 掠海漂移体 — 晕眩 / 冻结 / 沉睡 → 爬行模式 for good (0.5 s stun), then it only attacks its blocker; 不会攻击飞行单位. Its model follows (integration QA: melee operators hitting an enemy that still looked airborne): the sim's fx
formcrawlswitches the view to the skeleton's crawl clips (*_02, after itsChangeclip) —render/units.js FORMS,spine.js setForm, kept on the unit info for a view built later (a view built mid-battle starts crawling: UnitInfoform, §21.4). 吉兆飞鳞 — 晕眩 / 沉睡 → 8 s 晕眩模式 (it stays down while still asleep), a freeze grounds it when the freeze ends, its sprint is the final ×3. Of the 32 official floaters only these two hover for good in this mode; 喷气人 hovers for its 7 s 飞行模式 (§21.9) (the bot'sHOVER_KEYS;fieldModelcounts them as air routes on ground paths). - Skill effects (per PRTS 备注; the official per-ability targetMotion is not in the data): ground-only — 隐德来希 S2 血镰, 归溟幽灵鲨 拥抱自我, 琳琅诗怀雅 S3, 乌尔比安 S1 [ASSUMED: a 捕网 like 雪雉's]; PRTS 可对空 — 德克萨斯 S2, 忍冬 S2, 焰尾 S2, 缄默德克萨斯 S3, 凛御银灰 S2, 玛恩纳 S3 (his melee skill attacks:
targeting.canHitFly; §21.8), 锏 S3 归于宁静 (added by §20.3); no 对空 note, [ASSUMED] to hit air — 风丸 折纸生花, 乌尔比安 S3, 缄默德克萨斯 S2, 余 S2, 见行者 S2, 耀骑士临光 不畏苦暗 and the token appear bursts (“耀阳”, 沙之碑, 迷迭香的战术装备's stun, 纸偶). A stun / freeze / sleep from any of them drops a hovering 掠海漂移体. - #5 official: PRTS 索敌 (敌方) "阻挡→特殊优先级→仇恨值(更容易被攻击→…→最后部署的目标→不容易被攻击)→最早出现"; PRTS 卫戍协议/帮助 "按从上到下>从左到右的顺序部署。优先部署干员,随后为召唤物", the right-hand boss player mirrored.
- #5 changes:
Battle.startdeploys operators row-major (by column since 0.1.3, §23.23), then summons;aggroSeqranks every start-of-battle summon after all operators (deploySeqstays the deployment identity). An enemy's own rulee.profile.canTargetfilters its candidates before the sort (萨卡兹枯朽战车 no longer idles when its preferred target stands on high ground; it, 掠海漂移体 and “萨科塔之眼” ignore flying allies). Special priorities break ties by taunt, then latest deployed (targeting.js aggroCmp): 铳 / 昆图斯 highest DEF, 胄 highest / lowest ATK, the 碎铳之簧 bounce, 唱沙 / 沙狱 max HP, “自在” nearest. Kept (PRTS 作战机制 §隐匿 "我方干员并不会因为阻挡而解除隐匿"; 索敌的概念): a stealthed ally (隐匿 / 迷彩 / 排气格栅) is attacked only by the enemy it blocks (迷彩 is its own flagcamou, not 隐匿 for 隐匿 conditions, §20.2). [ASSUMED] "从上到下>从左到右" = row-major (superseded in 0.1.3: a column scan, §23.23); on a shared field (联防, boss) the players' i-th operators deploy together, then the summons (it used to be player after player, which made the leader prefer the later player's pieces). - Tests:
test/sim/playtest5-targeting.test.js(18; 13 fail on 731a01c),test/content/enemies_bosses.test.js(吉兆飞鳞),test/match/bot.test.js(a hovering wave counts as air),test/render/forms.test.js(the crawl clips); browser (opt-in)test/render/forms.browser.test.js(the real sim feeding the field view).
19.2 Blocking by contact radius, and the hand-over (#4) — sim/constants.js BLOCK_RADIUS, Battle._checkBlock / blockedTargets, ai.js
- Cause: an operator blocked only an enemy standing on its own tile (§5.5's old "|dx|,|dy| ≤ 0.5"). An enemy that passed a full front operator was blocked by the rear one, on the rear tile; when the rear operator died, the front one — which the enemy still overlapped — never took it.
- Official (PRTS 游戏数据基础 §阻挡半径; 作战机制 "中点判定…案例:阻挡"): contact = the enemy's position within the blocker's radius of its centre, compared as squared distances — ground 0.70709997 (² 0.49999037), air (起飞 / blockFly vs flyers) 0.8944, devices (阻隔工事 / 障碍物) 0.4472.
- Now (§5.5 Blocking): every tick, moving or not, an unblocked blockable enemy is blocked by the nearest operator in contact that has free capacity for its weight — the front operator takes over when the blocker dies, is withdrawn or is stunned (
stunreleases its enemies); an operator whose capacity frees up grabs an overlapping enemy; an enemy that finds no room walks on. A head-on enemy stops at contact, ≈ 0.71 tile from the blocker's centre, outside its tile (the small official deceleration is not modelled). A knocked-out or undeployed operator never blocks. - Consequences, both official: "可以选择且优先选择阻挡单位" (PRTS 选择器: the selector of units whose range is smaller than their block radius) — a melee blocker (data
positionMELEE: 要塞 / 领主 / 哨戒铁卫 included; every blocker since §20.3) may always target the enemies it blocks, in range or not, and targets them first (Battle.blockedTargets→ai.js acquireTargets,targeting.js sortEnemyTargets, the DEFAULT skill trigger — PRTS 卫戍协议/帮助 "敌人被近战干员自身阻挡"); 瑕光 S2 also sleeps the enemy she blocks (PRTS 备注). A rear operator whose range ends at the blocker's tile no longer reaches the enemy held in front of it (a regression sweep of 143 blocker × skill pairs: own-tile defenders 0.96–1.05×). - Ranged operators on melee tiles (卫戍协议 "所有行动内远程干员可部署在近战位") — superseded by §20.3: the integration QA had made them target by their range alone (PRTS 索敌的概念 "我方索敌优先级:阻挡(近战限定)"; "远程位干员通常而言不会优先攻击自己阻挡的目标;甚至如果这个目标被阻挡在该干员的身后…无法攻击到这个敌人"): no blocked-first priority, no out-of-range target, no skill cast for the enemy they held. The user's rule after playtest #6 — "阻挡了就一定要能打到" — makes every blocker hit what it blocks.
- 灰毫 S2 专注轰击 — superseded by §20.2: v2.4 read the 下半 重装 special strategy "受到伤害时释放技能" as skill 1 only (BWIKI "重装职业干员 技能1,受到伤害后自动释放"; the data row has
skillIndex 0, with-1as the all-skills sentinel — research 03 C1) and left S2 DEFAULT. The class rows now cover every MANUAL skill of the class, so 灰毫 S2 is TAKE_DAMAGE (the counter-evidence stays recorded in §20.2) — and DEFAULT again since §21.29, a deliberate deviation the owner decided (with 灰毫 S1). - [ASSUMED] the nearest blocker wins, ties by scan order.
- Tests:
test/sim/playtest5_blocking.test.js(16, incl. the user's case on act2 m01 with real operators, the ranged blocker and 银灰 as a melee-position ranged attacker), adjusted geometry inkits_alt_t5/engine-requests/enemies_bosses.
| Contact | Official (PRTS) | v2.3 | Now |
|---|---|---|---|
| ground operator | d² < 0.49999037 (r 0.7071) | the enemy's tile = the operator's tile | d² < 0.49999037 |
| air (blockFly / 起飞) vs flyers | r 0.8944 | same tile | d² < 0.79995137 |
| device (阻隔工事 / 障碍物) | r 0.4472 | same tile | d² < 0.2 |
| blocker gone / capacity freed | contact re-checked continuously | only on the enemy's own tile | every tick, nearest free blocker |
19.3 Knocked-out operators in 联防 (#2) — server/match/unite.js, sim/Battle.js, sim/constants.js FORCED_EXIT, render/app.js, render/units.js
- Cause:
unite.js uniteBattleOptsdropped every operator thatunitsEndreportedalive: false— and its summons — from the 联防 input (an old [ASSUMED] rule). Every client and the server build the unite field from that input, so the operator was missing for the authority, the partners, observers and theSP_COMBAT=serverfallback alike. - Official (PRTS 卫戍协议/帮助 §联防阶段): "部署完成后,将对应单位的生命比例、技力修改至与上一阶段结束时相同(召唤物仅修改技力,上一阶段为退场状态的干员强制退场)".
- Now: the operator is fielded with
carryState: { down: true }(§5.1); its summons are fielded as the board has them.Battle.startdeploys everyone (the initialdeployhook fires), then beforebattleStartsets its HP to 0 (the end-of-phase ratio, askill()) and withdraws it with removal reasonFORCED_EXIT='forcedExit'.Battle.isDownaccepts that reason, so it is in b.snapdownandfieldMetawith its ring, shows 0 HP on the live card, and redeploys by §5.5 (timer done, own tile free, DP ≥ cost) at full HP. The forced exit is no new knock-out: nokillhook, nodeathwith reason'killed', nodeathscount — 深海 / 不屈 revives, 崇高牺牲 layers and selfdead garrisons fired in the own combat. The client's'die'with that reason goes straight to the held knocked-down pose (UnitView.die(true): no fall, no burst, no sound). - The timer restarts at the operator's full redeploy time (70 s for most chess): the official setup carries only
hp/techper operator (research 09 §3HelpBattleInfo), and the user confirmed it after the v2.4 follow-up ("进联防复活时间确实是重新算" — a request to carry the remainder was withdrawn). A short 联防 may therefore end before it comes back (a real-sim reproduction ended at 28 s; in the integration QA's real matches 43 % of 1,564 knocked-out operators were still counting when their 联防 ended). Also [ASSUMED]: the forced exit happens beforebattleStart(since 0.1.3 the 阿戈尔 devour still counts the operator as standing for its marking — players' reports, owner's decision 2026-10-04); the timer is re-read after it, so a redeploy-time effect that starts with the battle covers it (机变 征召 "所有干员的再部署时间-50%": 70 → 35 s, as for a later knock-out — it read 70 s before the integration QA), while 征召's own row check ("场上至少有一行存在3名干员") does not count it [ASSUMED]. 召唤物仅修改技力: since 0.1.3unitsEndlists the board's summon pieces too and they carry{ sp }(one off the field at the end enters fresh [ASSUMED]); until then summons entered 联防 fresh. - Tests:
test/match/unitedown.test.js(both combat modes, both helper halves, the real sim),test/sim/downelem.test.js(incl. 征召),test/render/downring.test.js,test/match/combat.test.js; browser (opt-in)test/render/unitedown.browser.test.js.
19.4 Huge bosses: hit area, 自缚 (#10) — tools/build-data.mjs HIT_AREAS, sim/body.js, content/bosses.js SELF_BOUND, render/pick.js
- Cause: every range test took the enemy's position tile, and the data had no body size, so a boss at (3,10) was hit only by ranges covering (3,10).
- Official (PRTS 天赋): "巨型单位:受击判定区域为长4.95、宽2.95的长方形,向上偏移1.0" (管·隐秘核心 also 向右偏移1.0; PRTS盟约记录 gives the season's 阿利斯泰尔 and “萨米的意志” the same area); PRTS 作战机制 "巨型BOSS单位的每一个占据的格子都可以让其本身通过格子判定". The same talent lines list 自缚 (PRTS 异常效果: 无法移动, not recognised as 束缚) and 无法被阻挡. 铳, 卢西恩 and the boss parts are regular units.
- Now: enemies.json
hitArea {w, h, dx, dy}(by prefabKey);sim/body.jsis the one helper behind every range test on enemies (the engine tile index,enemiesInKeys,unitsInGrid,enemiesInRadius, nearest / farthest priority, kits, tokens, bonds, professions,support.onKeys). A huge enemy occupies every tile its rectangle overlaps — 5 × 3, rows 3–5 × cols 8–12 around (3,10): the whole fence-framed pipe block, so an operator on the 1×3 fence tiles (§19.7) hits it. Radius rules measure to the rectangle (0 inside) [ASSUMED: PRTS calls collider tests the most common]; splash around a struck target is a 中点判定 (PRTS 作战机制 "中点判定…案例:阻挡,酒神1天赋的1.3溅射半径"):enemiesInRadius(x, y, r, true)counts every enemy by its position — the engine's profession splash and aftershocks and the kits' target-centred splash (焰影苇草's 灼痕 burst, 新约能天使 火力电台, 仇白 S1) — so a splash on an escort 0.53 tiles from the rectangle but 1.41 from the boss's centre no longer reaches the boss. Its position stays its 判定中心 (projectiles, its own attacks, splash centred on it, distance-scaled numbers). Integration pass: the reaperrange front line, 凛御银灰 S3's lateral line and the 香槟炸弹 tile trigger are body-aware too; movement, pathing, terrain, tile-entry tracking, placement heuristics and range keys taken from blocked enemies stay on the position (a huge enemy never moves and is never blocked). - 自缚: the co-op 昆图斯 walked its official route (3,10) → (2,2) off the block and leaked at ≈ 280 game s.
SELF_BOUND(exactly the 7 hitArea keys) gives them a persistentnoMove+unblockablebuff at spawn (the mirrored co-op copy too). - Picking (
pick.js AREA_PICK): a press on the hit area or inside the drawn body box selects the boss, counted as 1 tile away, so an ally on the pressed tile or a nearer regular enemy wins. - [ASSUMED] 胄·隐秘核心 keeps the upward offset although its PRTS section omits it: enemy_database gives
enemy_9013_acstmk_2the prefabKeyenemy_9013_acstmk— one prefab, one collider — while the 管 copy that PRTS lists with a different offset has a prefab of its own (enemy_9021_acduml_2); ask at the next playtest if the hidden round feels off. The rectangle is fixed in map space (not mirrored). - Tests:
test/sim/playtest5_bossbody.test.js(12, incl. the centre-point splash),test/render/pick.test.js, the Final Assault leak regression intest/sim/battle.test.js; browser (opt-in)test/render/bossbody.browser.test.js.
| Boss | Hit area (PRTS) | Tiles at its spawn | Talent |
|---|---|---|---|
| 假想敌:胄 (+ 隐秘核心 copy) | 4.95 × 2.95, up 1.0 (copy: [ASSUMED] same) | (3,10) → rows 3–5 × cols 8–12 | 自缚、不可阻挡 |
| 假想敌:管 / 管·隐秘核心 | 4.95 × 2.95, up 1.0 / + right 1.0 | (3,10) / (3,9) → rows 3–5 × cols 8–12 | 自缚,无法被阻挡 |
| 盐风主教昆图斯 | 4.95 × 2.95, up 1.0 | (3,10) → rows 3–5 × cols 8–12 | 自缚,无法被阻挡 (walked and leaked before) |
| 阿利斯泰尔 / “萨米的意志” (卫戍 versions) | 4.95 × 2.95, up 1.0 | (3,10) → rows 3–5 × cols 8–12 | 自缚,无法被阻挡 |
| 假想敌:铳, 卢西恩, the parts | none (point) | its tile | 铳 不可阻挡 only; 卢西恩 blockable |
19.5 Element gauges and enemy element damage (#3, enemy side and pipeline) — sim/damage.js, content/enemies.js, content/bosses.js
- Verdict: official behaviour, not a remake bug. Every 海嗣 has 损伤抵抗 (
epResistance) 0 and 元素抗性 (epDamageResistance) 0 (in data/enemies.json only 转译基底·α has 损伤抵抗 10); gauges are 1000 (leaders 2000) and never decay; no 海嗣 talent changes element intake; every enemy element attacker deals ATK × its official ratio per hit × the round's ATK multiplier (47 checked). Boss levels override some values, which the runtime applies: 卢西恩 ATK 700 (solo 600) and 不祥幻影 200, × 0.12 per normal hit (skill 0.2) in boss_5 (level_act2autochess_h07_05(_s)); 盐风主教昆图斯 480 (solo 380) in h07_04. A blocked 底海滑动者 at 险境 R5 bursts 神经 on its 25th hit, ≈ 50 game s. "Weak" also has a player-side part — §19.6. - Official rules (PRTS 元素 / 游戏数据基础 / 伤害分类; gamedata_const termDescriptionDict): gauge gain = 损伤值 × max(5 %, 1 − 损伤抵抗 %) (PRTS 游戏数据基础: DMG_e "目标受到元素损伤时也可以使用该公式计算,只需要将 D 值改为目标的损伤抵抗即可"), then the 元素损伤倍率 effects; 元素伤害 = max(A(1 − 元素抗性 %), 5 %A); 元素脆弱 ("受到的元素伤害提升") raises 元素伤害 only; 脆弱 ("受到的物理、法术、真实伤害提升") never touches 元素伤害; bursts are 无来源 ("元素爆发通常造成无来源的伤害"; 无来源 = "无法被追溯伤害来源", yet the kill is traced).
- Fixed (W3b): 元素脆弱 is the mod
elementalTakenMul(元素伤害 only; it used to share the gauge rateelemTakenMul, so 炎佑's 20 % sped up fills instead of raising bursts); 元素抗性 reduces'elemental'with the 5 % floor (expectedFinalmirrors it); bursts carry DamageInfosourceless— no damage-dealt multiplier or penetration of the filler, and thehit/damaged/fatalhooks seesource: nullpluscredit(stats, kill and boss-pool share keep the filler; a real 深巡 bursting a 海嗣 dealt 10500 instead of 7000, 精准狙击镜 made a 侵蚀 burst 6500 instead of 5000); target-side reactions with no source condition readsource || credit(sharing: 圣杯, 盲信之誓, 余音; 抵挡: 星熊 战术装甲, 拉普兰德 日晷). The enemies' 凋亡 虚弱 is 50 % × remaining ÷ 15 s each tick (it ran 1 s ahead); 麻痹免疫 is honoured (假想敌:铳, the 胄 parts); 淤困's host burst spreads 1000 (was 780); 灼藤's first attack covers the 3×3; 节日爵士乐手 channels on one locked target (20 % arts + 10 % burn every 0.5 s, 21 ticks, ends on 沉默, no re-cast mid-channel). Side effect: 隐德来希's 心烛 cancels bursts on a candle ("只受隐德来希攻击的影响"). - Integration pass:
damage.js hasHp(alive, HP > 0; a boss: its pool) is the one predicate —applyElementrefuses a target with no HP left, so no rider in a killing blow'sdamagedhook can burst a corpse (the content copies in tier5 / tier6 / items / tokens now import it);'elemental'damage skipsdmgTakenMul(脆弱 and the other "受到的伤害±" effects [ASSUMED: they share that multiplier]) — only 元素脆弱 scales it; element healing (reduceElement) lowers every element type by the amount on its own (PRTS 菲莱 备注: 清除元素损伤 = "一次等同于自身最大元素值的全类型元素损伤治疗"; it healed the amount across all types in total); 淤困's "受到的元素损伤提高至130%" is anelementHitmultiplier at priority 20 like every operator-side "受到的元素损伤±", before a 损伤屏障 (it was theelemTakenMulmod, applied after the barrier). - Verified unchanged: the burst effects on both sides; cooldowns 10 / 10 / 15 s (enemies' 侵蚀 8 s), locking then resetting every gauge; leaders take no 侵蚀 in the Final Assault (PRTS 盟约记录); 炎佑's aura = 20 % 元素脆弱 within 1.5. The gauge's look is the official icon + white bar since §20.8 (it differed in v2.4). [ASSUMED] 爵士乐手's first tick one interval after the cast. The gauge intake's 5 % floor (
elementIntake, integration QA) is unreachable with the data (损伤抵抗 ≤ 10). - Tests:
test/sim/playtest5_elements.test.js(18),test/content/playtest5_followups.test.js(pipeline guard, 脆弱, element healing),combat.test.js,test/content/{kits_t5,tokens_devices,playtest4_summons,enemies_bosses}.
| 海嗣 (user's case) | Official ratio | Per hit / s at base ATK | Hits to a 1000 gauge | Time |
|---|---|---|---|---|
| 底海滑动者 (ATK 280, BAT 2.0) | 15 % ATK 神经 | 42 | 24 | 48 game s |
| 富营养的滑动者 (360) | 15 % 神经 | 54 | 19 | 38 game s |
| 骨海漂流体 (250) | 20 % 侵蚀 | 50 | 20 | 40 game s |
| 掠海漂移体 (500, BAT 4) | 50 % 侵蚀 | 250 | 4 | 16 game s |
| 元核孽生者 (400, season) | 5 %/s 神经 in r 2.5 (season override; PRTS's base page: ATK 500, 10 %) | 20 / s | — | 50 s |
| 深溟巢涌者 (140) | 5 % ATK 神经 per damage output | ≈ 7 / s | — | ≈ 143 s |
19.6 Element damage of the player side (#3, operator side) — sim/content/kits/tier{2,3,5,6}.js, tokens.js, items/battle.js
- Audit: only these deal 元素损伤 from the player side in 下半 — 盟约·辅助干员 (band 优等生), 菲莱, 烛煌, 妮芙 (T5 / hidden T6), 余, 塑心, 炎佑 (炎 ×6), 灼燃维式重锤 (+ 蒸汽之心); no bond, band, garrison, 机变 card or device deals any. Every number matched its blackboard (character_table at the chess status, battle_equip_table modules); the discrepancies were form, trigger and scope. "N%攻击力的…损伤" = N × ATK, "伤害N%的…" = N × the damage just dealt.
- The fix that matters most: 盟约·辅助干员's 迭代元素 used to pick the "first element not bursting" — a burst locks every gauge, so she only ever filled 神经. Now every damage she deals (HP > 0; PRTS corrects the timing to "造成伤害时") attaches 18 % ATK of 神经 → 灼燃 → 凋亡 (
PITHST_ELEMENTS); the element applied first bursts, so alone she bursts 神经 and in a team completes the 灼燃 / 凋亡 gauges 余 / 塑心 build — in the reviewer's runs the team's first 灼燃 / 凋亡 burst came ≈ 40 % sooner (9.0 s vs 16.0 s; 5.0 s vs 8.2 s). Teams without her are unchanged. [ASSUMED] the three apply together (the reading of the text). - Killing blows: element riders inside
damagedhooks run beforebattle.kill, while the target isaliveat 0 HP; a lethal hit used to burst the corpse (a second fatal, 烛煌 熔点引爆's heal and 350 % hit, 妮芙's stack). The riders check HP left and, since the integration,applyElementrefuses it (§19.5). - 塑心 S2 (integration): PRTS "受影响的干员即将造成伤害时,因该效果造成的凋亡损伤生效于当次触发的伤害之前,该造成的凋亡损伤的来源始终为塑心" — the rider is a late
hithandler, so the 凋亡 lands (and may burst) before the damage, a killing blow included; a hit dodged or absorbed after that still carried it [ASSUMED]. 纯烬 氤氲 is keyed per source (agoat2:mist:<id>): two 纯烬 keep their own stacks, ATK and heal source. - Tests:
test/content/playtest5_elements.test.js(9),test/content/playtest5_followups.test.js(塑心 S2 order, 氤氲 per source),kits_t1t2.test.js(1_15, 哈洛德 with 侵蚀),kits_t3.test.js(菲莱).
| Source (normal / elite) | Official | v2.3 | Now |
|---|---|---|---|
| 盟约·辅助干员 迭代元素 | 18 % ATK each of 神经(优先)/ 灼燃 / 凋亡 per damage dealt; RIT-X 21.24 % vs 精英 / 领袖, talent only | per attack hit (dodged too), 神经 only | fixed: all three per damage > 0 |
| 菲莱 S2 冥河诅咒 | when attacked (cd 2 s): 110/140 % ATK arts + 25 % ATK 凋亡 to ground enemies in x-4 (3×3) | radius 1.5 | fixed (3×3, body-aware, 近地悬浮 = air) |
| 菲莱 PRP-X | "阻挡敌人时,自身造成的元素损伤提升15%" on every fill she deals | S2 blast only | fixed (all fills, a hammer too) |
| 受到的元素损伤降低: 菲莱 神河谕使 / 哈洛德 我即军营 / 纯烬 火山灰疗愈 | −10 % (+2 SP on 凋亡 even for 0) / −12/15 % for allies over half / −12/14 % (×talent_scale in S3); before the 损伤屏障 | the 元素脆弱 multiplier (also cut 元素伤害) | fixed: elementHit at priority 20 |
| 哈洛德 trait / T1 | "元素损伤最严重" and the over-half checks | 侵蚀 ignored | fixed (侵蚀 counts) |
| 纯烬 氤氲 | per stack / s: HP 10 % ATK, element 10 % × trait ratio (5/6 % ATK), cached ATK | element 10 % ATK (2×) | fixed; per source (integration) |
| 塑心 S2 安魂的弥撒 | self + top-ATK ally: each damage + 15/20 % of her ATK 凋亡, before the damage | after the damage, skipped on a killing blow | fixed (integration) |
| 塑心 S1 / T1 / T2 / RIT-X / RIT-Y | 85/95 % ATK 凋亡; 10 % ATK/s; 精神逆构 ×1.2 (elite ×1.33 + 5 % 元素脆弱 in the burst); ×1.18 vs 精英 / 领袖; RIT-Y 音乐家的旅程: field-wide ×1.2 + 150 元素伤害/s during the burst, 10 % 元素脆弱 in range | same | OK |
| 余 T1 / S3 / S1 / T2 / PRP-X | 40 % ATK arts + 12 % ATK 灼燃/s on blocked enemies; 火墙 6/7 %; 30/40 % ATK on the attacker; T2 1.5 % max HP of HP and element per s with ≥ 4 operators on the field (elite ≥ 2; elite ≥ 4 also +14 % arts vs 灼燃 bursts); ×1.15 while blocking | same | OK |
| 烛煌 S1 / S2 / S3 / T1 / PRI-X | 30 % of the damage as 灼燃; +60/70 % ATK 元素伤害 on burst targets; 熔点引爆 350 % + 12 % heal; ×1.1 vs burst targets | same | OK |
| 妮芙 S2 / T1 / PRI-Y | 18/22 % of her arts damage as 凋亡; 失魂 40 % ATK 元素伤害/s (PRI-Y 烦恼问诊匣: every 0.7 s, SP +0.2/s) | same | OK |
| 炎佑 / 灼燃维式重锤 | 20 % ATK 灼燃 per damage, 20 % 元素脆弱 aura r 1.5 / 10 % of the arts damage (蒸汽之心: own hammer ×2) | same | OK (元素脆弱 now raises bursts, §19.5) |
| any rider on a killing blow | a dead unit takes no element | burst the corpse | fixed (hasHp + pipeline guard) |
19.7 Maps: blower machines, boss-field deployment (#6, #7) — render/board3d/layout.js, render/tiles.js, render/app.js, server/match/board.js, Match.startRound / bossGroupOf / deployFieldOf, ui/gameLogic.js, ui/fallbackField.js
- #6 cause: the 3D board builds only the current view's area, and for the normal / 联防 views it stopped at row 12. act2 m01's own blowers (#001 / #002, the partner's #101 / #102) stand on the separator row 13 facing DOWN into rows 12–10 (level_act2autochess_m01 predefines: (13, 5/9/13/17) and (6, 5/9/11/15), all DOWN, none hidden), so they were never built; the Pixi airflow streaks and the sim were right. Now the normal / 联防 field rects are rows 6–13 and one device rule serves both boards: every active device standing on a built tile is drawn (3D
stageDevices, 2D_stageDevicesminus its faded margin). Normal / prep: (13,5) and (13,9) above the lanes, and the boss field's (6,5) / (6,9) on the wall under the bench; 联防 adds (13,13) and (13,17); 最终攻势 / 隐秘核心: (6,5), (6,9), (6,11), (6,15). - #6, the boss field's machines under the bench: the integration QA hid the row-6 blowers (6,5) / (6,9) in the normal / 联防 views (reading the official shop camera as putting that wall under the shop bar); the user's follow-up, first-hand — "吹风机原版道中也该有" — puts them back: the official normal rounds show them, so every view draws every device on its built tiles again (the rule above, as the workstream had it). Their airflow covers drawn tiles only, so it shows in the Final Assault view. Unchanged (cosmetic, pre-existing): the official normal view also shows the partner's half beside the own field (its row-13 machine (13,13) at the top right); the remake builds the own half only.
- #7 cause: every legality check of a boss round's prep read the normal field (board (10–12, 8) = tile_forbidden) —
board.js buildDeployMap, the client'scanPlace/ highlights, the bot and the invariants — while the pieces stand on the player's half of the boss field, where (3–5, 8) and (3–5, 12) are tile_fence_bound (low ground, buildable ALL, fly-only passable). NowbuildDeployMap(stage, { field })takes'normal' | 'bossL' | 'bossR'(board row = boss-field row +BOARD_ROWS_ABOVE_BOSS7; the right-hand player mirrored col c → 20 − c, research 09 §1.2 ConvertChessPositionInfoToBossMap);Match.bossGroupOfis the one pairing lookup (deployFieldOf,nextEnemiesFor, the bot);Match.startRoundplans the round's enemies beforePlayerState.startRound, so R14 → R15 re-checks boss-half pieces on the boss field (they used to be withdrawn with 地形变化; rngWaves is used only there, no random stream changes); a quit before the boss fight re-pairs and re-checks at once; the client'sgameLogic.deployFieldOffeedsplacementContext({ field }). Operators on the fence tiles hit the huge boss (§19.4) but block no ground enemy there (§21.23); ground enemies cannot walk onto those tiles. - Audit (all 11 stages × normal / 联防 / boss L / boss R, server = client = builder deployTiles = the raw level files, except the 深水区 refusal and the 特制水上平台 exception of §21.3): the only wrong tiles were act2 m01's six boss tiles; on every stage normal-legal ⊆ boss-legal and boss L = boss R, also under every map card, so R13 → R14 and R14 → R15 move nothing. act2 m01 now deploys 13 melee + 3 ranged-only tiles on the normal board and 16 + 3 on each boss half. Devices: 3D = 2D for every stage × view; act1 m05's 特制水上平台 are ×64 (16 per normal half, 32 on the boss field). The DOM fallback board (
ui/fallbackField.js, no WebGL /?render=fallback) draws the player's boss half in the boss prep too —screens/game.jsnow hands it thebossPrepcamera, which it maps throughgameLogic.fieldTileon the own board's layout (integration QA: it drew the normal field's walls under legal highlights). Limitations:Match.unitStatspreviews on the normal field (stats only); 射击台 stays ranged-only [ASSUMED]. - Tests:
test/match/playtest5-deploy.test.js(incl. boss → boss R14 → R15 and the fallback board),test/render/playtest5-maps.test.js(every device on a built tile in every view, the row-6 machines under the bench included),test/ui/playtest2.test.js; browser (opt-in)test/render/board3d.browser.test.js(area key '6,13,0,10').
19.8 Settings gear; the shop bar over the bench on phones (#8, #9) — ui/gameComponents.js, render/projection.js, ui/fieldHost.js, render/app.js, css/devices.css
- #8: the ⚙ was a hand-written SVG path with uneven teeth on a rim that was not round (the extracted autochess UI has no gear sprite; 交流 is the official
emoji_btn, 📖 / ⛶ were clean).gearPath()now generates a regular 8-tooth gear (tip r 10, root r 7.4, hole r 3.3); on phones the corner glyphs aremax(.28rem, 17px), half their 34 px buttons (were 11 px). - #9, the user's viewport: the screenshot is 2772×1272 px with the page right of a 141 px cutout band; the page's own markers give DPR ≈ 3.48, so 756×366 CSS px (≈ 2.07:1) — the mock rendered there puts the exit and 准备就绪 buttons on the screenshot's pixels within ±1 px.
- Cause: the official shop camera (
left_shop_camera_param, boss*_boss_shop_*) fits the prep area exactly at 16:9 (bench near edge 811 px vs the shop bar at 813 px at 1920×1080), while the remake's HUD is in rem with a 40 px floor (css/theme.css), so below 432 CSS px of height it is relatively taller: v2.3 left the worst bench pad 54 % free at 756×366 (Final Assault prep 49 %), 71 % at 844×390, 49 % at 800×360. - Now: the view receives the HUD bands,
hudBands(kind, size)(top = the HUD layer's safe-area top + 2.16rem, the bond strip; bottom = 2.64rem + 3 px, the shop bar; the rem values copy the CSS, which a test checks), andpresetCamera('prep' | 'bossPrep', …, { hud })runsclearHudon the band from the bench's near edge to the back row: the official camera when it fits (0.5 px tolerance), else the smallest pan, else a zoom-out about the centre — a 2D pan / zoom of the image, so picking, the three.js camera and flights stay consistent. Every desktop prep camera is unchanged; the bar stays on the bottom edge (§18.1); the collapsed shop kept the camera — superseded by §21.27 (public issue #5): folding the shop moves the own prep board to the official shop-collapsed camera (left_prepare_camera_param,presetCamerashop: false) clear of the folded shop's band (hudBands(kind, size, { shop: false }): the tab and the corner buttons' hit areas), and unfolding back to the shop camera. Accepted: an armed shop card rises 4 px above the bar on a phone and covers ≈ 3 px of the pads' near corners (≈ 11 px under the official camera at 1920×1080). [ASSUMED] the bond strip's bottom (2.16rem, measured 2.14–2.15rem); the zoom anchor. - Tests:
test/render/projection.test.js(HUD clearance suite),test/ui/playtest5-ui.test.js; browser (opt-in,SP_E2E=1)test/ui/playtest5-ui.e2e.test.js(per-tile coverage at 16 viewports, the armed card, the gear).
| Viewport (CSS px) | Worst bench pad free, v2.3 → now (prep / Final Assault prep) | Camera now | Tile px |
|---|---|---|---|
| 756×366 (the user's Android) | 54 → 100 % / 49 → 100 % | ×0.844 | 41.5 → 35 |
| 798×366 + 41 px inset (same phone over the cutout) | 53 → 100 % / 49 → 100 % | ×0.844 | 41.5 → 35 |
| 844×390 (19.5:9, with or without notch) | 71 → 100 % / 66 → 100 % | ×0.905 | 44.2 → 40 |
| 800×360 (20:9) | 49 → 100 % / 45 → 100 % | ×0.828 | 40.8 → 33.8 |
| 915×412 / 914×411 / 960×411 | 83 → 100 % / 83 → 100 % | ×0.95 | 46.7 → 44.5 |
| 1920×1080, 1680×1050, 1560×1040, 2560×1080 | 100 % | official | unchanged |
| 1280×720 | 100 % / 96 → 100 % | official (Final Assault prep: pan up ≤ 2.8 px) | unchanged |
19.9 至简's price (#11) — sim/content/garrisons/meta.js SERVER_CHESS_PRICE, server/match/effectsMeta.js dispatch
- What the user saw: 至简 (Ⅲ) cost 2 资金; the user remembered 1. His 特质 garrison_13 reads 购买价格为1, but
bb.priceis 2, and the handler set the price tobb.price("blackboards are authoritative"). - Official: two chess carry this 特质 (
effectTypeSERVER_CHESS_PRICE) and both texts say 购买价格为1 — 至简 (Ⅲ, tier price 3,price2) and 红豆 (Ⅰ, tier price 2,price1, hidden in 下半). Only "bb.priceis the discount off the tier price" gives the text's 1 for both (3 − 2, 2 − 1). - Change: the handler lowers the price by
bb.price(ctx.modifyPrice(-p)), andEffectDispatcher.dispatchruns the priced chess's own 特质 first ononPrice(before globals / band / bonds), so 远见's discount and strategy caps act on the lowered price: 至简 stayed at 1 with 远见 150 (a floor of 1 then assumed for 远见; since 0.1.3 the −1 has no floor but 0, so 至简 drops to 0 — owner's decision 2026-10-04). The shop card keepsbasePrice3, so the price shows in the discount tone. docs/META.md §2.2 / §2.3 say the same. - Tests:
test/content/garrisons_meta.test.js(every SERVER_CHESS_PRICE owner: tier price −bb.price= the text's price =priceOf; buying 至简 costs 1; a global modifier already sees 1; 远见 150 takes 至简 to 0 since 0.1.3).