Project · 2026
Blood Rogue
A Diablo II-inspired roguelite deckbuilder playable in the browser, with phase-driven party combat, five acts, and eight playable classes.
- TypeScript
- HTML
- CSS
- Electron
- Playwright
- Prisma
- SQLite
Project snapshot
- Status
- Playable browser roguelite deckbuilder.
- Scope
- Five-act run structure, eight playable classes, three skill trees per class, bosses, elites, items, runes, and rewards.
- Runtime
- Plain TypeScript and DOM rendering on a single HTML page, with an Electron desktop package.
- Balance
- Headless simulation harness validates combat math, power curves, and economy tuning.
Technical proof
- Deterministic turn-based combat with visible enemy intents and scripted boss phase behavior.
- Profile persistence tracks active runs, stash, history, unlocks, and settings through SQLite and Prisma.
- No frontend framework or canvas: views are rendered by diffing plain-object state into string-generated DOM.
In the app
What it is
Blood Rogue is a browser-based roguelite deckbuilder built from scratch in TypeScript, drawing on Diablo II for its setting, classes, and item vocabulary. Runs span five acts — each a sequence of world_map -> encounter -> reward nodes — with quest events, shrines, aftermath events, and boss encounters routed through the same loop.
Combat and classes
Combat is deterministic and turn-based. The player fields a hero and one mercenary companion against procedurally chosen enemy groups. Enemies display visible intents each round; mercenaries act automatically; bosses follow scripted phase behaviors; elite enemies carry randomized affixes. Energy is spent to play cards from a shuffled deck, and card effects cover damage, guard, status conditions, summons, minion phases, chain triggers, and skill-window bonuses.
Seven classes are implemented — Amazon, Assassin, Barbarian, Druid, Necromancer, Paladin, and Sorceress — each with three skill trees. Skill points are earned on level-up and spent in trees; reaching five ranks in a skill unlocks the upgraded Card+ variant. Rewards after each encounter offer new cards, card upgrades, boons, items, runes, or runewords. Town screens between acts cover healing, belt refills, mercenary hire or revival, and a vendor with buy/sell flow.
Architecture tour
Runtime
The entire runtime is plain TypeScript compiled with tsc and loaded as IIFE modules on a single HTML page, no framework, no canvas. Game state lives in plain objects grouped by domain (src/combat, src/character, src/items, src/quests, src/rewards, src/town, src/exploration, src/meta, src/run), and views are rendered by diffing that state into string-generated DOM. The project also packages as a desktop app through Electron, sharing the same source.
Progression persists across sessions through a profile layer backed by SQLite via Prisma. The profile tracks active-run state, shared stash, run history, account-level unlocks, and preferred settings, so a run can be closed mid-expedition and picked back up later.
Balance validation
src/sim holds 59 TypeScript modules that make up a headless simulation and policy engine, separate from the browser runtime. It plays the game against itself: a decision-beam policy engine (sim-policy-decision-beam.ts, sim-policy-decision-candidates.ts) chooses actions for scripted AI builds across all eight classes, scored against threat, power-curve, and reward-EV models (sim-policy-scoring-balance-power.ts, sim-policy-threat.ts). Around it sit dozens of npm run sim:* entry points: power-curve reports, boss-strength reports, town-economy and charm-loadout labs, a progression RNG sweep, and even an ML dataset export with a baseline trainer for build-quality prediction. This is where card, charm, and enemy tuning actually gets checked before it ships, not by hand-playing runs.
Tests and hygiene gates
There are 979 test files in the repo (*.test.ts, excluding generated output and worktrees), run through npm test after a dedicated build:test compile step. npm test itself is gated behind close to twenty check-* scripts that run before the build: load-order checks, a CSS token/line-count baseline, a “runner hygiene” baseline, an effective-lines check, and a balance-docs hygiene check among them, so a change that violates a structural convention fails before a single test runs. test:e2e and test:snapshots cover UI-level regressions on top of that.