joshuaandClaude Opus 5.5 c852ecb2e4 V1.10.0: NPCs change money and sell things
- An NPC can change gold and cash at a rate of their own ("2 gold = $15"),
  the same both ways. Only gold and cash are taken: no silver, no copper
  and no change, and the amount has to be a whole multiple of the rate.
- An NPC can sell weapons and items from the campaign pools, each with a
  price in any coin or in cash, a level and an optional stock. The wares
  follow the NPC into every quest. An encounter can sell things itself
  too, for a stall with nobody behind it.
- Trading is open only in an NPC quest in progress, never in a battle,
  with an NPC who is in the scene and standing, for a character who is in
  it. Players see every rate and ware and trade for their own character
  in a new Trade card; the DM can trade for anyone.
- Only the DM sets trade up: "Trade" on an NPC's row on the campaign page,
  and the DM's part of the Trade card for an encounter's own wares. A
  ware's price and stock can be edited afterwards.
- Every exchange and purchase is written to the character's money log.
- All packages at version 1.10.0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 13:45:40 +02:00
2026-09-22 16:27:53 +02:00

The Sunken Reach — D&D 5e Campaign Manager

A web app for a D&D 5e Dungeon Master and their players to run a campaign together: campaign management, a guided dice-roll character builder, live character sheets, a mid-combat "quick stats" panel, weapons/inventory/spells, monsters, and a real-time encounter/combat tracker.

How it works

/docs describes the app as it currently behaves, one subject per file:

Document Covers
docs/CHARACTERS.md Building a character, every number on the sheet, levels, money, permissions
docs/WEAPONS.md The weapon pool, weapon levels and how damage grows
docs/SPELLS.md The spell list, who can use which spells, the spell library
docs/ITEMS.md The item pool, character inventory, items for sale
docs/ENCOUNTERS.md Battles and NPC quests, turns, rewards, trade, what players see
docs/MONSTERS.md The monster list and built-in bestiary
docs/NPCS.md Non-player characters, what they sell and the rates they change money at

CLAUDE_BUILD_PROMPT.md, docs/GAME_RULES_REFERENCE.md and docs/ROLL_YOUR_CHARACTER.md are the original specifications the first version was built from. The app has since moved on in places (gear levels, the builder's story steps, picking a motivation); the documents above are the current behaviour.

Stack

  • Backend: Node.js + Express, JavaScript, Prisma ORM, PostgreSQL, Socket.io, JWT auth (bcrypt + jsonwebtoken), zod validation.
  • Frontend: React + Vite + TypeScript, React Router, TanStack Query, socket.io-client.

Prerequisites

  • Node.js 18+ (tested on Node 22)
  • A running PostgreSQL instance (local install, Docker, or a hosted service like Neon/Supabase)

First-time setup

npm install   # installs both workspaces (backend + frontend)

# Backend: point at your Postgres instance and run migrations
cd backend
cp .env.example .env      # edit DATABASE_URL and JWT_SECRET
npx prisma migrate dev --name init
npx prisma db seed        # adds the admin account + the Aldenfell campaign if missing (see output for login/invite code)

# Frontend
cd ../frontend
cp .env.example .env      # defaults to http://localhost:4000, adjust if needed

Running in dev

In two terminals, from the repo root:

npm run dev:backend    # Express + Socket.io on http://localhost:4000
npm run dev:frontend   # Vite dev server, prints its own port (5173 by default)

Open the frontend URL, register an account (or log in with the seeded admin account — see the seed script's console output for credentials and invite code), and start playing.

The seed only adds: it never touches an existing account or campaign, so it is safe to run on a server with real data. It skips the campaign if the admin already has an active "Aldenfell"; archive or delete that one in the app (Campaign Settings) and run it again for a fresh copy. SEED_ADMIN_EMAIL, SEED_ADMIN_PASSWORD and SEED_ADMIN_NAME set the account it creates. npx prisma db seed -- --reset wipes every user and campaign first.

A second campaign, Hollowcrown: The Rift Between Worlds, has its own seed and is not part of the default one. It follows the same rules (it only adds, and skips if the admin already has an active Hollowcrown):

cd backend
npm run seed:hollowcrown

On a server, after pulling a new version, run npm run deploy in backend and then restart the backend. It applies new migrations and regenerates the Prisma client; without the second half the server keeps running with the old data model and pages that use anything new fail to load (for example "Could not load this encounter"). Change your password from the account page (click your name in the top bar).

Tests

cd backend
npm test

The calculation engine (src/game/calculations.js) is fully unit tested and runs with no external dependencies. The route-level integration tests in src/routes/api.integration.test.js need a real, migrated database reachable via DATABASE_URL — they skip themselves gracefully (with a console warning) if the DB isn't reachable, so npm test still passes in an environment with no database configured.

Project layout

/backend    — Express + Prisma API, Socket.io server
  /prisma   — schema.prisma, seed.js
  /seed-data — reference JSON (weapons seeded per-campaign; species/classes/skills
               are ported into src/game/data/*.js and used directly by the
               calculation engine, not stored as DB rows)
  /src
    /game       — calculations.js (the ONLY place D&D formulas live) + buildFlow.js
    /middleware — auth + campaign/character authorization
    /routes     — auth, campaigns, characters, encounters, notes
    /sockets    — Socket.io room wiring
/frontend   — React + Vite + TypeScript app
/docs       — game rules & character-builder specs (reference material)
S
Description
No description provided
Readme
7.1 MiB
Languages
JavaScript 66.3%
TypeScript 30.8%
CSS 2.9%