Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 103 additions & 0 deletions docs/prp-docs/cod-game-toolkit-prp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# PRP — CoD Game Toolkit + Gauntlet-Loop Demo Generator

**Status:** Phase 2a (toolkit foundation) implemented on `spike/cod-walking-skeleton`;
Phase 2b (the generator skill) designed here, not yet built.
**Category:** enhancements · **Feature:** `features/enhancements/051-cod-game-toolkit/`
**Branch:** `spike/cod-walking-skeleton`

## Summary

Harvest Matt Shumer's MIT [Claude-of-Duty](https://github.com/mshumer/Claude-of-Duty)
procedural primitives into a **reusable, asset-free R3F game toolkit** for the
ScriptHammer family, then build a **gauntlet-loop generator** that scaffolds a
playable game demo from a short spec. The toolkit is the "variable function"; each
game is a parameterization of it.

Nine slices (proven on Three r184) already harvested the primitives: physics
(swept-capsule + BVH), a procedural PBR material forge, an atmospheric sky + IBL,
procedural audio (surface-keyed footsteps), a GPU particle system, camera-feel
springs, a crouch/sprint/prone locomotion layer, and the two core gems (event bus +
quality tiers). All are 100% procedural — zero art/audio assets — which is exactly
what makes a browser 3D prototype hard, gift-wrapped.

## Why "harvest, not embed"

CoD runs its own imperative render loop + service-locator kernel; **R3F owns the
`<Canvas>` renderer and loop**. Two loops can't share one canvas, so we do not lift
the kernel — we vendor the framework-agnostic primitives under `src/lib/cod/` and
adapt each to R3F (materials bake off-screen; physics/particles tick in `useFrame`;
audio/springs are hooks; the OVERWATCH `ctx` becomes plain modules — an event bus,
a quality store, injected renderers). The kernel (engine/registry/prewarm/main) is
never vendored.

## Phase 2a — the packaged toolkit (this pass)

Public API: **`@/lib/cod`** (barrel). See `src/lib/cod/README.md`.

- **Core gems** (`src/lib/cod/core/`): `EventBus`/`bus` (game events without React
re-renders; `on` returns an unsubscribe closure) and `QUALITY_PRESETS` +
`useQuality()` (low/medium/high/ultra tiers; the renderer-generic fields only —
CoD's post-chain flags dropped). Both typed TS, ported from `registry.js:86-122`
and `config.js:21`.
- **Typed public surface**: a barrel (`index.ts`) + hand-written `.d.ts` for the
primary classes (`CharacterController`, `StaticWorld`); the material/particle/sky
classes are reached via the already-typed hooks.
- **Gems wired into the demo** (proof they're live, not dead): `useQuality` drives
the Canvas `dpr` (`renderScale`), texture `anisotropy`, and the dust particle pool
(`particleBudget`), with a HUD `<select>` + `?q=` param; `bus` carries the
`player:stance` event from inside the `<Canvas>` to the outer HUD badge (no
prop-drill) — the exact pattern generated game code will use.
- **Docs**: this PRP + `features/enhancements/051-cod-game-toolkit/`.

## Phase 2b — the gauntlet-loop `game-demo` generator (designed, not built)

A **net-new Claude Code skill** at `~/.claude/skills/game-demo/SKILL.md` (+ a
`references/` dir of subagent prompts, mirroring the `graphify` skill's shape). The
"gauntlet loop" (Matt Shumer's pattern: **Task → Build method: fan out subagents,
each with a blind critic → Bar: don't stop until every critic is wowed**) applied to
game scaffolding.

**Input:** a short game-spec — genre, theme, core mechanic, look — e.g. *"a
top-down survival-automation game on a dead-earth farm, darkly satirical."*

**Orchestration:**
1. **Plan** — one agent turns the spec into a build-list of independent pieces
(level/world, player mechanics, entities/AI, HUD/UI, audio-visual feel), each
expressed against the `@/lib/cod` public API.
2. **Fan out** — one builder subagent per piece (dispatched in a single message for
parallelism), each paired with a **blind critic** that scores its output against
the spec + a quality bar; loop the pair until the critic is satisfied.
3. **Scaffold** — builders drive **`plop component`** (the repo's 5-file generator,
`game` category → `Features/Game/*`) for components, and hand-author one
`'use client'` `ssr:false` `page.tsx` route (the `/game/3d` + `/game/cod-skeleton`
pattern). No hand-rolled partial components — plop guarantees the 5-file CI gate.
4. **Verify + iterate** — run `tsc` + `vitest` + `validate:structure` + a Playwright
smoke of the new route; feed failures back into the loop.

**Repo constraints the generator MUST honor** (from `CLAUDE.md` + recon):
- **Static export** — no `src/app/api/` / server routes; R3F components are
`ssr:false` dynamic imports; browser env only `NEXT_PUBLIC_*`.
- **Docker-first** — all commands as `docker compose exec scripthammer pnpm …`.
- **5-file component CI gate** (`validate-structure.js`) — always via plop.
- **Canvas a11y carve-out** — the `FallbackPanel` + WebGL-probe pattern, a Pa11y
exclusion, and a documented manual-a11y rationale (axe can't audit canvas).
- Only optional **save/share** of a generated demo would touch Supabase; the toolkit
+ prototype layer is 100% client-side.

**Deliverable of 2b:** the skill + at least one generated, verified playable demo
route, produced end-to-end from a spec.

## Not in scope

Vendoring CoD's kernel; the FPS app layer (weapons/ai/world/ui — a reference for a
milsim fork, not the toolkit); full `.d.ts` for the material/particle/sky classes;
the SpecKit `spec/plan/tasks` for this feature (run `/specify` to generate them the
idiomatic way).

## References

- Toolkit README: `src/lib/cod/README.md`
- Extraction map: `features/enhancements/051-cod-game-toolkit/research.md`
- Gauntlet-loop technique (source): the RoboNuggets transcript in the TranScripts
repo, `Claude/Claude_Edited/gauntlet_loop_claude_prompting_subagents_robonuggets.md`
- CoD: https://github.com/mshumer/Claude-of-Duty (MIT)
11 changes: 11 additions & 0 deletions features/IMPLEMENTATION_ORDER.md
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,17 @@ Feature **050** puts a catalog on top of the payment rails that features 038–0

---

## Game toolkit (enhancements)

| Order | Feature | Name | Depends On |
| ----- | ------- | ---------------- | ------------------- |
| — | **051** | CoD Game Toolkit | 047 (Three.js game) |

Phase 2a (toolkit foundation: `@/lib/cod` public API + core gems, wired into the
`/game/cod-skeleton` demo) is implemented on `spike/cod-walking-skeleton`. Phase 2b
(the gauntlet-loop game-demo generator skill) is designed in
`docs/prp-docs/cod-game-toolkit-prp.md`, not yet built.

## Related Documents

| Document | Location |
Expand Down
58 changes: 58 additions & 0 deletions features/enhancements/051-cod-game-toolkit/feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Feature 051 — CoD Game Toolkit

- **ID:** 051
- **Category:** enhancements
- **Status:** Phase 2a implemented (`spike/cod-walking-skeleton`); Phase 2b (generator) designed
- **Depends on:** 047 (Three.js Game / `/game/3d` island), 037 (game a11y tests)
- **PRP:** `docs/prp-docs/cod-game-toolkit-prp.md`

## Description

A reusable, **asset-free** procedural game toolkit for the ScriptHammer R3F stack,
harvested from the MIT Claude-of-Duty engine and exposed as a clean public API at
`@/lib/cod`: swept-capsule physics, a procedural PBR material forge, an atmospheric
sky + IBL, procedural audio, a GPU particle system, camera-feel springs, a
crouch/sprint/prone locomotion layer, and two core gems (an event bus + quality
tiers). A full first-person reference demo lives at `/game/cod-skeleton`.

The toolkit is the substrate for a future gauntlet-loop **game-demo generator**
(Phase 2b, spec'd in the PRP): a spec → a scaffolded, playable demo.

## User scenarios

### US-1 — a developer builds a 3D prototype on the toolkit
Import the primitives from `@/lib/cod`, build a collision world + drive a capsule
controller, skin surfaces with the procedural forge, and get sky/IBL/audio/particles
for free — no art or audio assets.
**Acceptance:** `quickstart.md`'s samples compile and run; the public API type-checks.

### US-2 — quality tiers scale the render to the device
A `?q=low|medium|high|ultra` param (or the HUD selector) changes render resolution,
texture anisotropy, and the particle budget.
**Acceptance:** `?q=low` renders at `renderScale` 0.72 (canvas backing ratio 0.72);
`?q=ultra` at 1.0. Verified via Playwright.

### US-3 — game events cross the Canvas boundary without prop-drilling
Systems inside the `<Canvas>` emit on `bus`; UI outside subscribes.
**Acceptance:** toggling a stance updates the HUD badge via `bus.on('player:stance')`.
Verified via Playwright (`stand → C → crouch`).

### US-4 — canvas accessibility carve-out (inherited from 047)
The WebGL route keeps the `FallbackPanel` + WebGL-probe pattern; axe covers only the
DOM chrome; a Pa11y exclusion + manual-review rationale apply.
**Acceptance:** the `.accessibility.test.tsx` for the demo components pass; no axe
violations on the DOM chrome.

## Verification

- `docker compose exec -T scripthammer pnpm exec tsc --noEmit` → 0 errors (barrel +
`.d.ts` type-check; the app conforms).
- `... pnpm exec vitest run src/lib/cod src/components/game/{CodSkeleton,ProceduralSky}`
→ all green, incl. real behavior tests for the EventBus + quality store.
- `... node scripts/validate-structure.js` → all components pass the 5-file gate.
- Playwright on `/game/cod-skeleton` → quality tiers + event bus proven live.

## Out of scope

CoD kernel; the FPS app layer; full `.d.ts` for material/particle/sky classes; the
generator skill itself (Phase 2b); the SpecKit `spec/plan/tasks` (run `/specify`).
102 changes: 102 additions & 0 deletions features/enhancements/051-cod-game-toolkit/quickstart.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# Quickstart — building on `@/lib/cod`

The toolkit is a set of R3F-friendly primitives. Everything imports from the barrel
`@/lib/cod`. R3F components must be loaded `ssr:false` (static export). Full working
reference: `src/components/game/CodSkeleton/CodSkeleton.tsx`.

## 1. Mount a WebGL route (static-export-safe)

```tsx
// src/app/game/my-demo/page.tsx
'use client';
import dynamic from 'next/dynamic';
import Loader from '@/components/game/Loader';
const MyGame = dynamic(() => import('@/components/game/MyGame'), {
ssr: false,
loading: () => <Loader />,
});
export default function Page() {
return <main className="container mx-auto"><MyGame /></main>;
}
```

## 2. Physics — a world + a capsule controller

```tsx
import { StaticWorld, CharacterController, MASK } from '@/lib/cod';
import * as THREE from 'three';

// Build once (pure CPU — no renderer needed):
const world = new StaticWorld();
const floor = new THREE.Mesh(new THREE.BoxGeometry(40, 2, 40));
floor.position.set(0, -1, 0);
world.addMesh(floor, 'dirt'); // surface tags: concrete/metal/wood/dirt/…
world.build();

const cc = new CharacterController(world, {
radius: 0.32, height: 1.75, stepHeight: 0.42,
mask: MASK.CHARACTER, position: { x: 0, y: 0.2, z: 10 },
});

// Each fixed step (in useFrame): caller owns velocity; move() clips it + returns distance.
cc.velocity.y += GRAVITY * dt;
cc.velocity.x = wishX; cc.velocity.z = wishZ;
const dist = cc.move(cc.velocity.x * dt, cc.velocity.y * dt, cc.velocity.z * dt);
camera.position.set(cc.position.x, cc.position.y + eye, cc.position.z);
// cc.grounded, cc.groundSurfaceName, cc.landingSpeed, cc.setHeight(h) (crouch) …
```

## 3. Procedural materials (needs the renderer at bake time)

```tsx
import { MaterialSystem } from '@/lib/cod';
const forge = new MaterialSystem({ renderer: gl }); // gl from useThree()
forge.init({});
const set = forge.getTextureSet('concrete'); // { albedo, normal, orm }
const mat = new THREE.MeshStandardMaterial({
map: set.albedo, normalMap: set.normal,
roughnessMap: set.orm, metalnessMap: set.orm, roughness: 1, metalness: 0,
});
```

## 4. The hooks (call inside `<Canvas>`; drive imperatively)

```tsx
import { useFootsteps, useFootstepDust, useCameraFeel } from '@/lib/cod';

const { resume, step } = useFootsteps(); // wire resume() onto the pointer-lock click
const { emit, tick } = useFootstepDust(512); // capacity (e.g. from a quality tier)
const { apply } = useCameraFeel();

// in the movement useFrame, after moving:
const didStep = step(dist, cc.grounded, cc.groundSurfaceName, gait);
if (didStep) emit(cc.position.x, cc.position.y, cc.position.z, cc.groundSurfaceName);
tick(dt);
apply(camera, cc, dist, dt, yaw); // head-bob + landing punch
```

## 5. Core gems — quality tiers + event bus

```tsx
import { useQuality, bus } from '@/lib/cod';

// Quality: drive dpr / anisotropy / particle budget from the active tier.
const { tier, preset, setTier } = useQuality(); // preset.renderScale, .anisotropy, .particleBudget
// <Canvas dpr={Math.min(2, devicePixelRatio * preset.renderScale)} />

// Events without React re-renders (works across the <Canvas> boundary):
bus.emit('player:footstep', { surface, position });
useEffect(() => bus.on('player:footstep', (e) => { /* … */ }), []); // returns unsubscribe
```

## 6. Sky + IBL

Use the reference `<ProceduralSky hour={16.5} />` component
(`src/components/game/ProceduralSky/`) inside your `<Canvas>` — it bakes an
atmospheric sky dome + a PMREM env map into `scene.environment` (no HDRI), so your
`MeshStandardMaterial`s get real image-based lighting.

---

See `src/lib/cod/README.md` for the full API table and the "harvest, not embed"
paradigm; each `src/lib/cod/**/NOTICE.md` carries the MIT attribution.
54 changes: 54 additions & 0 deletions features/enhancements/051-cod-game-toolkit/research.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Research — CoD → ScriptHammer toolkit extraction map

Feasibility spike + full harvest (2026-08). Source:
[Claude-of-Duty](https://github.com/mshumer/Claude-of-Duty) (MIT), ~65k LOC, 13
subsystems, Three r180, 100% procedural. Assessed against ScriptHammer (R3F + drei +
Three r184, Next static-export PWA) by multi-agent readers.

## The decision: harvest, not embed

CoD is a genuinely modular, MIT, `three`-only, **zero-asset** mini-engine with an
OVERWATCH service-locator (`ctx.get('id')`) + event bus and no cross-subsystem
imports. But it runs its own imperative render loop + kernel, and **ScriptHammer is
R3F (React owns the loop)** — two loops can't share one canvas. So we harvest the
framework-agnostic procedural primitives (the asset-free, hard-to-build things) and
let R3F own the render/post layer.

## Extract / Adapt / Skip

| Subsystem | Verdict | Harvested as | r184 |
|---|---|---|---|
| **physics** | EXTRACT ⭐ | `StaticWorld` (BVH) + `CharacterController` (swept capsule) — copies near-verbatim | ✅ 15/15 smoke |
| **materials** | EXTRACT ⭐ | `MaterialSystem` procedural PBR forge (triplanar, no UVs); bake off-screen | ✅ renders |
| **sky** | ADAPT | `<ProceduralSky>` — atmospheric sky dome + PMREM IBL env map (no HDRI); volumetrics dropped | ✅ renders |
| **audio** | EXTRACT ⭐ | `useFootsteps()` — Web-Audio procedural foley (surface-keyed) | ✅ (three-free) |
| **fx** | ADAPT | `ParticleLayer` GPU particle system (`useFootstepDust()`); atlas not needed | ✅ renders |
| **player** | ADAPT/split | `springs.js` (`useCameraFeel()` head-bob + landing); FPS controller skipped | n/a |
| **core** | ADAPT/split | `EventBus` + `QUALITY_PRESETS` (`useQuality()`); rng vendored; **kernel skipped** | n/a |
| **render** | SKIP | R3F + drei + `@react-three/postprocessing` replace it | — |
| **weapons/ai/world/ui** | SKIP | FPS app layer — a milsim reference, not the toolkit | — |

Net harvest ≈ the "hard parts" of a browser 3D prototype, gift-wrapped.

## Integration paradigm (the one rule)

R3F owns `<Canvas>` + the rAF loop. Adapt each primitive to it: materials **bake
off-screen** (save/restore the render target); physics + particles run a **fixed-step
tick in `useFrame`** writing transforms onto the camera/meshes; springs live in
`useFrame`; audio + the event bus + the quality store are plain modules/hooks. **Do
NOT** lift `render`/`core.engine` — that's the "two loops fighting one canvas" trap.

## r180 → r184

No blocking deltas found. Physics/audio are version-agnostic; materials/sky/particles
use standalone GLSL3 `ShaderMaterial`s (no `onBeforeCompile`, no chunk patching → the
r184-safe pattern); colorspace uses modern `colorSpace` (no legacy `encoding`). Each
slice was verified live under real WebGL (Playwright).

## Backlog reconciliation

Lands on the closed #48 `/game/3d` ssr:false island. The character controller unblocks
the eye-height-WASD half of #226 (milsim FPS) independent of its geodata blocker; CoD
weapons/ai are a reference, not a lift. Answers #226's "template-capability vs separate
app?" → **the toolkit is a template capability; specific games are forks that consume
it.** Supersedes the legacy gpbp FPS-placeholder prompts.
Loading
Loading