Archived
198 lines
10 KiB
Markdown
198 lines
10 KiB
Markdown
# Build Prompt: D&D Campaign Manager Web App
|
|
|
|
Hand this whole folder to Claude Code (or paste this file as the initial prompt
|
|
with the rest of the folder alongside it) to build the app. Read this file
|
|
fully before writing any code, then read `prisma/schema.prisma`,
|
|
`docs/GAME_RULES_REFERENCE.md`, and `docs/ROLL_YOUR_CHARACTER.md` — those three
|
|
are the authoritative data model and game-logic spec, not just background.
|
|
|
|
## What we're building
|
|
|
|
A web app for a Dungeons & Dragons 5e Dungeon Master and their players to run a
|
|
campaign together. Users log in and see the campaigns/stories they're active in.
|
|
Opening a campaign shows a player their character card (stats, spells,
|
|
inventory). A DM can configure campaign basics, add characters, and build full
|
|
character sheets through a guided dice-roll flow. Players can also build and
|
|
manage their own characters, take notes, and pull up a contextual "quick stats"
|
|
panel mid-combat (attack/damage bonus, HP, special abilities). The DM can create
|
|
and assign weapons, create monsters/enemies, run encounters, and track damage
|
|
live.
|
|
|
|
## Tech stack (fixed — do not substitute)
|
|
|
|
- **Backend:** Node.js + Express (or Fastify if you strongly prefer, but default
|
|
to Express), JavaScript (not TypeScript, to match the stack the user works in
|
|
day to day), Prisma ORM, PostgreSQL.
|
|
- **Frontend:** React + Vite + TypeScript.
|
|
- **Real-time:** Socket.io over WebSockets, self-hosted on the Express server —
|
|
no paid real-time SaaS (no Pusher, Ably, Supabase Realtime, etc.).
|
|
- **Auth:** Email + password. Hash passwords with bcrypt. Issue a JWT on login,
|
|
store it client-side, verify it as Express middleware. A DM invites players to
|
|
a campaign via a short invite code or a shareable `/join/:inviteCode` link
|
|
(see `Campaign.inviteCode` in the schema).
|
|
- **Repo layout:** monorepo, two top-level folders:
|
|
|
|
```
|
|
/frontend — React + Vite + TypeScript app
|
|
/backend — Express + Prisma API, Socket.io server
|
|
/prisma — (already provided) schema.prisma — move into /backend/prisma
|
|
/seed-data — (already provided) JSON reference data — move into /backend/prisma or /backend/src/data, wire into a seed script
|
|
/docs — (already provided) keep as top-level /docs for reference during and after the build
|
|
```
|
|
|
|
Move `prisma/` and `seed-data/` into `/backend` as part of scaffolding (or keep
|
|
top-level and point `backend/prisma/schema.prisma`'s location via
|
|
`package.json`'s `prisma.schema` field — either is fine, just be consistent).
|
|
|
|
## First command to run
|
|
|
|
```
|
|
cd backend && npx prisma validate
|
|
```
|
|
|
|
This schema was authored and structurally reviewed (brace/paren balance, no
|
|
duplicate models/enums, relation names cross-checked by hand) but could not be
|
|
run through `prisma validate` in the environment that generated it — its
|
|
sandbox blocked outbound access to Prisma's engine-binary CDN. Run it for real
|
|
as your first step before trusting the schema further.
|
|
|
|
## Database & seeding
|
|
|
|
1. `prisma migrate dev` from the schema as provided (adjust only if `prisma
|
|
validate` surfaces a real error).
|
|
2. Write `backend/prisma/seed.js` (or `.ts`) that reads the four JSON files in
|
|
`seed-data/` and:
|
|
- Does **not** need to load species/classes/skills into the database as rows
|
|
— those are fixed game-rule tables, not user data. Instead, port them into
|
|
a small constants module the calculation engine imports directly (e.g.
|
|
`backend/src/game/data/species.js` etc., generated from the JSON so the
|
|
numbers stay traceable to `docs/GAME_RULES_REFERENCE.md`).
|
|
- **Does** seed `weapons.json` into the `Weapon` table, but only as part of
|
|
a demo/dev campaign seed (weapons are per-campaign in the schema, since a
|
|
DM should be able to add homebrew weapons) — don't seed them globally.
|
|
3. Build the calculation engine described in `docs/GAME_RULES_REFERENCE.md` as
|
|
a single module, fully unit tested (ability modifier, proficiency bonus, HP
|
|
by level — including the level-1-uses-max-die rule, AC, save/skill bonuses,
|
|
spell DC/attack bonus). Every place in the API that returns a character's
|
|
derived stats must call this module — never duplicate the formulas.
|
|
|
|
## Backend API — suggested route groups
|
|
|
|
- `POST /auth/register`, `POST /auth/login`, `GET /auth/me`
|
|
- `GET /campaigns` (mine, as DM or player), `POST /campaigns` (create, DM
|
|
becomes owner), `GET /campaigns/:id`, `PATCH /campaigns/:id` (DM-only,
|
|
updates `startingItemCount`/`startingSpellCount`/`startingGold`/etc.)
|
|
- `POST /campaigns/:id/invite` (DM regenerates/fetches invite code),
|
|
`POST /campaigns/join/:inviteCode` (player joins)
|
|
- `GET /campaigns/:id/characters`, `POST /campaigns/:id/characters` (start a
|
|
build — DM for anyone, player for themselves)
|
|
- `GET /characters/:id`, `PATCH /characters/:id` (stats/notes updates),
|
|
`DELETE /characters/:id` (DM only)
|
|
- `POST /characters/:id/build/roll` (server-rolls the requested die for the
|
|
current build step — never trust a client-submitted roll — see
|
|
`docs/ROLL_YOUR_CHARACTER.md`), `POST /characters/:id/build/choose` (submit a
|
|
choice for the current step), `POST /characters/:id/build/finalize`
|
|
- `GET /characters/:id/quick-stats` (assembles the combat-context panel per
|
|
`docs/GAME_RULES_REFERENCE.md`)
|
|
- `POST /characters/:id/weapons`, `PATCH /characters/:id/weapons/:weaponId`
|
|
(equip/unequip/quantity), weapon CRUD under `/campaigns/:id/weapons` (DM)
|
|
- `POST /characters/:id/inventory`, `PATCH
|
|
/characters/:id/inventory/:itemId`
|
|
- Monster CRUD under `/campaigns/:id/monsters` (DM only)
|
|
- `POST /campaigns/:id/encounters`, `GET /encounters/:id`, `POST
|
|
/encounters/:id/participants` (add character/monster), `PATCH
|
|
/encounters/:id/participants/:participantId` (HP/conditions/initiative
|
|
updates — this is the hot path during combat), `POST
|
|
/encounters/:id/advance-round`
|
|
- `POST /notes`, `GET /campaigns/:id/notes`, `GET /characters/:id/notes`
|
|
|
|
Authorization rule of thumb: DM (campaign owner / `CampaignMember.role ===
|
|
'DM'`) can read/write everything in their campaign; a player can read their own
|
|
character fully, can read other players' character cards at whatever visibility
|
|
the DM allows (default: full read for party members — keep it simple for v1),
|
|
and can only write to their own character and their own notes.
|
|
|
|
## Real-time events (Socket.io)
|
|
|
|
Join a Socket.io room per campaign (`campaign:<id>`) on connect. Emit on:
|
|
|
|
- Character HP/condition changes (`character:updated`)
|
|
- Encounter participant HP/condition/initiative changes
|
|
(`encounter:participant-updated`)
|
|
- Encounter round advances (`encounter:round-advanced`)
|
|
- New character added / build finalized (`character:created`)
|
|
|
|
This is what makes HP changes and combat tracking show up live for everyone
|
|
looking at the same campaign/encounter — keep the payloads small (just the
|
|
changed record) and let the frontend re-fetch details if needed rather than
|
|
pushing full derived-stat blobs over the socket.
|
|
|
|
## Frontend — key screens
|
|
|
|
1. **Login / Register**
|
|
2. **Campaign list** (post-login home) — campaigns the user is a DM or player
|
|
in; a "Join campaign" flow that takes an invite code/link
|
|
3. **Campaign home** — DM sees campaign settings + character list + "start
|
|
encounter"; player sees their character card front and center
|
|
4. **Character card / sheet** — stats, AC, HP (editable inline for
|
|
authorized users), skills, saves, spells, inventory, weapons, notes
|
|
5. **Character builder** — walks `docs/ROLL_YOUR_CHARACTER.md` step by step:
|
|
big "Roll d_N_" buttons, reveal choices, assign ability scores, pick
|
|
skills, pick starting equipment, finalize
|
|
6. **Quick Stats panel** — a compact overlay/drawer callable from anywhere the
|
|
character is on screen, showing attack/damage bonus (per equipped
|
|
weapon), HP, AC, and special abilities — this is what a player pulls up
|
|
mid-attack, so it must be fast to open (no full page nav) and legible at a
|
|
glance
|
|
7. **DM encounter/combat tracker** — add characters and monsters to an
|
|
encounter, initiative order, per-combatant HP/condition tracking, round
|
|
counter, all live via Socket.io
|
|
|
|
Use React Query (or an equivalent fetch/cache layer) for API state and a
|
|
lightweight Socket.io client hook that invalidates/updates the relevant query
|
|
cache on each event above, rather than hand-rolling a parallel state store.
|
|
|
|
## Build phases (do these in order; each should be independently runnable/testable)
|
|
|
|
1. Monorepo scaffold: `/frontend` (Vite React-TS template) + `/backend`
|
|
(Express, `npm init`, Prisma installed, schema in place), root README with
|
|
dev-run instructions, `.env.example` in both.
|
|
2. Prisma migrate + seed script + the calculation engine module + its unit
|
|
tests (this is the foundation everything else depends on — get it fully
|
|
correct and tested before building UI on top of it).
|
|
3. Auth (register/login/JWT middleware) + campaign CRUD + invite/join flow.
|
|
4. Character builder end-to-end (dice-roll flow → finalized character),
|
|
backed by the calculation engine.
|
|
5. Character sheet view (read) + stat/note editing (write) on the frontend.
|
|
6. Quick Stats panel.
|
|
7. Weapons (campaign-level CRUD + assign to character) and inventory.
|
|
8. Monsters + encounters + combat/damage tracking.
|
|
9. Socket.io wiring for all the real-time events above.
|
|
10. Polish pass: loading/error states, mobile-usable layout (a DM running combat
|
|
from a phone or tablet at the table is a realistic use case), basic
|
|
automated tests for the calculation engine and the auth/campaign/character
|
|
API routes.
|
|
|
|
## Non-functional requirements
|
|
|
|
- Validate all request bodies server-side (e.g. `zod`) — don't trust the client
|
|
for anything that affects game state, especially dice rolls.
|
|
- Never let a non-DM, non-owning-player mutate a character or campaign they
|
|
don't have rights to — enforce this in middleware, not just by hiding UI.
|
|
- Keep the calculation engine pure and dependency-free (no DB calls inside it)
|
|
so it stays unit-testable in isolation.
|
|
- Match the naming and structure in `prisma/schema.prisma` and the JSON files
|
|
in `seed-data/` exactly — they're the result of a full review pass and are
|
|
meant to be used as-is, not redesigned.
|
|
|
|
## Reference material in this folder
|
|
|
|
- `prisma/schema.prisma` — full data model, described above
|
|
- `docs/GAME_RULES_REFERENCE.md` — every D&D formula the app needs, verified
|
|
against a hand-checked Excel calculator built earlier for this same campaign
|
|
- `docs/ROLL_YOUR_CHARACTER.md` — the dice-roll character build flow, step by
|
|
step, for the builder UI
|
|
- `seed-data/species.json`, `classes.json`, `skills.json`, `weapons.json` —
|
|
structured versions of the tables in `GAME_RULES_REFERENCE.md`, ready to
|
|
import into code or a seed script
|