reikhelm/game-web
Parley Hatch 5813ee3b5a feat(game): REIKHELM: DESCENT — playable first-person roguelike
The pieces finally meet: procgen dungeons + the baked EoB atlas pipeline +
the Vigor combat engine become one game. Three fixed pre-baked floors
(seeds 7 → 69 → 59, 208 frames), monsters/loot/encounters rerolled per run
and composited over the empty scenes; the Ember Sovereign waits at the
bottom.

- game-wasm: run state machine in Rust (manifest nav graph as dungeon
  truth, seeded spawning by theme/depth, embedded combat-core Encounter,
  vigor/fatigue persistence between fights, rest/potions/relics/flee,
  permadeath + victory). 8 native tests incl. a whole-run bot and a
  ScriptedPlayer boss-winnability probe used for tuning.
- game-web: no-framework front-end — title/death/victory screens, baked
  frames with screen-blend monster portraits, combat overlay (dual vigor
  bars, cast telegraphs, cooldown sweeps, floaters), minimap with fog,
  all SFX synthesized in WebAudio.
- balance: floor-scaled levels with a hero's edge over trash, no packs on
  floor 1, boss tuned by sim sweep (~35%/55% win at skill 50/80).
- campaign atlases now versioned (tools/.gitignore exception); boss
  portrait added to the Z-Image roster.

Verified end-to-end in-browser: an autopilot beat the full game through
the real UI (10 fights, 2 flees, Sovereign down), and died plenty before
that.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 23:30:50 -06:00
..
portraits feat(game): REIKHELM: DESCENT — playable first-person roguelike 2026-06-11 23:30:50 -06:00
audio.js feat(game): REIKHELM: DESCENT — playable first-person roguelike 2026-06-11 23:30:50 -06:00
config.json feat(game): REIKHELM: DESCENT — playable first-person roguelike 2026-06-11 23:30:50 -06:00
game.js feat(game): REIKHELM: DESCENT — playable first-person roguelike 2026-06-11 23:30:50 -06:00
index.html feat(game): REIKHELM: DESCENT — playable first-person roguelike 2026-06-11 23:30:50 -06:00
README.md feat(game): REIKHELM: DESCENT — playable first-person roguelike 2026-06-11 23:30:50 -06:00
style.css feat(game): REIKHELM: DESCENT — playable first-person roguelike 2026-06-11 23:30:50 -06:00

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

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 15 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.