A real-time, bilingual (es/en) text-based D&D game played in the browser. A single adventurer travels between Dawn Camp and a handful of cursed locations, each with its own town (store, player market, bounty, idle jobs) and its own infinite, procedurally generated wilds to explore. Combat is resolved with inline d20 skill checks. Spanish is the original UI language; English is the translation.
- Frontend: React 18 + Vite, plain CSS3 (no Tailwind, no CSS framework), procedural inline-SVG art
- Backend: Node + TypeScript, Hono with
@hono/node-server(built-in WebSocket support) - Database: better-sqlite3 (synchronous, single connection, writes serialize)
- Auth: HMAC-signed cookie sessions, guest login
- i18n: react-i18next (client) + a
t()helper (server); flat key maps ini18n/en.json/i18n/es.json - Tests: Vitest
pnpm install
pnpm dev # server (tsx watch) + client (vite) concurrently
pnpm test # vitest run
pnpm build # tsc typecheck + vite build- Server:
http://localhost:8787, the Hono API and/wsWebSocket. - Client:
http://localhost:5173, Vite dev server, proxies/apiand/wsto the server. - The SQLite database (
drakenfall.db, gitignored) is created and seeded on first run. For tests it runs in-memory (DB_PATH=:memory:).
| Variable | Default | Purpose |
|---|---|---|
PORT |
8787 |
HTTP/WS server port |
DB_PATH |
./drakenfall.db |
SQLite database file |
SESSION_SECRET |
random (per boot) | HMAC key for cookie sessions; set it so sessions survive restarts |
server/ Hono app: index.ts, db.ts, auth.ts, game.ts, chat.ts, routes.ts, ws.ts, i18n.ts
client/ React app: src/pages (Welcome, Create, World), src/components (ChatBox, art),
GameContext.tsx, api.ts, ws.ts, i18n.ts, index.css, main.tsx, App.tsx
shared/ types.ts (DTOs + WS protocol), content.ts (locations, monsters, shop, jobs, choices)
i18n/ en.json, es.json (flat key maps, kept symmetric)
schema.sql additive migrations
- Character creation asks only for a name, no class or gender. Every adventurer starts with a rusty dagger, 30 gold, and heads to Dawn Camp.
- Locations: camp (safe hub, tier 0), old forest, ruined tower, mist marsh, mountain pass, black citadel (tiers 1–4). Each has its own town and its own wilds.
- Town (per location):
- Store, weapons and consumables; stock gated by the location's tier.
- Player market, buy NPC-seeded listings, or list your own item for sale; listings you post are bought out by a simulated buyer after a short delay, paying you gold automatically.
- Bounty, one hunting contract per location: defeat 3 creatures there for gold + XP (not available at Dawn Camp).
- Jobs, idle timed tasks (gather firewood, mine ore, ...). One runs at a time, even while you do other things; claim its gold reward when the timer completes.
- Rest, only at Dawn Camp; fully restores HP and energy.
- Wilds: an infinite, procedurally generated hex map per location. Tiles (monster / loot / obstacle / empty) are generated deterministically when revealed, seeded per location, no edge to the map, and danger and rewards scale with distance from the entrance. Obstacles block movement.
- Combat: stepping onto a monster tile starts a fight, resolved inline in
the combat panel. Each attack is a d20 skill check:
roll d20 + (level + weapon bonus)vs difficulty10 + tile difficulty. Natural 20 = critical (double damage); natural 1 = fumble (extra damage taken); otherwise a hit lands ifroll + bonus >= difficultyand the monster retaliates if it survives. Melee and ranged are labeled Might and Precision. Fleeing steps back to the last visited tile. - Defeating a monster grants gold and XP scaled to the tile's difficulty, clears the tile, reveals its neighbors, and advances any active bounty.
- Energy is the action currency: every step and every attack costs 1. HP or energy hitting 0 triggers a collapse, you're dragged back to Dawn Camp, lose 15% of your gold (min 5), and recover to half HP / full energy.
- XP curve
150 * level²; leveling up grants +4 max HP, +2 max energy, and a full heal. - Inventory equips/unequips melee and ranged weapons and uses consumables (HP/energy potions); unequipped gear returns to the pack.
- Global chat is available from any screen, with seeded NPC chatter and auto-replies.
- REST under
/api: auth (guest,logout), and world actions,explore,attack,flee,travel,rest,buy,use,equip,unequip,missions,jobs,market,lang,chat, plusGET /api/worldfor the fullWorldPayload. - WebSocket (
/ws) pusheslog(personal adventure log lines),event(world events),chat(global chat messages),presence, andping. - Actions return an
ActionResponse(ok,message,roll,bonus,difficulty,critical,monster,collapsed,event, …).
- No ORM, plain better-sqlite3 queries, typed DTOs in
shared/types.ts. - Numbers are coerced to
numberin DTOs (SQLite returns strings). - All user-facing strings go through i18n keys (
en.json/es.json, flat and symmetric); game content lives in typed modules inshared/content.tswithes/enlocalized fields. - Server-authoritative: only
server/game.tsmutates state. - Deliberate simplifications are marked with
ponytail:comments. - Dark-fantasy theme (Cinzel/Spectral, gold accents) in plain CSS3.