61 KiB
DESIGN §30 — 0.2.5
Part of DESIGN.md. This chapter records 0.2.5, released on 2026-10-12.
30. Community follow-up
30.1 Fixed gifts and already displayed operator offers
Reports #488/#491/#495, PR #496 by @2321Robin, and the owner's Muelsyse/Yela report: the shared equipment cap added in 0.2.4 incorrectly prevented fixed acquisition gifts. 路标档案馆:路标月报#2 states both that equipment from any source occupies stock and that these operators' fixed gifts are unlimited. The owner confirmed on 2026-10-11: gifts still occupy stock, but exhaustion must never prevent the gift.
GameData.isUnlimitedGiftItem derives the families from SERVER_GAIN_EQUIP. The actual gift passes
requirePool:false through effectsMeta to acquireItem; only those families bypass availability. Ordinary and
advanced copies still count as 1/2. This covers Carnelian, Beeswax, Muelsyse, Kjera and Swire, and the same fixed
family awarded by the Damazti strategy. It does not make every strategy's equipment unlimited. Shop and item
reward purchases, random grants and 整备 upgrades keep their availability checks. When ownership exceeds the cap,
removing one copy need not immediately reopen stock. The proposed PR's zero-occupancy model was not adopted.
#498: the same video's approximately 2:50–2:57 example describes nine owned 溯光星源 plus one
already displayed card in each of four shops, allowing thirteen owned copies. Shop operator purchases therefore
do not recheck availability; acquireChess records the full deficit and later rolls still exclude exhausted stock.
The owner explicitly extended this behavior to already displayed operator reward offers; that extension is a
maintainer decision, not footage directly establishing every reward type. Item purchases/rewards remain bounded.
Funds, full-hand checks, per-offer sold flags, merge accounting and selling returns retain their ordinary rules.
Tests: test/match/community-round30.test.js, shared-stock.test.js, garrisons_meta.test.js.
Normative rules: META shared inventory and helper contract; design/match economy.
30.2 Measured acquisition subsets
PR #497 by @2321Robin, same original research video's rule table: Rockrock's subset weights are ordinary hammer
10; tremble/undying/haste/burn hammer 2 each; limited accelerator 1; shield drone 4; laser emitter 4. Flametail's
subset is Wild Mane 10, Ashlock 10, Fartooth 3. These are weighted subsets, still constrained by shared stock.
The generator owns these tables; data/choices.json is regenerated offline. Tests cover the table and exhausted
eligibility rather than asserting that subset draws ignore ownership.
30.3 Damage and target-selection corrections
- Clementia S3's erosion-burst listener now requires an enemy target. The generic targetability helper does not check allegiance, so a friendly erosion burst previously scheduled a shot. Enemy bursts retain the same behavior; the mode's existing boss erosion immunity is unchanged.
- Horn follows PRTS 号角: blocked targets first, ordinary ranged targets next, then a selectable ground target on her own tile. Her skills' automatic target check includes that tile. Other adjacent blind tiles do not become valid targets. Ordinary/S1/S3 melee attacks are single-target; S2 explicitly retains melee splash. Each shot captures its melee mode, so switching targets cannot remove splash from a shell in flight.
- The bounty 自制投石机 has normal splash radius 1.5 and three hits. The old radius 0.5 came from its 入戏 override, not its ordinary attack. Tests cover adjacent/diagonal allies and an outside target. No claim is made here about unrelated attack cadence.
- PR #502 by @Xinglan233: Vulpisfoglia's first qualifying damage instance marks the enemy and also deals 追凶's arts addition. Repeated damage does not extend the original window. Source: PRTS 忍冬.
- #489: PRTS 盟约记录 corrects both Kjerag Ice and Tear Gas from “攻击时” to “造成伤害时”. Only these handlers move to actual carrier damage, including DoT, skill and splash instances; bare attack events, elemental gauge fill, HP loss, zero damage and friendly targets do not trigger them. Existing probabilities/durations remain data-driven. Regression uses real Ascalon poison while she is disarmed.
Tests: community-round30.test.js, item-damage-procs.test.js, vulpis-first-hit.test.js under test/content,
plus existing item/operator/fortress suites. Normative rules: SIM and design/engine.
30.4 Client information and input
- PR #503 by @Xinglan233: a loadout containing only operator potential/cultivation changes is exportable.
- PR #505 by @Xinglan233: spectator
ownerOpsincludes resolved DIY selection, so bond members have correct identity. - #479 / PR #504 by @robll-v1, owner approved: before settlement, the HP tower shows the current HP and a separate pending-loss marker; settlement updates the number. This supersedes the earlier live pre-deduction display choice.
- #501 / PR #507 by @robll-v1, owner approved: a refresh request in flight disables another button/R request until it settles, including errors. This is a client interaction guard, not server-wide request idempotency.
- #484 / PR #494 by @YuriRestia, owner approved: bond tooltips list this match's banned visible members. Main text and list share one viewport-clamped, scrollable container; narrow screens stack the sections. Dynamic member changes update an open tooltip even if the displayed count stays equal. Long-press lists persist for reading and close on outside tap or Escape; ordinary tooltips keep their short behavior.
- PR #490 by @D-Roote: Korean UI corrections; the obsolete 3D failure key from the proposal is excluded in favor of the current recovery message. PR #499 by @YuriRestia: English Victoria text explicitly applies its bonus per item, through a narrow correction layer in the translation generator, retaining all placeholders.
Tests: refresh unit/E2E, loadout export E2E, match-info, watch-bonds, round30-tools E2E and i18n checks. Normative rules: PLAYING, I18N and design/client.
30.5 Developer simulation checker
PR #492 by @Cloudnyco, owner approved: /dev/sim-check.html replays generated BattleSpecs with the real browser
loader against Node traces. The corpus is the golden battle families, excluding full matches. The CLI generates
local ignored reference data and can run browser engines or compare an exported state block.
Review repairs: reject unknown/empty selections and unfinished runs; recover controls after start/step/result/dump errors; preserve negative zero and other special floats across JSON; verify shared content identity for specs, reference, runtime and data; reject stale inputs; escape reference text; allow long mobile result lists to scroll. Reports state the first detected interval, not an exact first divergent frame. State dumps can diagnose only their recorded fields; snapshot/deep-only differences may not expose a differing field. The bounded unit-state traversal and sampled snapshots do not prove equality of all memory, events or rendered frames. See SIM §11.
30.6 Rechecked reports without a gameplay change
Real browser loading verifies seven different Yan operators plus ordinary/elite Muelsyse carrying the converter and Yan item: count 9, tier 3 and two actual 炎佑. All 36 normal/advanced conversion-item pairings retain battle membership. The preparation/combat conversion feedback was not reproduced in these cases; this does not exclude an unreported configuration. Normal/hidden Helm missile tests confirm the existing shell and its 3×3 stun.
Lost Colossus and Zaaro exist in the source catalog, but the fixed bounty draw groups remain based on the earlier 22-match evidence. The owner chose to retain those groups until direct grouping evidence is found; catalog presence alone is insufficient to assign a group or probability. No AI formation change from #452, WebRTC #487 or built-in HTTPS #478 is included.
30.7 One visual clip per operator attack
#506: a single attack formerly kept an operator's animation looping for 1.4 attack intervals, allowing a second visual swing without another simulated attack. The owner approved playing one clip per real attack, retaining operator rhythm scaling, enemy speed rules, skill selection and damage timing. A queued wind-up can restart during the preceding attack's recovery; its actual attack consumes that preparation rather than replaying the clip. After recovery the actor returns to its base/skill-idle clip. With no look-ahead, operators still start at the strike frame: completing a late wind-up would delay the visual hit, so that separate policy is deliberately unchanged. Tests cover single/continuous fast attacks, real Provence metadata, skill idle and attack interpolation.
30.8 Xi invincibility and session recovery
#510: PRTS “自在” specifies ten seconds of invincibility after its five-second rebirth;
the linked status also excludes hostile selection. Xi opts into reborn.invincibleUntargetable for that same
window. The general invulnerable flag and other enemies' rebirth policies are unchanged. Attacks and
target-requiring automatic skills wait; presence-only SKILL_RANGE and SP-only skills retain their existing
trigger rules. In-flight damage and elemental fill still deal zero during invincibility. Tests cover all four
trigger types, projectile arrival and resumed attacks after expiry.
PR #511 by @RainoutOvO: the connection recovery banner now sits above the AI-away overlay, so “continue here” remains clickable after another page replaces the session. It remains below global modals. Tests use actual pointer hit-testing and clicks, including a global confirmation dialog over the recovery button.
30.9 Golden changes
Before the follow-up changes below (§30.11 onward), all 287 scenarios were regenerated, with no additions or deletions; 45 differed from 0.2.4. This table records that initial candidate. Follow-up changes are recorded separately below. Each cause below was checked by an isolated counterfactual rerun: reverting the named change or combination restores the old digest exactly. Shared RNG and later economy/target choices can amplify a local correction. The two co-op paths involving Rockrock can mask a damage-item effect when each is reverted alone; their combined rollback, not mere operator presence, establishes the attribution.
| Cause / combination | Changed scenarios |
|---|---|
| catapult normal splash radius | roster-013, standin-06 |
| catapult normal splash radius; Kjerag Ice / Tear Gas damage triggers and resulting RNG order | roster-040 |
| Kjerag Ice / Tear Gas damage triggers and resulting RNG order | bond-maniShip-high, boss-boss_2-pair, boss-boss_3-pair, boss-boss_5-pair, coop2-ABYSS-10-boosted, coop2-NORMAL-70-diy, hidden-boss_8-pair, hidden-boss_9-solo, roster-005, roster-012, roster-019, roster-033, roster-047, solo-ABYSS-1, solo-FUNNY-1, solo-FUNNY-2, solo-HARD-1, solo-NORMAL-1, solo-NORMAL-2, standin-07 |
| Kjerag Ice / Tear Gas damage triggers and resulting RNG order; Fortress own-tile targeting / melee splash | coop2-HARD-4 |
| Kjerag Ice / Tear Gas damage triggers and resulting RNG order; Fortress own-tile targeting / melee splash; Rockrock acquisition-subset weights; Vulpisfoglia first-hit addition | coop3-ABYSS-5, coop4-ABYSS-7 |
| Kjerag Ice / Tear Gas damage triggers and resulting RNG order; Rockrock acquisition-subset weights | coop2-NORMAL-8-serverrun, coop4-FUNNY-6 |
| Kjerag Ice / Tear Gas damage triggers and resulting RNG order; Rockrock acquisition-subset weights; Vulpisfoglia first-hit addition | coop2-NORMAL-1-coverage-standins, coop2-NORMAL-3 |
| Kjerag Ice / Tear Gas damage triggers and resulting RNG order; Vulpisfoglia first-hit addition | coop2-NORMAL-5-coverage-diy, roster-026 |
| Fortress own-tile targeting / melee splash (including Horn and Ashlock) | boss-boss_5-solo, roster-004, roster-023, roster-032, roster-034, roster-043, solo-HARD-9-boosted |
| Vulpisfoglia first-hit addition | bond-soloShip-low, bond-yanShip-low, roster-007, roster-009, roster-039 |
| Xi post-rebirth hostile selection immunity | roster-011 |
The fixed-gift, displayed-offer overdraw, Flametail-subset and Clementia changes do not move these corpus digests individually; their dedicated regressions cover the reported cases. UI, translation and developer-tool changes do not alter simulation rules. The 140 DIY-only corpus cases retain their previous digests.
30.10 Bond-layer audio
#482: the client only sounded an increase in active bond count, missing actual layer gains in prep and battle.
The official audio table maps ON_ACT1AUTOCHESS_ADD_BOND to act1autochess_b_ui_buffup, with
maxSoundAllowed=1 and popOldest=true. The supplied match recording
also contains the cue around Matterhorn acquisition and battle layer gains. The maintainer approved that bank's
single-channel replacement policy with no extra cooldown; the recording alone does not settle every timing edge.
Own live prep gains now send an ephemeral m.layer; the displayed battle's existing layer tuples use the same
audio channel. One multi-layer gain is one cue, inactive bonds included; capped/zero gains are silent. New cues stop
the previous source and invalidate its pending decode. Field entry, exit and mute cancel pending/playing cues.
Snapshots, resync and settlement bookkeeping do not synthesize gains. Existing activation audio shares the channel.
The change does not affect layer amounts, simulation events or RNG. Tests cover server unicast/phase/cap behavior,
asynchronous replacement and cancellation, muted/missing audio and late field entry.
30.11 Automatic skill-area follow-up
Reports described Kroos the Keen Glint and Degenbrecher waiting for enemies to reach a smaller range. With full SP, normal/elite forms and all four facings, Kroos S1/S2 cast at the outer normal range and Degenbrecher S2/S3 cast in their own skill range outside normal attack range. The supplied still image does not establish SP, cooldown or the running client version, so those reports are not marked fixed. The documented operation cooldown and Kroos's normal-attack timing still apply.
The broader real-record audit covered 1,850 manual-skill configurations and 2,232 directional trigger cases. It found three independent omissions under the previously approved expanded auto-cast policy:
- 司霆惊蛰 S2 has a terrain-derived range with no static rangeId. Its extra trigger now uses the same low-tile flood and permanent range extension as the running skill, only when it strictly contains the initial range. High ground, field bounds, normal attacks and its SEARCH rule remain unchanged.
- 斩业星熊 S3 can attack flyers while active, but its trigger inherited the inactive ground-only profile. Its actual expanded area now accepts selectable flyers, like the existing 陈 S3 exception.
- 予愿安洁莉娜 S3's opening area is 3-9 plus the surrounding x-4. Its ACTIVE_RANGE trigger now includes that same union, without extending it to a future flight destination or changing its anchored post-move attack area.
These changes preserve SP, control and operation cooldown gates and do not admit stealth/sleep/untargetable enemies to an ACTIVE_RANGE condition. Tests cover all DIY forms, facings, ground/air targets, permanent extension and gates.
30.12 Random boss partners, retained for Hidden Core
The maintainer requested random pairing each match and explicitly chose to retain partners for the hidden boss. A separate seeded shuffle assigns pairs and left/right sides before Final Assault preparation. The same plan drives prep previews, legal deployment maps and the actual battle. Hidden Core retains it; a departure keeps intact pairs and pairs up the remaining single players. A new Match starts a fresh draw; a repeated seed remains reproducible.
m.public.bossPairs supplies the actual plan to the client camera, placement and green partner frames; seat-order
inference survives only as compatibility with older servers missing this field. The audit checks coverage, saved
pair stability and actual fields instead of recomputing seat pairs. Lowest-seat connected-human authority and
other-pair watch restrictions are unchanged. Regression includes 2/3/4 players, departures, hidden continuation,
mirror coordinates, reconnects and real-browser prep-to-battle views.
30.13 联防 HP/SP restoration and Egir opening devour
The report was reproduced through normal BattleResult → uniteBattleOpts → a new battle: an Egir survivor's
carried HP was charged for opening devour again. Some survivors died and revived at full HP, consuming a revival
slot and resetting SP; others lost HP twice. Two 1% clamps also raised legal low HP, and a 1-HP floor changed the
ratio when the new maximum was smaller.
The maintainer approved reconstructing Egir marks/base-ATK/block attributes without repeating opening devour HP loss on a carried survivor. Deployment and battle-start effects establish the new stats, then the living operator's exact positive HP ratio and total SP are restored. Forced-down carry still contributes to the attribute chain and stays down without spending a revival slot. Summons carry SP only; previous active skills are not resumed. Normal, boss and fresh no-carry units keep their usual devour damage, and later knock-outs/redeploys work normally.
PRTS 帮助 specifies restoring HP ratio and SP after deployment. The detailed suppression of repeated opening death/revive effects is the approved implementation of that contract, not a claim that the wiki documents each internal callback. Tests include real carry construction, prior full-health food that would otherwise die anew, remaining revival slots, values below 1%/1 HP, and ordinary/boss behavior.
30.14 Touch's team medic after its holder is eliminated
On 2026-10-12 the maintainer explicitly approved retaining the implementation, including both elimination and voluntary departure after a confirmed selection. This resolves the local policy choice, not the official-evidence gap.
The observed loss happens in newly constructed fields and the next preparation, not by removing an already spawned medic from a teammate's ongoing battle. Both prep and battle eligibility formerly searched only living holders.
[ASSUMED] A confirmed Touch selection grants the team medic for this match even after that player is eliminated or leaves. Official data marks the effect initial and unlimited in rounds and describes a medic on every player's field, but does not explicitly document the holder's elimination. This local candidate adopts the persistent grant under the maintainer's request to make evidence-based local choices pending review. A draft focus is insufficient; a confirmed choice followed by departure counts. New matches have no inherited grant. Recipients must be alive.
Only Touch is retained in contentInfo.matchBands; ordinary teammate contexts and other eliminated strategies
stay excluded. Prep still displays the reserve medic, with the battle replacing it by Touch at the existing elite
threshold. Tests cover LP elimination, departure, both medic variants, draft confirmation, fresh matches and the
normal/unite/boss/hidden BattleSpec paths.
30.15 Other reports rechecked without changing their rules
- 联防 order: PRTS 帮助 includes downed operators in deployed headcount. The implementation reads deployed count, not deployment capacity. Nine deployed with casualties may rank ahead of eight survivors; the active-bond and layer tie-breaks retain their existing documented uncertainty.
- 乌尔比安 S3: PRTS 乌尔比安 gives the next tile beyond an occupied anchor landing as a fallback before his own tile. The pictured report alone does not contradict this; his existing own-board, occupied/downed reservation and return rules remain unchanged.
- 叙拉古: fixed-layer cases last exactly
32 + 0.4Lgame seconds, which is half as many real seconds at 2×. The formula tooltip reads current layers while an already deployed buff retains its initial duration/ASPD; a new deployment reads the new layers. No direct source settled whether official in-battle gains should extend the old buff, so that separate uncertainty is retained; durations are not doubled globally. - 塑心: hidden-layer bonds can still receive layers under the documented PRTS rule (§25.21.6). The old exclusion was specific to 余's strategy (§27.21), not a global ban. Selecting the highest hidden layer count is retained.
- Push/pull: 176 force/mass cases and real Degenbrecher boss cases did not reproduce a weight bypass. Mode leaders
retain their existing displacement guard; an ordinary
rank=BOSSenemy is not automatically the same as a protected mode leader. Weight, static-body and immunity checks remain. 余 S2 can teleport 铳 in one step under its separate teleport rules; this is a possible source of the reported movement, not an identification of the screenshot. Boss trigger-edge tests distinguish a large rendered model from its actual hit area; the still image alone does not establish a missed auto-cast.
30.16 Follow-up golden changes
All 287 scenarios were recomputed after §30.11–30.14. Only five match digests change from the preceding candidate:
coop2-NORMAL-3, coop4-FUNNY-6, coop2-ABYSS-10-boosted, coop2-NORMAL-70-diy, and
coop2-NORMAL-5-coverage-diy. In each, restoring only the old ordered boss pairing in an isolated process reproduces
the preceding digest exactly; the changed partner/side also changes preparation choices and later combat totals.
All five already differed from 0.2.4, so the final release-relative total remains 45 scenarios (§30.9 plus these
additional pairing effects). There are no new corpus cases or removals. The other follow-up fixes do not move this
corpus; their dedicated regressions cover the reported and newly found cases.
30.17 Loadout sync preparation
PR #540 by @Xinglan233. Restoring defaults while a loadout sync was still waiting on backups.json could put the
previous skill back on the server. prepare() assigned its request number only after the await, so the older
payload was treated as the newer one. A stale missing-data result or a failed download could also replace a sync
the reset had already finished.
installPrefSync now keeps a preparation revision. An edit of a watched field, including restoring defaults, and
welcome increment it before the new flush is scheduled. A preparation that started at an older revision does not
send and does not change sync status when it finishes, whether it built a payload, found the data missing, or
failed. Debounce, the close flush, request ordering and the RATE / lock retry stay as they are. The same engine
covers 干员持有 and 自选编队. Golden unchanged. Tests: test/ui/loadout-sync-stale.test.js.
30.18 Loadout import with ops while backups are missing
PR #538 by @Xinglan233. Importing a loadout file that includes ops checked only that chess.json was loaded.
cultivationCharIds then dropped 自选 operators, which only backups.json names, and the import still saved. That
cleared a 练度 the file was not meant to erase. ops: {} is the same replacement.
After a successful parse, loadoutOpsImportRefusal refuses the whole import with the 自选编队 data check
(importRefusal('diy')) before any write. A file with no ops still imports skills and modules once chess.json is
ready and leaves the current 练度 in place. Golden unchanged. Tests: test/ui/loadout-ops-import.test.js (node, no
browser).
30.19 Player package scripts and doc links
GitHub #531 and #530. The player zip copied the repository package.json. npm test is node --test, and test/
is not in the zip, so it reported 0 tests and exited 0. Nine other scripts name files or tools the zip does not
contain (golden, golden:update, build-data, check:imports, ci, package, package:lite, lint,
typecheck). README, CHANGELOG, NOTICE, THIRD-PARTY-NOTICES, PLAYING and DEPLOY also keep relative links to
developer docs, pictures and ../../releases/latest. Twenty-three of those, in README, PLAYING and DEPLOY, point
at developer files.
The stage rewrites package.json only. A script stays when every node <file> it names is in the zip and it names
at least one (start, dev, setup, doctor, launch, postinstall, vendor, assets). The others are
removed, so npm test is a missing script and exits non-zero. The repository package.json is unchanged. The same
stage rewrites those six Markdown files: a relative link whose target ships stays relative; a picture becomes
https://github.com/sganggs/Stronghold-Protocol/raw/v<tag>/<path>; another file becomes .../blob/v<tag>/<path>;
../../releases/latest becomes .../releases/latest. The tag is v plus the package version with a trailing
-dev removed. [ASSUMED] an unreleased 0.2.5-dev build points those links at v0.2.5, the tag that version will
use. Source copies in the git checkout stay relative. Golden unchanged. Tests: test/package-player.test.js,
test/package.test.js, test/docs-paths.test.js.
30.20 Official damage numbers and the title-page repository link
Issues #412 and #543. #412's font swap, element-colour pass and bold crit type are not part of this change.
The new setting 「官方伤害数字」 (officialDamageNumbers) defaults off. While it is off, every rounded hit and
heal still pops the existing typed number (merge, colours, 90-number ceiling, sparks and slashes unchanged).
Turning 「显示伤害数字」 off still hides every digit, including official ones. The value is sanitised with the
other settings (a missing or non-boolean saved value stays off) and copied into the live field view.
The official rule is PRTS 作战机制 「伤害红字(暴击)」 (fetched 2026-10-12): show a
red integer of the final damage when 最终伤害 ≥ 期望伤害 × 1.5, or when a skill forces red. Expected damage follows
the normal formula except that DEF/RES penetration, post-mitigation bonuses (temporary attack-scale increases,
damage-scale increases such as 脆弱, amount rewrites, barrier absorption), final-damage resets and special HP
mechanisms are not counted. Invulnerable targets multiply expected damage by 0, and 0 ≥ 0 × 1.5 still shows red 0.
A dodge of a hit that would have been red shows yellow MISS (#ff9000) instead. After 卫戍协议:盟约 the official
client no longer caps red numbers at 10; this client does not restore that cap. The existing 90-number ceiling
stays as a renderer limit [ASSUMED: 「无限制」 means the old cap of 10 is not brought back].
In this sim the comparison runs inside dealDamage and does not change HP, shields, hooks or RNG:
- Expected damage is mitigation of
dmg.amountwith penetration forced to 0, timesdmg.mulonly. - The compared total is the pre-shield amount after source and target multipliers. The integer on the existing
dmgtuple — what both modes display — is what remains after shields and anhpDamagerewrite. - A qualifying tuple that already fires (
amount >= 0.5, not silent) gains a fifth element'r'. Other tuples stay four elements. Element gauge fills are not marked. Heals are not marked, so official mode hides them. - A dodge that already fires sets
num: 'm'on its fx payload only when the same test says the landed hit would be red. That test does not roll RNG again and does not refresh a dirty attacker's stats (a dirtysource.sread rescales HP).
[ASSUMED] gaps, left unmarked rather than guessed:
dmg.amountalready includes the attack's stated ATK × 攻击倍率 (atkScaleMulis baked in by callers). It is not divided back out.dmg.mulstays on both sides of the comparison.- Cached-ATK expected damage (raw ATK, ignoring the attack scale and the target's DEF/RES), forced red, blue numbers and yellow BLOCK are not represented on DamageInfo. The three wiki examples are not hard-coded.
- An invulnerable or sleeping hit, and a qualifying total below 0.5, still emit no
dmgevent, so official mode cannot show that red 0. Emitting one would incrementevents.dmgand could play an impact. The renderer can draw a red 0 when a tuple['dmg', id, 0, type, 'r']is supplied. Liftoff and a cancelled or hit-capped attack are not red 0. - PRTS does not say that ordinary hits are silent. That is the requested setting: official mode shows only the marked red integer and the yellow MISS. The sentence in the issue comment is not a PRTS sentence.
- The random position is about ±0.35 tiles across and 0.15–0.55 tiles above the head. The simplified board keeps its fixed slot and only changes the colour / the MISS label.
- Sparks and the melee slash stay in both modes.
#543: the title footer carries a text link labelled 「项目仓库」 to
https://github.com/sganggs/Stronghold-Protocol, target="_blank" and rel="noopener noreferrer". It does not
replace 设置 → 问题反馈.
Tests: test/sim/official-damage-numbers.test.js, test/ui/official-damage-numbers.test.js,
test/render/official-numbers.test.js. Golden digests do not move: no new event kind is emitted, and the extra
tuple field is not part of the digest. Player notes: PLAYING; client notes: design/client.md.
30.21 Official battlefield scenes (#532)
Report: the web board draws the metal theme for every battlefield. The reporter asked for the sand theme. PR #533 by @andrew05060414.
Findings (2026-10-11; the official hot-update lists and asset indexes, read from the CDN, and the scene bundles of
version 26-04-13-12-31-42_e9b560):
- The official board is not assembled at run time. Every battlefield is a prebuilt scene bundle, shipped only while the season runs. DESIGN §15 said otherwise from an off-season list and is corrected there.
- Which art a battlefield uses is the dependency list of its scene bundle: the sand theme for act1 m01–m07 and act2
m02 / m04,
map_lakegarden+map_syracuse_metrofor act2 m01,map_kxmr+map_lm_centerfor act2 m03. The same in the 上半 version of 2025-11 and the 下半 versions of 2026-03 / 04. No battlefield uses the metal theme. - The sand theme bundle holds no mesh because the models are baked into each scene ("Combined Mesh (root: scene)").
- A scene's three sub-graphics
HIDDEN@1…3are the left normal field, the right normal field and the boss field. python3 tools/local-extract/fetch_scenes.py --listagainst that Android hot-update list names 33 bundles and 22,801,308 bytes. That is the compressed CDN download, not the extracted size in the full zip.
Change: tools/local-extract/scenes.py exports a battlefield's scene (meshes in board space, materials, key light,
lightmap pages with the baked shadow mask in alpha, albedo textures) to public/assets/local/scene/<stageId>/;
extract.py --only scene runs it, reading the scene bundles from the client, --scenes <dir> or
.cache/official-scenes/*; tools/local-extract/fetch_scenes.py downloads the in-season bundles from the official
CDN (md5-checked against that version's hot-update list). The 3D board draws a stage's scene when the local-art
manifest lists it (board3d/load.js loadScenePack, scene.js setScenePack); the procedural metal board stays the
fallback and still draws the devices, gates, field edges and terrain overlays. Chunks of the parts on screen that
share a material and a lightmap page are one mesh (mergeSceneChunks), so a battlefield such as act2 m02 (36 chunks,
5 lightmap pages) stays inside the board's draw-call budget. A switched-off HIDDEN part is left out of that mesh.
Owner decision (2026-10-12): the eleven scenes belong to the 0.2.5 asset pack. The maintainer's full release zip
includes the extracted files. A lite package or a source install may download and extract them on that machine
(node tools/setup.mjs --scenes; --no-scenes skips). Missing Python 3.8+ or UnityPy / lz4 / Pillow is a warning,
and the metal board remains. The repository never stores the bundles or the extracted files (.gitignore already
ignores public/assets/, data/local-assets.json and .cache/, and names .cache/official-scenes/). Packaging
does not fail when the scenes are absent; the summary line battlefield scenes says whether this machine has them.
scenes.py and fetch_scenes.py ship in a player zip only after they are committed, because the packager reads
git ls-files.
No gameplay change: the scene is art under the same grid, camera and unit layer. Golden results are not moved by this change.
[ASSUMED] the light gains SCENE_LIGHT (range 8.0, ambient 0.2, direct 0.8, indirect 0.4) and the RGBM
reading of the lightmap pages (Unity linear decode, range 5), both in DESIGN §15. Not done: the scene's normal /
metallic / emission maps, map effects, sky box and post-processing; the tile graphics a level switches at run time
(mire, tide) still use the remake's overlays. The extracted byte size inside a full zip was not measured here.
Tests: test/render/board3d-scene.test.js (the scene table against data/stages.json, board-space conversion, chunk
layout shared by scenes.py and load.js, sub-graphic groups, RGBM decode, the loader with a fake store, BoardScene
with a stub renderer: scene on / areas / fallback). test/scene-assets.test.js (setup's scene step, the full plan
ships extracted scene files, the lite plan ships none, git ignores the bundles).
30.22 Prepared 外勤医疗 target order and preparation inspection
PR #523 by @YinqiMargatroid, continuing #451. PR #451 made 预备干员-医疗 visible in preparation and left its initial aggro order unchanged. The owner decided on 2026-10-12 to take both halves of this report as the author describes them. The retained team grant in §30.14 is unchanged: a confirmed Touch selection still supplies the medic after the holder is eliminated or leaves.
In the recording 卫戍协议, the right-side 联防 board at 19:13 has Touch and 哈洛德 inside the same flying 守墓石像's attack range when it locks its target, and the attack selects 哈洛德. The 17:01 preparation frame shows 哈洛德 with no equipment, and his position makes him the right player's first ordinary deployment under the current column scan. The 19:36 clip supplies another targeting comparison. PRTS 卫戍协议/帮助 gives the column order and places operators before summons; PRTS 作战机制 gives 索敌 as blocker, then special priority, then 仇恨, then earliest appearance. These observations support an effective priority earlier than ordinary operators for the prepared 外勤医疗 slot. They do not expose the official unit identity or the exact deployment counter.
The battle sim still creates the selected map-character variant at battleStart, after the board deployment.
[ASSUMED] The prepared slot keeps an earlier effective targeting order whether it stays 预备干员-医疗 or is replaced
by Touch. Only aggroSeq receives a negative pre-initial value (i - chars.length across every prepared character
on the field); deploySeq remains the actual spawn's identity for deployment hooks and scheduled effects. 0 is not
used, because aggroCmp treats a falsy aggroSeq as deploySeq. [ASSUMED] When several players have prepared slots,
their relative order follows the existing player iteration, the same way other initial placements do. A map character
spawned ad hoc after battle start, without preBattleAggroSeq, keeps ordinary summon order. Redeploy and device
spawn still assign aggroSeq from the new deploySeq. The flying 守墓石像 tests check both variants at target-lock
time, a right-side 联防 Touch ahead of the left medic and behind no ordinary operator, and the higher-taunt exception.
The further recording 准备阶段 at 0:17 shows 预备干员-医疗 selected, with its detail card and
attack range visible before combat. This shows an inspection affordance. It does not establish that the official game
already simulates the medic as a combat entity during preparation. The client lets the existing read-only preparation
view open its operator-style card and board-space range by tap, including on the mirrored boss half and in the flat
fallback renderer. The original board-space record is kept for the card; only the rendered view is transformed. The
preview stays immovable and is not sold or retreated. The battle creation point is unchanged. The same click path
accepts another map-character variant if one is included in prepMapChars.
Normative lines: SIM.md §1 and §2 (aggroSeq, spawnToken preBattleAggroSeq), design/engine.md §5.5, design/client.md
Interactions, design/network.md preparation state. Tests: test/content/bands.test.js, test/content/tokens_devices.test.js,
test/ui/inspect-range.test.js, test/ui/playtest6_summons.test.js, test/render/browser.test.js (needs RENDER_E2E=1).
npm run golden:update recomputed all 287 scenarios against the preceding 0.2.5 candidate. No scenario was added or
removed. Only coop2-NORMAL-14-standins moved. Its human seat holds band_amedic, so the team grant places the
prepared map character on both fields; the earlier aggroSeq changes who those enemies strike, and the later bond
and combat totals follow. The match stays a round-14 victory with team LP 31. The partner (band_chiave) first
differs at round 11: activated layers 149 → 161, with 拉特兰 5 → 11 and 精准 24 → 30 in that settle row. Round 12
continues the same layer gap (191 → 206). The human settle row first differs at round 13: kills 21 → 22, leaks
36 → 35. Final human totals: kills 272 → 273, leaks 44 → 43, damage dealt 2,564,165 → 2,565,610, boss damage
193,091 → 189,876. Partner boss damage 586,909 → 590,124, damage dealt 3,838,927 → 3,843,408, activated layers
267 → 282; final bonds move 奥术 27 → 30, 拉特兰 17 → 23 and 精准 53 → 59. This scenario was not among the 45 that
already differed from 0.2.4 (§30.9, §30.16), so the release-relative total is now 46. The other 286 digests match
the preceding candidate.
30.23 Silenced 枯朽 death fog, and 自选 in the promotion reward
Two owner decisions of 2026-10-12. Neither is a new pool or a change to any other silence rule.
#470. enemy_1267_nhpbr (萨卡兹枯朽战士) and enemy_1267_nhpbr_2 (萨卡兹枯朽战士组长) share one death ability,
PollutedDie 【污染秽蚀】 (kitPolluted). A death while flags.silence is set creates no zone. The owner's official-play
recall is that killing one which already shows the silence icon leaves no fog. That recall contradicts the PRTS /
in-game text 「不可沉默」. Both handbook lines are format: "NORMAL" ("被击倒时释放污染秽蚀,使周围我方持续损失生命"),
not SILENCE. 0.2.0 already withheld the fog (§25.18.2), from the general 死亡爆炸 line "如无特殊说明,此能力默认可沉默"
and a client-template reading of projectile_on_killed. That section stays as the 0.2.0 reading. The reason recorded
now is the owner's recall, against 「不可沉默」. The local gamedata cache does not contain the string 「不可沉默」; the
contradiction is the issue title and the owner's statement.
The same ability is only those two keys. Checked and left unchanged: 萨卡兹枯朽战车 / 尖端 (enemy_1272_nhtank, _2)
秽蚀轰击 is an attack skill with _ignoreSilence and silenceImmune; 枯朽之种 (enemy_1269_nhfly) self-destructs and
is silence-immune; 枯朽萃聚使徒 (enemy_1321_wdarft) summons seeds and is silence-immune; 萨卡兹枯朽前锋 / 辟路前锋
(enemy_1422_lrsldr, _2) deal arts while in a 源石污染区; 假想敌:蚀裂 (enemy_9006_actoxi) has a different death
cloud that was already silenceable; 高能源石虫's 死亡爆炸 is a different ability. A silence that has expired does not
stop the fog. A leak (reason !== 'killed') still creates none. A killing blow that would apply silence only if the
target is still alive is not this case: the report is the icon already showing.
#515. The promotion reward (three different operators, one tier above the 调度中心, at most 6, price 0) includes
自选 operators of the newly reached tier, the way the ordinary shop includes them once the 调度中心 is at that tier.
pushRewardOffer with no explicit ids now passes diyStockEntries() into pool.roll. The tier filter is unchanged,
including the downward walk when a tier has no further distinct chess (playtest #6 item 19). Shop level 4 rolls reward
tier 5, so a stocked tier-5 自选 piece can appear and a tier-6 piece cannot. Shop level 5 rolls reward tier 6, so a
stocked tier-6 piece can appear; a tier-5 piece appears only if that roll walks down. Shop level 6 still can roll tier
6. Shop level 3 rolls tier 4 and has no tier-4 自选 piece. The ordinary shop (economy.js, diyRollEntries) still
waits for shopLevel 5 / 6. Effect offers that pass explicit ids do not roll. A 自选 piece is not guaranteed. A slot
whose every bond is banned still has no stock. left <= 0 stays out. Copy counts 8 / 5 stay [ASSUMED] and unchanged.
Seen: 卫戍协议/帮助 干员的获得与精锐化 describes a temporary refresh of three operators one tier above the 调度中心 and
does not say that 自选 are excluded. The next section's note "仅在调度中心等级 ≥ 干员所在等阶时,该干员才有可能出现在栏位中"
is the persistent shop: binding it to the refresh would leave a level-4 调度中心 with no tier-5 operator.
shopLevelDisplayDataDict lists the tier-5 DIY slots at level 5 and the tier-6 slots at level 6; those rows are visible
shop chess, and no second pool excludes them. No parsed page names 自选 inside the refresh. The inclusion is the
owner's decision of 2026-10-12, not a new [ASSUMED]. 0.2.0 §25 had [ASSUMED] that the reward used the slot's shop
level; that sentence stays there, and this section reverses it.
Normative lines: server/match/player/acquire.js pushRewardOffer, server/match/player/diy.js, META.md §3,
DATA.md rewardOffer and §18, design/match.md economy, PLAYING.md 自选编队, SIM.md enemy damage zones,
server/sim/content/enemies/archetypes.js kitPolluted. Tests: test/match/diy-shop.test.js (the reward tallies;
the level-4 ordinary shop still rolls no tier-5 自选 piece), test/content/enemies_bosses.test.js (both keys, silenced
and after the silence runs out).
npm run golden:update recomputed all 287 scenarios. No scenario was added or removed, and no digest moved.
#470 does not change the sim: the fog was already withheld. #515 can change a promotion reward only when the seat
has stocked 自选 of the reward tier and the 调度中心 is still below that slot's shop level. The two DIY matches start
at shop level 5. coop2-NORMAL-5-coverage-diy rolls its only reward after the shop is already 6, where both iterators
include the tier-6 stock, so the offer is the same. coop2-NORMAL-70-diy rolls four tier-6 rewards while the shop is
still 5. On this seed the card the seat takes is the same operator as before. Rounds 8–10 differ only in an untaken
card (round 8 swaps the other two; rounds 9 and 10 replace the third). The other level-5 offer is unchanged. Both
level-6 offers match the previous roll, including the one that shows the tier-6 自选 piece, which the shop gate already
allowed. The digest records
shop draws, fielded pieces, board hashes and the rng state, not an untaken reward card, so the stored scenario does
not move. The release-relative total against 0.2.4 stays 46.
30.24 Funnel normal attacks keep a body hit
PR #537 by @Xinglan233. PRTS 分支特性信息 驭械术师 says a drone that has not been released by a skill deals additional damage on top of the operator's attack: 20 % ATK at first, +15 on the same target, cap 110 %. A skill-released drone deals skill damage.
The profession used to return that ramp as the whole normal-attack multiplier, so the body hit was missing.
resolveHit now keeps a 100 % ATK body instance and separate drone instances. funnelMul holds only the drone ramp;
an attack's dmgMul (至简's talent) is applied once to both. Extra attached drones raise hits, not the body count.
至简's charged S2 sets funnelBodyHits, so two body hits and two additions share one ramp step. Both share one
attackId and one attack hook; only the body is isAttack. Additions carry addition and funnel and are not also
emitted as a normal attack.
[ASSUMED] Body instances precede additions at the existing projectile impact, and a multi-hit volley takes one ramp
step. Ordinary attacks and any skill that only buffs or retargets the normal attack keep the body. info.isSkill is
not the switch: every attack or targeting override sets it. funnelBodyHits === 0 means the attack is drone-only
(the ramp stays inside those hits and no body is added). 荒芜拉普兰德 S2 sets that. 至简 S2 sets funnelBodyHits
to two, so two body hits and two additions still share one ramp step.
耶拉 S2, base and hidden, follows the 洛洛 S2 split. skchr_kjera_2 (docs/research/03-operators.md) says 自身与浮游单元
攻击, and the lock lasts until the enemy dies or the skill ends. While it runs, funnelMul is detached, so her own
shot is one 100 % isAttack hit inside her range and the unreleased addition is off. 1 + attack@cnt drones tick
on their own (kjera:drone, arts, isSkill, not isAttack) and may stay outside her range. Cold is rolled once
on the body and once on each drone hit. Ending the skill restores the multiplier.
[ASSUMED] Those released drones use the trait ramp (20 % → +15 % on the same target → the cap). Her blackboard has no drone scale; the branch page gives that ramp to drones that were not released. The base form continues its per-target ramp, and two drones on one enemy in the same tick take one step. That map is written back when the skill ends. The hidden form restarts each drone at the initial scale, and those scales are not written back onto the single trait ramp. There is no flight delay. A lock that can no longer be targeted is dropped and the drone retargets. Stun and disarm do not pause the released drones. An in-flight shot from before the skill can still step the live ramp; the skill keeps its own copy, and a shot that lands after the skill ends can step the restored ramp.
[ASSUMED] 荒芜拉普兰德 S1 says 释放浮游单元 and does not say she stops attacking. The toggle is read as a retarget of
the unreleased attack: one body hit plus the two additions, at the raised ATK. A 洛洛-style release is not used.
澄闪's skills and 荒芜拉普兰德 S3 stay on their own dealDamage paths. 澄闪 S3 and the skill-drone simulator on
淬羽 are unchanged.
Kits that stored the ramp on dmgMul (至简, both 耶拉 forms, 澄闪 S2) now store it on funnelMul.
Tests: test/sim/funnel-body.test.js, and the funnel cases in kits_t3, kits_t4, kits_t6, kits_alt_t6,
kits_alt_t2 and op_gdglow. The golden moves from this correction, measured against the candidate after §30.29,
are §30.30.
30.25 Redeploy resets the drone ramp
PR #536 by @Xinglan233. The same branch page says that changing target, or redeploying the operator, restores the drone damage to its initial value at once.
installFunnel resets funnelTarget, funnelScale and the per-target funnelMap (耶拉 / 至简) on a normal
deployment, including automatic revival against the same living target. A free move (moveRedeploy, the same carry
as a 秘术师's stored energy and 乌尔比安 S3) keeps the ramp. An in-flight drone still applies the ramp it computes on
impact, so a shot launched before the reset can step the new ramp. That limit is documented and not changed.
Tests: test/sim/funnel-redeploy.test.js, test/match/funnel-redeploy.test.js.
30.26 洛洛 S2 keeps its lock outside her range
PR #535 by @Xinglan233. The official skill table skchr_rockr_2, as recorded in docs/research/03-operators.md,
says the released drone stays locked until the enemy is killed or the skill ends, then returns. Leaving her attack
range is not a return condition. The branch page says a skill-released drone deals skill damage, and that additional
damage belongs to drones that were not released.
S2 now runs one independent skill tick (rockr:drone, one drone at both ranks). While the skill is up the trait
multiplier is detached, so new body shots are ordinary attacks and the unreleased addition is off. The drone's hits
are arts skill damage (isSkill, not isAttack). It inherits the trait ramp on its own copy, so an older projectile
cannot change that ramp. A living selectable lock stays valid outside the body's range. Ending the skill writes the
ramp back and restores the multiplier and the overload cap. S1 and other operators' funnel attacks keep their
existing paths. 立于磐石 is unchanged.
[ASSUMED] There is no flight delay. A lock that becomes untargetable is dropped and the drone retargets. Body control (stun and the like) does not pause the released drone.
Tests: test/content/rockrock-s2-lock.test.js, and the 洛洛 case in kits_t1t2.
30.27 Hit tiles use half-even rounding
PR #520 by @Cloudnyco. PRTS 作战机制 (oldid 433935, 「碰撞体积与位置识别」) says the struck unit's coordinates are
rounded half to even (四舍六入五成双) onto the tile they occupy. Math.round sends an exact .5 toward +∞, so an enemy
held on an outer half-tile was indexed off the board and dropped from the hit index.
roundHalfEven is used for hit tiles only: body.js posKey, the point cases of bodyOnTile and bodyTileReach,
the point-enemy index in battle/queries.js, and targeting.js tileKeyOf. Movement, knockback, ai.js, a unit's
standing tile for range keys, and onFrontLine still use Math.round. #519's signed-zero case in powi was already
on this candidate and is left as it is.
[ASSUMED] FIELD_EDGE_INSET = 1/1048576 pulls a clamped enemy just inside the outer half-tile, so half-even rounding
keeps it on the edge tile. An inset of 1/1024 was rejected because it moved unbalance steps. The inset has no
official source.
Tests: test/sim/hit-tile-round.test.js, test/sim/detmath.test.js, the edge landing in
feedback5-raid-own-board, the 连星 probe in op_wang, and the 麦哲伦 probe in feedback5-wv-summon-triggers.
30.28 Bots soft-reserve a living teammate's opening bonds
PR #518 by @IceCodeNew, for issue #514. This is not an official rule. It extends the accepted #407 card penalty.
Opening-band choice and focus planning used to ignore a living teammate's selected strategy until someone from it was
deployed, so a bot could take that faction's high cards. A living teammate's strategy now soft-reserves its bonds for
both choices, before any member is deployed. The reservation ends when that teammate is eliminated (alivePlayers).
It does not hard-exclude a strategy, reserve stock, or stop an immediate merge. A reserved band's draw weight is a
quarter of its unreserved weight (SOFT_BAND_RESERVE = 0.25) and stays above zero, so the strategy can still be
drawn. Focus planning applies the existing −6 teammate penalty to those bonds as well. Mode-inactive bonds stay
weight 0 (DESIGN §21.26). A solo match has no teammate to reserve against. The shared-pool −18 card penalty is
unchanged, and a stronger merge can still win it.
[ASSUMED] The quarter weight is a project choice. The PR text called the reservation soft; its diff hard-zeroed the weight. This candidate follows the text. The reservation is equal for human and AI teammates.
Tests: the two #514 cases in test/match/bot-smarter.test.js. One proves the focus leaves the reserved bond while
the teammate lives and returns after elimination. The other proves a reserved strategy is still drawn, and drawn more
often once that teammate is eliminated. The existing card test still shows a merge outscoring the −18 penalty.
30.29 Golden changes after the funnel, hit-tile and bot-reservation ports
All 287 scenarios were recomputed once after §30.24–§30.28 and compared with this candidate before those ports
(HEAD 7fd0478d, git show HEAD:test/golden/*.json). No scenario was added or removed. 43 digests differ from
that candidate. They are additional to §30.9 and §30.16.
Against released 0.2.4 the total is now 64. Of the previous 45, 21 are byte-for-byte the digests already on this
candidate and are not repeated below: roster-011, roster-012, roster-013, roster-032, roster-033,
roster-034, roster-039, roster-040, roster-043, bond-maniShip-high, bond-yanShip-low,
boss-boss_2-pair, boss-boss_5-solo, hidden-boss_8-pair, hidden-boss_9-solo, solo-NORMAL-2,
coop2-NORMAL-3, solo-HARD-9-boosted, coop2-ABYSS-10-boosted, standin-06, standin-07. The other 24 of
those 45 moved again, and 19 scenarios that still matched 0.2.4 on this candidate now differ.
Opening strategy ids do not change in any of the 20 match digests. A solo match has no teammate, so §30.28 cannot
move one. Co-op seeds keep the same bandId and the same bot-draw counts except coop4-ABYSS-7. The recorded
match moves are battle totals from §30.24–§30.27, and the later gold and LP those totals change. No isolated
counterfactual was re-run; the groups below follow who is actually on the board.
| Cause | Changed scenarios |
|---|---|
| Funnel body hit plus redeploy reset, and 洛洛 S2's independent lock (§30.24–§30.26). These boards include 洛洛 | roster-004, roster-005, roster-023, roster-024, bond-victoriaShip-low, bond-victoriaShip-high |
| Funnel body hit plus redeploy reset (§30.24–§30.25). 至简 and/or 耶拉, not 洛洛 | roster-007, roster-009, roster-026, roster-028, bond-deputShip-low, bond-emptyShip-low, bond-kjeragShip-high, bond-maniShip-low, bond-miraShip-low, bond-sargonShip-high, bond-soloShip-low, bond-visiShip-low |
| Same body hit and redeploy reset on 荒芜拉普兰德 | roster-017, roster-019, roster-036, roster-046, roster-047, boss-boss_3-pair, boss-boss_5-pair, boss-boss_7-pair, hidden-boss_9-pair |
| 澄闪 DIY: the normal attack is a body hit plus one drone addition (§30.24) | diy-098, diy-099 |
| Hit-tile half-even rounding and the edge inset (§30.27). No funnel operator is deployed; the damage total stays 1388100 and the hook counts and the snapshot sample at index 27 move | standin-04 |
Full matches. Opening bands are unchanged. Final lineups include a funnel operator, except coop2-HARD-4 (both seats eliminated, lineup empty) and coop3-ABYSS-5 (the surviving lineup has none), whose first divergence is round 7 and round 5; those two are the hit-tile change, or a funnel operator who is not left in the stored lineup |
solo-FUNNY-1, solo-FUNNY-2, solo-NORMAL-1, solo-HARD-1, solo-ABYSS-1, coop2-HARD-4, coop2-NORMAL-1-coverage-standins, coop2-NORMAL-5-coverage-diy, coop2-NORMAL-8-serverrun, coop2-NORMAL-70-diy, coop3-ABYSS-5, coop4-FUNNY-6, coop4-ABYSS-7 |
Result flips inside that match list: solo-NORMAL-1 defeat → victory (team LP 0 → 2); solo-FUNNY-1 team LP 15 → 23
and solo-FUNNY-2 team LP 8 → 23, both still victories, with every settled round row unchanged; coop2-NORMAL-8-serverrun
team LP 26 → 27; coop4-ABYSS-7 eliminated at round 12 → defeat at round 13, and its shop and bot draw counts change.
The other matches keep the same end reason. standin-04 is the only battle in this set with no funnel operator on
the board.
30.30 Golden changes after the skill-only funnel body correction
All 287 scenarios were recomputed after the §30.24 correction (a skill that only buffs or retargets keeps the body;
耶拉 S2 uses the 洛洛 split) and compared with this candidate after §30.29 (HEAD e0f37718,
git show HEAD:test/golden/*.json). No scenario was added or removed. fields, standins and diy are unchanged
(澄闪 stays on dealDamage). 19 digests differ. They are additional to §30.9, §30.16 and §30.29. Seventeen of them
already moved in §30.29 and move again. roster-038 and coop2-NORMAL-14-standins did not move in §30.29.
Opening strategy ids do not change in any of the 20 match digests. No isolated counterfactual was re-run. The roster and bond rows name the skill index the corpus assigns: 耶拉 index 1 is 心随意动, 荒芜拉普兰德 index 0 is 慵怠者悲鸣. 至简 is on two of those roster boards at index 1; her S2 already kept two body hits, and her totals move because the fight around 耶拉 changed.
| Cause | Changed scenarios |
|---|---|
耶拉 S2, base form, skill index 1. roster-007 also fields 至简 S2 |
roster-007, roster-009 |
| 耶拉 S2, skill index 1. No 荒芜拉普兰德 on the board | bond-emptyShip-low, bond-kjeragShip-high, bond-miraShip-low, bond-soloShip-low |
| 荒芜拉普兰德 S1, skill index 0. The toggle keeps one body hit and both unreleased additions | roster-036, roster-038 |
Battle-total flips in that table: roster-007 killed 132 → 137, leaks 41 → 37, coins 4 → 6, deaths 11 → 10, damage
234357 → 243212 (耶拉's own damage 9204 → 17443; 至简 8936 → 9558). roster-009 keeps the same kills and leaks;
耶拉's damage 2 → 3 and 至简's kills 28 → 29, damage 7594 → 7168. roster-036 killed 75 → 76, leaks 55 → 54,
damage 1160465 → 1189341 (荒芜拉普兰德 60604 → 99946, kills 2 → 3). roster-038 damage 3467641 → 3468742
(荒芜拉普兰德 kills 4 → 14, damage 106163 → 187571). The four bonds keep the same kills and leaks. bond-emptyShip-low
healing 381 → 362; 耶拉's damage 6585 → 11450 (kills 1 → 3), bond-kjeragShip-high 6927 → 16312,
bond-miraShip-low 10165 → 18617 (kills 2 → 4), bond-soloShip-low 1963 → 2764.
Match end and the final lineup. A bot's chosen skill is not stored. Where the final lineup still contains 耶拉, her default skill is S2, which is the skill this correction changes.
| Final lineup | Changed scenarios and the recorded end |
|---|---|
| 耶拉, lineup unchanged | solo-FUNNY-1 (3_20_b): victory, team LP 23 → 27; only round 8's bond layers move (steadShip 34 → 35). solo-FUNNY-2 (3_20_a): victory, team LP 23 → 29; all eight stored round rows are identical, so the LP change is the boss round. solo-NORMAL-1 (3_20_b): victory, team LP 2 → 11; first divergence round 10 (kjeragShip 13 → 14). solo-HARD-1 (3_20_a and 至简 3_13_a): still a defeat, team LP 0, boss pool 215042 → 212054; first divergence round 8, the board hash. coop2-NORMAL-8-serverrun (ai_1 3_20_a): victory, team LP 27 → 37 (ai_0 LP 10 → 19, ai_1 LP 17 → 18); first divergence round 8, ai_1 killed 22 → 23, leaks 1 → 0. coop2-NORMAL-5-coverage-diy (ai_0 3_20_a): victory, team LP 33 → 34 (h_0 LP 27 → 28); first divergence round 13, ai_0's board hash, killed 26 → 21, leaks 17 → 22 |
| 耶拉 was on the pre-change lineup; that seat is then eliminated and the stored lineup is empty | solo-ABYSS-1: defeat at round 13 → eliminated at round 12, team LP 0 → null. The pre-change lineup had 耶拉 3_20_a and 至简 3_13_a. First divergence round 8: LP 27 → 18, killed 19 → 10, leaks 0 → 9. coop4-ABYSS-7: still a defeat at round 13, team LP 0, boss pool 4821366 → 3417504. ai_1 is eliminated at round 13; the pre-change lineup had 至简 3_13_a and 耶拉 3_20_a. First divergence round 8: ai_1 LP 19 → 20, killed 2 → 3, leaks 7 → 6. ai_0's lineup also changes and has no funnel chess id before or after |
| 耶拉 plus other funnel operators, lineup unchanged, same victory and the same team LP 130 | coop4-FUNNY-6: ai_0 耶拉 3_20_a, ai_1 荒芜拉普兰德 6_18_a, ai_3 洛洛 2_10_a. First divergence round 11, ai_0's kjeragShip layers 26 → 23 |
| No 耶拉 on the final lineup. The diverging round's pieces and skill choice are not stored | coop2-NORMAL-1-coverage-standins: still a defeat, team LP 0, boss pool 25040 unchanged. ai_0's final lineup includes 荒芜拉普兰德 6_18_a. First divergence round 7, the other seat's miraShip layer 5 → 6. coop2-NORMAL-14-standins: victory, team LP 31 → 40 (ai_0 LP 13 → 22). Neither final lineup contains a funnel chess id. First divergence round 10 bond layers (raidShip 26 → 28, steadShip 16 → 18); that round's board hash is unchanged |
30.31 Strategy skip eligibility
The strategy-selection page only enables skipping while another unpicked player remains. The published order
keeps finished players for display, so its total length cannot establish that a skip has a recipient. The client
counts order entries absent from draft.picks, alongside the existing turn and quota checks. The server's
skipBand already rejects the final player with BAD_TARGET / nobody to pass to; its rule remains unchanged.
Regression covers normal two-, three- and four-human browser flows, the final turn, and exhausted quota.