Skip to content

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.

Role: Creator
  • TypeScript
  • HTML
  • CSS
  • Electron
  • Playwright
  • Prisma
  • SQLite

Project snapshot

Live browser game title screen with Begin Expedition and Battle Arena options.
Live deployment screenshot.
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

Hero select screen showing the Amazon class with base attributes and a playstyle profile.
Hero select — eight classes, each with attributes, playstyle bars, and unlock gates.
Encounter route choice during a run, with three route cards offering different rewards.
In-run route choice — each path trades a combat modifier against a reward type.
Town camp screen with healer, blacksmith, vendor, and mercenary captain NPCs.
Wake Camp — town services between encounters: healer, vendor, mercenary hire, stash.

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.