# REIKHELM: DESCENT

A first-person dungeon-crawler roguelike, three floors deep. Eye of the Beholder
presentation over the reikhelm procgen + AI-render pipeline, with the Vigor
combined-pool combat system as the fight engine. Pure Rust→WASM + vanilla JS —
no game engine.

## Play

```bash
wasm-pack build game-wasm --target web --out-dir ../game-web/pkg --no-typescript
python3 tools/serve.py --root game-web --port 8001
# open http://127.0.0.1:8001
```

**Explore** `W/S` step · `A/D` turn · `R` rest (hold) · `G` drink draught ·
`T` drink tonic · `Enter` take the stairs
**Combat** `1–5` abilities · `Space` toggle auto-attack · `Tab` switch target ·
`F` flee (costs ceiling, the monsters remember)

## The shape of a run

- Three fixed floors (atlas seeds **7 → 69 → 59**), pre-baked to pixel art by
  `tools/bake_atlas.py`; monsters, treasure, and loot reroll **per run** from the
  fate seed. The baked scenes are empty ruins by design — inhabitants are
  composited at runtime.
- Your **vigor pool is health, mana, and stamina at once** (the Vigor system,
  see `combat/`). Damage and effort drain the same pool; fatigue lowers its
  ceiling and persists **between** fights. Resting restores fill quickly and
  fatigue slowly; tonics clear fatigue; relics widen the pool for the run.
- The deepest room of floor III holds **the Ember Sovereign**. Kill him to win.
  Death is permanent.

## Architecture

```
game-wasm/            the whole game state machine (Rust, natively tested)
  src/manifest.rs     parses the baked atlas manifests (nav graph = source of truth)
  src/spawn.rs        seeded monster/treasure placement + balance constants
  src/run.rs          explore/combat/dead/victory state machine, persistence
  src/views.rs        one JSON payload per interaction; combat views match combat-web
  src/monster_ai.rs   the shared enemy brain
game-web/             thin browser view (no framework)
  game.js             input → wasm call → render from returned state
  audio.js            all SFX synthesized in WebAudio, zero assets
  config.json         the combat table: combat/configs/default.json + the boss
tools/out/atlas/<seed>/   the baked campaign art (committed; _work/ is not)
```

The dungeon geometry is read from the atlas **manifest**, never regenerated —
the graph the game walks is exactly the graph the baked frames depict.

## Balancing notes

- Tuned via `cargo test -p game-wasm -- --nocapture`: a whole-run bot plays the
  real campaign headlessly, and a `ScriptedPlayer` probe sweeps boss win rates
  (target: ~35% at skill 50, ~55% at skill 80, with potions/flee on top).
- Floor 1 spawns no packs; the wraith debuts on floor 2; player gains a level
  per descent so trash stays beneath a hero's edge — depth pressure comes from
  kits, packs, and fatigue attrition.
