707 lines
60 KiB
Markdown
707 lines
60 KiB
Markdown
# DESIGN §30 — 0.2.5-dev
|
||
|
||
Part of [DESIGN.md](../DESIGN.md). Community reports after 0.2.4.
|
||
|
||
## 30. Community follow-up
|
||
|
||
Unreleased. Sources and decisions below describe the candidate behavior, not a published release.
|
||
|
||
### 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](https://www.bilibili.com/video/BV1eLXXBqEgF/)
|
||
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 号角](https://prts.wiki/w/号角): 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 [自制投石机](https://prts.wiki/w/自制投石机) 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 忍冬](https://prts.wiki/w/忍冬).
|
||
- #489: [PRTS 盟约记录](https://prts.wiki/w/卫戍协议:盟约_下半/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 `ownerOps` includes 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 “自在”](https://prts.wiki/w/“自在”) 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](https://www.bilibili.com/video/BV1VxwNzZEqK/)
|
||
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 帮助](https://prts.wiki/w/卫戍协议/帮助) 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 帮助](https://prts.wiki/w/卫戍协议/帮助) 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 乌尔比安](https://prts.wiki/w/乌尔比安) 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.4L` game 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=BOSS` enemy 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 [作战机制](https://prts.wiki/w/作战机制) 「伤害红字(暴击)」 (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.amount` with penetration forced to 0, times `dmg.mul` only.
|
||
- The compared total is the pre-shield amount after source and target multipliers. The integer on the existing
|
||
`dmg` tuple — what both modes display — is what remains after shields and an `hpDamage` rewrite.
|
||
- 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 dirty `source.s`
|
||
read rescales HP).
|
||
|
||
[ASSUMED] gaps, left unmarked rather than guessed:
|
||
|
||
- `dmg.amount` already includes the attack's stated ATK × 攻击倍率 (`atkScaleMul` is baked in by callers). It is
|
||
not divided back out. `dmg.mul` stays 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 `dmg` event, so official mode
|
||
cannot show that red 0. Emitting one would increment `events.dmg` and 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_metro` for act2 m01, `map_kxmr` + `map_lm_center` for 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…3` are the left normal field, the right normal field and the boss field.
|
||
- `python3 tools/local-extract/fetch_scenes.py --list` against 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.
|
||
|
||
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 [卫戍协议](https://b23.tv/RQLI8WS), 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 卫戍协议/帮助](https://prts.wiki/w/卫戍协议/帮助)
|
||
gives the column order and places operators before summons; [PRTS 作战机制](https://prts.wiki/w/作战机制) 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 [准备阶段](https://b23.tv/4JXAp12) 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 分支特性信息 驭械术师](https://prts.wiki/w/分支特性信息/data) 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 |
|