Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Dystrail Docs

Welcome to the Dystrail documentation hub. These pages collect specs, reference notes, and public-facing messaging for the game.

What you can do

  • Understand the gameplay loop and controller flows via the Journey pages (System Overview, Daily Flow, Balance, and Data Packs).
  • Grab ready-to-share messaging from the press kit and Discord post templates.
  • Follow along with implementation details as the web and engine evolve.
  • Remix the game by editing the bundled data packs (see Journey → Data Packs & Modding for JSON locations and knobs).

Journey System Overview

Purpose: Explain the moving parts of a Dystrail run: how configs are layered, how determinism is enforced, and which data packs drive behavior.

Core architecture

  • One JourneyController simulates every day from mile 0 to the boss gate (default route length 2100.0 in boss::ROUTE_LEN_MILES).
  • Each day emits a DayRecord (kind: Travel, Partial, NonTravel) with miles already partial-adjusted and tagged.
  • Game mode → policy family mapping:
    • Classic family (GameMode::Classic) — baseline Oregon Trail pacing.
    • Deep family (GameMode::Deep) — same pacing bands, higher variance/quirks.
  • Strategy overlay: Balanced, Aggressive, Conservative, ResourceManager. The overlay only changes numbers; code paths stay uniform.

Determinism & RNG streams

  • Seeds derive from HMAC-SHA256(master_seed, domain_tag) → 64-bit seeds (see journey::RngBundle).
  • Independent streams: rng_travel, rng_breakdown, rng_crossing, rng_encounter.
  • Atomic crossings: exactly one RNG draw per crossing event regardless of outcome branch, preventing seed drift.
  • Replay guarantee: same seed + same data packs = identical simulation trace (used by tester suites).

Data packs and source of truth

All policy and tuning data live in dystrail-web/static/assets/data/ and are bundled into the shipped WASM build.

  • Journey families: journey/classic.json, journey/deep.json.
  • Strategy overlays: journey/overlays/*.json (merged on top of family data per field).
  • Supporting packs:
    • boss.json (distance required, weights, rounds, min/max chance, biases)
    • crossings.json, camp.json, exec_orders.json, endgame.json
    • pacing.json, personas.json, result.json, store.json, vehicle.json, weather.json, game.json

Effective config = family ⊕ overlay. Examples (Classic + Balanced overlay):

  • Victory miles: 2100 → 2400 (overlay).
  • Partial ratio: 0.50 → 0.51.
  • Travel base mpd: 14.6 → 15.6; clamp remains 10.0–22.0.
  • Wear base/fatigue: 0.07 / 0.08 → 0.073 / 0.245.
  • Breakdown base/beta: 0.01 / 0.10 → 0.015 / 0.185.
  • Crossing probs: pass/detour/terminal 0.72 / 0.16 / 0.12 → 0.66 / 0.24 / 0.10; bribe/permit tuning updated accordingly.

Daily Flow & Mechanics

This page walks through the exact order of operations for a single day, with the key formulas and thresholds used in engine code.

Step-by-step day

  1. Player inputs: choose pace (PaceId: Steady, Heated, Blitz) and diet (DietId).
  2. Travel miles
    • Start from travel.mpd_base (family/overlay).
    • Multiply by pace factor (Classic: Steady 1.0, Heated 1.12, Blitz 1.3).
    • Multiply by weather factor (Classic: Clear 1.0, Storm 0.82, HeatWave 0.78, ColdSnap 0.9, Smoke 0.86).
    • Apply exec-order/weather/diet modifiers if configured.
    • Clamp to [mpd_min, mpd_max] (Classic: 10.0–22.0).
  3. Partial travel (detour/repair/etc.): miles × partial_ratio (Classic 0.50; Balanced overlay 0.51).
  4. Wear accumulation
    • Classic baseline: wear = base (0.07) + fatigue_k (0.08) * fatigue(distance, comfort_miles=1250).
    • Balanced overlay: base 0.073, fatigue_k 0.245, comfort_miles 1750.
  5. Breakdown roll
    • Classic base: p = breakdown.base (0.01) * pace_factor * weather_factor + beta (0.10) * wear_component.
    • Overlay tweaks: base 0.015, beta 0.185; pace multipliers Steady 0.94, Heated 1.08, Blitz 1.28; weather multipliers Clear 1.0, Storm 1.25, HeatWave 1.35, ColdSnap 1.1, Smoke 1.15.
    • Targeted part weights (Classic): tire 44, battery 22, alternator 18, pump 16.
  6. Crossing resolver (if scheduled for the day)
    • Classic probs: pass 0.72, detour 0.16, terminal 0.12; detour costs 1–3 days.
    • Bribe: pass bonus 0.2, terminal penalty 0.2, diminishing returns 0.4 (Balanced overlay adjusts to 0.192 / 0.045 / 0.38).
    • Permit can disable terminal for eligible tags (default ["checkpoint"]).
  7. Encounter resolver
    • Uses cooldowns and history windows (constants.rs: cooldown 1 day, history window 10, repeat window 6, soft cap threshold 5).
  8. Daily tick (supplies/sanity/HP)
    • For each channel: base × pace × diet × weather × exec (see DailyTickConfig).
    • Rounded to integers and clamped to stat caps. Classic family uses zeros; overlays add decay (e.g., Balanced health.decay = 0.12).
    • Rest requests can heal HP if rest_heal is set (overlays can enable).
  9. Endgame/boss gates
    • Endgame (Deep) from endgame.json: starts at 1750 mi, guard at 1950 mi, health floors 45–50 HP, wear multipliers 0.6–0.7, stop caps (window 10, max 2 full stops), wear shave 0.7.
    • Boss gate: requires distance ≥ distance_required (defaults to 2100 mi). Boss chance is weighted by supplies, sanity, allies, pants penalty, and policy bias (BalancedBossBias in boss.json: Classic bonus 0.30, Deep multiplier 1.1, Deep bonus 0.08). Outcomes: PassedCloture, SurvivedFlood, PantsEmergency, Exhausted.

Day record semantics

  • Travel: full mileage credit.
  • Partial: mileage multiplied by partial_ratio (detours, repairs, shared travel).
  • NonTravel: camps, blockers, endgame/boss-only days.
  • Travel ratio uses (Travel + Partial) / total_days and is checked against guards.

Balance Targets & Acceptance Guards

These are the numeric rails that keep runs inside the intended “Oregon Trail” feel and the gates the tester enforces.

Global pacing targets

  • Route length target: ~2,000–2,400 mi (boss gate at distance_required, default 2,100; overlays may raise victory miles).
  • Day window: 84–180 days.
  • Mean miles per day: 10–20.
  • Travel ratio: ≥ 0.90 (guards block configs that drop below this).

Classic family expectations

  • Boss reach: 30–50% of runs.
  • Boss win: ~20–35%.
  • Survival (any non-early wipe ending): 60–80%.
  • Failure mix: no single family (vehicle, sanity, exposure, crossings) > 50% of failures over large samples.
  • Aggressive: lower survival and more terminal crossings than Balanced.
  • Conservative / ResourceManager: higher survival and boss reach but boss win should stay ≲ 40%.

Deep family expectations

  • Same distance/duration/mpd bands as Classic.
  • Higher per-run variance is allowed; means must still satisfy the Classic bands.
  • Crossings/failures may be a bit harsher or stranger, but not arcade-short or ultra-long.

Guard rails (from journey config)

  • guards.min_travel_ratio: Classic 0.90; overlays may adjust.
  • guards.target_distance: Classic 2000.0; overlay can raise (e.g., 2400.0).
  • guards.target_days_min/max: 84 / 180.
  • Tester sweeps (dystrail-tester) validate these across many seeds; failing guards fails CI.

Crossing & breakdown reference numbers

  • Classic crossings: pass 0.72, detour 0.16, terminal 0.12; detour days 1–3.
  • Bribe: pass bonus 0.2, terminal penalty 0.2, diminishing returns 0.4 (Balanced overlay tweaks).
  • Breakdown (Classic base): p = base 0.01 * pace_factor * weather_factor + beta 0.10 * wear_component.
  • Overlay example (Balanced): base 0.015, beta 0.185; pace multipliers 0.94 / 1.08 / 1.28; weather multipliers 1.0 / 1.25 / 1.35 / 1.1 / 1.15.

Data Packs & Modding Guide

Everything that shapes a run lives in JSON under dystrail-web/static/assets/data/ and ships with the WASM build. Editing these files lets you reskin or satirize the game without touching Rust.

Where things live

  • journey/classic.json, journey/deep.json: base family configs (mpd ranges, partial ratio, wear/breakdown, crossings, guards).
  • journey/overlays/*.json: strategy overlays that override family fields (Balanced, Aggressive, Conservative, ResourceManager).
  • boss.json: distance gate, round count, stat weights, min/max chance, balanced bias (Classic bonus, Deep multiplier/bonus).
  • crossings.json, camp.json, exec_orders.json, endgame.json: crossings odds/detours, camp actions, executive orders, endgame behavior.
  • pacing.json, weather.json, vehicle.json: pace multipliers, weather impacts, vehicle wear/parts weights.
  • personas.json, store.json, result.json, game.json: flavor, pricing, outcomes, and high-level game toggles.

How to make a new variant

  1. Copy and edit JSON in static/assets/data/. Change numbers, names, or odds to your liking.
  2. Run tests: just lint or cargo test --workspace --all-features --locked to ensure acceptance guards still pass.
  3. Build the web client: just build-release (or let the CI build job run). The JSON is bundled into dystrail-web/dist.
  4. Ship it: host dist (GitHub Pages via included workflows) and share the Play link/download.

Knobs to twist for satire

  • Crossings: raise terminal, shrink pass, or make detour_days.max huge; tweak bribe bonuses/penalties.
  • Travel feel: drop mpd_base, shrink mpd_min, or slash partial_ratio to 0.2 for a slog; invert for speed-runs.
  • Breakdowns: spike breakdown.base and beta, or overweight a single part in part_weights.
  • Endgame: set wear_multiplier to 0 to make finale trivial, or raise health_floor to punish.
  • Boss: rename outcomes in boss.json, push distance_required up/down, or skew stat weights to reward pants hoarding.
  • Economy/Flavor: rewrite store.json prices, camp.json actions, personas.json lines, weather.json names to match your satire.

Notes

  • Keep guards reasonable if you want CI to stay green; otherwise relax them knowing tests may fail.
  • Determinism is preserved as long as you keep the same RNG structure—changing JSON won’t break replays for a given seed, it only changes the outcomes via new numbers.