2704 lines
135 KiB
Markdown
2704 lines
135 KiB
Markdown
# Aetherbound Guild - Gameplay, Battle, Economy And Save
|
|
|
|
Complete gameplay rules, deterministic battle, progression, economy, pacing, failure, persistence and recovery.
|
|
|
|
## Owner Review / Owner 审核
|
|
|
|
重点确认招募、自动排阵、战斗、击倒与下关恢复、装备、持续成长、经济、难度、失败与存档规则。这里回答“每一步怎么玩、怎么算、输赢后发生什么”。
|
|
|
|
本文件是当前六份审核文档之一。历史拆分文件仅保留在 Git 历史中;文内规则、表格和 ID 仍是完整开发依据。
|
|
|
|
## P0 Consolidation Role
|
|
|
|
This document is the systems authority inside the consolidated project
|
|
contract. It defines what each recurring decision changes, which state owns
|
|
the result, how Coin/XP/Equipment/Company/Contract values persist, and how
|
|
failure, recovery and Save receipts behave. It does not define page layout or
|
|
art treatment; those belong to `03_PAGES_AND_UX.md` and
|
|
`04_ART_ANIMATION_AUDIO.md`.
|
|
|
|
## 2026-08-17 Continuous-Growth Owner Override
|
|
|
|
This override is the current authority everywhere this file conflicts with the
|
|
older FG-4 implementation model below:
|
|
|
|
- one Guild save owns one persistent Expedition Company across Contract runs;
|
|
- New Game creates exactly four stable provisional offers and commits exactly
|
|
two of them once as the initial persistent roster;
|
|
- a zero-HP Recruit is knocked out for the current battle, remains owned and is
|
|
battle-ready again for the next stage with the same identity and growth;
|
|
- Contract clear or withdrawal closes only Contract-local progress, formation,
|
|
encounter and temporary-effect state, then returns to the Guild; and
|
|
- death-funded Replacement Credit, Company-collapse roster deletion, Recruit
|
|
archiving and loss of levels/XP/equipment at Contract close are superseded.
|
|
|
|
The exact next-stage HP amount is intentionally frozen for the later balance
|
|
conversion. DEV-2 guarantees persistent ownership and next-stage availability;
|
|
it must not invent a full-heal rule or permanent casualty.
|
|
|
|
The older sections that still name `Fallen`, `Replacement Credit`,
|
|
`run_creation_draft`, short-lived `current_run` Recruits, Company collapse or
|
|
roster archiving are `FROZEN_LEGACY_FG4` evidence. Their deterministic battle,
|
|
economy and Save-v11 details remain preserved until the named later player-order
|
|
conversion, but those conflicting lifecycle effects are not current product
|
|
authority. Battle-local `Fallen` event/code identifiers may temporarily remain
|
|
as implementation names only; product-facing meaning is `Knocked Out` and never
|
|
permanent roster removal.
|
|
|
|
For DEV-2, the current persistence boundary is:
|
|
|
|
```text
|
|
Guild_slot
|
|
initial_company_draft or null
|
|
expedition_company or null
|
|
|
|
initial_company_draft
|
|
draft_id, seed, status, source_revision
|
|
offers[0..3], offer_set_hash
|
|
selected_offer_ids[0..2], selection_revision
|
|
|
|
expedition_company
|
|
company_id, created_from_draft_id
|
|
owned_recruits[2..], reserve_recruit_ids[]
|
|
persistent inventory, Coin, levels, XP, Professions, Traits and history
|
|
active_contract or null
|
|
```
|
|
|
|
An uncommitted draft and a committed initial Company are mutually exclusive.
|
|
The same saved seed always produces the same four IDs in the same positions.
|
|
Selection may contain zero, one or two distinct offer IDs; only exactly two can
|
|
enter review. Commit atomically removes the draft, writes one Company containing
|
|
only the selected two Recruit records and stores an idempotent receipt. A retry
|
|
returns that receipt and never creates a second Company or rerolls the offers.
|
|
|
|
<!-- Historical section source: SYSTEMS_AND_BATTLE; retained in Git history. -->
|
|
|
|
## Aetherbound Guild: Systems And Battle Authority
|
|
|
|
> Authority: generated-Recruit state, Profession grammar, Market/run handoffs,
|
|
> ordered Party-line geometry, deterministic auto-battle, equipment and Artifact
|
|
> hooks, targeting, movement, knockout, recovery, and causal diagnosis.
|
|
>
|
|
> Revision: recruit-line-design-r2 / 2026-08-10
|
|
>
|
|
> Product intent is defined in
|
|
> [01_GAME_DESIGN.md](01_GAME_DESIGN.md). Prices, rewards,
|
|
> progression rates, capacity selection, and pacing are defined in
|
|
> [02_GAMEPLAY_AND_BALANCE.md](02_GAMEPLAY_AND_BALANCE.md). Durable commit and recovery
|
|
> semantics are defined in
|
|
> [02_GAMEPLAY_AND_BALANCE.md](02_GAMEPLAY_AND_BALANCE.md).
|
|
|
|
### 1. System Invariants
|
|
|
|
1. Each side occupies one ordered horizontal line. Position zero is front; a
|
|
larger position is farther rearward. There is no second spatial axis.
|
|
2. Allied simultaneous footprint begins at four and reaches ten. Reserve and
|
|
cumulative hires are separate and never redefine this limit.
|
|
3. Battle is automatic. The player may start, pause, change speed, inspect, or
|
|
retreat, but cannot activate attacks/skills, reorder, equip, or promote while
|
|
resolving.
|
|
4. A Recruit has one generated identity, one current fixed Profession, five
|
|
rolled attributes from an equal level budget, exactly one Trait, equipment,
|
|
level, and survival history.
|
|
5. Zero HP means Knocked Out. The Recruit leaves the resolving line immediately,
|
|
never respawns in that battle, and is never replaced by Reserve during that
|
|
battle. The Recruit remains in the persistent Expedition Company and is
|
|
battle-ready again for the next stage with equipment and growth unchanged.
|
|
6. The same start state, ruleset, content revision, seed, and retreat timestamp
|
|
produces the same state hashes and result.
|
|
7. Display frame rate and pause/1x/2x/4x do not change simulation time or draws.
|
|
8. Every targeting exception, failed action, movement conflict, retarget,
|
|
immunity, proc suppression, and outcome has one inspectable reason.
|
|
9. All damage has a declared type, power source, defense, rounding boundary,
|
|
critical rule, modifier groups, and floor/nullification behavior.
|
|
10. No standard battle exceeds 150 simulation seconds.
|
|
|
|
### 2. Authoritative State Model
|
|
|
|
```text
|
|
profile
|
|
settings, accessibility, language, input mappings
|
|
Renown, unlock breadth, Region/Contract progress, Records, collection facts
|
|
persistent Expedition Company, inventory, Coin and Recruit growth
|
|
|
|
Contract expedition
|
|
contract_run_id, contract_id, difficulty_id, ruleset_version, content_revision
|
|
generation_seed, Market index, cleared Rounds, Line capacity
|
|
route, refresh/lock counters, deployed formation, temporary Artifacts
|
|
knockout events, boss state, Contract result
|
|
|
|
Recruit
|
|
recruit_id, generated_name_parts, rendered_name_snapshot, appearance_seed
|
|
origin_profession_id, current_profession_id, promotion_state
|
|
level, xp, five base attributes, Trait id
|
|
equipped_item_id or null, effective stats, battles entered, victories,
|
|
hire receipt
|
|
|
|
encounter
|
|
encounter_id, battle_offer_id, root_seed, phase, tick
|
|
ordered allied/enemy actor sequences and footprints
|
|
HP, resources, readiness, cooldowns, casts, statuses, barriers
|
|
event queue, draw counters, retreat state, objective, outcome, event journal
|
|
|
|
presentation only
|
|
camera, interpolation, animation frame, particles, selected inspector,
|
|
expanded tooltip, pointer, audio playback cursor
|
|
```
|
|
|
|
Presentation never authors state. Battle arithmetic uses signed 64-bit integer
|
|
fixed point with 100 units per displayed point. Percentages use basis points
|
|
(`10,000 = 100%`). A formula rounds only where this document says `floor`,
|
|
`ceil`, or `round_half_up`.
|
|
|
|
### 3. Run And Market State Machine
|
|
|
|
```text
|
|
Contract_created
|
|
-> Market_open
|
|
-> battle_offer_committed
|
|
-> countdown
|
|
-> resolving
|
|
-> outcome_locked
|
|
-> diagnosis
|
|
-> reward_or_failure_applied
|
|
-> next_Market | boss_Muster | Contract_return_to_Guild
|
|
```
|
|
|
|
The Market snapshot contains three Recruit offers, four equipment offers, and
|
|
three battle offers. Offer generation, prices, locks, and refreshes are owned by
|
|
the economy authority. This document owns these system rules:
|
|
|
|
- leaving and reopening a Market preserves all offer IDs and generated values;
|
|
- buying removes exactly that offer and never silently fills the empty slot;
|
|
- locked unbought offers persist to the next Market as declared;
|
|
- equipment, order, promotion, and dismissal are legal only while Market is
|
|
open or during boss Muster;
|
|
- battle commit stores an immutable deployed lineup, inventory ownership,
|
|
effective-stat snapshot, enemy lineup, seed, risk, and result rules;
|
|
- no Market edit after commit can affect that encounter;
|
|
- result inspection never applies or skips a reward operation.
|
|
|
|
### 4. Generated Recruit Contract
|
|
|
|
#### 4.1 Generation
|
|
|
|
Recruit generation uses labeled deterministic streams so a name draw cannot
|
|
change attributes or Trait:
|
|
|
|
```text
|
|
recruit_name, recruit_appearance, recruit_profession,
|
|
recruit_attributes, recruit_trait
|
|
```
|
|
|
|
For offer level `L` from 1 through 10:
|
|
|
|
```text
|
|
attribute_budget(L) = 30 + 5*L
|
|
attribute_floor(L) = 3 + L
|
|
attribute_cap(L) = 12 + 3*L
|
|
```
|
|
|
|
`Might + Finesse + Insight + Vigor + Resolve` equals the exact budget. Every
|
|
attribute stays within floor/cap. The generator first assigns the floor to all
|
|
five, then distributes the remaining points one at a time by the labeled stream
|
|
without exceeding cap. Because totals are equal at a level, no Recruit is
|
|
strictly superior only because its random total is larger.
|
|
|
|
Every Recruit row stores its inputs and output hash. Generated name components
|
|
and modular visual parts create identity and readable variation but no hidden
|
|
mechanical modifier.
|
|
|
|
#### 4.2 Trait
|
|
|
|
Each Recruit receives exactly one of 36 authored Traits. A Trait row has one
|
|
visible benefit and one visible cost, both mechanical. It cannot be removed,
|
|
stacked, upgraded, rerolled, or suppressed by changing language or appearance.
|
|
|
|
A Trait is invalid if its cost never changes a decision in either of its two
|
|
fixed fixtures, or if its benefit is only a larger unconditional value. Trait
|
|
tags may participate in hidden-hybrid eligibility only when that condition is
|
|
shown after discovery.
|
|
|
|
#### 4.3 Survival History
|
|
|
|
The persistent Recruit records battles entered, victories, retreats survived,
|
|
allies ahead at commit, first knocked-out ally witnessed, promotion, knockout
|
|
events and final dismissal if any. History may unlock a title or catalog fact
|
|
but cannot grant a hidden stat. Contract close adds an immutable summary Record
|
|
without making that Recruit unplayable or replacing its stable ID.
|
|
|
|
### 5. Profession Catalog And Promotion
|
|
|
|
The fixed catalog contains 12 base, 24 regular advanced, and 6 hidden hybrid
|
|
Professions. Content owns identities and instances; this document owns schema
|
|
and topology.
|
|
|
|
#### 5.1 Topology
|
|
|
|
- Every base Profession has exactly two regular advanced descendants.
|
|
- Every regular advanced Profession has exactly one parent base.
|
|
- Every hidden hybrid declares exactly two compatible base-family IDs and an
|
|
original battlefield responsibility distinct from both parents.
|
|
- A Recruit has exactly one current Profession. Equipment never changes the
|
|
stored Profession ID.
|
|
- A Recruit's origin base is immutable even after promotion.
|
|
|
|
#### 5.2 Promotion
|
|
|
|
A base Recruit becomes eligible after reaching level 4 and surviving three
|
|
completed battles. Promotion is one-way and can happen once. The Market review
|
|
shows new action rules, lost rules, target/reach changes, resource loop, price
|
|
or opportunity cost, and the two validation fixtures before confirm.
|
|
|
|
The two regular descendants are always deterministic choices once eligible.
|
|
A hidden hybrid appears as an additional choice only when its revealed catalog
|
|
predicate is satisfied. Allowed predicate inputs are origin base, Trait tags,
|
|
equipped behavior tags, current Artifact tags, adjacent deployed Profession
|
|
families, and cleared Contract facts. A predicate cannot read generated name,
|
|
appearance, playtime, wall clock, or social state.
|
|
|
|
Promotion preserves that Recruit's individual level and XP, HP percentage
|
|
outside battle, Trait, equipped item, survival history, and recruit ID. It
|
|
replaces current Profession, action table, resource meter, and derived
|
|
Profession bases atomically. It is not a free job switch or reversible respec.
|
|
|
|
#### 5.3 Automatic Action Unlocks
|
|
|
|
A Profession action kit unlocks by that Recruit's individual level. Levels are
|
|
bounded to `1..10`; there is no Company level and another Recruit's XP cannot
|
|
unlock these rules:
|
|
|
|
| Recruit level | Available automatic rules |
|
|
|---:|---|
|
|
| 1 | basic action, resource rule, identity passive |
|
|
| 2 | first technique and its priority row |
|
|
| 3 | second technique and its priority row |
|
|
| 4 | signature and its priority row; promotion becomes possible after the survival requirement |
|
|
|
|
Promoting replaces the action kit with the promoted Profession's level-valid
|
|
kit. A direct regular-advanced hire uses the same level schedule. Falling below
|
|
an unlock level is impossible, and equipment cannot unlock a later action
|
|
early. Every unlocked action is automatic; the player prepares through hire,
|
|
equipment, deployment, and order rather than selecting it during battle.
|
|
|
|
#### 5.4 Profession Row
|
|
|
|
```text
|
|
profession_id, tier (base | regular_advanced | hidden_hybrid)
|
|
parent_base_ids, unlock/promotion predicate
|
|
battlefield responsibility, preferred depth, failure mode, counter tags
|
|
base HP, base Tempo, base defenses, resource schema
|
|
basic action, two techniques, signature, passives
|
|
automatic priority table and target policies
|
|
movement/reach/protection/projectile/healing rules
|
|
two decision-opposed validation fixtures
|
|
presentation and accessibility event requirements
|
|
```
|
|
|
|
An advanced or hidden Profession must transform target, timing, position,
|
|
resource flow, protection, or risk ownership. A stronger scalar copy fails.
|
|
|
|
### 6. Party-Line Geometry
|
|
|
|
#### 6.1 Ordered Footprint
|
|
|
|
Each actor declares footprint 1 or 2. Standard Recruits use footprint 1. An
|
|
actor with footprint 2 is one unit ID, one HP pool, and one action state
|
|
occupying a contiguous block.
|
|
|
|
The authoritative line is an ordered actor sequence with no interior empty
|
|
space after normalization. For actor `A`:
|
|
|
|
```text
|
|
depth(A) = sum(footprint(X) for every living actor X ahead of A)
|
|
front_cell(A) = depth(A)
|
|
rear_cell(A) = depth(A) + footprint(A) - 1
|
|
```
|
|
|
|
Allied deployed footprint cannot exceed current Line capacity. Recruits left
|
|
out of the sequence are Reserve. Order ties and simultaneous insertion use
|
|
stable actor ID only after all player-visible rules are equal.
|
|
|
|
#### 6.2 Cross-Line Distance And Same-Line Distance
|
|
|
|
For opposing source `S` and target `T`:
|
|
|
|
```text
|
|
cross_line_distance(S,T) = 1 + depth(S) + depth(T)
|
|
```
|
|
|
|
For allied actors:
|
|
|
|
```text
|
|
same_line_distance(S,T) =
|
|
max(0, max(front_cell(S), front_cell(T)) -
|
|
min(rear_cell(S), rear_cell(T)) - 1)
|
|
```
|
|
|
|
Adjacent footprints therefore have same-line distance zero. Every action
|
|
declares a maximum cross-line or same-line reach. No visual closeness overrides
|
|
the integer distance.
|
|
|
|
#### 6.3 Protection
|
|
|
|
```text
|
|
protection_stacks(T) = min(2, depth(T))
|
|
```
|
|
|
|
Each occupied footprint ahead contributes one stack up to two. Protection is
|
|
not a generic defense stat; actions use one declared mode:
|
|
|
|
| Mode | Rule |
|
|
|---|---|
|
|
| `frontbound` | Only targets with `depth=0` are candidates |
|
|
| `screened` | Deeper targets may be candidates, but resolved damage is multiplied by `max(0.70, 1 - 0.15*protection_stacks)` |
|
|
| `intercepted_projectile` | Projectile collides with the first eligible footprint in its current path |
|
|
| `lobbed` | Ignores collision but uses screened damage |
|
|
| `bypass_protection` | Ignores stacks; must be declared in preview and telegraph |
|
|
|
|
Healing and boons do not gain a hidden bonus from protection. The inspect view
|
|
shows stacks, contributing actors, target legality, and expected multiplier.
|
|
|
|
### 7. Encounter Lifecycle
|
|
|
|
| Phase | Legal player actions | Commit behavior |
|
|
|---|---|---|
|
|
| Preview | Inspect enemy order, actions, risk, reward, timeout, retreat | Offer is stable; no spend |
|
|
| Prepare | Equip, promote, dismiss, deploy, reorder | Changes remain reversible until battle confirm |
|
|
| Countdown | Pause, speed, inspect | Three simulation seconds; lineup locked |
|
|
| Resolve | Pause, speed, inspect, retreat | Automatic action events and retreat timestamp journaled |
|
|
| Outcome locked | Inspect | Victory, defeat, timeout, or retreat cannot change |
|
|
| Diagnose | Inspect causal timeline | No reward applied by viewing/skipping |
|
|
| Settle | Choose fixed reward if eligible | One atomic result operation |
|
|
| Finalized | Continue to Market/run result | Casualties, equipment return, Coin/Credit, and reward agree |
|
|
|
|
### 8. Deterministic Time, Events, And Draws
|
|
|
|
#### 8.1 Fixed Tick And Priority
|
|
|
|
One simulation tick is exactly 100 milliseconds. At each tick:
|
|
|
|
1. accept lifecycle control stamped for this tick, including retreat start;
|
|
2. expire statuses and barriers whose end tick equals this tick;
|
|
3. resolve periodic status events due this tick;
|
|
4. complete casts, channels, projectile arrivals, and summon expiries;
|
|
5. select actions for actors already ready and legal;
|
|
6. resolve voluntary and forced movement intents;
|
|
7. apply simultaneous damage, healing, barrier, and resource deltas by batch;
|
|
8. finalize action-outcome records, mark zero-HP actors Fallen, and run declared
|
|
outcome/death triggers once;
|
|
9. compact lines, insert legal summons, and evaluate objective/retreat/phase;
|
|
10. advance readiness, cooldown, cast, status, timeout, and retreat clocks.
|
|
|
|
Events in the same numeric batch read one pre-batch state and apply together. An
|
|
actor alive at batch start completes its already-scheduled event even if the
|
|
same batch makes it Fallen. A death trigger cannot restore the source above
|
|
zero HP and cannot recursively produce another death trigger in the same tick.
|
|
An outcome-triggered derived action is queued no earlier than priority 4 of the
|
|
next tick; it cannot join or reopen the batch that produced its outcome.
|
|
|
|
#### 8.2 Labeled Determinism
|
|
|
|
The encounter root seed supplies independent labels:
|
|
|
|
```text
|
|
target_tie, critical, status_apply, proc_choice,
|
|
summon_variant, reward
|
|
```
|
|
|
|
For a draw:
|
|
|
|
```text
|
|
roll_bp = hash64(root_seed, label, actor_id, action_sequence,
|
|
target_id, draw_index) mod 10,000
|
|
```
|
|
|
|
The event journal records label and draw index. Adding an equipment proc cannot
|
|
shift target, critical, status, or reward results in another stream. There is
|
|
no random damage variance or hidden miss chance.
|
|
|
|
### 9. Attributes And Derived Statistics
|
|
|
|
| Attribute | Primary responsibility |
|
|
|---|---|
|
|
| Might | physical power and Guard contribution |
|
|
| Finesse | Tempo, physical precision, and critical pressure |
|
|
| Insight | arcane power and status Potency |
|
|
| Vigor | maximum HP, Guard, and movement stability |
|
|
| Resolve | healing power, Ward, and status Tenacity |
|
|
|
|
At level `L`:
|
|
|
|
```text
|
|
PhysicalPower = WeaponPower + 2.00*Might + 0.50*Finesse
|
|
ArcanePower = FocusPower + 2.00*Insight + 0.50*Resolve
|
|
HealingPower = FocusPower + 1.20*Insight + 1.20*Resolve
|
|
MaxHP = ProfessionBaseHP + 8*L + 10*Vigor
|
|
Guard = ArmorGuard + 1.20*Vigor + 0.60*Might
|
|
Ward = ArmorWard + 1.20*Resolve + 0.60*Insight
|
|
Potency = Insight + 0.50*Finesse + FlatPotency
|
|
Tenacity = Resolve + 0.50*Vigor + FlatTenacity
|
|
Tempo = clamp(ProfessionBaseTempo + 0.40*Finesse + FlatTempo, 20, 100)
|
|
Initiative = clamp(ProfessionBaseInitiative + 0.25*Finesse + FlatInitiative,
|
|
0, 95)
|
|
```
|
|
|
|
Derived values round down to 1/100 after the full expression. Profession bases
|
|
must stay within `BaseHP 140-420`, `BaseTempo 24-55`, and `BaseInitiative 0-45`
|
|
unless a fixed exception fixture is approved. A MaxHP change outside battle
|
|
preserves current HP percentage and does not heal.
|
|
|
|
### 10. Readiness And Automatic Action Selection
|
|
|
|
At battle start, `Readiness = Initiative`. Each tick while an actor can act:
|
|
|
|
```text
|
|
Readiness = min(150, Readiness + Tempo*0.1)
|
|
```
|
|
|
|
At `Readiness >= 100`, the actor scans its Profession priority rows from top to
|
|
bottom. The first row with a legal action, true visible condition, and legal
|
|
target starts. It subtracts 100 Readiness and retains overflow. If none is
|
|
legal, readiness remains capped and the actor checks again next tick.
|
|
|
|
Priority conditions may read only current HP bands, resource, status, casts,
|
|
depth, protection, ally/enemy counts, summon space, cooldown, phase, and preview
|
|
tags. They cannot read future draws. Every Profession has a legal fallback or
|
|
an explicit `NO_REACHABLE_TARGET` idle result.
|
|
|
|
| Timing field | Boundary |
|
|
|---|---|
|
|
| Wind-up | 0.1-1.5 s |
|
|
| Major telegraph | at least 1.5 s; first boss use at least 2.5 s |
|
|
| Channel/cast | 0.3-4.0 s |
|
|
| Recovery | 0-2.0 s |
|
|
| Cooldown | 0-30 s |
|
|
|
|
The automatic table is part of Profession identity. Players compare it but do
|
|
not script arbitrary condition programs.
|
|
|
|
### 11. Target Selection, Reach, And Retarget
|
|
|
|
An action constructs candidates in this order:
|
|
|
|
1. required side and living/Fallen/summon state;
|
|
2. cross-line or same-line reach;
|
|
3. protection mode;
|
|
4. required status, cast, HP band, or tag;
|
|
5. exclusion, immunity, and maximum targets;
|
|
6. target policy.
|
|
|
|
Core target policies are `frontmost`, `rearmost`, `lowest_hp_ratio`,
|
|
`highest_power`, `active_cast`, `marked`, and `self`. Numeric ties use depth in
|
|
the policy's natural direction, then `target_tie`. The default hostile policy is
|
|
frontmost. Enemy-specific score tables must be shown in preview when they can
|
|
select something other than the declared policy result.
|
|
|
|
At action start the target unit ID is reserved. At completion, the action uses
|
|
one declared invalid-target policy:
|
|
|
|
- `retarget_same_policy`: rebuild candidates and choose again;
|
|
- `fizzle`: apply no primary effect and use the stated cooldown refund;
|
|
- `ground_resolve`: resolve at the stored depth and affect current occupants.
|
|
|
|
A target moving to another depth remains the same target if still legal. Every
|
|
retarget or fizzle logs the first rejecting filter.
|
|
|
|
#### 11.1 Forecast Is Not Resolution
|
|
|
|
A forecast is a diagnostic prediction made from the committed preview or the
|
|
state at automatic action selection. It stores predicted target IDs, depth,
|
|
timing, and the rules that could change them. It never reserves a target and
|
|
never fires a battle trigger.
|
|
|
|
At action start, target selection creates the authoritative reservation. If its
|
|
target differs from forecast because an earlier action, movement, collapse, or
|
|
status changed state, the journal records `FORECAST_DIVERGED` with the first
|
|
changed input. That is not a hit, miss, dodge, resistance, or action outcome.
|
|
The reserved action then resolves normally.
|
|
|
|
Likewise, if a reserved target moves before completion but remains targetable
|
|
and inside all declared reach/protection filters, the action follows the same
|
|
unit ID. Its terminal component outcome can be `AO_APPLIED`; the detail reason
|
|
is `TARGET_MOVED_STILL_LEGAL`. Movement alone never creates an avoidance roll.
|
|
|
|
#### 11.2 Terminal Outcome IDs
|
|
|
|
Every started action produces one or more terminal outcome records. Records are
|
|
per effect component and actual recipient; routing/invalidity records can name
|
|
the intended target, and action-level cancellation records have no recipient.
|
|
An area or multi-component action can therefore produce mixed outcomes without
|
|
collapsing them into an invented accuracy value.
|
|
|
|
| Outcome ID | Exact precondition | Ordering point | Player-visible causal label |
|
|
|---|---|---|---|
|
|
| `AO_APPLIED` | Legal component changes HP, barrier, status, resource, order, or another declared authoritative field on its actual recipient | Finalized after priority-7 deltas | `Applied`; use `Followed target` detail for `TARGET_MOVED_STILL_LEGAL` |
|
|
| `AO_NO_CHANGE` | Component reaches a legal recipient but caps/current state yield zero primary change | Finalized after priority-7 deltas | `No change` plus cap/current-state reason |
|
|
| `AO_INVALID_REACH` | Reserved target is living/targetable but current reach or protection filter is illegal | Completion validation at priority 4 | `Out of reach` plus old/current depth and required reach |
|
|
| `AO_INVALID_UNTARGETABLE` | Reserved target is living but a declared Untargetable rule excludes it | Completion validation at priority 4 | `Untargetable` plus rule ID and remaining duration |
|
|
| `AO_INVALID_FALLEN` | Reserved target is absent from the living line because it became Fallen before completion | Completion validation at priority 4 | `Target already Fallen` plus death event ID |
|
|
| `AO_INVALID_FILTER` | Reserved target fails another declared side/state/tag candidate filter after the three specific invalid checks | Completion validation at priority 4 | `Target no longer eligible` plus filter ID |
|
|
| `AO_INTERCEPTED` | Current projectile/protection path deterministically redirects the intended target's component to an eligible interceptor | Routing after target validation at priority 4 | `Intercepted by {actor}` plus path/protection rule ID |
|
|
| `AO_BLOCKED` | An explicit Block charge or named deterministic component blocker nullifies the component before its primary delta | Defense allocation at priority 4; charge/delta commits in priority 7 | `Blocked` plus blocker actor/rule ID |
|
|
| `AO_IMMUNE` | A declared immunity tag fully rejects the component before any resistance draw | Component defense at priority 4 | `Immune` plus immunity tag/source ID |
|
|
| `AO_RESISTED` | A resistible status component is not immune and its stored `status_apply` roll is at/above `ApplyChanceBp` | Component defense at priority 4 | `Resisted` plus roll, threshold, Potency, and Tenacity |
|
|
| `AO_FIZZLED` | Invalid-target policy is `fizzle`, retarget finds no legal candidate, or ground resolution has no eligible occupant | After invalid-target policy at priority 4 | `Action fizzled` plus invalid outcome/filter and refund |
|
|
| `AO_INTERRUPTED` | A started wind-up/channel is cancelled by a declared interrupt before completion | When interrupt commits at priority 4 | `Interrupted` plus interrupter/rule and refund |
|
|
|
|
`AO_BLOCKED` is not generic zero damage: barrier absorption is `AO_APPLIED`
|
|
because barrier state changes. `AO_IMMUNE` requires a named immunity. A legal
|
|
heal into full HP is `AO_NO_CHANGE`, not resistance. `AO_RESISTED` is limited to
|
|
the existing deterministic status-application check and never implies an
|
|
accuracy/evasion stat or a general attack miss.
|
|
|
|
An action summary is derived only for display:
|
|
|
|
```text
|
|
APPLIED_ALL = every primary component record is AO_APPLIED
|
|
APPLIED_PARTIAL = at least one primary component is AO_APPLIED and at least one is not
|
|
APPLIED_NONE = no primary component is AO_APPLIED
|
|
```
|
|
|
|
Summary values are not trigger IDs. The terminal records remain authoritative.
|
|
|
|
#### 11.3 Resolution And Routing Order
|
|
|
|
At priority 4, each completing action uses this order:
|
|
|
|
1. If a committed interrupt already cancelled the action, emit one
|
|
`AO_INTERRUPTED` action record and stop.
|
|
2. Revalidate each reserved target against current state in this first-failure
|
|
order: Fallen/absent, Untargetable, other declared candidate filter, then
|
|
reach/protection. Emit the corresponding `AO_INVALID_*` reservation record.
|
|
3. Apply the skill's declared invalid-target policy. A successful
|
|
`retarget_same_policy` reserves a new target and continues without refund;
|
|
`ground_resolve` snapshots eligible current occupants at the stored depth;
|
|
otherwise emit `AO_FIZZLED` and stop that action.
|
|
4. Resolve current projectile/protection paths. Emit `AO_INTERCEPTED` for each
|
|
redirected intended target, then create component records for the actual
|
|
interceptor. Interception itself never counts as an applied component.
|
|
5. Expand components and area recipients in current depth then stable-ID order.
|
|
A footprint-2 actor is expanded once unless the skill explicitly says once
|
|
per footprint.
|
|
6. Allocate finite Block charges and deterministic blockers. Same-tick claims
|
|
use completion tick, target depth, source stable ID, then action sequence; one
|
|
charge cannot block two components. Emit `AO_BLOCKED` for each allocation.
|
|
7. Check declared immunity before consuming a resistance or critical draw. Emit
|
|
`AO_IMMUNE` on rejection.
|
|
8. For remaining resistible status components, consume and record exactly one
|
|
`status_apply` draw. Emit `AO_RESISTED` when the roll fails the threshold.
|
|
9. Queue remaining numeric/status/movement deltas for priority 7. After the
|
|
simultaneous batch, emit `AO_APPLIED` when primary state changed or
|
|
`AO_NO_CHANGE` when it did not.
|
|
10. At priority 8, derive the action summary, apply at most one declared refund,
|
|
claim outcome triggers, log suppressed duplicates, and queue derived actions
|
|
for the next tick.
|
|
|
|
An invalid reservation record remains terminal evidence even when retarget or
|
|
ground resolution continues. The redirected/new recipient receives its own
|
|
terminal component record, so diagnosis can say both why the original target
|
|
was not affected and what actually happened.
|
|
|
|
#### 11.4 Journal, Refund, And Proc Contract
|
|
|
|
Every outcome record stores:
|
|
|
|
```text
|
|
root_action_id, action_instance_id, action_sequence, skill_id, source_id
|
|
selection_tick, completion_tick, effect_component_id, component_tags
|
|
forecast_target_ids, reserved_target_id, reserved_depth
|
|
actual_recipient_id, current_depth, outcome_id, reason_id
|
|
retarget_policy, resolution_disposition, redirected_action_or_component_id
|
|
interceptor_id, blocker_id, immunity_tag as applicable
|
|
roll_label, draw_index, roll_bp, threshold_bp as applicable
|
|
raw_delta, effective_delta, barrier_delta, status_or_movement_delta
|
|
resource_spent, cooldown_committed, refund_resource, refund_cooldown_ticks
|
|
trigger_claim_keys, fired_trigger_ids, suppressed_trigger_ids
|
|
parent_action_id, derived_depth
|
|
```
|
|
|
|
Resource cost, Readiness, and cooldown are committed when the action starts.
|
|
Default refund/proc behavior is:
|
|
|
|
| Outcome/disposition | Default refund | Primary effect procs |
|
|
|---|---|---|
|
|
| Invalid record followed by successful retarget/ground resolution | none | evaluate only the new actual recipient's component outcomes |
|
|
| `AO_FIZZLED` | skill's declared `fizzle_resource_refund_bp` and `fizzle_cooldown_refund_bp` | no on-hit/on-damage/on-heal/on-status proc |
|
|
| `AO_INTERRUPTED` | skill's declared interrupt refund fields | `on_interrupt` and matching `on_outcome` only |
|
|
| `AO_INTERCEPTED` | none | intended target can observe `AO_INTERCEPTED`; primary procs evaluate on actual recipient only |
|
|
| `AO_BLOCKED`, `AO_IMMUNE`, `AO_RESISTED` | none unless exact ID appears in `refund_on_outcome_ids` | matching defensive hook and `on_outcome` only; no primary effect proc |
|
|
| `AO_NO_CHANGE` | none | `on_outcome` only; no effective-delta proc |
|
|
| `AO_APPLIED` | none | only hooks whose required effective delta/status/movement actually occurred |
|
|
|
|
An outcome refund is evaluated once per root action after every component record
|
|
is final. The skill declares `refund_match = all_primary | any_primary`; default
|
|
is `all_primary`. Refund totals clamp to the originally committed cost/cooldown
|
|
and never multiply by targets, components, or repeated outcome records.
|
|
|
|
Outcome triggers use:
|
|
|
|
```text
|
|
trigger_id, observed_role (source | intended_target | actual_recipient)
|
|
outcome_ids[], component_tags[], once_scope (action | recipient | component)
|
|
allow_derived (default false), resulting_action_or_hook
|
|
```
|
|
|
|
Default `once_scope` is `action`. Before firing, create:
|
|
|
|
```text
|
|
trigger_claim_key = hash(root_action_id, trigger_id,
|
|
scope_recipient_or_component)
|
|
```
|
|
|
|
The first claim fires in outcome-record order; later identical claims log
|
|
`DUPLICATE_TRIGGER_CLAIM` and do nothing. Derived actions keep the same
|
|
`root_action_id`, increment `derived_depth`, and cannot trigger a row with
|
|
`allow_derived=false`. No derived chain can exceed depth 4 or the hook's declared
|
|
per-action cap.
|
|
|
|
### 12. Projectile And Area Resolution
|
|
|
|
A projectile declares speed in footprints per second, collision mode, pierce
|
|
count, and damage retention. At launch:
|
|
|
|
```text
|
|
arrival_ticks = max(1, ceil(cross_line_distance / projectile_speed / 0.1))
|
|
```
|
|
|
|
At arrival, an intercepted projectile traverses the **current** opposing order
|
|
from front to its reserved target and collides with the first eligible actor.
|
|
After each pierced actor, remaining damage is multiplied by the declared
|
|
retention in `0.50-0.90`. A reserved target that moved behind a new protector
|
|
can therefore be intercepted. Lobbed and bypassing projectiles follow their
|
|
declared protection mode instead.
|
|
|
|
Areas are contiguous footprint intervals, not visual circles. An area row
|
|
declares start policy, length, falloff, and whether a footprint-2 actor is hit
|
|
once or once per footprint; default is once per actor.
|
|
|
|
### 13. Healing, Support, And Protection Access
|
|
|
|
Support actions use same-line distance. Default heal selection is lowest HP
|
|
ratio among legal allies; exact ratio ties choose the frontmost, then
|
|
`target_tie`. A heal cannot pass its declared same-line reach because the
|
|
animation appears to span the line.
|
|
|
|
Barrier applies after healing in the same batch and is capped at 40% of target
|
|
MaxHP across all generic barriers. Excess is logged as waste. `Block` is an
|
|
explicit charge that nullifies one eligible hit after interception and before
|
|
barrier. Area, true, or `unblockable` events must declare ineligibility.
|
|
|
|
Protection stacks do not redirect heals. A support Profession may explicitly
|
|
target `nearest_ally_ahead`, `nearest_ally_behind`, or a contiguous interval;
|
|
the predicted support path updates when order changes.
|
|
|
|
### 14. Movement, Push, Pull, And Summons
|
|
|
|
#### 14.1 Reordering Actions
|
|
|
|
Battle movement is an automatic action, never free dragging:
|
|
|
|
- `advance`: swap the actor block with the adjacent actor block ahead;
|
|
- `withdraw`: swap with the adjacent actor block behind;
|
|
- `push N`: move the target toward rear through up to `N` adjacent block swaps;
|
|
- `pull N`: move the target toward front through up to `N` swaps;
|
|
- `insert`: add a legal summon before/after its source;
|
|
- `return`: remove a temporary summon or restore an explicitly stored order.
|
|
|
|
Footprint-2 actors move as indivisible blocks. Simultaneous conflicting intents
|
|
resolve larger declared movement priority first, then higher source Potency,
|
|
then stable source ID. Losing intents fail with `DESTINATION_RESERVED`.
|
|
|
|
Forced movement stops before an `Immovable` actor. Each unresolved forced step
|
|
at a line boundary or blocker deals:
|
|
|
|
```text
|
|
collision_damage = max(1, floor(0.08 * source_PhysicalPower))
|
|
```
|
|
|
|
once per action, not once per blocked step. `Rooted` blocks voluntary movement;
|
|
`Immovable` blocks forced movement. Rejections name `ROOTED`, `IMMOVABLE`,
|
|
`NO_ADJACENT_ACTOR`, `DESTINATION_RESERVED`, or `CAST_LOCKED`.
|
|
|
|
#### 14.2 Summon Footprint
|
|
|
|
Each side has temporary summon allowance of two footprint beyond its committed
|
|
deployment. A summon declares footprint 1 or 2 and insertion side relative to
|
|
its source. It is legal only when:
|
|
|
|
```text
|
|
current_living_footprint + summon_footprint
|
|
<= committed_deployment_footprint + 2
|
|
```
|
|
|
|
A failed insertion spends no summon resource and returns `NO_SUMMON_SPACE`.
|
|
Summons never enter Reserve, hold equipment, create Replacement Credit, count
|
|
as cumulative hires, or persist after battle.
|
|
|
|
#### 14.3 Death Collapse
|
|
|
|
After every death batch, Fallen actors are removed and both lines compact while
|
|
preserving the relative order of survivors. New depths become authoritative
|
|
before summon insertion and objective checks at priority 9. A scheduled action
|
|
that reserved a Fallen target follows its retarget policy; it does not target
|
|
the actor that happened to collapse into the old depth unless it uses
|
|
`ground_resolve`.
|
|
|
|
Reserve Recruits never fill a collapsed position during battle. At the next
|
|
Market the player explicitly chooses any replacement and new order.
|
|
|
|
### 15. Damage, Critical, Healing, And Floors
|
|
|
|
#### 15.1 Damage
|
|
|
|
Skills declare `Base`, `Coefficient`, `PowerSource`, type, pierce, protection
|
|
mode, and whether critical is legal.
|
|
|
|
```text
|
|
RawDamage = max(0, Base + Coefficient * selected_power)
|
|
EffectiveDefense = clamp(relevant_defense - pierce, -75, 500)
|
|
|
|
if EffectiveDefense >= 0:
|
|
DefenseMultiplier = 100 / (100 + EffectiveDefense)
|
|
else:
|
|
DefenseMultiplier = 2 - 100 / (100 - EffectiveDefense)
|
|
|
|
CritChanceBp = clamp(skill_base_crit_bp
|
|
+ 20*(source_Finesse - target_Resolve)
|
|
+ explicit_crit_bp,
|
|
0, 5000)
|
|
crit = can_crit and critical_roll_bp < CritChanceBp
|
|
CritMultiplier = clamp(declared_crit_multiplier, 1.25, 2.00) if crit else 1.00
|
|
|
|
OutgoingGroup = clamp(1 + sum(outgoing_modifiers), 0.25, 3.00)
|
|
IncomingGroup = clamp(1 + sum(incoming_modifiers), 0.25, 3.00)
|
|
EncounterGroup = clamp(1 + sum(encounter_modifiers), 0.25, 3.00)
|
|
GroupProduct = clamp(OutgoingGroup * IncomingGroup * EncounterGroup, 0.10, 4.00)
|
|
|
|
Mitigated = RawDamage * DefenseMultiplier * CritMultiplier * GroupProduct
|
|
Screened = Mitigated * max(0.70, 1 - 0.15*protection_stacks)
|
|
DamageFloor = ceil(0.04 * RawDamage)
|
|
FinalDamage = 0 if explicit_nullification else
|
|
max(1, DamageFloor, floor(Screened))
|
|
HPDamage = max(0, FinalDamage - barrier_absorbed)
|
|
```
|
|
|
|
Use `Screened=Mitigated` for non-screened modes. Guard defends physical, Ward
|
|
defends arcane, and true damage uses zero defense, cannot critical, ignores
|
|
screening, and is capped at 25% target MaxHP per non-boss event. Barrier absorbs
|
|
after the floor; an explicit nullification can produce zero. No other ordinary
|
|
reduction bypasses the four-percent floor.
|
|
|
|
Fixed boundary: `RawDamage=125`, defense `500`, GroupProduct `0.10`, no critical,
|
|
and no screening yields `2.083...` before the floor. `DamageFloor=5`, so the
|
|
resolved damage is `5` before barrier. Explicit nullification yields zero.
|
|
|
|
#### 15.2 Healing
|
|
|
|
```text
|
|
RawHeal = max(0, Base + Coefficient * HealingPower)
|
|
FinalHeal = floor(RawHeal * OutgoingHealGroup * ReceivedHealGroup)
|
|
EffectiveHeal = min(FinalHeal, MaxHP - current_HP)
|
|
```
|
|
|
|
Healing does not critical unless the skill declares a visible healing-critical
|
|
rule. Overheal has no effect unless one named hook converts it; conversion is
|
|
capped at 50% of overheal and still respects the barrier cap.
|
|
|
|
### 16. Status Effects And Resolution Order
|
|
|
|
Each status declares:
|
|
|
|
```text
|
|
status_id, category, source_id, target_id
|
|
base_chance_bp, potency_source, resist_stat
|
|
duration_ticks, interval_ticks, max_stacks
|
|
stack_rule (add | refresh | replace | independent)
|
|
dispel/immunity tags, on_apply, on_tick, on_expire
|
|
presentation and causal-report events
|
|
```
|
|
|
|
For a resistible status:
|
|
|
|
```text
|
|
ApplyChanceBp = clamp(BaseChanceBp + 25*(Potency - Tenacity), 500, 9500)
|
|
applies = status_apply_roll_bp < ApplyChanceBp
|
|
```
|
|
|
|
Guaranteed effects bypass the roll but not declared immunity. If simultaneous
|
|
effects apply the same status, resolve `replace`, then `add`, then `refresh`,
|
|
then independent instances by source ID. Expiry occurs before periodic ticks,
|
|
so a status with `end_tick == current_tick` does not tick again.
|
|
|
|
Hard control effective duration is:
|
|
|
|
```text
|
|
duration = clamp(BaseDuration * 100/(100 + Tenacity), 0.2, 3.0)
|
|
```
|
|
|
|
After hard control ends, the actor gains four seconds of Control Guard, reducing
|
|
new hard-control duration by 75%. No actor may be unable to act for more than
|
|
50% of any rolling ten-second window. Boss caps and immunities are previewed.
|
|
|
|
### 17. Equipment, Artifacts, And Hooks
|
|
|
|
Each Recruit has exactly one universal active equipment slot. An item may be
|
|
presented as armament, guard, garb, charm, or kit, but those forms describe
|
|
eligibility, attachment and visual language rather than simultaneous body
|
|
slots. A Recruit therefore owns zero or one `equipped_item_id`.
|
|
|
|
Equipping a candidate is one atomic replacement: the candidate leaves run
|
|
inventory, the previous item returns to inventory if present, and the Recruit's
|
|
single slot points to the candidate. An Artifact is a shared run rule and never
|
|
occupies this slot. An item may react to an allied Recruit's item event, but no
|
|
effect may require its wearer to hold a second item.
|
|
|
|
All 320 equipment rows must alter a decision and declare one named hook. Scalar
|
|
budget may support the hook but cannot be the row's only identity. One item ID
|
|
cannot be owned by two Recruits. Company-unique effects declare a unique group;
|
|
later copies retain local behavior but show the shared portion dormant.
|
|
|
|
Artifacts are shared run rules. A run holds at most three active Artifacts. A
|
|
fourth acquisition requires replacing one or declining the new row after an
|
|
exact before/after preview. All 60 must change formation, targeting, market,
|
|
risk, resource, promotion, summon, or reward valuation and must name a conflict.
|
|
|
|
Legal battle hooks are:
|
|
|
|
```text
|
|
before_target, after_target, before_cast, on_cast_complete, on_interrupt,
|
|
before_damage, after_damage, on_critical, before_heal, after_heal,
|
|
on_barrier_break, on_status_apply, on_status_expire,
|
|
on_advance, on_withdraw, on_push, on_pull, on_protection_change,
|
|
on_resource_gain, on_signature, on_summon, on_fallen, after_collapse
|
|
```
|
|
|
|
Each hook has a trigger limit, minimum 0.5-second cooldown unless a fixed loop
|
|
proof allows otherwise, and recursion declaration. Derived events cannot
|
|
retrigger their source by default.
|
|
|
|
### 18. Death, Dismissal, And Replacement
|
|
|
|
#### 18.1 Battle Death
|
|
|
|
At zero HP, a non-summon Recruit becomes Fallen:
|
|
|
|
- it cannot act, target, protect, receive healing, or be promoted;
|
|
- its death trigger resolves once at priority 8;
|
|
- it is removed for line collapse;
|
|
- no timer, item, Profession, Artifact, difficulty rule, or Reserve state can
|
|
respawn, resurrect, or substitute for it during the encounter;
|
|
- its recruit ID and optional equipped item ID enter the pending outcome
|
|
casualty set;
|
|
- it remains visible in diagnosis with the preceding five seconds of causes.
|
|
|
|
At outcome application, each casualty and all returned equipment commit with
|
|
Replacement Credit as one transaction. Victory, defeat, timeout, and retreat
|
|
do not change the permanence of already Fallen Recruits.
|
|
|
|
#### 18.2 Dismissal
|
|
|
|
Dismissal is a Market-only operation. It is illegal for a deployed Recruit in a
|
|
committed battle, a Fallen Recruit awaiting outcome, or a Recruit referenced by
|
|
a pending promotion. It also uses the product authority's canonical count:
|
|
|
|
```text
|
|
live_recruit_count = count(unique Recruit IDs owned by the current run
|
|
whose state is living,
|
|
across deployed party and Reserve)
|
|
dismissal_legal = Market_open and target_is_living and
|
|
live_recruit_count >= 2
|
|
```
|
|
|
|
The preview and commit independently evaluate `dismissal_legal`. Review shows
|
|
equipment return, rebate, lost level/history, and resulting deployable
|
|
footprint. A legal confirm removes the Recruit once and asserts the post-state
|
|
has `live_recruit_count >= 1`.
|
|
|
|
If the count is one, or a once-valid preview becomes stale after another
|
|
serialized operation leaves one living Recruit, reject `FINAL_LIVE_RECRUIT`.
|
|
The rejection removes no Recruit or equipment, grants no Coin or Replacement
|
|
Credit, changes no counters, and creates no protected recovery offer. Repeated
|
|
confirm returns the same rejection. An interrupted pending dismissal must
|
|
revalidate the active count before it can apply and is rejected without mutation
|
|
when that count is one.
|
|
|
|
Dismissal never creates Replacement Credit or a Meta reward. Explicit run
|
|
abandonment is a distinct Market/result transaction, and casualty-driven
|
|
replacement/Company collapse remains exclusive to casualty settlement.
|
|
|
|
#### 18.3 Replacement
|
|
|
|
Replacement is an ordinary hire using Coin and eligible Replacement Credit.
|
|
After a casualty, the next Market protects one base-Profession recovery offer
|
|
from refresh until bought or explicitly declined. Reserve never fills the line
|
|
automatically. A run closes only when the player cannot legally deploy one
|
|
living Recruit after all available recovery purchases or explicitly abandons.
|
|
A blocked final dismissal never creates or refreshes this offer and never invokes
|
|
the casualty recovery predicate.
|
|
|
|
### 19. Battle Control, Retreat, Timeout, And Outcome
|
|
|
|
Pause, 1x, 2x, and 4x affect presentation only. Inspection while paused can
|
|
open actor state, target reasoning, current action, queued major event, line
|
|
depths, protection, status order, and causal log.
|
|
|
|
Retreat becomes legal at tick 50 (5.0 seconds). Confirm begins a 20-tick global
|
|
withdrawal. Automatic actions continue and Recruits can fall during it. At the
|
|
end of the 20th tick, living allied non-summon actors escape and outcome becomes
|
|
retreat. Repeating retreat confirm returns the same command receipt.
|
|
|
|
At 120 seconds, Overtime begins:
|
|
|
|
```text
|
|
for each completed 10-second Overtime band:
|
|
all damage dealt multiplier += 0.25
|
|
all healing and new barrier multiplier -= 0.20
|
|
damage bonus caps at +0.75; healing/barrier penalty caps at -0.60
|
|
```
|
|
|
|
At tick 1500, if the objective is not satisfied, the result is timeout defeat.
|
|
Living Recruits survive; Fallen Recruits remain lost. Timeout grants no battle
|
|
reward and applies the failure transaction in the save authority.
|
|
|
|
Victory is evaluated after simultaneous damage, death, collapse, and summon
|
|
events. If both victory and all-allied-Fallen predicates become true in the same
|
|
batch, the declared objective decides: elimination objectives are a mutual
|
|
defeat; survival/objective-complete battles are victory with zero living
|
|
deployed Recruits and can close the run if no Reserve remains.
|
|
|
|
### 20. Enemy And Battle-Offer Contract
|
|
|
|
Every ordinary/elite enemy and boss row declares:
|
|
|
|
```text
|
|
enemy_id, ordinary | elite | boss, Region/Contract placement
|
|
footprint, preferred depth, movement rules
|
|
basic/technique/signature actions and automatic priority
|
|
target/protection/reach/projectile/heal/summon rules
|
|
first-major-action window and telegraph
|
|
phase predicates, immunities, counter tags, two rational responses
|
|
interaction with at least two older mechanics
|
|
first-failure contribution and fixed fixtures
|
|
```
|
|
|
|
Before commitment, a battle offer shows exact enemy order and footprint, target
|
|
policies, protection bypass, earliest major windows, movement, summon allowance,
|
|
resistances, objective, timeout, retreat consequence, Coin reward, and reward
|
|
family. A hidden visual identity may remain concealed for discovery; a mechanic
|
|
needed for a fair choice may not.
|
|
|
|
The complete envelope is 100 ordinary/elite identities plus 16 bosses across 8
|
|
Regions. Presentation variants do not count toward those totals.
|
|
|
|
### 21. Causal Diagnosis
|
|
|
|
The result report presents, in order:
|
|
|
|
1. committed allied and enemy order;
|
|
2. predicted and actual first targets;
|
|
3. first divergence from predicted action/target/timing;
|
|
4. first protection change, projectile interception, or reach failure;
|
|
5. first decisive major action and available pre-battle counter categories;
|
|
6. every Fallen Recruit with the preceding five simulation seconds;
|
|
7. collapse, displacement, summon-space, retarget, and proc-suppression events;
|
|
8. effective damage, prevention, healing, control, and resource totals;
|
|
9. reward/failure/casualty operation IDs in technical details.
|
|
|
|
The report can compare one alternate order or equipment removal against the same
|
|
seed in a no-reward rehearsal. It may explain categories but cannot auto-equip a
|
|
supposed universal answer.
|
|
|
|
### 22. Content Schemas
|
|
|
|
#### 22.1 Skill Row
|
|
|
|
```text
|
|
skill_id, owner_profession_id, tags, timing
|
|
candidate filters, target policy, protection mode, retarget policy
|
|
invalid-target outcome mapping, fizzle/interrupt refund fields
|
|
reach, projectile/area/movement/summon fields
|
|
Base, Coefficient, PowerSource, type, pierce, critical rule
|
|
resource cost/gain, cooldown, status rows, proc hooks
|
|
refund_on_outcome_ids, refund_match
|
|
outcome triggers (trigger_id, observed_role, AO_* outcome_ids,
|
|
component_tags, once_scope, allow_derived, resulting action/hook)
|
|
automatic priority condition, illegal reasons, causal labels
|
|
animation/VFX/SFX/caption/reduced-motion requirements
|
|
fixed input and expected event output
|
|
```
|
|
|
|
#### 22.2 Equipment Row
|
|
|
|
```text
|
|
equipment_id, form, acquisition band, list-price inputs
|
|
eligible Profession/action tags
|
|
changed decision and named behavior hook
|
|
stat support budget, trigger, cooldown, recursion
|
|
order/target/timing/resource/risk tradeoff
|
|
two interaction fixtures and one loss/comparison fixture
|
|
presentation/accessibility/localization needs
|
|
```
|
|
|
|
#### 22.3 Artifact Row
|
|
|
|
```text
|
|
artifact_id, unlock/offer placement, shared hook
|
|
run rule changed, conflict group, replacement comparison
|
|
Market/battle/risk inputs and outputs
|
|
two builds whose valuation changes in opposite directions
|
|
fixed seed fixture and causal event
|
|
```
|
|
|
|
#### 22.4 Battle Offer Row
|
|
|
|
```text
|
|
battle_offer_id, Region, Contract, Round, risk tier
|
|
enemy lineup/order/levels, seed labels, objective
|
|
preview fields, Coin reward, reward family, retreat/failure receipt
|
|
expected duration, profile assertion, causal-report requirements
|
|
```
|
|
|
|
### 23. Static Validation Gates
|
|
|
|
- all Profession topology counts and parent links reconcile to 42;
|
|
- all 36 Traits have one benefit, one cost, and two decision fixtures;
|
|
- all 320 equipment and 60 Artifact rows name a behavior change;
|
|
- all 100 ordinary/elite and 16 boss rows create a distinct counter question;
|
|
- no deployed footprint exceeds current Line capacity;
|
|
- target preview equals the first simulated target for fixed fixtures;
|
|
- identical state/seed/retreat inputs yield identical hashes at every tick;
|
|
- all actions stay inside reach, critical, defense, barrier, control, proc, and
|
|
timeout bounds;
|
|
- every projectile and movement conflict produces the declared collision/order;
|
|
- every death returns the exact owned equipment once and never introduces a
|
|
Reserve automatically;
|
|
- invalid actions spend no resource and return one enumerated reason;
|
|
- every started action/component has a declared terminal `AO_*` record and every
|
|
outcome trigger references only IDs in Section 11.2;
|
|
- duplicate trigger claims are journaled and cannot fire twice;
|
|
- all standard fixtures resolve or timeout at/before tick 1500.
|
|
|
|
### 24. Required Fixed Test Vectors
|
|
|
|
| Vector | Setup | Expected result |
|
|
|---|---|---|
|
|
| Frontbound | Three enemies; target policy frontmost | Only depth-zero enemy is a candidate |
|
|
| Screened rear | Target has two footprint ahead | Legal screened hit uses `0.70` multiplier |
|
|
| Cross-line reach | source depth 1, target depth 2, reach 4 | distance `1+1+2=4`, legal; reach 3 is illegal |
|
|
| Heal tie | Two allies at equal HP ratio in reach | Frontmost wins, then deterministic tie draw |
|
|
| Projectile intercept | Reserved rear target gains a new front summon before arrival | Current front summon is hit unless bypass/lobbed |
|
|
| Push footprint | Push a footprint-2 target one step | Whole block swaps; it never splits |
|
|
| Blocked push | Forced move has two remaining steps at boundary | One collision event, not two |
|
|
| Summon full | committed footprint 10 plus temporary footprint 2 | another footprint-1 summon fails `NO_SUMMON_SPACE` |
|
|
| Simultaneous lethal | Two actors complete lethal actions in one batch | Both events apply, both actors become Fallen |
|
|
| Death collapse | Front actor falls before a reserved cast completes | Survivors compact; cast follows its declared retarget rule |
|
|
| Reserve behavior | Deployed actor falls while living Reserve exists | Reserve does not enter until next Market preparation |
|
|
| Damage floor | Raw 125, defense 500, group product 0.10 | damage 5 before barrier; explicit nullification 0 |
|
|
| Critical boundary | roll 2499 with chance 2500 bp | critical; roll 2500 is not |
|
|
| Status expiry | periodic tick equals end tick | expiry occurs; no final periodic tick |
|
|
| Retreat | confirm at tick 50, no terminal predicate | outcome retreat after tick 70 batch; later duplicate has no effect |
|
|
| Speed parity | identical battle at 1x and 4x | all authoritative hashes equal |
|
|
| Timeout | objective incomplete at tick 1200/1500 | Overtime applies, then timeout defeat at tick 1500 |
|
|
| Casualty equipment | Fallen Recruit owns one item | Recruit removed and that item ID returned exactly once; null slot returns nothing |
|
|
| Final dismissal | one living Recruit across deployed plus Reserve | preview/commit reject `FINAL_LIVE_RECRUIT`; no state or recovery-offer change |
|
|
| Moved target remains legal | Reserved target changes depth but remains targetable and in reach | same ID receives `AO_APPLIED`; detail `TARGET_MOVED_STILL_LEGAL`; no refund |
|
|
| Moved target leaves reach | Reserved target moves from distance 2 to 3 against reach 2; policy `fizzle` | `AO_INVALID_REACH` then `AO_FIZZLED`; declared fizzle refund once |
|
|
| Target becomes Untargetable | Living reserved target gains declared Untargetable before completion | `AO_INVALID_UNTARGETABLE`; row retarget/fizzle policy applies; no resistance draw |
|
|
| Target falls first | Reserved target becomes Fallen in an earlier tick | `AO_INVALID_FALLEN` names death event; collapsed-depth actor is not substituted |
|
|
| Interception | New front protector intercepts projectile aimed at legal rear target | intended target `AO_INTERCEPTED`; protector gets its own component outcome |
|
|
| Explicit Block | Actual recipient owns one eligible Block charge | `AO_BLOCKED`; one charge consumed; no primary proc or default refund |
|
|
| Immunity | Status target has matching immunity tag | `AO_IMMUNE`; no `status_apply` draw and no status proc |
|
|
| Resistance | No immunity; stored roll 7000, threshold 6500 | `AO_RESISTED` with roll/threshold; no general attack miss |
|
|
| Mixed area | Three recipients: one changed, one immune, one already capped | `AO_APPLIED`, `AO_IMMUNE`, `AO_NO_CHANGE`; summary `APPLIED_PARTIAL` |
|
|
| Duplicate trigger suppression | Mixed area yields three matching records for one once-per-action trigger | first claim fires; later claims log `DUPLICATE_TRIGGER_CLAIM`; one derived action next tick |
|
|
|
|
### 25. Acceptance Checklist
|
|
|
|
- [ ] One ordered line owns protection, reach, projectiles, healing, movement,
|
|
summon footprint, death collapse, and replacement behavior.
|
|
- [ ] Battle formulas define tick order, random labels, damage floor, critical,
|
|
status order, timeout, and fixed vectors.
|
|
- [ ] Deterministic outcomes distinguish forecast divergence, target
|
|
invalidation, interception, Block, immunity, resistance, application, and
|
|
duplicate trigger suppression without accuracy/evasion.
|
|
- [ ] Generated Recruit and fixed Profession schemas are complete without
|
|
requiring authored individual narrative content.
|
|
- [ ] Fully automatic combat exposes only the approved diagnostic controls and
|
|
retreat.
|
|
- [ ] Content counts and no-filler tests match the product authority.
|
|
- [ ] Save/failure owns durable death and operation recovery; economy owns all
|
|
purchase, reward, capacity, and pacing numbers.
|
|
|
|
<!-- Historical section source: ECONOMY_AND_BALANCE; retained in Git history. -->
|
|
|
|
## Aetherbound Guild: Economy And Balance Authority
|
|
|
|
> Authority: Market offers, prices, locks, refreshes, Coin and Replacement
|
|
> Credit, rewards, progression rates, Line-capacity selection, difficulty
|
|
> parameters, first-clear pacing, solvency, recovery, and anti-dominance tests.
|
|
>
|
|
> Revision: anti-dominance-fixture-r1 / 2026-08-11
|
|
>
|
|
> [01_GAME_DESIGN.md](01_GAME_DESIGN.md) owns the player promise
|
|
> and content envelope. [02_GAMEPLAY_AND_BALANCE.md](02_GAMEPLAY_AND_BALANCE.md) owns
|
|
> battle and content schemas. [02_GAMEPLAY_AND_BALANCE.md](02_GAMEPLAY_AND_BALANCE.md)
|
|
> owns exactly-once application, run failure, interruption, and recovery.
|
|
|
|
### 1. Economy Invariants
|
|
|
|
1. All gameplay value is earned in play. There is no paid currency, paid
|
|
reroll, ad reward, energy, streak, offline income, or real-time wait.
|
|
2. Coin and Replacement Credit exist only in the current run. Renown and
|
|
progression facts persist, but cannot buy raw combat stats, starting Coin,
|
|
or starting Line capacity.
|
|
3. Every Market contains one steady battle offer. A random offer or refresh is
|
|
never required to obtain a baseline counter for a mandatory boss.
|
|
4. Death is costly through lost level, Profession, Trait, history, and order,
|
|
but returned equipment and Credit make ordinary replacement recoverable.
|
|
5. Dismissal, selling, refresh, lock, reward, and purchase formulas cannot
|
|
create Coin-positive loops.
|
|
6. All 320 equipment and 60 Artifact rows change a decision. Economy tiers and
|
|
prices may not turn a scalar variant into a gameplay identity.
|
|
7. Reward, Market, Recruit-generation, and combat random streams are separated.
|
|
Reload, speed, input repeat, and unrelated draws do not change offers.
|
|
8. The Standard first clear is solvent on deterministic steady offers without
|
|
Meta power, random high-tier equipment, or repeated failed-run farming.
|
|
|
|
### 2. Resource Ledger
|
|
|
|
| ID | Player name | Persistence/cap | Sources | Sinks or closure |
|
|
|---|---|---|---|---|
|
|
| `coin` | Coin | Current run; nonnegative; no hard cap | Run start, battle rewards, reward cards, equipment sales, dismissal rebate | Recruit/equipment purchase, refresh, lock; cleared when run closes |
|
|
| `replacement_credit` | Replacement Credit | Current run; cap 54 | Fallen non-summon Recruits | Optional reduction on a later Recruit hire; cleared when run closes |
|
|
| `xp` | Experience | Current Recruit; level cap 10 | Completed battles entered by that Recruit | Level thresholds; disappears with Recruit/run close |
|
|
| `renown` | Renown | Guild; cap 9,999 | first Contract clears, first discoveries, mastery goals, limited repeat objective | Catalog breadth, previews, challenge laws, hidden-hybrid discovery clues |
|
|
| `clear_fact` | Region/Contract clear | Guild fact, not spendable | first valid boss result | Region order, ending, postgame access |
|
|
| `discovery_fact` | Catalog discovery | Guild fact, not spendable | first valid inspection/acquisition/encounter | Collection, deterministic future pool access, completion |
|
|
|
|
There is no material or crafting currency in the base contract. Equipment is
|
|
bought, rewarded, equipped, returned after death, sold, or closed with the run.
|
|
Adding a persistent resource requires an Owner-approved contract revision.
|
|
|
|
### 3. Run Opening And Capacity Economy
|
|
|
|
A run begins with:
|
|
|
|
- two of four provisional generated Recruits selected at zero Coin price;
|
|
- `52 Coin`;
|
|
- zero Replacement Credit;
|
|
- Line capacity four;
|
|
- exactly one starting item on each provisional Recruit and no extra equipped
|
|
slots;
|
|
- no active Artifact.
|
|
|
|
The first Market contains at least two base-Profession Recruit offers at level
|
|
1. The two together cost 28 Coin, leaving 24 for an equipment, lock, refresh,
|
|
or reserve decision.
|
|
|
|
For `C` cleared ordinary Rounds in `0..12`:
|
|
|
|
```text
|
|
line_capacity(C) = min(10, 4 + floor(C/2))
|
|
```
|
|
|
|
Capacity therefore becomes `4,5,6,7,8,9,10` at clear counts
|
|
`0,2,4,6,8,10,12`. It is granted automatically after result settlement, is
|
|
not sold, and resets next run. Purchases and cumulative hires do not change it.
|
|
|
|
### 4. Market Board And Offer Generation
|
|
|
|
Every ordinary Market contains:
|
|
|
|
| Row | Slots | Generation constraint |
|
|
|---|---:|---|
|
|
| Recruit | 3 | At least one base Profession; after a casualty, one protected recovery offer |
|
|
| Equipment | 4 | At least three different behavior tags and two legal current users unless Company is empty |
|
|
| Battle | 3 | Exactly one steady offer and two distinct higher-risk or different-counter offers |
|
|
|
|
Boss Muster uses the same Recruit/equipment counts but one fixed boss offer.
|
|
It cannot refresh or replace the boss.
|
|
|
|
Each Market is generated from:
|
|
|
|
```text
|
|
hash(run_seed, contract_id, Market_index, row_id, slot_index, refresh_index)
|
|
```
|
|
|
|
Labeled substreams are `recruit`, `equipment`, `battle`, and `reward`. An offer
|
|
stores its complete generated fields and hash before presentation. Reopening,
|
|
changing language, inspecting another screen, or closing the application does
|
|
not redraw it.
|
|
|
|
#### 4.1 Offer Level And Pool
|
|
|
|
For ordinary Round `R` in `1..12`:
|
|
|
|
```text
|
|
offer_level(R) = min(10, 1 + floor((R-1)/3))
|
|
```
|
|
|
|
Boss Muster uses the Round-12 offer level. Base Professions remain in every
|
|
pool. Regular advanced direct hires may appear from Round 7 after their catalog
|
|
unlock. Hidden hybrids are promotion outcomes and cannot be bought directly.
|
|
|
|
Equipment uses Region band `B` in `0..7` and item tier `T` in `0..3`. At least
|
|
one tier-0/1 legal item remains possible in every ordinary Market. A boss
|
|
counter has one deterministic Region objective or fixed Muster offer; it is
|
|
never refresh-gated.
|
|
|
|
#### 4.2 Lock
|
|
|
|
The player may lock at most three unbought Recruit/equipment offers. Each newly
|
|
locked offer costs `2 Coin` once and persists through the next Market or until
|
|
bought/declined. Unlocking does not refund Coin. Battle offers can be locked
|
|
only within the current Market so the player can refresh other rows; they never
|
|
carry beyond a committed battle.
|
|
|
|
The protected recovery offer needs no paid lock, survives refresh, and expires
|
|
only when bought, explicitly declined, or the run closes.
|
|
|
|
#### 4.3 Refresh
|
|
|
|
Refreshing replaces every unlocked, unbought offer except a fixed boss and a
|
|
protected recovery offer. Let `N` be prior refreshes in the current Market and
|
|
`L` the number of paid locked offers:
|
|
|
|
```text
|
|
refresh_cost(N,L) = min(24, 6 + 3*N + 2*L)
|
|
```
|
|
|
|
`N` starts at zero each Market and increments atomically with the new board.
|
|
The first no-lock refresh costs 6, the second 9, and the seventh and later cost
|
|
24. Insufficient Coin rejects the operation without changing offers or `N`.
|
|
|
|
### 5. Prices, Sales, And Dismissal
|
|
|
|
#### 5.1 Recruit Price
|
|
|
|
For offer level `L` and Profession tier `P` (`0` base, `1` regular advanced):
|
|
|
|
```text
|
|
hire_list_price(L,P) = 10 + 4*L + 10*P
|
|
```
|
|
|
|
Trait and rolled distribution do not add a hidden price because level totals
|
|
are equal-budget and every Trait has a cost. A Recruit's generated offer stores
|
|
the list price.
|
|
|
|
Replacement Credit is optional at confirm. If `use_credit` is true:
|
|
|
|
```text
|
|
credit_used = min(replacement_credit_balance, hire_list_price)
|
|
coin_cost = hire_list_price - credit_used
|
|
```
|
|
|
|
If false, `credit_used=0`. Coin cost, Credit spend, Recruit creation, and offer
|
|
removal apply atomically. Credit cannot buy equipment, refreshes, locks, or
|
|
Meta progression.
|
|
|
|
#### 5.2 Equipment Price And Sale
|
|
|
|
For Region band `B` in `0..7` and item tier `T` in `0..3`:
|
|
|
|
```text
|
|
equipment_list_price(B,T) = 12 + 3*B + 6*T
|
|
sale_value = floor(0.30 * coin_price_paid)
|
|
```
|
|
|
|
An equipment reward stores `coin_price_paid=0` and therefore sells for zero;
|
|
its value is the behavior it enables. Purchased copies never sell above 30%.
|
|
Locked, equipped, or battle-committed items must be released before sale.
|
|
|
|
#### 5.3 Dismissal Rebate
|
|
|
|
The product authority's canonical count and precondition apply before rebate
|
|
calculation:
|
|
|
|
```text
|
|
live_recruit_count = count(unique Recruit IDs owned by the current run
|
|
whose state is living,
|
|
across deployed party and Reserve)
|
|
dismissal_legal = Market_open and target_is_living and
|
|
live_recruit_count >= 2
|
|
```
|
|
|
|
Only for a legal dismissal:
|
|
|
|
```text
|
|
if survived_battles >= 2:
|
|
dismissal_rebate = min(10, floor(0.25 * coin_price_paid))
|
|
else:
|
|
dismissal_rebate = 0
|
|
```
|
|
|
|
`coin_price_paid` excludes Credit. Equipment returns before the Recruit is
|
|
removed, all in one operation. Dismissal creates no Credit, Renown, XP transfer,
|
|
offer reroll, or reward. A same-level replacement always costs more Coin than
|
|
the rebate unless Credit came from an unrelated death.
|
|
|
|
When `live_recruit_count == 1`, preview and commit reject
|
|
`FINAL_LIVE_RECRUIT` before the rebate formula. The blocked action grants zero
|
|
Coin, zero Credit, no protected recovery offer, and no economic or roster
|
|
mutation. `Abandon Run` remains a separately confirmed run-close operation and
|
|
does not pay a dismissal rebate.
|
|
|
|
Counterexample trace:
|
|
|
|
```text
|
|
state: Market open; one living Recruit; Coin=3; Credit=0;
|
|
no protected recovery offer; Recruit paid 32 Coin and survived 3 battles
|
|
attempt: preview dismissal, then submit the same confirm after interruption
|
|
result: FINAL_LIVE_RECRUIT at preview and commit
|
|
after: one living Recruit; Coin=3; Credit=0; no protected offer;
|
|
dismissed_total unchanged; calculated dismissal rebate is not applied
|
|
next legal terminal choice: continue Market play or confirm Abandon Run
|
|
```
|
|
|
|
The hypothetical eight-Coin rebate cannot make the final dismissal legal.
|
|
Casualty settlement may later create Credit or a protected offer if the Recruit
|
|
falls; it is not simulated by dismissal.
|
|
|
|
### 6. Battle Income And Reward Drafts
|
|
|
|
Battle risk `Q` is `0` steady, `1` pressured, or `2` elite. For Region band
|
|
`B` in `0..7` and ordinary Round `R`:
|
|
|
|
```text
|
|
battle_coin(B,R,Q) =
|
|
floor((18 + 2*R + 2*B + 7*Q) * difficulty_reward_multiplier)
|
|
reward_budget(B,R,Q) = 12 + 2*R + 3*B + 6*Q
|
|
```
|
|
|
|
Coin pays once on victory before the reward choice. Defeat, timeout, and retreat
|
|
pay no battle Coin. Economic consequences after those outcomes are owned only
|
|
by the save/failure authority.
|
|
|
|
The victory reward draft is stable:
|
|
|
|
| Risk | Cards | Required composition |
|
|
|---|---:|---|
|
|
| Steady | 2 choose 1 | one legal equipment/tag card; one `floor(0.50*reward_budget)` Coin card |
|
|
| Pressured | 3 choose 1 | two distinct legal equipment/tag cards; one Coin or Recruit-discount card |
|
|
| Elite | 3 choose 1 | one Artifact-eligible card, one equipment/tag card, one Coin/recovery card |
|
|
|
|
An Artifact card offers three fixed Artifact IDs and the player chooses one.
|
|
If three Artifacts are already active, comparison/replacement is part of the
|
|
same operation. Declining a reward is legal and grants nothing. A reward card
|
|
cannot be rerolled by leaving the result.
|
|
|
|
The boss awards no spendable Coin because the run closes. A first boss clear
|
|
grants the persistent values in Section 9 and one fixed discovery choice.
|
|
|
|
### 7. Death Credit And Recovery Solvency
|
|
|
|
When a hired or provisional Recruit becomes Fallen, use its original list price
|
|
and actual Coin paid:
|
|
|
|
```text
|
|
credit_grant = min(original_list_price,
|
|
10 + floor(coin_price_paid/2))
|
|
new_credit_balance = min(54, old_credit_balance + credit_grant)
|
|
```
|
|
|
|
Credit, permanent Recruit removal, and equipment return apply in one casualty
|
|
operation. A provisional Recruit has `coin_price_paid=0` and an implicit level-1
|
|
base list price of 14, so it grants 10 Credit. Summons grant none.
|
|
|
|
After one or more casualties, the next Market reserves one base-Profession
|
|
recovery offer with all of these properties:
|
|
|
|
- its level is `max(1, offer_level-1)`;
|
|
- its Profession supplies at least one missing primary responsibility among
|
|
protection, damage, reach, or sustain when such a catalog row is unlocked;
|
|
- it is not replaced by refresh and needs no lock fee;
|
|
- its list price uses the normal formula and Credit can reduce it to zero;
|
|
- buying or explicitly declining it clears protection, not remaining Credit.
|
|
|
|
The guarantee makes a replacement visible, not automatically optimal. A lost
|
|
advanced or promoted Recruit returns as a cheaper base Recruit unless the
|
|
player chooses to spend more on another offer.
|
|
|
|
#### 7.1 Three-Death Fixture
|
|
|
|
At Round 7, three base Recruits originally bought for 18 Coin each fall. The
|
|
current base list price is 22, the player has 12 Coin, and Credit cap is 54:
|
|
|
|
```text
|
|
grant per Recruit = min(22, 10 + floor(18/2)) = 19
|
|
balance after three grants = min(54, 57) = 54
|
|
replacement Coin costs = 0, 0, 12
|
|
ending Coin = 0; ending Credit = 0
|
|
```
|
|
|
|
Three legal replacements are therefore purchasable in the next Market. The
|
|
player has lost their levels, Traits, promotion progress, history, and all
|
|
liquid flexibility, so the loss is recoverable but not profitable.
|
|
|
|
### 8. XP, Levels, And Promotion Pace
|
|
|
|
For Recruit level `L` from 1 through 9:
|
|
|
|
```text
|
|
xp_to_next(L) = 20 + 10*L
|
|
battle_xp(R,Q) = 10 + 2*R + 4*Q
|
|
```
|
|
|
|
Living deployed Recruits receive 100% on victory, 50% on defeat/timeout when at
|
|
least one deployed Recruit survives, and 50% on completed retreat. Reserve and
|
|
Fallen Recruits receive zero. Difficulty and display speed do not multiply XP.
|
|
|
|
XP is applied after casualty determination. A Recruit can gain multiple levels
|
|
from one award, checking the formula at each new level, but cannot exceed 10.
|
|
At cap, excess is discarded and shown. Promotion eligibility at level 4 plus
|
|
three survived battles is owned by the systems authority.
|
|
|
|
There is no permanent Recruit level, inheritance, XP item, or Meta stat. New
|
|
hires catch up through offer level and price rather than receiving another
|
|
Recruit's progress.
|
|
|
|
#### 8.1 Enemy Rank And Within-Run Scaling
|
|
|
|
Regions add mechanics, not a permanent vertical stat ladder. Within a run,
|
|
enemy stat pressure follows the current offer level and risk:
|
|
|
|
```text
|
|
encounter_rank(R,Q,boss) =
|
|
min(6, offer_level(R) + Q + (1 if boss else 0))
|
|
rank_multiplier = 1 + 0.08*(encounter_rank - 1)
|
|
```
|
|
|
|
Apply `rank_multiplier` once to an enemy row's base MaxHP, power, Guard, Ward,
|
|
Potency, and Tenacity, rounding half up. Tempo, Initiative, reach, target rules,
|
|
cooldowns, footprint, and status durations do not scale. Difficulty then applies
|
|
its declared HP and damage/healing multipliers once.
|
|
|
|
Identity base budgets are content-authored within the systems ranges and must
|
|
pass fixed same-rank comparisons. A later Region cannot substitute a larger
|
|
base budget for a new mechanic, and no mandatory battle assumes a prior run's
|
|
Recruit or equipment.
|
|
|
|
### 9. Renown And Meta Progression
|
|
|
|
For Region number `G` in `1..8` and Contract rank `K` in `{1,2}`:
|
|
|
|
```text
|
|
first_clear_renown(G,K) = 8 + 2*G + 4*K
|
|
repeat_objective_renown = min(2, unclaimed_repeat_goals_for_contract)
|
|
```
|
|
|
|
First discovery of a Profession, Trait, equipment behavior family, Artifact,
|
|
ordinary/elite enemy, or boss grants one Renown once. A row's presentation
|
|
variants grant none. Repeat objectives are finite checklist entries; replaying
|
|
an unchanged clear after they are claimed grants zero Renown.
|
|
|
|
Renown purchases only breadth:
|
|
|
|
| Unlock | Cost range | Allowed effect |
|
|
|---|---:|---|
|
|
| Catalog pool packet | 4-8 | Add sidegrade equipment/Trait/Artifact rows to future eligible pools |
|
|
| Advanced Profession clue/trial | 8-12 | Reveal requirements and enable a deterministic trial |
|
|
| Hidden-hybrid clue/trial | 14-18 | Reveal one predicate and enable its mastery trial |
|
|
| Threat archive upgrade | 6-10 | Expose additional exact preview fields for encountered families |
|
|
| Oath law | 12-20 | Add a postgame optional run modifier and its completion Record |
|
|
|
|
Forbidden effects include percentage power, HP, defense, Coin gain, cheaper
|
|
shops, free refreshes, starting equipment, starting Recruits, extra reward
|
|
cards, larger starting Line capacity, or casualty reversal.
|
|
|
|
All mandatory Standard counters are in the default or deterministic Contract
|
|
pool. A player never needs a Renown purchase to finish the first clear.
|
|
|
|
#### 9.1 Guild Charter Transaction
|
|
|
|
`PRO-002` presents the existing Renown authority as the **Guild Charter**. It is
|
|
not a new economy, currency, progression tree, or permanent-stat layer. The
|
|
board contains exactly eight horizontal Region seals in campaign order. A seal
|
|
opens only its authored packet cards and prerequisite tracks; no radial layout
|
|
or copied node graph is permitted.
|
|
|
|
Each claim uses one stable `charter_claim_id` and the existing cost range above:
|
|
|
|
```text
|
|
preview = (charter_claim_id, region_id, prerequisite_ids[], renown_cost,
|
|
effect_family, future_run_effect, balance_revision)
|
|
commit = verify preview + debit Renown + append one claim receipt atomically
|
|
retry = return the original receipt; never debit or grant twice
|
|
```
|
|
|
|
The preview must show current Renown, remaining Renown, every prerequisite,
|
|
whether each prerequisite is satisfied, the exact future-run breadth change,
|
|
and the statement `No permanent combat power`. A failed prerequisite, stale
|
|
balance revision, insufficient balance, duplicate claim, or interrupted write
|
|
cannot partially reveal a packet or consume Renown. Respec and refund are not
|
|
part of the contracted board; claims are permanent breadth unlocks.
|
|
|
|
The first-clear choice after the sixteenth boss creates no new currency and
|
|
grants no choice-specific combat value. Both branches settle the same earned
|
|
Renown and first-clear Records exactly once, archive the surviving Company, and
|
|
close Coin/Credit with the run. **Complete the First Chronicle** changes only
|
|
the next destination to Final Ledger/Title. **Open the Oath Season** additionally
|
|
sets postgame eligibility and enters rule review; its first playable state is a
|
|
new generated run after that review. Ordinary New Run remains available and
|
|
uses the same persistent breadth already owned.
|
|
|
|
### 10. Equipment And Artifact Distribution
|
|
|
|
The 320 equipment entries are divided into eight Region bands of 40. Every band
|
|
contains all five item forms (armament, guard, garb, charm, and kit), all
|
|
primary responsibilities, and at least three opposed order/reach/protection
|
|
plans. Every Recruit has one universal active slot, so form controls eligibility
|
|
and presentation rather than granting simultaneous slots. Tier describes price
|
|
and availability, not strict power. A lower tier must lead one fixed comparison
|
|
in which it is the rational choice.
|
|
|
|
Each item has a numeric support budget separate from its required behavior:
|
|
|
|
```text
|
|
item_stat_budget(T) = 30 + 8*T
|
|
```
|
|
|
|
The behavior hook reserves 12-24 budget points. At most 50% of the total budget
|
|
may be generic HP, power, Guard, or Ward. Remaining numeric support uses:
|
|
|
|
| Stat | Cost |
|
|
|---|---:|
|
|
| +1 Might, Finesse, Insight, Vigor, or Resolve | 2 |
|
|
| +1 WeaponPower, FocusPower, ArmorGuard, or ArmorWard | 1 |
|
|
| +6 MaxHP | 1 |
|
|
| +1 Potency or Tenacity | 1 |
|
|
| +0.20 Tempo or Initiative | 1 |
|
|
|
|
The one equipped item's numeric support is added before derived-stat rounding.
|
|
Region band does not increase this budget; later bands introduce new
|
|
interactions rather than automatic vertical power.
|
|
|
|
All 320 entries pass the systems behavior fixture. No scalar-only allowance or
|
|
minimum subset exists.
|
|
|
|
The 60 Artifacts are distributed across first-clear, elite, boss, mastery, and
|
|
postgame pools. Every run sees at least one deterministic Artifact choice by the
|
|
end of Round 6 and at least one additional choice by boss Muster. No Artifact is
|
|
required for a mandatory boss. Duplicate active Artifact IDs are illegal.
|
|
|
|
Bad-luck protection uses behavior tags:
|
|
|
|
```text
|
|
if two consecutive eligible equipment drafts omit the selected behavior tag:
|
|
the third eligible draft contains that tag
|
|
```
|
|
|
|
The counter resets when offered, not accepted. The selected tag may be changed
|
|
at any Market for zero Coin before a draft is generated. It cannot name an
|
|
undiscovered exact item.
|
|
|
|
### 11. Capacity Selection Simulation
|
|
|
|
The `10-12` target range was evaluated with an explicit analytical workload
|
|
model. It is a design simulation, not human readability evidence.
|
|
|
|
For profile `P` and candidate capacity `C`:
|
|
|
|
```text
|
|
prep_seconds(P,C) = ceil(base_P + inspect_P*C +
|
|
reorder_P*C*(C-1)/2)
|
|
battle_p95_seconds(C) = ceil(52 + 5.4*C)
|
|
causal_events_per_second(C) = 1.2 + 0.24*C
|
|
```
|
|
|
|
The pair term models order comparisons, not every permutation. The gate is
|
|
`prep <= 150 s` for every profile, battle p95 `<=120 s`, causal events `<=4.0/s`,
|
|
and the three-death fixture recoverable in at most one Market.
|
|
|
|
| Profile | `base` | `inspect` | `reorder` | C=10 prep | C=11 prep | C=12 prep |
|
|
|---|---:|---:|---:|---:|---:|---:|
|
|
| Normal | 20 | 4.0 | 0.8 | 96 s | 108 s | 121 s |
|
|
| Active | 25 | 5.0 | 1.4 | 138 s | 157 s | 178 s |
|
|
| Efficient | 15 | 3.0 | 0.5 | 68 s | 76 s | 84 s |
|
|
| Adversarial | 20 | 4.5 | 1.3 | 124 s | 141 s | 160 s |
|
|
| Returning | 30 | 5.5 | 1.3 | 144 s | 162 s | 182 s |
|
|
|
|
| Candidate | Profiles passing prep | Battle p95 | Causal events/s | Three-death recovery | Result |
|
|
|---:|---:|---:|---:|---:|---|
|
|
| 10 | 5/5 | 106 s | 3.60 | 1 Market | Pass |
|
|
| 11 | 3/5 | 112 s | 3.84 | 1 Market | Fail profile gate |
|
|
| 12 | 2/5 | 117 s | 4.08 | 1 Market | Fail profile and event gates |
|
|
|
|
Ten is the largest candidate passing every declared gate. Human testing still
|
|
owns whether ten simultaneous allies plus enemies remain visually legible.
|
|
|
|
### 12. Contract And First-Clear Pacing
|
|
|
|
Each Region contains two Contracts. Every Contract is twelve ordinary Rounds,
|
|
boss Muster, and one boss. The Normal authored means are:
|
|
|
|
| Region | Contract 1 | Contract 2 | Region total | Content pressure |
|
|
|---:|---:|---:|---:|---|
|
|
| 1 | 50 min | 54 min | 104 min | recruit valuation, front/rear, reach |
|
|
| 2 | 54 min | 58 min | 112 min | protection bypass and healing access |
|
|
| 3 | 56 min | 60 min | 116 min | projectiles and interception |
|
|
| 4 | 58 min | 62 min | 120 min | movement, push, and pull |
|
|
| 5 | 60 min | 64 min | 124 min | summons and footprint pressure |
|
|
| 6 | 62 min | 66 min | 128 min | status ordering and control |
|
|
| 7 | 64 min | 68 min | 132 min | promotion and Artifact conflicts |
|
|
| 8 | 66 min | 70 min | 136 min | full-system synthesis |
|
|
|
|
Successful Contract time sums to `972 minutes`. This time is active Market,
|
|
battle, result, and boss play; it excludes loading and forced waiting.
|
|
|
|
#### 12.1 Five Full-Clear Profiles
|
|
|
|
| Profile | Successful Contract minutes | Failed/abandoned-run minutes | Guild/onboarding/recap minutes | Total | First-clear hours |
|
|
|---|---:|---:|---:|---:|---:|
|
|
| Efficient | 768 | 120 | 90 | 978 | 16.30 h |
|
|
| Normal | 972 | 120 | 78 | 1,170 | 19.50 h |
|
|
| Active | 1,152 | 150 | 90 | 1,392 | 23.20 h |
|
|
| Adversarial | 928 | 300 | 92 | 1,320 | 22.00 h |
|
|
| Returning | 1,024 | 120 | 161 | 1,305 | 21.75 h |
|
|
|
|
Every profile is inside 15-25 hours. The efficient profile cannot skip required
|
|
bosses or decisions; it saves time through faster comparison and presentation
|
|
speed. Adversarial includes more elite offers and recoverable losses. Returning
|
|
includes recap/segmentation overhead, not forced repetition.
|
|
|
|
Mastery targets 60+ cumulative hours. At least 70% of post-ending time must use
|
|
a previously unclear Profession, Trait, equipment behavior, Artifact conflict,
|
|
enemy/boss fixture, or Oath law. An unchanged clear may contribute at most 20%
|
|
of any mastery milestone.
|
|
|
|
### 13. Exact Twenty-Minute Pacing Traces
|
|
|
|
All timestamps are simulation-independent wall time spent actively playing a
|
|
representative 1,200-second session. `M` lists Market openings. `B` lists battle
|
|
start-end intervals. `D` lists meaningful decisions; confirmation, inspection,
|
|
speed changes, and reward animation do not count.
|
|
|
|
| Profile | `M` Market opens (s) | `B` battle intervals (s) | `D` decision timestamps (s) | Maximum decision gap |
|
|
|---|---|---|---|---:|
|
|
| Normal | `[0,300,600,900]` | `[(180,270),(480,570),(780,870),(1080,1170)]` | `[25,90,175,280,325,390,475,580,625,690,775,880,925,990,1075,1180]` | 105 s |
|
|
| Active | `[0,330,660,990]` | `[(210,300),(540,630),(870,960),(1140,1200)]` | `[20,75,135,205,310,350,410,475,535,640,680,740,805,865,970,1015,1070,1135]` | 105 s |
|
|
| Efficient | `[0,260,520,780,1040]` | `[(130,230),(390,490),(650,750),(910,1010),(1170,1200)]` | `[35,125,240,295,385,500,555,645,760,815,905,1020,1075,1165]` | 115 s |
|
|
| Adversarial | `[0,290,610,940]` | `[(150,260),(440,580),(760,910),(1100,1200)]` | `[30,80,145,270,320,375,435,590,640,700,755,920,970,1030,1095]` | 165 s |
|
|
| Returning | `[0,270,590,930]` | `[(140,240),(420,550),(760,890),(1090,1180)]` | `[25,85,135,250,300,355,415,560,620,680,755,900,950,1015,1085,1190]` | 145 s |
|
|
|
|
The gap includes time zero to the first decision and the last decision to 1,200.
|
|
Every battle that completes before the trace boundary is followed by its next
|
|
decision within 15 seconds. Decision kinds across each trace must include
|
|
Recruit/equipment valuation, order or deployment,
|
|
battle-risk commitment, reward/recovery, and next-Market budget. Adversarial's
|
|
165-second gap is a long elite battle and remains below the product veto because
|
|
the battle itself is capped at 150 simulation seconds and the result decision
|
|
appears immediately afterward.
|
|
|
|
### 14. Standard Solvency Trace
|
|
|
|
A lowest-reward run takes only steady offers. In Region band `B`, ordinary
|
|
battle income is:
|
|
|
|
```text
|
|
sum(battle_coin(B,R,0), R=1..12)
|
|
= sum(18 + 2*R + 2*B)
|
|
= 372 + 24*B Coin
|
|
```
|
|
|
|
With starting Coin, total pre-boss resources are `424 + 24*B Coin`. The
|
|
baseline plan spends:
|
|
|
|
| Required purchase | Cost |
|
|
|---|---:|
|
|
| Eight base hires needed to grow from two provisional Recruits to capacity ten | 156 |
|
|
| Two tier-0/1/2/3 items in Region band `B` | `168 + 24*B` |
|
|
| Two refreshes in one no-lock Market | 15 |
|
|
| Three locks | 6 |
|
|
| Total | `345 + 24*B` |
|
|
|
|
The Region-band income and equipment-price terms cancel exactly, so the run
|
|
reaches boss Muster with `79 Coin` before optional sales, risky-offer
|
|
bonuses, reward Coin cards, or Credit. The deterministic boss-counter objective
|
|
costs no more than one omitted baseline equipment purchase. Standard solvency
|
|
passes when every Region seed can follow an equivalent plan with at least 40
|
|
Coin or one complete three-casualty recovery set entering boss Muster.
|
|
|
|
### 15. Anti-Dominance And Balance Gates
|
|
|
|
#### 15.1 Required Strategy Tests
|
|
|
|
| Candidate policy | Why it might dominate | Rejection fixture |
|
|
|---|---|---|
|
|
| Always refresh twice | More chances at desired synergies | Costs at least 15 Coin per Market before locks; 12-Market use costs 180, exceeds the 79-Coin baseline reserve and must lose one fixed low-variance seed |
|
|
| Never refresh | Preserves all Coin | Must lose value in at least two of five seeds where one refresh exposes a counter tag worth more than 6 Coin |
|
|
| Buy every Recruit | Reserve breadth and death insurance | Reserve earns no XP/Coin; dismissal returns at most 25% paid Coin; fixed plan must lose equipment/refresh flexibility and fail one boss comparison |
|
|
| Dismiss after every battle | Attempt to cycle Traits/offers | No Credit, zero rebate before two survivals, maximum 10 later; cycle is strictly Coin-negative and loses history/level |
|
|
| Never dismiss | Avoid sunk-cost loss | Must lose one fixture where releasing an obsolete low-level Reserve funds a reachable counter without weakening deployed footprint |
|
|
| Never replace | Preserve Coin after death | Must lose one post-casualty fixture because lower footprint changes reach/protection and leaves Coin unused |
|
|
| Always take steady | Minimize death | Must trail pressured/elite in at least one build-breadth objective and one Artifact timing fixture |
|
|
| Always take elite | Maximize rewards | Expected casualties and missed steady recovery must reduce clear rate or terminal Coin in at least two profiles |
|
|
|
|
No policy needs to be bad everywhere. A policy is dominant if it weakly beats
|
|
all alternatives in every fixed state and strictly beats one; any such result
|
|
blocks the design.
|
|
|
|
#### 15.2 Executable Fixed-Seed Comparator
|
|
|
|
The Gate 6 build-policy proof is
|
|
[ANTI_DOMINANCE_FIXED_SEEDS.json](product/fixtures/ANTI_DOMINANCE_FIXED_SEEDS.json),
|
|
recalculated by `python3 tools/simulate_anti_dominance.py`. It is a prebattle
|
|
feasibility comparator, not a second combat model: the fixture declares the
|
|
locked route's ordered enemy inputs, required counter tags, minimum deployed
|
|
footprint, and casualty counts. The script replays the Market transactions,
|
|
tests that the resulting deployed build covers those route requirements, and
|
|
then applies the Section 6 battle-Coin formula only on a clear. It introduces
|
|
no accuracy, evasion, random draw, hidden generator state, or formula override.
|
|
|
|
Every seed declares all of the following rather than asking a generator for
|
|
them at test time:
|
|
|
|
| Fixture field | Required declared input or recalculation |
|
|
|---|---|
|
|
| State | Round, cleared Rounds, Region band, difficulty, Coin, Credit before casualties, prior refresh count, paid locks, and expected capacity/offer-level/refresh-cost values |
|
|
| Company | Every living Recruit ID, deployed/Reserve assignment, counter tags, Coin paid, and survived-battle count |
|
|
| Recovery | Every casualty's original list price, Coin paid, expected Credit grant, post-casualty Credit, and the protected offer ID or explicit null |
|
|
| Boards | All three Recruit, four equipment, and three battle offers on both the opening and first-refresh board, including exact price inputs and stored prices |
|
|
| Route | The paid-locked route preserved through refresh, ordered enemy inputs, risk, required tags, deployed footprint, outcome casualties, and stored battle Coin |
|
|
| Oracle | Exact action trace and outcome metrics for every policy plus the expected winner |
|
|
|
|
Each fixture's opening Coin is the post-lock balance; the declared paid lock is
|
|
already sunk. Its count still enters the first-refresh formula, and the locked
|
|
route is byte-identical on the refresh board so every policy faces the same
|
|
combat contract.
|
|
|
|
The script rejects a missing row or field, a sixth or duplicate seed, a changed
|
|
locked route or protected offer, a noncanonical price/reward/Credit value, an
|
|
illegal deployment, or an expected-result mismatch. A casualty state must have
|
|
exactly one protected Recruit on both boards. The protected offer remains
|
|
unchanged through refresh. Dismissal checks the canonical living count before
|
|
target selection; with one living Recruit it records `FINAL_LIVE_RECRUIT`, pays
|
|
no rebate, and may still perform a separate legal equipment purchase. Casualty
|
|
Credit and protected recovery are inputs to later hire transactions, never a
|
|
dismissal side effect.
|
|
|
|
The four fixed policy families are intentionally narrow and deterministic:
|
|
|
|
| Policy | Exact algorithm |
|
|
|---|---|
|
|
| `refresh_first` | Pay exactly the declared first refresh when affordable, then buy at most one affordable refreshed equipment offer that maximizes missing route-tag coverage; ties use lower price then offer ID |
|
|
| `hire_first` | Buy at most one affordable opening-board Recruit, preferring the protected offer, then missing route-tag coverage, lower Coin cost after optional Credit, lower list price, and offer ID; deploy it only when capacity has a vacancy |
|
|
| `dismiss_first` | If legal, dismiss one Reserve with least route relevance, then highest rebate and offer ID tie-breaks; otherwise record the rejection; buy at most one affordable opening-board equipment counter |
|
|
| `no_replace` | Never refresh, hire, or dismiss; buy at most one affordable opening-board equipment counter using the same coverage/price/ID ordering |
|
|
|
|
The comparison is lexicographic and all higher values are better:
|
|
`route_cleared`, ending Coin, ending Credit, ending living Recruit count, then
|
|
counter-tag coverage. The JSON also records deployed/required footprint,
|
|
battle Coin, Market Coin spent, dismissal rebate, Credit used, Line capacity,
|
|
and exact actions. This ordering does not convert Credit into Coin and does not
|
|
claim that the fixture policies are exhaustive play. It makes one declared
|
|
prebattle choice comparable without hiding a utility weight.
|
|
|
|
For two policies `A` and `B`, `A` weakly dominates `B` only when `A`'s complete
|
|
comparison tuple is at least `B`'s on all five seeds and strictly greater on at
|
|
least one. The run fails if either policy has no win, has no loss, or any
|
|
ordered policy pair meets that definition. `--self-test` first accepts the
|
|
canonical matrix, then creates an in-memory matrix in which `refresh_first`
|
|
beats every peer on every seed and proves that the dominance check rejects it.
|
|
|
|
| Stable seed | Fixed state counterfactual | Expected winner |
|
|
|---|---|---|
|
|
| `AD-FS-001-REFRESH-COUNTER` | First refresh exposes the locked pressured route's only affordable wardbreak counter | `refresh_first` |
|
|
| `AD-FS-002-PROTECTED-HIRE` | One casualty leaves both a protection and fourth-deployed-Recruit gap | `hire_first` |
|
|
| `AD-FS-003-DISMISSAL-REBATE` | A legal four-Coin obsolete-Reserve rebate makes the current reach counter affordable | `dismiss_first` |
|
|
| `AD-FS-004-PRESERVE-COIN` | The current line already clears; extra transactions lose Coin or living roster value | `no_replace` |
|
|
| `AD-FS-005-CREDIT-CROSSOVER` | Final-Recruit dismissal is illegal and a zero-Coin protected hire beats two equipment paths | `hire_first` |
|
|
|
|
Canonical verification is:
|
|
|
|
```sh
|
|
python3 tools/simulate_anti_dominance.py --self-test
|
|
python3 tools/simulate_anti_dominance.py
|
|
python3 tools/simulate_anti_dominance.py | shasum -a 256
|
|
```
|
|
|
|
Two default runs must be byte-identical. The summary prints every policy's
|
|
clear, ending Coin, ending Credit, ending living count, and counter coverage
|
|
for every seed, the win counts, a canonical result SHA-256, and
|
|
`ABG_ANTI_DOMINANCE_OK no_weak_dominance=1`.
|
|
|
|
#### 15.3 Complete Gates
|
|
|
|
1. **Solvency:** all eight Region bands have a steady Standard plan reaching
|
|
boss Muster with 40 Coin or the full declared recovery set.
|
|
2. **Recovery:** the Section 7.1 three-death state fields three legal base
|
|
replacements in one Market and does not increase total Coin-equivalent value.
|
|
3. **Capacity:** candidate ten is the only candidate passing all profile,
|
|
battle-duration, event-rate, and recovery gates.
|
|
4. **Profession diversity:** each base branch pair and hidden hybrid has opposed
|
|
fixtures; no identity leads both without a different Company/order cost.
|
|
5. **Equipment/Artifact diversity:** every row changes two observable events;
|
|
no price/tier creates strict catalog-wide superiority.
|
|
6. **Market diversity:** across five fixed run seeds and five profiles, at least
|
|
three different first purchases, battle risks, and final orders are optimal.
|
|
7. **Failure pressure:** one death is consequential; three are recoverable; a
|
|
full-company loss can close a run without making next-run Meta power required.
|
|
8. **Decision density:** exact traces reproduce their maximum gaps, and no
|
|
battle/result handoff exceeds 15 real seconds after resolution at 1x.
|
|
9. **Campaign:** all profiles finish within 15-25 hours and mastery has new
|
|
decision content through 60+ hours.
|
|
|
|
### 16. Difficulty Contracts
|
|
|
|
Difficulty is chosen before run creation and stored for the entire run:
|
|
|
|
| Difficulty | Enemy HP | Enemy damage/healing | Reward multiplier | Audience |
|
|
|---|---:|---:|---:|---|
|
|
| Wayfinder | 0.90x | 0.90x | 1.00x | learning and accessibility-forward play |
|
|
| Standard | 1.00x | 1.00x | 1.00x | authored first-clear baseline |
|
|
| Oathbound | 1.10x | 1.12x | 1.15x | prepared high-risk play |
|
|
|
|
Multipliers apply once after enemy derived stats and once to battle Coin where
|
|
declared. They do not change offer access, Recruit attribute budgets, XP,
|
|
Renown, target rules, telegraph length, Replacement Credit, Line capacity, or
|
|
save behavior. Fallen Recruits remain permanent on every difficulty.
|
|
|
|
Postgame Oath laws are opt-in rule changes, not hidden difficulty multipliers.
|
|
Each declares Market/formation/enemy/reward changes and its Record. Laws may not
|
|
alter transaction integrity or reintroduce permanent Meta stats.
|
|
|
|
### 17. Fixed Economy Test Vectors
|
|
|
|
| Vector | Inputs | Expected result |
|
|
|---|---|---|
|
|
| Capacity | cleared Rounds 0,2,4,6,8,10,12 | `4,5,6,7,8,9,10` |
|
|
| Base hire | L=3, P=0 | 22 Coin list price |
|
|
| Advanced hire | L=3, P=1 | 32 Coin list price |
|
|
| Credit hire | list 22, Credit 19, use=true | 3 Coin, Credit becomes zero |
|
|
| Item price | B=4, T=2 | `12+12+12=36` Coin |
|
|
| Item sale | 36 Coin paid | 10 Coin |
|
|
| Refresh | N=0/L=0; N=1/L=0; N=7/L=0 | 6, 9, 24 Coin |
|
|
| Steady income | B=0, R=5, Standard | 28 Coin |
|
|
| Region steady income | B=7, R=5, Standard | 42 Coin |
|
|
| Elite income | B=0, R=5, Oathbound | `floor((18+10+14)*1.15)=48` Coin |
|
|
| Level XP | L=4 | 60 XP to level 5 |
|
|
| Battle XP | R=5, Q=2 | 28 XP to living deployed winner |
|
|
| Death Credit | original list 22, paid 18 | 19 Credit |
|
|
| Dismissal | paid 32, survived 3 | 8 Coin; no Credit |
|
|
| Final dismissal | one living Recruit, paid 32, survived 3, Coin 3 | reject `FINAL_LIVE_RECRUIT`; Coin remains 3, no rebate/offer/roster change |
|
|
| Solvency | any B; start plus 12 steady wins minus baseline | 79 Coin |
|
|
| Decision gaps | five Section 13 traces | 105, 105, 115, 165, 145 seconds |
|
|
| Capacity candidate | C=10 | 5/5 prep, 106 s p95, 3.60 events/s, one-Market recovery |
|
|
|
|
### 18. Acceptance Checklist
|
|
|
|
- [ ] Market board, offer levels, prices, refresh, lock, purchase, sale, and
|
|
reward formulas are complete.
|
|
- [ ] Coin, Credit, XP, Renown, and progression facts have explicit persistence,
|
|
sources, sinks, caps, and non-power boundaries.
|
|
- [ ] Guild Charter preview/claim/retry uses existing Renown, the declared cost
|
|
ranges, exactly-once receipts, and no permanent raw power.
|
|
- [ ] Death recovery is affordable without making dismissal/death profitable.
|
|
- [ ] The dismissal rebate is unreachable when the target is the final living
|
|
Recruit; abandonment and casualty recovery remain separate.
|
|
- [ ] Capacity ten follows a declared five-profile analytical selection.
|
|
- [ ] Five exact Market/battle/decision traces and campaign budgets meet 15-25
|
|
first-clear and 60+ mastery targets.
|
|
- [ ] Solvency and anti-dominance gates cover refresh, hire, dismiss, replace,
|
|
and risk policies.
|
|
- [ ] All fixed content counts and no-filler requirements match the other
|
|
authorities.
|
|
- [ ] Both final-campaign choices settle identical earned economy receipts,
|
|
archive the Company, and differ only in destination/postgame eligibility.
|
|
|
|
<!-- Historical section source: SAVE_AND_FAILURE_CONTRACT; retained in Git history. -->
|
|
|
|
## Aetherbound Guild: Save And Failure Contract
|
|
|
|
> Authority: versioned persistence, pending/applied operations, exactly-once
|
|
> commit, retry, duplicate protection, deterministic battle resume, Recruit
|
|
> death, dismissal, run failure, backup, corruption, migration, rollback, cloud
|
|
> conflict, clock behavior, bounded offline work, delete, and recovery UX.
|
|
>
|
|
> Revision: recruit-line-design-r3 / 2026-08-11
|
|
>
|
|
> [01_GAME_DESIGN.md](01_GAME_DESIGN.md) owns the player promise.
|
|
> [02_GAMEPLAY_AND_BALANCE.md](02_GAMEPLAY_AND_BALANCE.md) owns battle state and casualty
|
|
> predicates. [02_GAMEPLAY_AND_BALANCE.md](02_GAMEPLAY_AND_BALANCE.md) owns normal
|
|
> prices, rewards, pacing, and difficulty multipliers. This document is final
|
|
> authority for operation and gameplay-failure application.
|
|
|
|
### 1. Persistence Invariants
|
|
|
|
1. A confirmed operation applies exactly once or not at all. Cost, result,
|
|
receipt, and final status are one committed state.
|
|
2. A durable `pending` row contains immutable intent and zero gameplay deltas.
|
|
It can be safely finalized or rejected; it can never contain a spent cost.
|
|
3. Suspend, crash, power loss, retry, duplicate input, device change, or cloud
|
|
transport cannot redraw a Market/reward, duplicate an item/Renown grant,
|
|
reverse a valid casualty, or change a deterministic battle.
|
|
4. The newest valid local commit remains fully playable offline. Platform and
|
|
cloud copies never become gameplay authority silently.
|
|
5. A Fallen Recruit never respawns in the same battle and never returns to the
|
|
same run through Reserve substitution, reload, backup choice, rehearsal,
|
|
migration, reward, promotion, difficulty, or clock manipulation.
|
|
6. Death removes the Recruit but returns exactly the Recruit's optional
|
|
`equipped_item_id` to run inventory. Neither half of that result can commit
|
|
alone; an empty slot returns nothing.
|
|
7. Offline gameplay advances by zero simulation ticks and grants zero Coin,
|
|
XP, Renown, offers, healing, or progress.
|
|
8. Migration is copy-first, deterministic, sequential, and reversible until a
|
|
later verified boundary. It never rerolls generated Recruit fields.
|
|
9. Corruption recovery identifies the exact verified boundary. If later
|
|
irreversible run operations cannot be reconstructed, the run is sealed
|
|
rather than resumed from a state that could reverse death or duplicate value.
|
|
10. Save, conflict, recovery, overwrite, abandon, dismissal, and delete flows
|
|
support touch, mouse, keyboard, controller, localization, and accessibility.
|
|
11. A Run seed is reserved in a durable `run_creation_draft` before any opening
|
|
Recruit is generated. At most one draft or `current_run` exists, and a
|
|
draft can only become the `current_run` that carries that same seed.
|
|
|
|
### 2. Save Topology And Envelope
|
|
|
|
The product supports three independent Guild slots. Each slot holds one active
|
|
commit, four rotating verified backups, one run-start checkpoint, one latest
|
|
Market checkpoint, one optional pre-migration copy, and redundant closure
|
|
receipt ledgers.
|
|
|
|
```text
|
|
profile_save
|
|
profile_schema_version
|
|
settings_revision, language, accessibility, input mappings
|
|
Guild_slot_index[]
|
|
platform_service_outbox (non-authoritative, bounded)
|
|
|
|
Guild_slot
|
|
Guild_id, lineage_id, slot_id
|
|
save_schema_version, ruleset_version, content_revision
|
|
commit_id, parent_commit_id, commit_sequence
|
|
generation_root_seed, next_run_ordinal
|
|
Region/Contract progress, Renown, unlocks, Records, discoveries
|
|
run_creation_draft or null
|
|
current_run or null
|
|
operation_journal, compact_applied_ids
|
|
closure_receipt_head, run_creation_receipt_head
|
|
payload_sha256, envelope_crc32
|
|
|
|
run_creation_draft
|
|
draft_id, run_id, Contract, difficulty, status
|
|
seed_ordinal, run_seed, source_commit_id
|
|
ruleset_version, content_revision, opening_role_weight_snapshot
|
|
opening_offers[0..3] or empty, opening_offer_set_hash or null
|
|
selected_offer_ids[2] or empty, selection_revision
|
|
draft_hash, run_creation_receipt_head
|
|
|
|
current_run
|
|
run_id, Contract, difficulty, run_seed, status
|
|
Market/cleared-Round/boss state
|
|
Coin, Replacement Credit, capacity, offers, locks, refresh counters
|
|
living Recruits, Reserve, inventory, Artifacts
|
|
encounter or null, pending outcome/reward or null
|
|
hired/fallen/dismissed totals
|
|
```
|
|
|
|
IDs and seeds are 128-bit values rendered as lowercase hexadecimal. Commit
|
|
sequence is a local monotonic unsigned integer. CRC32 catches incomplete media
|
|
writes; SHA-256 covers canonical payload bytes. Checksums are integrity tools,
|
|
not online signatures or anti-cheat requirements.
|
|
|
|
`run_creation_draft` and `current_run` are mutually exclusive. A draft with
|
|
status `seed_reserved` has an empty offer array and selection. `offers_ready`
|
|
has exactly four complete offers and no accepted pair; `selection_ready` has the
|
|
same four offers and exactly two distinct selected IDs. No state may serialize a
|
|
partial offer array, one selected ID, or a draft whose seed ordinal is not less
|
|
than `next_run_ordinal`.
|
|
|
|
Wall-clock fields may be stored for display and diagnostics only. They cannot
|
|
select conflict winners or calculate gameplay.
|
|
|
|
### 3. Versioning And Compatibility
|
|
|
|
| Field | Changes when | Compatibility rule |
|
|
|---|---|---|
|
|
| `profile_schema_version` | settings/slot index layout changes | profile-only ordered migration |
|
|
| `save_schema_version` | Guild/run/operation serialization changes | sequential copy migration required |
|
|
| `ruleset_version` | combat, target, price, failure, or progression formula changes | committed battle finishes under stored version |
|
|
| `content_revision` | stable catalog row or localization data changes | stable-ID mapping or explicit legacy handling |
|
|
|
|
The current schema and previous three shipped schemas must have tested adjacent
|
|
migrations. Older schemas may have a longer supported chain; absence of a chain
|
|
blocks that slot with export/recovery options and never authorizes reset.
|
|
|
|
A run-creation draft, battle, reward draft, Market board, or outcome already
|
|
committed stays on its stored ruleset/content snapshot. A new ruleset becomes
|
|
active only before seed reservation or a new Market is generated, after all
|
|
prior result operations are finalized. It cannot rematerialize an existing
|
|
opening four.
|
|
|
|
No shipped gameplay save exists during this design-only stage. These rules bind
|
|
all later prototype and production schemas and do not imply a current runtime
|
|
migration obligation.
|
|
|
|
### 4. Operation Identity And Status
|
|
|
|
Every state-changing action has:
|
|
|
|
```text
|
|
operation_id = hash(Guild_id, lineage_id, commit_sequence_at_intent,
|
|
operation_kind, source_stable_id, local_nonce)
|
|
intent_hash = sha256(canonical_operation_inputs)
|
|
status: pending | applied | rejected
|
|
```
|
|
|
|
The journal row stores:
|
|
|
|
```text
|
|
operation_id, operation_kind, intent_hash, status
|
|
pre_commit_id, result_commit_id
|
|
cost_delta, result_delta, rejection_reason
|
|
run_id, Market_index, encounter_id, simulation_tick as applicable
|
|
receipt_hash, previous_closure_receipt_hash as applicable
|
|
```
|
|
|
|
Rules for duplicate IDs:
|
|
|
|
- same `operation_id` and same `intent_hash`: return stored pending status,
|
|
applied receipt, or rejection without creating a new operation;
|
|
- same ID and different hash: reject `OPERATION_ID_CONFLICT`, applying nothing;
|
|
- new ID: validate and enter the transaction procedure below.
|
|
|
|
Applied IDs remain in the full journal through run close. They then compact into
|
|
an immutable membership set retained for the Guild lineage. Receipt-bearing
|
|
casualty, reward, boss-clear, Renown, run-close, migration, and delete operations
|
|
remain in the redundant closure ledger until the Guild slot is deleted.
|
|
|
|
The detailed journal is bounded to 4,096 rows. At a Market with no pending
|
|
operation, reaching 3,584 rows compacts the oldest finalized non-closure rows
|
|
into sorted exact-ID segments containing operation ID, intent hash, final status,
|
|
and receipt hash. The newest 512 detailed rows and every closure receipt remain
|
|
expanded. Lookup checks exact segments before accepting a new ID; probabilistic
|
|
membership is forbidden because a false match could reject a valid purchase or
|
|
hide a duplicate.
|
|
|
|
### 5. Pending-To-Applied Transaction Procedure
|
|
|
|
Every durable transaction uses these steps:
|
|
|
|
1. Look up ID/hash before current-state validation and return any existing row.
|
|
2. Validate the active commit, legal phase, referenced IDs, target, and intent.
|
|
3. Build a candidate child commit containing a `pending` row with the complete
|
|
immutable intent and **zero** `cost_delta`/`result_delta`.
|
|
4. Validate, write, flush, atomically point to, and read back that pending child.
|
|
5. From the pending intent and unchanged parent facts, compute the complete cost
|
|
and result. No new random draw is allowed; generated outputs were fixed in
|
|
intent or derive from stored labeled seeds.
|
|
6. Build one child that replaces `pending` with `applied`, includes all cost and
|
|
result deltas, receipt, and closure link when required.
|
|
7. Validate schema, nonnegative resources, unique ownership, Recruit/equipment
|
|
consistency, Market lineage, battle hashes, and operation transition.
|
|
8. Serialize to a new file, write integrity values, flush file and directory,
|
|
atomically replace the active pointer, then read back.
|
|
9. Rotate backups only after the applied child reads back and validates.
|
|
|
|
A precondition failure after step 3 produces a `rejected` child with zero deltas
|
|
and one reason. It cannot return to pending. Presentation, sound, achievement,
|
|
cloud upload, and screen transition occur only after step 9.
|
|
|
|
#### 5.1 Crash And Retry Boundaries
|
|
|
|
| Crash point | Reachable active state | Recovery/retry |
|
|
|---|---|---|
|
|
| Before pending pointer switch | Parent only | retry creates same intent; no cost exists |
|
|
| After pending switch | Pending intent, zero deltas | deterministic finalize or reject once |
|
|
| During applied pointer switch | Pending or complete applied child | validate selected pointer; finalize or return receipt |
|
|
| After applied switch, before backup rotation | Complete applied child | return stored receipt; never reapply |
|
|
| During backup/closure replication | Applied active plus older replicas | repair replicas from active receipt; no gameplay replay |
|
|
|
|
Recovery may finalize a pending buy, casualty, reward, or run-close operation
|
|
only from its fixed intent. It never asks the player to redraw or reconfirm a
|
|
result whose confirm was already durably recorded. A pending destructive action
|
|
that lacks a valid target after recovery becomes rejected with no deltas.
|
|
|
|
### 6. Transaction Boundaries
|
|
|
|
| Action | Pending intent contains | Applied child contains |
|
|
|---|---|---|
|
|
| Begin run creation | Contract, difficulty, expected `next_run_ordinal`, derived draft/run IDs and seed, version/role-weight snapshot | one `seed_reserved` draft and ordinal increment; no offers or `current_run` |
|
|
| Materialize opening offers | draft ID/hash, run seed hash, versions, expected empty offer set | all four indexed offer records/IDs/output hashes and one offer-set hash |
|
|
| Set opening selection | draft ID/hash, offer-set hash, expected selection revision, exactly two distinct offer IDs | same four offers, canonical selected pair, revision increment, receipt |
|
|
| Commit run creation | draft ID/hash, offer-set hash, selection revision, exact pair and output hashes | draft removed; same run ID/seed and selected Recruit records in one new `current_run`; opening Coin/capacity and run-start receipt |
|
|
| Buy Recruit | offer ID, recruit output hash, Credit choice, expected price | Coin/Credit cost, offer removal, Recruit ownership |
|
|
| Buy/sell equipment | offer/item ID and expected receipt | Coin delta and exactly one ownership change |
|
|
| Lock/refresh | Market hash, selected locks, expected cost | Coin cost, counters, complete fixed offer board |
|
|
| Equip/reorder | Recruit, candidate item, expected prior item or null, and/or order IDs | one-slot replacement receipt, complete legal inventory and Party-line state |
|
|
| Promote | Recruit, old/new Profession, eligibility proof | one Profession replacement and derived-state refresh |
|
|
| Dismiss | Recruit, optional equipped item ID, rebate preview, expected `live_recruit_count` | Recruit removal, optional item return, Coin rebate, post-count at least one |
|
|
| Commit battle | battle offer, lineup, stats, seed, risk | immutable encounter start snapshot |
|
|
| Start retreat | encounter, accepted tick | one retreat timestamp; no duplicate start |
|
|
| Lock outcome | objective and final state hashes | immutable victory/defeat/timeout/retreat result |
|
|
| Apply casualties | exact Recruit/optional-item/Credit set | all Recruit removals, all present item returns, all Credit grants |
|
|
| Claim reward | fixed draft/card/disposition | one reward and draft removal |
|
|
| Close run | terminal cause and persistent grants | Coin/Credit clear, run result, Records/Renown once |
|
|
| Change difficulty/law | Guild-only selected rule set | next-run configuration only |
|
|
|
|
An operation cannot use a presentation animation as a commit boundary. A
|
|
double-tap, key repeat, controller repeat, OS lifecycle retry, or cloud retry
|
|
returns the same status/receipt.
|
|
|
|
### 7. Run Creation, Market, And Round Save Points
|
|
|
|
#### 7.1 Seed-First Run Creation
|
|
|
|
`Begin run creation` derives values without an ambient RNG or wall clock:
|
|
|
|
```text
|
|
seed_ordinal = next_run_ordinal
|
|
run_seed = low128(sha256(generation_root_seed || lineage_id ||
|
|
"run-seed" || u64(seed_ordinal)))
|
|
draft_id = low128(sha256(run_seed || "run-creation-draft"))
|
|
run_id = low128(sha256(run_seed || "run-id"))
|
|
```
|
|
|
|
Its pending intent fixes the chosen Contract/difficulty and generation snapshot
|
|
but generates no Recruit. Its applied child creates the `seed_reserved` draft,
|
|
increments `next_run_ordinal` once, mirrors a seed-reservation receipt into both
|
|
closure ledgers, and is flushed, pointed to, and read back before the opening
|
|
generator may run. A retry from the unchanged parent derives the same ordinal
|
|
and seed. Once a draft exists, a new begin request returns
|
|
`RUN_CREATION_DRAFT_ACTIVE` and that draft; it cannot consume another ordinal.
|
|
If `current_run` exists, begin returns `RUN_ALREADY_ACTIVE` and applies nothing.
|
|
|
|
`Materialize opening offers` uses only the read-back draft and the content
|
|
authority's fixed mappings for indices `0..3`. One applied child stores all four
|
|
complete Recruit records, offer IDs, output hashes, and their canonical
|
|
offer-set hash. A missing index, duplicate ID, output-hash mismatch, changed
|
|
generation snapshot, or any fifth offer rejects with zero deltas and leaves the
|
|
same reserved seed. The materialization receipt is mirrored redundantly and is
|
|
sufficient to regenerate and verify the same four records.
|
|
|
|
The UI may hold a zero- or one-card highlight transiently, but the durable
|
|
`Set opening selection` operation accepts only a canonical unordered pair of two
|
|
distinct IDs from the saved four. A different legal pair replaces the prior
|
|
pair and increments `selection_revision`; repeating the same operation ID/hash
|
|
returns its original receipt. A new operation requesting the already accepted
|
|
pair returns `SELECTION_UNCHANGED` and that receipt without a commit. The player
|
|
may revise the pair until Run commit without regenerating an offer.
|
|
|
|
Back or Cancel exits the view only; it does not discard the draft, change its
|
|
Contract/difficulty, consume an ordinal, or expose a refresh action. Save and
|
|
Quit, suspension, process restart, locale/timezone change, and wall-clock
|
|
rollback reopen the same four offers and latest durable pair. No player-facing
|
|
action allocates a replacement draft while one exists.
|
|
|
|
`Commit run creation` revalidates the draft ID/hash, offer-set hash, exact
|
|
selection revision, pair, and both selected output hashes. Its applied child is
|
|
one atomic state: it removes the draft, creates `current_run` with the same
|
|
`run_id` and `run_seed`, transfers the two unchanged Recruit records, establishes
|
|
52 Coin and Line capacity four, and writes the run-start checkpoint and receipt.
|
|
There is no intermediate state with both objects or neither object. A request
|
|
for a stale draft/revision returns `STALE_RUN_CREATION_DRAFT` plus the current
|
|
draft summary and applies nothing. A retry naming an already promoted draft and
|
|
matching its commit receipt returns `RUN_CREATION_ALREADY_APPLIED` plus the
|
|
original receipt; a conflicting pair/hash is rejected. Neither path generates
|
|
offers or creates a second Run.
|
|
|
|
The Run-creation commit receipt is indexed by both `operation_id` and
|
|
`draft_id`. Therefore a duplicate confirm with the original ID and a recovery
|
|
retry that acquired a new operation ID both resolve to the one applied receipt
|
|
when their draft/pair/hash intent matches. A reused `draft_id` with different
|
|
intent returns `RUN_CREATION_COMMIT_CONFLICT` and cannot reach the generic
|
|
new-ID path.
|
|
|
|
##### Run-Creation Crash And Retry Matrix
|
|
|
|
| Durable boundary | Reachable state after recovery | Exact retry result |
|
|
|---|---|---|
|
|
| Before begin-pending pointer | parent, no draft, unchanged ordinal | derive the same ordinal/seed and retry |
|
|
| After begin-pending pointer | fixed seed intent, zero deltas | finalize the same `seed_reserved` draft once |
|
|
| After seed-reservation pointer/read-back | `seed_reserved`, no offers | materialize indices `0..3` from that seed |
|
|
| Before/during materialization pointer | seed-only or one complete four-offer child | validate pointer; regenerate same four or return receipt |
|
|
| After materialization pointer | `offers_ready`, complete saved four | reopen exact four; no generator call on normal load |
|
|
| Before/during selection pointer | prior pair or one complete revised pair | validate pointer; finalize pair or return its receipt |
|
|
| After selection pointer | `selection_ready` with exact pair/revision | reopen same pair; later revision remains legal |
|
|
| Before/during Run-commit pointer | complete draft or complete `current_run` | finalize from pending intent or return original commit receipt |
|
|
| After Run-commit pointer, before replica rotation | `current_run`, no draft | repair replicas; never recreate draft or transfer twice |
|
|
|
|
Every row uses the generic pending-to-applied procedure in Section 5. A corrupt
|
|
candidate is never partially merged with its parent.
|
|
|
|
#### 7.2 Market, Round, And Run Save Points
|
|
|
|
Safe commits occur after:
|
|
|
|
- run-seed reservation, complete opening-offer materialization, each accepted
|
|
two-Recruit selection revision, and atomic Run creation;
|
|
- Market generation, lock, refresh, buy, sell, equip, reorder, promote, and
|
|
dismissal;
|
|
- battle offer commit and countdown start;
|
|
- accepted retreat start;
|
|
- encounter outcome lock;
|
|
- casualty application;
|
|
- reward claim or failure application;
|
|
- capacity increase, boss Muster, boss result, and run close.
|
|
|
|
`Save and Quit` completes the current tick/transaction and points to the same
|
|
active commit. It does not create a selectable branch or reroll point. Loading a
|
|
Market returns the exact offers, locks, refresh counter, Coin, Credit, Company,
|
|
Reserve, inventory, and order.
|
|
|
|
Failed attempts increment `Market_index` but not `cleared_rounds`. The next
|
|
Market still prepares the same required Round number with a new precommitted
|
|
board from the run seed. A boss failure reopens boss Muster with the same fixed
|
|
boss and newly derived Recruit/equipment offers. This progression cannot be
|
|
reset by closing or restoring a player-selectable save.
|
|
|
|
### 8. Battle Interruption And Deterministic Resume
|
|
|
|
An encounter snapshot stores:
|
|
|
|
```text
|
|
encounter_id, ruleset_version, content_revision, root_seed
|
|
simulation_tick and per-label draw counters
|
|
ordered actor IDs/footprints, fixed-point HP/stats/resources/readiness
|
|
casts, projectiles, movement intents, summons, statuses, barriers, cooldowns
|
|
objective/phase/Overtime/retreat state
|
|
automatic event journal and accepted lifecycle commands
|
|
snapshot_hash, previous_snapshot_hash
|
|
```
|
|
|
|
The game writes a resumable snapshot every ten ticks, at every accepted retreat
|
|
command, after every Fallen/collapse batch, and at outcome lock. On normal
|
|
suspend it completes the current priority batch before acknowledging suspend.
|
|
|
|
If killed earlier, recovery:
|
|
|
|
1. loads the newest valid encounter snapshot;
|
|
2. replays deterministic automatic events and accepted lifecycle commands from
|
|
the previous valid hash to the last journaled tick;
|
|
3. verifies all labeled draw counters and state hashes;
|
|
4. resumes at the first unprocessed priority of the next tick.
|
|
|
|
No wall time is substituted. A week-long suspend resumes the same tick. Camera,
|
|
animation, particles, and audio may restart; authoritative events may not.
|
|
|
|
If all encounter snapshots are corrupt but the committed battle start and event
|
|
journal validate, replay from countdown using the same seed and retreat tick.
|
|
If replay hash diverges, seal the run as `recovery_incomplete` under Section 12;
|
|
never offer a fresh seed or pre-battle edit after observed casualties.
|
|
|
|
### 9. Recruit Death, Equipment Return, And Dismissal
|
|
|
|
#### 9.1 Casualty Settlement
|
|
|
|
Fallen state exists in battle snapshots as soon as the death batch resolves.
|
|
Run ownership changes only in the casualty settlement after outcome lock. The
|
|
pending intent lists, for every casualty:
|
|
|
|
```text
|
|
recruit_id, final Profession/level/XP/Trait/history hash
|
|
equipped_item_id or null
|
|
original list price, Coin paid, computed Credit grant
|
|
death tick and causal event ID
|
|
```
|
|
|
|
Applied settlement must satisfy:
|
|
|
|
```text
|
|
all casualty Recruit IDs absent from living Company and Reserve
|
|
every non-null casualty equipped_item_id present exactly once in run inventory
|
|
Replacement Credit equals the capped sum
|
|
fallen total increases by exact casualty count
|
|
no non-casualty Recruit or item changes ownership
|
|
```
|
|
|
|
If any condition fails, the candidate is rejected and the outcome remains
|
|
locked pending recovery. The game cannot enter the next Market with a partial
|
|
casualty result.
|
|
|
|
Casualties apply on victory, defeat, timeout, and retreat. A same-batch mutual
|
|
defeat does not preserve either Fallen Recruit. Rehearsal simulations create no
|
|
operation and cannot affect ownership.
|
|
|
|
#### 9.2 Dismissal
|
|
|
|
Dismissal is an explicit destructive operation from a Market. The pending
|
|
review shows generated name, Profession, Trait, level, survival history,
|
|
returned equipment, Coin rebate, resulting deployable footprint, and no Credit
|
|
grant. Default focus is cancel. Both preview and commit use the product
|
|
authority's canonical count:
|
|
|
|
```text
|
|
live_recruit_count = count(unique Recruit IDs owned by the current run
|
|
whose state is living,
|
|
across deployed party and Reserve)
|
|
dismissal_legal = Market_open and target_is_living and
|
|
live_recruit_count >= 2
|
|
```
|
|
|
|
Dismissal is rejected for a Recruit referenced by a committed battle, pending
|
|
promotion, pending casualty settlement, or another dismissal. It is also
|
|
rejected as `FINAL_LIVE_RECRUIT` when the canonical count is one.
|
|
|
|
Preview rejection creates no operation ID, pending row, or save mutation. A
|
|
crafted or stale confirm is revalidated before a pending gameplay operation can
|
|
apply. Its `FINAL_LIVE_RECRUIT` response has zero cost/result deltas: no Recruit
|
|
or equipment moves, no Coin rebate, no Credit, no protected recovery offer, no
|
|
dismissed-total change, and no commit-sequence advance. The implementation may
|
|
retain only the rejected intent response keyed by operation ID and intent hash
|
|
so duplicate confirms return the same response; this is not an applied/pending
|
|
gameplay receipt and cannot later become legal after the Company changes.
|
|
|
|
A pending dismissal recovered after interruption stores its parent commit and
|
|
expected count. Before transition to applied, it must prove the active parent is
|
|
unchanged, pre-count is at least two, and post-count is exactly pre-count minus
|
|
one and at least one. Otherwise it becomes the same zero-mutation rejected
|
|
intent response. A successfully applied dismissal returns its original receipt
|
|
on duplicate confirm and cannot remove another Recruit or pay a second rebate.
|
|
|
|
Explicit `Abandon Run` is a separate run-close operation; it does not reuse a
|
|
dismissal ID or pay a rebate. Casualty settlement is also separate and remains
|
|
the only path by which a final Recruit's death can create Credit, a protected
|
|
offer, recovery viability evaluation, or Company collapse.
|
|
|
|
#### 9.3 No Resurrection Boundary
|
|
|
|
Applied casualty receipts are closure receipts. An older backup that predates a
|
|
casualty can become active only after the receipt chain deterministically
|
|
replays through that casualty or after the affected run is sealed. A recovery
|
|
UI must never present "continue run" from an older state containing a Recruit
|
|
whose later valid casualty receipt is known.
|
|
|
|
### 10. Duplicate Purchase, Reward, And Persistent Grant Protection
|
|
|
|
UI debounce is optional convenience; operation IDs are authority.
|
|
|
|
For purchase:
|
|
|
|
```text
|
|
if operation applied: return stored Recruit/item and receipt
|
|
if pending: finalize same fixed offer and price
|
|
if offer absent under a new operation ID: reject OFFER_ALREADY_CONSUMED
|
|
```
|
|
|
|
For reward:
|
|
|
|
```text
|
|
verify card ID belongs to stored draft
|
|
pending contains exact selected card and replacement choice if required
|
|
applied removes draft and adds one item/Artifact/Coin result
|
|
```
|
|
|
|
For Renown, clear, and discovery facts, the stable grant ID is derived from
|
|
Guild ID plus objective/content ID. A repeated boss outcome or platform retry
|
|
finds the existing applied ID and grants nothing again.
|
|
|
|
Optional platform achievements derive from applied Records. The outbox holds at
|
|
most 100 idempotent events. When full, older events are regenerated from Records
|
|
rather than blocking play or changing gameplay resources.
|
|
|
|
### 11. Backup, Corruption, And Closure Receipts
|
|
|
|
#### 11.1 Backup Set
|
|
|
|
Each Guild slot stores:
|
|
|
|
- `active`: newest validated commit;
|
|
- `backup_1..4`: distinct prior verified transaction boundaries;
|
|
- `run_creation`: latest verified seed/offers/selection draft boundary until
|
|
atomic Run commit;
|
|
- `run_start`: current run creation commit until run close;
|
|
- `Market_checkpoint`: latest Market-open commit;
|
|
- `pre_migration`: source bytes before the latest migration;
|
|
- `closure_A` and `closure_B`: alternating append-only closure receipt ledgers;
|
|
- `quarantine`: corrupt bytes and diagnostics, never auto-deleted.
|
|
|
|
Backups rotate only after active read-back. Storage pressure removes diagnostic
|
|
logs before any active, closure, run-creation, run-start, Market, or
|
|
pre-migration copy. Seed reservation, offer materialization, selection revision,
|
|
and Run commit receipts are mirrored into both closure ledgers under
|
|
`run_creation_receipt_head`. Older selection receipts may compact only after an
|
|
exact-ID segment and the latest complete pair/revision snapshot are retained.
|
|
|
|
#### 11.2 Validation Order
|
|
|
|
1. envelope parse and size bounds;
|
|
2. CRC32 and SHA-256;
|
|
3. schema and required fields;
|
|
4. Guild/lineage/parent/sequence links;
|
|
5. operation status transitions and receipt chain;
|
|
6. nonnegative Coin/Credit/Renown and cap checks;
|
|
7. Recruit uniqueness, generated output hashes, and Profession topology;
|
|
8. equipment ownership uniqueness and equip references;
|
|
9. run-creation seed ordinal, draft/current-run exclusivity, indexed opening
|
|
offer hashes, and selection revision;
|
|
10. Market seed ancestry, offers, locks, and counters;
|
|
11. encounter snapshot, event journal, draw counters, and outcome;
|
|
12. persistent clear/discovery IDs and run-close consistency.
|
|
|
|
A settings error falls back to defaults without touching gameplay. Optional
|
|
cloud/platform failure never labels a local save corrupt.
|
|
|
|
#### 11.3 Recovery Algorithm
|
|
|
|
1. Validate active, then backups newest to oldest.
|
|
2. Validate both closure ledgers and choose the longest common valid chain.
|
|
3. Select the newest valid gameplay commit whose ancestry matches the slot.
|
|
4. Before Run commit, reconstruct a draft only from a valid seed-reservation
|
|
receipt plus a complete matching receipt chain. Regenerate the four offers
|
|
and verify their saved hashes, then restore the latest complete pair/revision.
|
|
5. After a Run-commit receipt exists, never reconstruct its former draft. Recover
|
|
the matching `current_run`, or seal it if the full transferred state cannot
|
|
be verified.
|
|
6. Replay later closure receipts only when parent/result hashes form a complete
|
|
chain and each result validates.
|
|
7. If the chain reconstructs the newer state, write a recovered child and
|
|
continue normally.
|
|
8. If a missing/corrupt gap crosses a Run commit, casualty, reward, boss clear,
|
|
Renown grant,
|
|
or run close, restore persistent Guild state only through the verified
|
|
boundary and seal the current run as `recovery_incomplete`.
|
|
9. If no active, backup, draft checkpoint, or redundant receipt proves the
|
|
reserved seed, mark the slot `creation_recovery_blocked`; do not advance the
|
|
ordinal, allocate another seed, or offer a playable branch. Export and the
|
|
existing explicit Guild-slot delete/reset recovery remain available.
|
|
10. Preserve all rejected bytes in quarantine and write a diagnostic manifest.
|
|
|
|
Sealing clears run-only Coin/Credit and prevents reward or casualty farming.
|
|
Recovered persistent facts are never guessed beyond the last verified receipt.
|
|
The UI states the boundary and any unverifiable operations; it never claims full
|
|
preservation without matching hashes.
|
|
|
|
### 12. Failure State Model And Consequences
|
|
|
|
Failure is classified before one operation calculates consequences:
|
|
|
|
| Kind | Predicate | Casualties | Coin fee | Run transition |
|
|
|---|---|---|---:|---|
|
|
| Victory with losses | objective succeeds with one or more Fallen Recruits | Apply all | 0 | pay reward, next Market/boss/clear |
|
|
| Encounter defeat | all deployed non-summons Fallen or objective failure before timeout | Apply all | difficulty table | no reward; recovery Market if viable |
|
|
| Timeout defeat | objective incomplete at tick 1500 | Apply prior Fallen only | difficulty table | no reward; recovery Market if viable |
|
|
| Voluntary retreat | withdrawal completes | Apply all Fallen before completion | difficulty table | no reward; recovery Market if viable |
|
|
| Run abandonment | player confirms from Market/result | No new casualties | clear all run Coin/Credit | close run, no unearned boss grant |
|
|
| Company collapse | no living Recruit and no affordable protected recovery hire | already applied | 0 additional | close run |
|
|
| Process interruption | suspend/crash/power loss | none beyond deterministic replay | 0 | resume/reconstruct; not gameplay failure |
|
|
| Save corruption | active data fails validation | none invented | 0 | recover or seal; not gameplay failure |
|
|
|
|
Difficulty fees are the sole numeric failure-fee authority:
|
|
|
|
| Difficulty | Encounter defeat | Timeout | Retreat |
|
|
|---|---:|---:|---:|
|
|
| Wayfinder | 0 Coin | `min(2,current Coin)` | `min(4,current Coin)` |
|
|
| Standard | `min(2,current Coin)` | `min(4,current Coin)` | `min(6,current Coin)` |
|
|
| Oathbound | `min(4,current Coin)` | `min(6,current Coin)` | `min(8,current Coin)` |
|
|
|
|
Fee, casualty settlement, failure totals, consumed battle offer, and next-Market
|
|
seed advance are one result transaction. Fees never make Coin negative and do
|
|
not alter Credit. Retreat is therefore a controlled loss rather than a free way
|
|
to cycle battle/Market offers.
|
|
|
|
#### 12.1 Recovery Viability And Run Close
|
|
|
|
After failure settlement:
|
|
|
|
```text
|
|
can_continue = living_recruit_count > 0
|
|
or coin + replacement_credit >= protected_offer_list_price
|
|
```
|
|
|
|
If true, open a recovery Market using the same required cleared-Round number.
|
|
If false, show Company collapse and close the run after confirmation/automatic
|
|
receipt. Explicitly declining the only affordable protected offer recomputes the
|
|
predicate and previews run close before confirm.
|
|
|
|
This predicate runs only after casualty/failure settlement or protected-offer
|
|
decline. Dismissal cannot reduce the canonical live count below one and cannot
|
|
invoke this recovery/closure path. A player who wants to end a run while one or
|
|
more Recruits live must use the separate `Abandon Run` transaction.
|
|
|
|
Boss defeat follows the same rule. Cleared-Round count remains 12; boss Muster
|
|
reopens if viable. No failure restores a consumed reward, dead Recruit, prior
|
|
Market, or refresh counter.
|
|
|
|
#### 12.2 Run Close
|
|
|
|
Run close stores terminal cause, Company history, all casualties/dismissals,
|
|
boss/clear facts, discoveries, and eligible Renown. It then clears run-only
|
|
Coin, Credit, Recruits, inventory, Artifacts, Market, and encounter state.
|
|
Survivors become nonplayable history Records. One operation applies closure and
|
|
persistent grants; retry returns the same run result.
|
|
|
|
### 13. Migration Contract
|
|
|
|
Migration runs only at startup or a Market with no pending operation. It never
|
|
runs during countdown, battle, outcome, casualty settlement, reward selection,
|
|
or run close.
|
|
|
|
1. Validate and copy source bytes to `pre_migration`.
|
|
2. Build an adjacent-version plan; skipping a required step is illegal.
|
|
3. Resolve stable content IDs and list legacy/missing rows.
|
|
4. Apply each pure deterministic migration to a copy.
|
|
5. Preserve generated name parts, rendered-name snapshot, appearance seed,
|
|
attributes, Trait, Profession, level, and survival history for living
|
|
Recruits; do not draw replacements.
|
|
6. Recompute derived stats from preserved source fields under the boundary rule.
|
|
7. Validate operation, ownership, Market, battle, and closure chains.
|
|
8. Write/read a new lineage child, then switch active pointer.
|
|
9. Preserve pre-migration bytes until two later valid Market commits and run
|
|
close or 30 launches, whichever is later.
|
|
|
|
A removed equipment/Artifact ID becomes a sealed legacy record with its stable
|
|
ID and original ownership. At the next safe Market, the player chooses one of
|
|
two deterministic same-band behavior replacements or leaves it sealed. A
|
|
removed Profession keeps its stored ruleset for the current committed battle;
|
|
afterward a deterministic parent-compatible replacement review is required.
|
|
|
|
If migration fails, discard the candidate and leave the source active under its
|
|
last supported ruleset where possible. Mark only that slot `migration_blocked`
|
|
and offer details/export. Never reset, auto-sell, reroll, or auto-equip.
|
|
|
|
### 14. Local, Cloud, Multi-Device, And Rollback
|
|
|
|
Cloud transports complete validated commits plus closure ledgers. It does not
|
|
merge Coin, Recruits, equipment, offers, casualties, rewards, or operation rows.
|
|
|
|
| Commit topology | Resolution |
|
|
|---|---|
|
|
| Identical commit | no action |
|
|
| Local ancestor of cloud | offer verified cloud descendant; retain local backup |
|
|
| Cloud ancestor of local | keep local; queue upload |
|
|
| Different Guild/lineage | import only into an empty slot after validation |
|
|
| Same lineage, divergent descendants | never merge; choose one active branch, archive the other read-only |
|
|
|
|
Conflict comparison shows commit boundary, Region/Contract, Market/encounter,
|
|
cleared Rounds, living/fallen counts, Renown, and closure receipt head. Wall
|
|
clock is context only. Default is cancel/inspect.
|
|
|
|
The unchosen divergent branch cannot be duplicated into a second playable slot,
|
|
because that would duplicate generated offers and allow alternate casualty or
|
|
reward outcomes. It remains exportable/read-only until explicitly deleted.
|
|
|
|
A developer/support rollback follows the same recovery algorithm as corruption.
|
|
It may activate an older commit only after replaying later valid closure receipts
|
|
or sealing the affected run. No debug, platform, or user-facing path may resume
|
|
an older branch that reverses a known applied casualty or reopens a known claimed
|
|
reward.
|
|
|
|
### 15. Clock Rollback And Bounded Offline Behavior
|
|
|
|
All gameplay time uses simulation ticks or monotonic in-session duration. Wall
|
|
clock is display/diagnostic data.
|
|
|
|
```text
|
|
wall_delta = current_wall_clock - last_seen_wall_clock
|
|
if wall_delta < -300 seconds or wall_delta > 180 days:
|
|
record CLOCK_ANOMALY
|
|
change no gameplay state
|
|
```
|
|
|
|
While closed, the game advances exactly zero ticks and generates no Market,
|
|
offer, Coin, Credit, XP, Renown, healing, promotion, unlock, enemy outcome, or
|
|
reward. Return opens the saved state plus a compact recap.
|
|
|
|
Background work is bounded:
|
|
|
|
- at most one local save flush at a time;
|
|
- at most three complete cloud payloads queued per Guild slot;
|
|
- at most 100 idempotent platform events;
|
|
- at most 30 seconds of best-effort upload after an explicit foreground save,
|
|
cancellable without affecting the local commit;
|
|
- no retry loop blocks exit, local play, or a new local commit.
|
|
|
|
Changing clock, timezone, locale, sleep duration, or network availability cannot
|
|
change generated names, offers, difficulty, results, or Records.
|
|
|
|
### 16. Confirmation, Overwrite, Delete, And Reset
|
|
|
|
These actions require an effect review and a separate confirm: dismissal with
|
|
history, promotion, Artifact replacement, protected recovery-offer decline that
|
|
would close a run, run abandonment, difficulty/Oath-law change, cloud branch
|
|
choice, migration fallback, Guild-slot overwrite, and Guild-slot delete.
|
|
|
|
Delete moves the complete Guild slot and closure ledgers to recoverable local
|
|
trash for seven successful launches or until explicit empty-trash confirmation.
|
|
A profile-wide reset additionally requires selecting a displayed localized
|
|
phrase in a second dialog; typing is not required. Default focus is cancel, and
|
|
destructive actions cannot share a single repeated input with the opener.
|
|
|
|
Every accepted confirm receives an operation ID. Repeating it returns the first
|
|
receipt and cannot dismiss, grant, overwrite, close, or delete twice.
|
|
|
|
### 17. Accessibility, Localization, And Input Requirements
|
|
|
|
- `Saving`, `Saved`, `Pending`, `Recovered`, `Sealed`, `Conflict`, and `Blocked`
|
|
use text plus distinct icons, never color/spinner alone.
|
|
- Closing during `Pending` is legal; recovery finalizes/rejects from intent.
|
|
- Recovery and conflict dialogs support 130% UI scale, screen-reader order,
|
|
complete touch path, keyboard/controller focus, and reduced motion.
|
|
- Casualty review lists generated name, Profession, equipment returned, Credit,
|
|
death tick, and first cause; it does not rely on animation or sound.
|
|
- Technical IDs/hashes are hidden by default but available in details and an
|
|
exportable diagnostic manifest.
|
|
- Localized messages use semantic IDs and typed values, never string fragments.
|
|
- Controller disconnect or focus loss pauses presentation and commits nothing
|
|
until a new explicit confirm is accepted.
|
|
- Haptic/audio feedback may acknowledge a save or casualty but cannot be the
|
|
only state indication.
|
|
|
|
### 18. Required Recovery And Failure Matrix
|
|
|
|
| Test | Fault/input | Expected invariant |
|
|
|---|---|---|
|
|
| Seed reservation crash | crash after begin-pending or applied pointer | same ordinal/seed; zero offers before applied draft read-back; one draft after retry |
|
|
| Offer materialization crash | crash around four-offer pointer | seed-only draft or complete indices `0..3`; retry yields identical IDs/output hashes |
|
|
| Selection revision crash | revise accepted pair and crash around pointer | prior complete pair or new complete pair with one revision/receipt; never one selected ID |
|
|
| Run creation duplicate | repeat original ID or use a new ID naming the applied draft/pair | same original receipt, same seed/two Recruit records, no draft and no second Run |
|
|
| Run creation stale draft | submit old draft hash or selection revision | `STALE_RUN_CREATION_DRAFT`, current summary returned, zero mutation |
|
|
| Run creation cancel/restart | Cancel, Save and Quit, or process restart before commit | same draft, four offer IDs/hashes and latest durable pair; no new ordinal/seed |
|
|
| Run draft clock rollback | wall clock moves back 24 hours before Run commit | diagnostic only; same draft/offers/pair and zero gameplay delta |
|
|
| Run draft active corruption | invalid active checksum, intact draft checkpoint/backup | recover newest valid draft boundary and exact four/pair; no replacement seed |
|
|
| Run draft replica corruption | active/backups invalid, redundant creation receipts intact | rebuild same seed/four/pair, or same committed Run if commit receipt exists |
|
|
| Run draft total corruption | no valid primary, backup, checkpoint, or creation receipt | `creation_recovery_blocked`; quarantine/export, no new draft or free reroll |
|
|
| Pending purchase | crash after pending pointer | zero cost until deterministic applied child; one Recruit after retry |
|
|
| Applied purchase | duplicate same operation ID | one cost, one Recruit, same receipt |
|
|
| Refresh crash | crash after Coin validation | old complete board or new complete board and cost; never mixed |
|
|
| Legal dismiss duplicate | start with two live Recruits; repeat confirm | one Recruit removal, zero-or-one item return, one rebate; final live Recruit remains |
|
|
| Final dismiss preview | one live Recruit, low Coin, no protected offer | `FINAL_LIVE_RECRUIT`; no operation ID or mutation; `Abandon Run` remains separate |
|
|
| Stale final dismiss | preview at two, another serialized dismissal leaves one, submit old confirm | zero-delta `FINAL_LIVE_RECRUIT`; no rebate, protected offer, or count change |
|
|
| Interrupted final dismiss | recover pending intent against active count one | reject without apply/receipt beyond rejected intent response; one Recruit remains |
|
|
| Battle suspend | suspend during projectile at tick 417 | identical next event/draw counters/final hash |
|
|
| Death snapshot | crash one tick after front Recruit falls | replay contains same death and collapse |
|
|
| Outcome crash | crash before casualty settlement | locked result reopens; settlement applies exact set once |
|
|
| Casualty partial candidate | omit the non-null equipped item in candidate | validation rejects; no Recruit removal/Credit commits |
|
|
| Reward duplicate | double-tap selected card | one item/Artifact/Coin result and draft removal |
|
|
| Boss grant duplicate | replay same boss-clear result | one clear fact and Renown grant |
|
|
| Three deaths | fixed economy fixture | Recruits removed, all equipment returned, 54 Credit capped |
|
|
| Standard retreat | 10 Coin at/after tick 70 | 6 Coin fee, all earlier casualties apply, no reward |
|
|
| Wayfinder timeout | 1 Coin at tick 1500 | 1 Coin fee, living Recruits survive, no reward |
|
|
| Company collapse | zero living, 9 Coin+Credit, protected price 14 | run closes; no free Recruit invented |
|
|
| Recoverable company | zero living, 14 Coin+Credit, protected price 14 | recovery Market opens; purchase still requires confirm |
|
|
| Clock rollback | wall clock moves back 24 hours | diagnostic only; zero gameplay delta |
|
|
| Thirty-day offline | close mid-run and return | zero tick/resource/offer progress; exact recap |
|
|
| Active corruption | invalid active checksum, intact receipts/backups | reconstruct newest valid chain or seal run at stated boundary |
|
|
| Casualty receipt beyond backup | selected backup contains later-Fallen Recruit | replay casualty or seal run; never resume with Recruit |
|
|
| All battle snapshots corrupt | valid start/journal remains | replay same seed/retreat tick or seal on hash divergence |
|
|
| Migration failure | stable-ID mapping invalid | source stays active; candidate discarded; export offered |
|
|
| Cloud divergence | same lineage, different descendants | one chosen active, other read-only; no merge/duplicate slot |
|
|
| Run-close retry | crash after persistent grants before screen transition | same result, Coin/Credit remain cleared, no duplicate Renown |
|
|
|
|
### 19. Acceptance Checklist
|
|
|
|
- [ ] Schema, ruleset, content, lineage, commit, and operation versions are
|
|
separate and migration is copy-first.
|
|
- [ ] Pending/applied/rejected operations preserve zero-delta pending and atomic
|
|
cost/result receipts.
|
|
- [ ] Run creation durably reserves one seed before offer generation, stores an
|
|
all-or-nothing indexed four, receipts revisable exact-pair selection, and
|
|
promotes that same seed and pair into `current_run` exactly once.
|
|
- [ ] Market, battle, casualty, reward, and run-close retries are idempotent.
|
|
- [ ] Battle suspend/crash reproduces deterministic state or seals the run on an
|
|
unverifiable divergence.
|
|
- [ ] Death, equipment return, Credit, dismissal, replacement, defeat, timeout,
|
|
retreat, and Company collapse are explicit and testable.
|
|
- [ ] Preview, stale confirm, duplicate confirm, and interruption cannot dismiss
|
|
the final living Recruit or route dismissal through casualty recovery.
|
|
- [ ] Backups, closure receipts, corruption, migration, rollback, and cloud
|
|
conflicts cannot knowingly reverse casualties or duplicate rewards.
|
|
- [ ] Clock rollback and bounded offline behavior produce zero gameplay progress.
|
|
- [ ] Confirmation, delete, accessibility, localization, and input rules cover
|
|
every destructive and recovery state.
|