Files
ark-stronghold-protocol/docs/history/0.2.5.md
T

400 lines
32 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 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. This covers ordinary attacks and unreleased skill attacks that set `funnelBodyHits`. Released skill paths that
do not set it (耶拉 S2, 澄闪 S1 while its own drones are out) keep their existing skill-damage path and do not gain a
second body. Whether those released skills should also have a body attack is not settled here. 澄闪 S3 and the
skill-drone simulator on 淬羽 stay on their own `dealDamage` paths.
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_t6`, `kits_alt_t6`, `kits_alt_t2`
and `op_gdglow`.
### 30.18 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.19 洛洛 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.20 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.21 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.22 Golden changes after the funnel, hit-tile and bot-reservation ports
All 287 scenarios were recomputed once after §30.17–§30.21 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.21 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.17–§30.20, 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.17–§30.19). These boards include 洛洛 | `roster-004`, `roster-005`, `roster-023`, `roster-024`, `bond-victoriaShip-low`, `bond-victoriaShip-high` |
| Funnel body hit plus redeploy reset (§30.17–§30.18). 至简 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.17) | `diy-098`, `diy-099` |
| Hit-tile half-even rounding and the edge inset (§30.20). 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.