- JavaScript 92.9%
- CSS 7%
| .agents | ||
| .amp | ||
| public | ||
| scripts | ||
| src | ||
| tests | ||
| .gitignore | ||
| AGENTS.md | ||
| index.html | ||
| package-lock.json | ||
| package.json | ||
| QA.md | ||
| README.md | ||
| vite.config.js | ||
Ruinspire — The Violet Spire
A single-player fantasy deckbuilding roguelike built with React and Vite. New journeys span three seeded acts of 10–12 stops each, with branching routes, authored events, shops, campfires, tactical encounters, and carried supplies. Three fixed-seed trials offer curated loadouts; ordinary campaigns offer an optional three-boss summit after the final act. Start as the Wayfarer and unlock Lumen by winning a complete campaign. A guided opening teaches the starter deck and first turn; completed ordinary runs unlock card options and harder Ascents. The Quiet Violet art direction uses original generated character illustrations, sparse vector scenery, and symbolic ivory cards; no artwork is extracted from another game.
Run locally
Requires Node.js 20.19+ or 22.12+.
npm ci
npm run dev
npm test # deterministic rules and progression tests
npm run build # production output in dist/
npm run preview # serve the production build
npm run qa:content # validate generated routes and content references
npm run test:e2e # browser checks against an already served production build
npm run qa:balance # seeded bot simulations; see QA.md for interpretation
Deploy dist/ to any static host. No backend or API keys are needed. Fonts load from Google Fonts, with local serif/sans-serif fallbacks.
Run in an Amp orb
.agents/setup installs locked dependencies and builds the game when a new orb is prepared. .amp/services.yaml declares the supervised preview on port 5173 with a portal and HTTP health check.
Run amp orb services ensure to start the preview and print its portal URL. After editing source, run npm run build and reload the preview. Generated portal state and build output are not committed.
Play
- Select an enemy when fighting a group, then click a card or use number keys 1–8. Press E or click End Turn to resolve enemy intentions in order. Tap an intention to inspect the move.
- Normal turns refill 3 Energy and draw 5 cards, up to the eight-card hand cap. Unplayed Retain cards stay in hand. The discard pile reshuffles when the draw pile empties. On narrow screens, scroll the hand horizontally.
- Combat fits the viewport in portrait and landscape. Energy and End Turn stay on screen; journey screens and dialogs scroll separately. Tap the compact oath chip for its rules.
- Choose a traveler and challenge before a new run. New players get a skippable Strike → Guard → End Turn lesson; the guided-battle checkbox replays it for Wayfarer Ascent 0. Replacing an unfinished run requires confirmation. Existing saved runs are preserved.
- The Deck button groups copies and filters cards by name, keywords, type, and Energy cost. Rewards are previewed before confirmation; they add one card or can be skipped. Next-stop choices explain their risks and benefits. The full route is optional.
- The Oath of Patience limits you to 3 cards per turn and grants 6 Block at the start of the next turn. Break it once per combat for 2 Energy and no card limit, at the cost of 4 maximum HP for the entire run. A new combat renews the oath, not the lost health.
- Lumen’s Oath of Radiance allows 4 cards, grants 3 Block next turn, and can be broken for 1 Energy at a cost of 3 maximum HP. Lumen has a distinct starter deck and reward pool.
- Block absorbs incoming damage and resets each turn. Burn ignores enemy Block, triggers before the enemy acts, and decreases by 1. Exhausted cards return next combat.
- Weak reduces outgoing attack damage by 25%; Vulnerable increases received attack damage by 50%. Each multiplier rounds down. Attack bonuses apply to every hit. Enemy previews account for earlier enemies applying statuses during the same phase.
- At campfires, choose healing or one upgrade lasting this run. Merchants can remove one selected card for 35 gold. Shop stock and event rewards can only be claimed once.
- Runs and your profile save separately in the current browser's local storage. Finish an ordinary run to unlock Kindled Ward; win once or finish three ordinary runs to unlock Lantern Echo. Unlocks enter future reward and shop pools, not the starter deck. Win an ordinary Ascent to unlock the next (up to Ascent 3); select it explicitly at the ending or in the restart dialog. Higher Ascents strengthen enemies and escalate long fights. New runs reset health, gold, deck, and relics; records and unlocks remain. There are no permanent stat bonuses.
- Choose Authored trial at startup or restart for Briarcoal Vigil, Stillglass Crossing, or Loose Thread Ascent. Win each to unlock the next. Each attempt uses the same preset seed, deck, extra relics, Ascent and card pool, with a distinct record identity. Trial wins do not unlock ordinary cards, travelers or Ascents. Replay retains the preset; ordinary replay stays separate.
- Intermediate bosses lead to another act without resetting HP or the deck. At the final boss of a new ordinary campaign, Finish ascent safely records a win; reviewing or cancelling the optional summit records nothing. Continuing carries your build into three solo bosses and risks losing the entire run. No intervening card, relic or supply rewards or extra healing are added; ordinary battle gold and actual relic healing still apply. Uncashed merchant pledges stay uncashed. Older saves and trials retain their original endings.
- Settings shows recent run history, separate trial/summit records and unlock milestones, and can export the current run and profile. Unreadable or incompatible saves open recovery choices instead of being overwritten. Session-only play leaves existing storage untouched.
- Settings holds relic details, combat log, help, and restart confirmation. Sound effects are optional and off initially. Combat uses discrete action animations and written outcomes, including Block absorption, mixed card effects, and Burn before enemy actions. Reduced motion preserves the written outcomes without movement.
Code
src/game.js owns the public API and run lifecycle; src/combat.js and src/journey.js own their reducers. src/state.js supplies seeded randomness and card-pile operations. Definitions live in src/content/; profile rules and browser loading live in src/profile.js and src/storage.js. src/main.jsx connects the application, src/ui/ holds shared cards/dialogs and the tutorial, and src/presentation.jsx renders scenery and combat effects. src/style.css contains responsive layouts and reduced-motion support. Art assets live in public/art/. AGENTS.md records parallel ownership and expansion contracts.
Campaign routes, subsequent draws, card rewards, merchant stock, and relic rewards use seeded randomness. The guided first fight preserves its teachable Bell Guardian and opening hand, and its reward offers two introductory choices. Existing version-1 saves migrate without regenerating their decks or routes; runs without a generated route retain the authored seven-floor chapter. The public createRun(seed, {campaign: true}) creates the expanded campaign; the default remains an authored legacy start for compatibility and fixtures. See QA.md for browser coverage, reproducible simulations, and verification limitations.