@playcraft/cli 0.0.53 → 0.0.56
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -0
- package/REMIX.md +103 -19
- package/dist/atom-tree/fusion-hints.js +8 -3
- package/dist/atom-tree/fusion-intent.js +62 -0
- package/dist/atom-tree/selection.js +3 -1
- package/dist/commands/create.js +439 -159
- package/dist/commands/image.js +79 -69
- package/dist/commands/levels.js +29 -0
- package/dist/commands/patterns.js +75 -0
- package/dist/commands/remix.js +47 -0
- package/dist/commands/skills.js +48 -17
- package/dist/index.js +13 -6
- package/dist/remix/clone-source.js +208 -0
- package/dist/remix/clone.js +255 -0
- package/dist/scaffold/templates.js +35 -0
- package/dist/utils/agent-api-client.js +10 -3
- package/dist/utils/remix-image-attachments.js +4 -32
- package/dist/utils/sandbox-path.js +20 -0
- package/package.json +3 -3
- package/project-template/.claude/agents/creative-director.md +114 -0
- package/project-template/.claude/agents/developer.md +5 -2
- package/project-template/.claude/agents/game-designer.md +41 -20
- package/project-template/.claude/agents/refs/README.md +8 -0
- package/project-template/.claude/agents/refs/creative-director-guide.md +35 -0
- package/project-template/.claude/agents/refs/creative-director-kickoff-guide.md +52 -0
- package/project-template/.claude/agents/refs/creative-discovery-guide.md +76 -0
- package/project-template/.claude/agents/refs/creative-north-star-examples.md +82 -0
- package/project-template/.claude/agents/refs/developer-dev-handoff.md +1 -1
- package/project-template/.claude/agents/refs/game-designer-fusion-depth-playbook.md +28 -0
- package/project-template/.claude/agents/refs/game-designer-gameplay-sufficiency.md +4 -4
- package/project-template/.claude/agents/reviewer.md +11 -0
- package/project-template/.claude/hooks/README.md +12 -12
- package/project-template/.claude/hooks/block-developer-preflight.mjs +49 -13
- package/project-template/.claude/hooks/block-ring1-tasklist.mjs +1 -1
- package/project-template/.claude/hooks/user-prompt.mjs +3 -13
- package/project-template/.claude/hooks/validate-workflow-stop.mjs +209 -12
- package/project-template/.claude/settings.json +2 -2
- package/project-template/.claude/skills/playcraft-playable-optimization/SKILL.md +20 -20
- package/project-template/.claude/skills/session-analyzer/scripts/resolve-session.mjs +0 -1
- package/project-template/.claude/snapshot.mjs +0 -1
- package/project-template/.codebuddy/settings.json +1 -1
- package/project-template/.cursor/rules/playcraft-orchestrator.mdc +1 -1
- package/project-template/CLAUDE.md +9 -9
- package/project-template/docs/project-state.md +1 -1
- package/project-template/docs/team/agent-conduct.md +18 -18
- package/project-template/docs/team/collaboration.md +19 -7
- package/project-template/docs/team/workflow-changelog.md +15 -0
- package/project-template/fusion-patterns/catalog.json +77 -0
- package/project-template/package.json +2 -0
- package/project-template/skills/_shared/scripts/create-playable.mjs +31 -11
- package/project-template/skills/_shared/scripts/dev-start.mjs +3 -3
- package/project-template/skills/_shared/scripts/disclosure-config.mjs +65 -39
- package/project-template/skills/_shared/scripts/harness-smoke.mjs +3 -1
- package/project-template/skills/_shared/scripts/lib/fusion-intent.mjs +88 -0
- package/project-template/skills/_shared/scripts/lib/skill-fidelity.mjs +185 -0
- package/project-template/skills/_shared/scripts/slice-set.mjs +29 -2
- package/project-template/skills/_shared/scripts/state-get.mjs +124 -7
- package/project-template/skills/_shared/scripts/state-store.mjs +25 -2
- package/project-template/skills/_shared/scripts/validate-level-sketch.mjs +444 -0
- package/project-template/skills/_shared/scripts/verify-design-consistency.mjs +728 -0
- package/project-template/skills/playable-record/scripts/lib/dev-server.mjs +46 -11
- package/project-template/skills/playable-record/scripts/record-playable.mjs +2 -1
- package/project-template/skills/playable-record/scripts/verify-contract.mjs +4 -4
- package/project-template/skills/playable-record/scripts/verify-firstwin.mjs +3 -3
- package/project-template/skills/playable-record/scripts/verify-fusion.mjs +37 -2
- package/project-template/skills/playable-record/scripts/verify-slice.mjs +2 -2
- package/project-template/templates/design-brief.template.md +34 -0
- package/project-template/templates/design-methodology.md +1 -2
- package/project-template/templates/design.template.md +65 -0
- package/project-template/templates/developer-log.template.md +12 -1
- package/project-template/templates/five-axis-framework.md +12 -0
- package/project-template/templates/game-designer-creative-process.template.md +99 -0
- package/project-template/templates/level-sketch.template.json +56 -0
- package/project-template/templates/north-star.template.md +43 -0
- package/project-template/templates/project-state.template.md +1 -1
- package/project-template/vite.config.ts +4 -5
- package/project-templates/phaser/.claude/agents/creative-director.md +114 -0
- package/project-templates/phaser/.claude/agents/game-designer.md +41 -20
- package/project-templates/phaser/.claude/agents/refs/creative-director-guide.md +35 -0
- package/project-templates/phaser/.claude/agents/refs/creative-director-kickoff-guide.md +52 -0
- package/project-templates/phaser/.claude/agents/refs/creative-discovery-guide.md +76 -0
- package/project-templates/phaser/.claude/agents/refs/creative-north-star-examples.md +82 -0
- package/project-templates/phaser/.claude/agents/refs/developer-dev-handoff.md +1 -1
- package/project-templates/phaser/.claude/agents/refs/game-designer-fusion-depth-playbook.md +28 -0
- package/project-templates/phaser/.claude/agents/reviewer.md +11 -0
- package/project-templates/phaser/.claude/hooks/block-ring1-tasklist.mjs +2 -2
- package/project-templates/phaser/.claude/hooks/user-prompt.mjs +17 -18
- package/project-templates/phaser/.claude/hooks/validate-workflow-stop.mjs +209 -12
- package/project-templates/phaser/.claude/skills/playcraft-playable-optimization/SKILL.md +20 -20
- package/project-templates/phaser/.cursor/rules/playcraft-orchestrator.mdc +1 -1
- package/project-templates/phaser/CLAUDE.md +88 -54
- package/project-templates/phaser/docs/DESIGN.md +25 -0
- package/project-templates/phaser/docs/project-state.md +1 -1
- package/project-templates/phaser/docs/team/collaboration.md +19 -7
- package/project-templates/phaser/docs/team/workflow-changelog.md +14 -0
- package/project-templates/phaser/fusion-patterns/catalog.json +77 -0
- package/project-templates/phaser/package.json +3 -1
- package/project-templates/phaser/skills/_shared/scripts/dev-start.mjs +3 -3
- package/project-templates/phaser/skills/_shared/scripts/disclosure-config.mjs +65 -39
- package/project-templates/phaser/skills/_shared/scripts/harness-smoke.mjs +3 -1
- package/project-templates/phaser/skills/_shared/scripts/lib/fusion-intent.mjs +88 -0
- package/project-templates/phaser/skills/_shared/scripts/lib/skill-fidelity.mjs +185 -0
- package/project-templates/phaser/skills/_shared/scripts/state-get.mjs +124 -7
- package/project-templates/phaser/skills/_shared/scripts/state-store.mjs +25 -2
- package/project-templates/phaser/skills/_shared/scripts/validate-level-sketch.mjs +444 -0
- package/project-templates/phaser/skills/_shared/scripts/verify-design-consistency.mjs +728 -0
- package/project-templates/phaser/skills/playable-record/scripts/lib/dev-server.mjs +46 -11
- package/project-templates/phaser/skills/playable-record/scripts/record-playable.mjs +2 -1
- package/project-templates/phaser/skills/playable-record/scripts/verify-contract.mjs +4 -4
- package/project-templates/phaser/skills/playable-record/scripts/verify-firstwin.mjs +3 -3
- package/project-templates/phaser/skills/playable-record/scripts/verify-fusion.mjs +37 -2
- package/project-templates/phaser/skills/playable-record/scripts/verify-slice.mjs +2 -2
- package/project-templates/phaser/templates/design-brief.template.md +42 -11
- package/project-templates/phaser/templates/design-methodology.md +1 -2
- package/project-templates/phaser/templates/design.template.md +77 -0
- package/project-templates/phaser/templates/five-axis-framework.md +50 -1
- package/project-templates/phaser/templates/game-designer-creative-process.template.md +99 -0
- package/project-templates/phaser/templates/level-sketch.template.json +56 -0
- package/project-templates/phaser/templates/north-star.template.md +43 -0
- package/project-templates/phaser/templates/project-state.template.md +1 -1
- package/project-templates/phaser/vite.config.ts +4 -5
- package/project-templates/playable-template-phaser/.playcraftignore +10 -0
- package/project-templates/playable-template-phaser/CLAUDE.md +178 -0
- package/project-templates/playable-template-phaser/PLAYCRAFT-MIGRATION.md +328 -0
- package/project-templates/playable-template-phaser/README.md +381 -0
- package/project-templates/playable-template-phaser/assets/brain.png +0 -0
- package/project-templates/playable-template-phaser/assets/button.png +0 -0
- package/project-templates/playable-template-phaser/assets/interact.png +0 -0
- package/project-templates/playable-template-phaser/assets/logo.webp +0 -0
- package/project-templates/playable-template-phaser/assets/warn_brain.png +0 -0
- package/project-templates/playable-template-phaser/builds.config.js +12 -0
- package/project-templates/playable-template-phaser/globals.d.ts +2 -0
- package/project-templates/playable-template-phaser/index.html +12 -0
- package/project-templates/playable-template-phaser/package.json +45 -0
- package/project-templates/playable-template-phaser/scripts/base64-to-mp3.js +340 -0
- package/project-templates/playable-template-phaser/scripts/convert-to-webp.js +370 -0
- package/project-templates/playable-template-phaser/src/Game.ts +154 -0
- package/project-templates/playable-template-phaser/src/config/LoadingConfig.ts +40 -0
- package/project-templates/playable-template-phaser/src/config/ThemeConfig.ts +81 -0
- package/project-templates/playable-template-phaser/src/index.css +18 -0
- package/project-templates/playable-template-phaser/src/index.html +9 -0
- package/project-templates/playable-template-phaser/src/index.ts +30 -0
- package/project-templates/playable-template-phaser/src/json5.d.ts +4 -0
- package/project-templates/playable-template-phaser/src/managers/AudioManager.ts +559 -0
- package/project-templates/playable-template-phaser/src/scenes/MainScene.ts +301 -0
- package/project-templates/playable-template-phaser/src/scenes/PreloadScene.ts +256 -0
- package/project-templates/playable-template-phaser/src/theme/README.md +194 -0
- package/project-templates/playable-template-phaser/src/theme/blue-theme/assets/.gitkeep +24 -0
- package/project-templates/playable-template-phaser/src/theme/blue-theme/index.ts +27 -0
- package/project-templates/playable-template-phaser/src/theme/blue-theme/theme.data.json5 +19 -0
- package/project-templates/playable-template-phaser/src/theme/green-theme/assets/.gitkeep +3 -0
- package/project-templates/playable-template-phaser/src/theme/green-theme/index.ts +20 -0
- package/project-templates/playable-template-phaser/src/theme/green-theme/theme.data.json5 +19 -0
- package/project-templates/playable-template-phaser/src/theme/index.ts +1 -0
- package/project-templates/playable-template-phaser/src/theme/red-theme/assets/.gitkeep +3 -0
- package/project-templates/playable-template-phaser/src/theme/red-theme/index.ts +20 -0
- package/project-templates/playable-template-phaser/src/theme/red-theme/theme.data.json5 +19 -0
- package/project-templates/playable-template-phaser/src/theme/theme.metadata.md +60 -0
- package/project-templates/playable-template-phaser/src/theme/theme.schema.json5 +149 -0
- package/project-templates/playable-template-phaser/src/ui/BrainAgeUI.ts +681 -0
- package/project-templates/playable-template-phaser/src/ui/CountdownUI.ts +565 -0
- package/project-templates/playable-template-phaser/src/ui/PlayButton.ts +51 -0
- package/project-templates/playable-template-phaser/src/ui/ScoreUI.ts +207 -0
- package/project-templates/playable-template-phaser/src/ui/SuccessUI.ts +43 -0
- package/project-templates/playable-template-phaser/src/ui/index.ts +15 -0
- package/project-templates/playable-template-phaser/tsconfig.json +26 -0
- package/project-templates/playable-template-phaser/vite.config.ts +64 -0
- package/project-templates/playable-template-phaser//346/226/260/346/211/213/345/274/200/345/217/221/346/214/207/345/215/227.md +209 -0
- package/project-templates/playable-template-three/README.md +795 -0
- package/project-templates/playable-template-three/assets/Stork.glb +0 -0
- package/project-templates/playable-template-three/assets/button.png +0 -0
- package/project-templates/playable-template-three/assets/logo.webp +0 -0
- package/project-templates/playable-template-three/build.json +7 -0
- package/project-templates/playable-template-three/builds.config.example.js +151 -0
- package/project-templates/playable-template-three/builds.config.js +158 -0
- package/project-templates/playable-template-three/package.json +36 -0
- package/project-templates/playable-template-three/scripts/convert-to-webp.js +370 -0
- package/project-templates/playable-template-three/scripts/theme-builds.js +514 -0
- package/project-templates/playable-template-three/src/Game.ts +215 -0
- package/project-templates/playable-template-three/src/config/LoadingConfig.ts +27 -0
- package/project-templates/playable-template-three/src/config/ThemeConfig.ts +36 -0
- package/project-templates/playable-template-three/src/global.d.ts +108 -0
- package/project-templates/playable-template-three/src/index.css +18 -0
- package/project-templates/playable-template-three/src/index.html +9 -0
- package/project-templates/playable-template-three/src/index.ts +20 -0
- package/project-templates/playable-template-three/src/managers/AudioManager.ts +538 -0
- package/project-templates/playable-template-three/src/scenes/PreloadScene.ts +305 -0
- package/project-templates/playable-template-three/src/theme/README.md +225 -0
- package/project-templates/playable-template-three/src/theme/blue-theme/assets/.gitkeep +28 -0
- package/project-templates/playable-template-three/src/theme/blue-theme/index.ts +45 -0
- package/project-templates/playable-template-three/src/theme/current.ts +12 -0
- package/project-templates/playable-template-three/src/theme/green-theme/assets/.gitkeep +3 -0
- package/project-templates/playable-template-three/src/theme/green-theme/index.ts +34 -0
- package/project-templates/playable-template-three/src/theme/red-theme/assets/.gitkeep +3 -0
- package/project-templates/playable-template-three/src/theme/red-theme/index.ts +34 -0
- package/project-templates/playable-template-three/tsconfig.json +24 -0
- package/project-templates/threejs/.claude/agents/creative-director.md +114 -0
- package/project-templates/threejs/.claude/agents/game-designer.md +41 -20
- package/project-templates/threejs/.claude/agents/refs/creative-director-guide.md +35 -0
- package/project-templates/threejs/.claude/agents/refs/creative-director-kickoff-guide.md +52 -0
- package/project-templates/threejs/.claude/agents/refs/creative-discovery-guide.md +76 -0
- package/project-templates/threejs/.claude/agents/refs/creative-north-star-examples.md +82 -0
- package/project-templates/threejs/.claude/agents/refs/developer-dev-handoff.md +1 -1
- package/project-templates/threejs/.claude/agents/refs/game-designer-fusion-depth-playbook.md +28 -0
- package/project-templates/threejs/.claude/agents/reviewer.md +11 -0
- package/project-templates/threejs/.claude/hooks/block-ring1-tasklist.mjs +2 -2
- package/project-templates/threejs/.claude/hooks/user-prompt.mjs +17 -18
- package/project-templates/threejs/.claude/hooks/validate-workflow-stop.mjs +209 -12
- package/project-templates/threejs/.claude/skills/playcraft-playable-optimization/SKILL.md +20 -20
- package/project-templates/threejs/.cursor/rules/playcraft-orchestrator.mdc +1 -1
- package/project-templates/threejs/CLAUDE.md +88 -54
- package/project-templates/threejs/docs/DESIGN.md +25 -0
- package/project-templates/threejs/docs/project-state.md +1 -1
- package/project-templates/threejs/docs/team/collaboration.md +19 -7
- package/project-templates/threejs/docs/team/workflow-changelog.md +14 -0
- package/project-templates/threejs/fusion-patterns/catalog.json +77 -0
- package/project-templates/threejs/package.json +3 -1
- package/project-templates/threejs/skills/_shared/scripts/dev-start.mjs +3 -3
- package/project-templates/threejs/skills/_shared/scripts/disclosure-config.mjs +65 -39
- package/project-templates/threejs/skills/_shared/scripts/harness-smoke.mjs +3 -1
- package/project-templates/threejs/skills/_shared/scripts/lib/fusion-intent.mjs +88 -0
- package/project-templates/threejs/skills/_shared/scripts/lib/skill-fidelity.mjs +185 -0
- package/project-templates/threejs/skills/_shared/scripts/state-get.mjs +124 -7
- package/project-templates/threejs/skills/_shared/scripts/state-store.mjs +25 -2
- package/project-templates/threejs/skills/_shared/scripts/validate-level-sketch.mjs +444 -0
- package/project-templates/threejs/skills/_shared/scripts/verify-design-consistency.mjs +728 -0
- package/project-templates/threejs/skills/playable-record/scripts/lib/dev-server.mjs +46 -11
- package/project-templates/threejs/skills/playable-record/scripts/record-playable.mjs +2 -1
- package/project-templates/threejs/skills/playable-record/scripts/verify-contract.mjs +4 -4
- package/project-templates/threejs/skills/playable-record/scripts/verify-firstwin.mjs +3 -3
- package/project-templates/threejs/skills/playable-record/scripts/verify-fusion.mjs +37 -2
- package/project-templates/threejs/skills/playable-record/scripts/verify-slice.mjs +2 -2
- package/project-templates/threejs/templates/design-brief.template.md +42 -11
- package/project-templates/threejs/templates/design-methodology.md +1 -2
- package/project-templates/threejs/templates/design.template.md +77 -0
- package/project-templates/threejs/templates/five-axis-framework.md +50 -1
- package/project-templates/threejs/templates/game-designer-creative-process.template.md +99 -0
- package/project-templates/threejs/templates/level-sketch.template.json +56 -0
- package/project-templates/threejs/templates/north-star.template.md +43 -0
- package/project-templates/threejs/templates/project-state.template.md +1 -1
- package/project-templates/threejs/vite.config.ts +4 -5
- package/project-template/docs/DESIGN.md +0 -65
|
@@ -5,36 +5,61 @@
|
|
|
5
5
|
* real listening URL. Previously these were copy-pasted into verify-contract,
|
|
6
6
|
* verify-firstwin and record-playable, which let the port-detection logic drift.
|
|
7
7
|
*
|
|
8
|
-
* Vite runs with strictPort:false, so when the preferred port (
|
|
8
|
+
* Vite runs with strictPort:false, so when the preferred port (5173) is taken it
|
|
9
9
|
* auto-increments. `detectDevUrl` parses Vite's `Local:` stdout line to recover
|
|
10
10
|
* the actual port instead of guessing — this is what keeps devUrl correct under
|
|
11
11
|
* port drift (see docs/harness/experiment-lessons.md §9.2).
|
|
12
|
+
* Do not use 6000: Chromium treats it as an unsafe port (ERR_UNSAFE_PORT).
|
|
12
13
|
*/
|
|
13
14
|
|
|
14
15
|
import { spawn } from 'node:child_process';
|
|
16
|
+
import http from 'node:http';
|
|
17
|
+
|
|
18
|
+
/** Preferred Vite dev port — avoids PlayCraft frontend (:3000) and backend (:3001). */
|
|
19
|
+
export const DEFAULT_DEV_PORT = 5173;
|
|
20
|
+
export const DEFAULT_DEV_URL = `http://127.0.0.1:${DEFAULT_DEV_PORT}/`;
|
|
15
21
|
|
|
16
22
|
const LOCAL_URL_RE = /Local:\s+(http:\/\/(?:localhost|127\.0\.0\.1):\d+\/)/;
|
|
23
|
+
const ANSI_RE = /\x1b\[[0-9;]*m/g;
|
|
24
|
+
|
|
25
|
+
/** Probe a URL with node:http (undici `fetch` can fail on 127.0.0.1 in some runtimes). */
|
|
26
|
+
function probeHttp(url) {
|
|
27
|
+
return new Promise((resolve, reject) => {
|
|
28
|
+
const req = http.get(url, (res) => {
|
|
29
|
+
res.resume();
|
|
30
|
+
resolve(res.statusCode ?? 0);
|
|
31
|
+
});
|
|
32
|
+
req.setTimeout(2000, () => {
|
|
33
|
+
req.destroy();
|
|
34
|
+
reject(new Error('probe timeout'));
|
|
35
|
+
});
|
|
36
|
+
req.on('error', reject);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
17
39
|
|
|
18
|
-
/** Poll a URL
|
|
40
|
+
/** Poll a URL until any non-5xx response arrives. */
|
|
19
41
|
export async function waitForHttp(url, timeoutMs = 30000) {
|
|
20
42
|
const start = Date.now();
|
|
43
|
+
let lastErr = '';
|
|
21
44
|
while (Date.now() - start < timeoutMs) {
|
|
22
45
|
try {
|
|
23
|
-
const
|
|
24
|
-
if (
|
|
25
|
-
|
|
26
|
-
|
|
46
|
+
const status = await probeHttp(url);
|
|
47
|
+
if (status < 500) return;
|
|
48
|
+
lastErr = `HTTP ${status}`;
|
|
49
|
+
} catch (err) {
|
|
50
|
+
lastErr = err instanceof Error ? err.message : String(err);
|
|
27
51
|
}
|
|
28
52
|
await new Promise((r) => setTimeout(r, 250));
|
|
29
53
|
}
|
|
30
|
-
throw new Error(`Server not ready: ${url}`);
|
|
54
|
+
throw new Error(`Server not ready: ${url}${lastErr ? ` (${lastErr})` : ''}`);
|
|
31
55
|
}
|
|
32
56
|
|
|
33
57
|
/** Spawn `npm run dev` (Vite) bound to 127.0.0.1 with piped stdio for URL detection. */
|
|
34
58
|
export function startDevServer(cwd) {
|
|
35
|
-
return spawn('npm', ['run', 'dev', '--', '--host', '127.0.0.1'], {
|
|
59
|
+
return spawn('npm', ['run', 'dev', '--', '--host', '127.0.0.1', '--port', String(DEFAULT_DEV_PORT), '--no-open'], {
|
|
36
60
|
cwd,
|
|
37
61
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
62
|
+
env: { ...process.env, BROWSER: 'none' },
|
|
38
63
|
});
|
|
39
64
|
}
|
|
40
65
|
|
|
@@ -53,7 +78,7 @@ export function startHttpServer(port, cwd) {
|
|
|
53
78
|
* @returns {string | null}
|
|
54
79
|
*/
|
|
55
80
|
export function parseLocalUrl(text) {
|
|
56
|
-
const m = String(text).match(LOCAL_URL_RE);
|
|
81
|
+
const m = String(text).replace(ANSI_RE, '').match(LOCAL_URL_RE);
|
|
57
82
|
return m ? m[1] : null;
|
|
58
83
|
}
|
|
59
84
|
|
|
@@ -66,25 +91,35 @@ export function parseLocalUrl(text) {
|
|
|
66
91
|
* @returns {Promise<string>}
|
|
67
92
|
*/
|
|
68
93
|
export function detectDevUrl(child, preferredUrl, timeoutMs = 30000) {
|
|
69
|
-
return new Promise((res) => {
|
|
94
|
+
return new Promise((res, rej) => {
|
|
95
|
+
let buf = '';
|
|
70
96
|
const timer = setTimeout(() => {
|
|
71
97
|
cleanup();
|
|
72
98
|
res(preferredUrl);
|
|
73
99
|
}, timeoutMs);
|
|
74
100
|
const onData = (data) => {
|
|
75
|
-
|
|
101
|
+
buf += String(data);
|
|
102
|
+
const url = parseLocalUrl(buf);
|
|
76
103
|
if (url) {
|
|
77
104
|
clearTimeout(timer);
|
|
78
105
|
cleanup();
|
|
79
106
|
res(url);
|
|
80
107
|
}
|
|
81
108
|
};
|
|
109
|
+
const onExit = (code) => {
|
|
110
|
+
clearTimeout(timer);
|
|
111
|
+
cleanup();
|
|
112
|
+
const tail = buf.trim().slice(-1500);
|
|
113
|
+
rej(new Error(`dev server exited ${code} before publishing a Local URL${tail ? `\n${tail}` : ''}`));
|
|
114
|
+
};
|
|
82
115
|
const cleanup = () => {
|
|
83
116
|
child.stdout?.off('data', onData);
|
|
84
117
|
child.stderr?.off('data', onData);
|
|
118
|
+
child.off('exit', onExit);
|
|
85
119
|
};
|
|
86
120
|
child.stdout?.on('data', onData);
|
|
87
121
|
child.stderr?.on('data', onData);
|
|
122
|
+
child.once('exit', onExit);
|
|
88
123
|
});
|
|
89
124
|
}
|
|
90
125
|
|
|
@@ -11,6 +11,7 @@ import { chromium } from 'playwright';
|
|
|
11
11
|
import { evaluateContract, snapshotContractInPage } from './verify-contract.mjs';
|
|
12
12
|
import {
|
|
13
13
|
detectDevUrl,
|
|
14
|
+
DEFAULT_DEV_URL,
|
|
14
15
|
pickServerPort,
|
|
15
16
|
startDevServer,
|
|
16
17
|
startHttpServer,
|
|
@@ -314,7 +315,7 @@ async function main() {
|
|
|
314
315
|
externalServer = true;
|
|
315
316
|
} else if (opts.useDev) {
|
|
316
317
|
server = startDevServer(projectRoot);
|
|
317
|
-
const preferredUrl = opts.url ||
|
|
318
|
+
const preferredUrl = opts.url || DEFAULT_DEV_URL;
|
|
318
319
|
console.log(`[record] Starting dev server, waiting for ${preferredUrl}`);
|
|
319
320
|
const detectedUrl = await detectDevUrl(server, preferredUrl, 30000);
|
|
320
321
|
opts.url = detectedUrl;
|
|
@@ -19,14 +19,14 @@
|
|
|
19
19
|
* Exit 0 = contract satisfied, 2 = contract broken, 1 = unexpected error.
|
|
20
20
|
*
|
|
21
21
|
* Usage:
|
|
22
|
-
* node skills/playable-record/scripts/verify-contract.mjs --root=. --use-dev --url=http://localhost:
|
|
22
|
+
* node skills/playable-record/scripts/verify-contract.mjs --root=. --use-dev --url=http://localhost:5173/
|
|
23
23
|
* node skills/playable-record/scripts/verify-contract.mjs --root=. # standalone http-server
|
|
24
|
-
* node skills/playable-record/scripts/verify-contract.mjs --url=http://localhost:
|
|
24
|
+
* node skills/playable-record/scripts/verify-contract.mjs --url=http://localhost:5173/ --json
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
27
|
import { resolve } from 'node:path';
|
|
28
28
|
import { fileURLToPath } from 'node:url';
|
|
29
|
-
import { detectDevUrl, startDevServer, startHttpServer, waitForHttp } from './lib/dev-server.mjs';
|
|
29
|
+
import { detectDevUrl, startDevServer, startHttpServer, waitForHttp, DEFAULT_DEV_URL } from './lib/dev-server.mjs';
|
|
30
30
|
import { writeValidatorArtifact } from '../../_shared/scripts/lib/validator-artifacts.mjs';
|
|
31
31
|
|
|
32
32
|
const DEFAULT_SCENE = 'MainScene';
|
|
@@ -178,7 +178,7 @@ export async function runContractProbe(opts) {
|
|
|
178
178
|
external = true;
|
|
179
179
|
} else if (opts.useDev) {
|
|
180
180
|
server = startDevServer(projectRoot);
|
|
181
|
-
url = await detectDevUrl(server, opts.url ||
|
|
181
|
+
url = await detectDevUrl(server, opts.url || DEFAULT_DEV_URL, 30000);
|
|
182
182
|
} else {
|
|
183
183
|
server = startHttpServer(opts.port, projectRoot);
|
|
184
184
|
url = `http://127.0.0.1:${opts.port}/game/index.html`;
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* polls getState() until isGameOver && currentPhase === 'ENDCARD', then exits 0.
|
|
7
7
|
*
|
|
8
8
|
* Usage:
|
|
9
|
-
* node skills/playable-record/scripts/verify-firstwin.mjs --root=. --use-dev --url=http://localhost:
|
|
9
|
+
* node skills/playable-record/scripts/verify-firstwin.mjs --root=. --use-dev --url=http://localhost:5173/
|
|
10
10
|
* npm run verify:firstwin
|
|
11
11
|
*
|
|
12
12
|
* Exit 0 = first level winnable (ENDCARD reached)
|
|
@@ -18,7 +18,7 @@ import { readdir, readFile } from 'node:fs/promises';
|
|
|
18
18
|
import { resolve } from 'node:path';
|
|
19
19
|
import { fileURLToPath } from 'node:url';
|
|
20
20
|
import { evaluateContract, snapshotContractInPage } from './verify-contract.mjs';
|
|
21
|
-
import { detectDevUrl, startDevServer, startHttpServer, waitForHttp } from './lib/dev-server.mjs';
|
|
21
|
+
import { detectDevUrl, startDevServer, startHttpServer, waitForHttp, DEFAULT_DEV_URL } from './lib/dev-server.mjs';
|
|
22
22
|
import { writeValidatorArtifact } from '../../_shared/scripts/lib/validator-artifacts.mjs';
|
|
23
23
|
|
|
24
24
|
const DEFAULT_SCENE = 'MainScene';
|
|
@@ -198,7 +198,7 @@ export async function runFirstWinProbe(opts) {
|
|
|
198
198
|
/* external server */
|
|
199
199
|
} else if (opts.useDev) {
|
|
200
200
|
server = startDevServer(projectRoot);
|
|
201
|
-
url = await detectDevUrl(server, opts.url ||
|
|
201
|
+
url = await detectDevUrl(server, opts.url || DEFAULT_DEV_URL, 30000);
|
|
202
202
|
} else {
|
|
203
203
|
server = startHttpServer(opts.port, projectRoot);
|
|
204
204
|
url = `http://127.0.0.1:${opts.port}/game/index.html`;
|
|
@@ -19,8 +19,14 @@ import { readFile, readdir } from 'node:fs/promises';
|
|
|
19
19
|
import { existsSync } from 'node:fs';
|
|
20
20
|
import { join, resolve, basename } from 'node:path';
|
|
21
21
|
import { writeValidatorArtifact } from '../../_shared/scripts/lib/validator-artifacts.mjs';
|
|
22
|
+
import {
|
|
23
|
+
detectFusionIntent,
|
|
24
|
+
readFusionIntentFromProject,
|
|
25
|
+
} from '../../_shared/scripts/lib/fusion-intent.mjs';
|
|
22
26
|
|
|
23
27
|
// `[*_\s]*` tolerates markdown emphasis in the §6 template form `- **Fusion type**:`.
|
|
28
|
+
const FUSION_COMPOSITE_ATOMS = new Set(['arrow_pick_match_rules.aigameplay']);
|
|
29
|
+
|
|
24
30
|
const FUSION_TYPE_RE =
|
|
25
31
|
/Fusion\s+type[*_\s]*:\s*(Single|A-Visual|B1-Passive|B2-Active|C-Sequential|D-Parallel)/i;
|
|
26
32
|
const REAL_FUSION_TYPES = new Set(['A-Visual', 'B1-Passive', 'B2-Active', 'C-Sequential', 'D-Parallel']);
|
|
@@ -56,6 +62,19 @@ export const GAMEPLAY_SIGNATURES = {
|
|
|
56
62
|
'escape_core',
|
|
57
63
|
'canMoveArrow',
|
|
58
64
|
],
|
|
65
|
+
'path_elimination_rules.aigameplay': [
|
|
66
|
+
'path_elimination',
|
|
67
|
+
'canPathExitBoard',
|
|
68
|
+
'removedPaths',
|
|
69
|
+
'findFirstRemovablePath',
|
|
70
|
+
],
|
|
71
|
+
'arrow_pick_match_rules.aigameplay': [
|
|
72
|
+
'arrow_pick_match',
|
|
73
|
+
'slideOutToTray',
|
|
74
|
+
'trayCapacity',
|
|
75
|
+
'LevelValidator',
|
|
76
|
+
'pick_and_match',
|
|
77
|
+
],
|
|
59
78
|
};
|
|
60
79
|
|
|
61
80
|
function parseArgs(argv) {
|
|
@@ -212,7 +231,23 @@ export async function verifyFusion(root, overrides = {}) {
|
|
|
212
231
|
gameplayAtoms = readSelectedGameplayAtoms(tree);
|
|
213
232
|
}
|
|
214
233
|
|
|
215
|
-
|
|
234
|
+
const singleCompositeFusion =
|
|
235
|
+
gameplayAtoms.length === 1 && FUSION_COMPOSITE_ATOMS.has(gameplayAtoms[0] ?? '');
|
|
236
|
+
|
|
237
|
+
if (gameplayAtoms.length < 2 && !singleCompositeFusion) {
|
|
238
|
+
const { intentTags, rawIntent } = readFusionIntentFromProject(root);
|
|
239
|
+
if (detectFusionIntent(intentTags, rawIntent)) {
|
|
240
|
+
const selected = gameplayAtoms[0] ?? '(none)';
|
|
241
|
+
return {
|
|
242
|
+
ok: false,
|
|
243
|
+
skipped: false,
|
|
244
|
+
problems: [
|
|
245
|
+
`fusion intent detected but only one gameplay atom selected (${selected}) — pick a fusion skill (e.g. arrow_pick_match_rules.aigameplay) or document Single + downgrade reason in DESIGN.md ## 6`,
|
|
246
|
+
],
|
|
247
|
+
gameplayAtoms,
|
|
248
|
+
fusionIntent: true,
|
|
249
|
+
};
|
|
250
|
+
}
|
|
216
251
|
return { ok: true, skipped: true, reason: 'single gameplay atom — fusion no-op PASS' };
|
|
217
252
|
}
|
|
218
253
|
|
|
@@ -224,7 +259,7 @@ export async function verifyFusion(root, overrides = {}) {
|
|
|
224
259
|
|
|
225
260
|
if (!type) {
|
|
226
261
|
problems.push('DESIGN.md missing Fusion type: — classify fusion in ## 6. Gameplay Fusion');
|
|
227
|
-
} else if (type === 'Single' && !isPseudoFusion) {
|
|
262
|
+
} else if (type === 'Single' && !isPseudoFusion && !singleCompositeFusion) {
|
|
228
263
|
problems.push(
|
|
229
264
|
`DESIGN.md Fusion type is Single but atom-tree selected ${gameplayAtoms.length} gameplay atoms — use a real fusion type or document pseudo-fusion`,
|
|
230
265
|
);
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
import fs from 'node:fs';
|
|
22
22
|
import path from 'node:path';
|
|
23
23
|
import { fileURLToPath } from 'node:url';
|
|
24
|
-
import { detectDevUrl, startDevServer, startHttpServer, waitForHttp } from './lib/dev-server.mjs';
|
|
24
|
+
import { detectDevUrl, startDevServer, startHttpServer, waitForHttp, DEFAULT_DEV_URL } from './lib/dev-server.mjs';
|
|
25
25
|
import { getActiveSlice, readState } from '../../_shared/scripts/state-store.mjs';
|
|
26
26
|
|
|
27
27
|
function parseArgs(argv) {
|
|
@@ -57,7 +57,7 @@ export async function runSliceProbe(opts) {
|
|
|
57
57
|
/* external server */
|
|
58
58
|
} else if (opts.useDev) {
|
|
59
59
|
server = startDevServer(root);
|
|
60
|
-
url = await detectDevUrl(server, opts.url ||
|
|
60
|
+
url = await detectDevUrl(server, opts.url || DEFAULT_DEV_URL, 30000);
|
|
61
61
|
} else {
|
|
62
62
|
server = startHttpServer(opts.port, root);
|
|
63
63
|
url = `http://127.0.0.1:${opts.port}/game/index.html`;
|
|
@@ -3,16 +3,13 @@
|
|
|
3
3
|
<!--
|
|
4
4
|
Living discovery log — NOT a substitute for docs/DESIGN.md (the playable seed contract).
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
Rings 2–4:
|
|
11
|
-
- Deepen periphery, layout intent, Hook/CTA, style direction as those rings disclose.
|
|
12
|
-
- Ring 2 (slice-axis): append periphery discovery → 2–4 developer slices; not a feature backlog.
|
|
6
|
+
Maintained across all rings — each ring's clarify phase appends new discovery.
|
|
7
|
+
Record how core action / feedback / progress / constraints were discovered.
|
|
8
|
+
Feed the slice queue; do NOT write layout, style, CTA polish, or production plans here.
|
|
13
9
|
|
|
14
10
|
Update rules:
|
|
15
11
|
- Create at the start of clarify; append after each brainstorm round.
|
|
12
|
+
- Maintain across rings — do not discard prior-ring discovery.
|
|
16
13
|
- Preserve source tags; do not polish away evidence.
|
|
17
14
|
|
|
18
15
|
Source tags: [USER] [WEB] [SKILL] [AGENT] [ASSUMPTION][confidence: high/medium/low]
|
|
@@ -56,6 +53,40 @@ Append-only evidence (newest first or chronological — pick one and stay consis
|
|
|
56
53
|
|
|
57
54
|
## Brainstorm Log
|
|
58
55
|
|
|
56
|
+
### Creative Angle
|
|
57
|
+
|
|
58
|
+
- **Idea hook**: {If only one core moment survives — from Phase 1 candidates}
|
|
59
|
+
- **Emotional payoff**: {What the player should feel after the first win}
|
|
60
|
+
- **Anti-generic**: {Why this is not "ordinary arrow + tray match-3"}
|
|
61
|
+
|
|
62
|
+
### Pattern picked
|
|
63
|
+
|
|
64
|
+
- Pattern: {id from `playcraft patterns match`} [{SOURCE}]
|
|
65
|
+
- Why this over alternatives: {one line}
|
|
66
|
+
|
|
67
|
+
### Reference insight
|
|
68
|
+
|
|
69
|
+
- {3–5 bullets from patterns match or `playcraft tools research --focus gameplay`} [{WEB|SKILL}]
|
|
70
|
+
- researchSkippedReason: {only if no research/patterns — explain why}
|
|
71
|
+
|
|
72
|
+
### Motion moments (Ring 1)
|
|
73
|
+
|
|
74
|
+
- First tap: {slide_out visual — what moves, how long}
|
|
75
|
+
- First blocked: {blocked shake/tint — teach dependency}
|
|
76
|
+
- First match-3: {match_clear in tray — payoff moment}
|
|
77
|
+
- Rejected motion: {why not swap/gravity cascade / board match3 — e.g. "主手势是 path tap,三消仅在托盘"}
|
|
78
|
+
|
|
79
|
+
### Mechanic variation
|
|
80
|
+
|
|
81
|
+
- {One axis beyond stitching two skills: e.g. tray pressure curve / blocked dependency layers / board phase unlock}
|
|
82
|
+
- rejectedShallowFusion: {why not all-free opening or ≤4 paths on 8×8}
|
|
83
|
+
|
|
84
|
+
### Gameplay Lens (3 questions)
|
|
85
|
+
|
|
86
|
+
1. Core action in one verb: {…}
|
|
87
|
+
2. After 3–5 steps the player gets: {…}
|
|
88
|
+
3. With sound off for 10s, gameplay guessable? {yes/no + why}
|
|
89
|
+
|
|
59
90
|
### Round 1
|
|
60
91
|
|
|
61
92
|
- Asked: {question and why it mattered for core action / feedback}
|
|
@@ -64,9 +95,9 @@ Append-only evidence (newest first or chronological — pick one and stay consis
|
|
|
64
95
|
|
|
65
96
|
---
|
|
66
97
|
|
|
67
|
-
## Core Discovery
|
|
98
|
+
## Core Discovery
|
|
68
99
|
|
|
69
|
-
>
|
|
100
|
+
> Record core gameplay discovery for the current ring. Later rings deepen periphery, layout intent, Hook/CTA, and style direction as those rings disclose.
|
|
70
101
|
|
|
71
102
|
### Player action chain
|
|
72
103
|
|
|
@@ -78,11 +109,11 @@ Append-only evidence (newest first or chronological — pick one and stay consis
|
|
|
78
109
|
|
|
79
110
|
- Engine hint basis: {phaser / threejs + why} [{SOURCE}]
|
|
80
111
|
- Semantic placeholders: {player; target; feedback; reward; …} [{SOURCE}]
|
|
81
|
-
- Out of scope
|
|
112
|
+
- Out of scope: layout zones, style direction, CTA copy, asset production (unless current ring discloses them)
|
|
82
113
|
|
|
83
114
|
---
|
|
84
115
|
|
|
85
|
-
## Quality check
|
|
116
|
+
## Quality check
|
|
86
117
|
|
|
87
118
|
- [ ] Core action is explicit, not implied
|
|
88
119
|
- [ ] Feedback timing is stated
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# Design Methodology Foundations
|
|
2
2
|
|
|
3
|
-
> **When to read**:
|
|
4
|
-
> Artist and Reviewer may also reference this for context on emotional intent.
|
|
3
|
+
> **When to read**: `@creative-director` and `@game-designer` during Ring 1 creative/spec (via `npm run state:get` → `referenceReads`). Ground Narrative Level and Emotional Arc before writing north-star or DESIGN §0.
|
|
5
4
|
|
|
6
5
|
---
|
|
7
6
|
|
|
@@ -13,6 +13,32 @@
|
|
|
13
13
|
|
|
14
14
|
---
|
|
15
15
|
|
|
16
|
+
## 0. Creative North Star
|
|
17
|
+
|
|
18
|
+
> Ring 1 reader entry. Write this **before** mechanics tables. Copy from `docs/north-star.md`: **One-liner** verbatim; **Micro-Story Spine** field-value match (trim per field).
|
|
19
|
+
|
|
20
|
+
### Micro-Story Spine
|
|
21
|
+
|
|
22
|
+
**Narrative level**: {L0 | L1 | L2}
|
|
23
|
+
**Who**: {from north-star}
|
|
24
|
+
**Situation**: {from north-star}
|
|
25
|
+
**Stakes**: {from north-star}
|
|
26
|
+
**Payoff image**: {from north-star}
|
|
27
|
+
**Emotional arc**: {from north-star}
|
|
28
|
+
|
|
29
|
+
**One-liner**: {One vivid sentence — what feeling/tension the player is playing with, not a mechanic checklist.}
|
|
30
|
+
|
|
31
|
+
**System Dynamic**: {One sentence — what system tension keeps the player acting.}
|
|
32
|
+
|
|
33
|
+
**Why this fusion**: {2–3 sentences — how arrow / elimination / tray **serve** the idea above, not "because the user asked for fusion". Start with: "This fusion serves §0 because…"}
|
|
34
|
+
|
|
35
|
+
**Rejected vibe**:
|
|
36
|
+
|
|
37
|
+
- {Not this — e.g. not a casual match-3 binge}
|
|
38
|
+
- {Not this — e.g. not a pure arrow logic puzzle with no payoff}
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
16
42
|
## Player Action
|
|
17
43
|
|
|
18
44
|
{What the player can do first: tap / drag / aim / choose. Name the visible target and why the action is obvious.}
|
|
@@ -29,6 +55,18 @@
|
|
|
29
55
|
|
|
30
56
|
{How the first level guarantees success: constrained setup, guided target, no failure timer, locked failure path, or simplified rule.}
|
|
31
57
|
|
|
58
|
+
## Core Fun & Feedback
|
|
59
|
+
|
|
60
|
+
### Peak Moment
|
|
61
|
+
|
|
62
|
+
{1-2 core satisfying moments: when triggered → what the player sees/hears → emotional response. Be concrete — name the visual element, the sound cue, the motion.}
|
|
63
|
+
|
|
64
|
+
### Feedback Layers
|
|
65
|
+
|
|
66
|
+
| Action | Instant (<100ms) | Result | Enhancement (combo/special) |
|
|
67
|
+
| -------- | ---------------------- | ------------------------- | --------------------------- |
|
|
68
|
+
| {action} | {press/touch feedback} | {success animation/sound} | {combo/chain payoff} |
|
|
69
|
+
|
|
32
70
|
## Semantic Placeholders
|
|
33
71
|
|
|
34
72
|
| Placeholder | Meaning in Ring 1 | Required? |
|
|
@@ -48,3 +86,42 @@
|
|
|
48
86
|
## Open Assumptions
|
|
49
87
|
|
|
50
88
|
{Only list assumptions that materially affect Ring 1. Use "none" if clear.}
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## 6. Gameplay Fusion
|
|
93
|
+
|
|
94
|
+
Fusion type: {Single | A-Visual | B1-Passive | B2-Active | C-Sequential | D-Parallel}
|
|
95
|
+
|
|
96
|
+
This fusion serves §0 because {link back to One-liner / System Dynamic keywords}.
|
|
97
|
+
|
|
98
|
+
{One paragraph: which parent mechanics combine, interaction contract (not just skill names), and why this fusion is playable first.}
|
|
99
|
+
|
|
100
|
+
## 7. Gameplay Seed
|
|
101
|
+
|
|
102
|
+
First Level Scenario: {8×8 board, path count, blocked vs opening moves, tray pressure — must match docs/level-sketch.json}
|
|
103
|
+
|
|
104
|
+
Player Fantasy: {2–3 sentences from the player's POV. **Expand from Micro-Story Who + Situation** — not a generic "solve puzzle + match-3" line.}
|
|
105
|
+
|
|
106
|
+
Decision Moment: {One aha or mis-tap moment — e.g. blocked path feedback → find unlock order.}
|
|
107
|
+
|
|
108
|
+
## 8. Feedback & Animation Spine
|
|
109
|
+
|
|
110
|
+
### 8.1 Event Catalog(必填)
|
|
111
|
+
|
|
112
|
+
| Event | Trigger | Visual (Ring 1) | Audio placeholder | Duration budget | Blocks input? |
|
|
113
|
+
| ----------- | --------------------------- | --------------------------------------- | ----------------- | --------------- | ------------- |
|
|
114
|
+
| slide_out | valid tap on free path | path container tween along direction | sfx_slide | 300–400ms | yes |
|
|
115
|
+
| blocked | invalid tap on blocked path | board shake + path red tint | sfx_blocked | ~300ms | no |
|
|
116
|
+
| fly_to_tray | after slide_out | head bezier to tray slot | sfx_land | 350–450ms | yes |
|
|
117
|
+
| match_clear | 3 same color in tray | tray burst + scale pop | sfx_match | 200–300ms | yes |
|
|
118
|
+
| win | board empty + tray clear | overlay + optional confetti placeholder | sfx_win | ≤800ms | yes |
|
|
119
|
+
|
|
120
|
+
### 8.2 Atom / Skill refs
|
|
121
|
+
|
|
122
|
+
- Primary: `slide_out_to_tray_animation.aicomponent` (defaults: slide 350ms, fly 400ms)
|
|
123
|
+
- Secondary: `path_animation.aicomponent` (blocked shake)
|
|
124
|
+
|
|
125
|
+
### 8.3 Juice escalation (Ring 1 minimal)
|
|
126
|
+
|
|
127
|
+
- Combo/streak: optional; if n/a, state "flat feedback, escalate in Ring 2"
|
|
@@ -5,12 +5,35 @@
|
|
|
5
5
|
> **Stage relevance**:
|
|
6
6
|
>
|
|
7
7
|
> - **ring 1 `loop: clarify`**: Only **Axis 1 (Mechanic)** and **Axis 3 (Interaction Modality)** must be resolved — they define core playability (可解性) and time/operation budget. The other axes only need a rough direction signal (e.g. "2D", "L1 character present", "Competence arc").
|
|
8
|
-
> - **ring 2 `loop: clarify` (after MC)**: Axis 2 (Dimension), Axis 4 (Narrative Level), and Axis 5 (Emotional Arc) get their full specification when visual direction is confirmed by MC selection.
|
|
8
|
+
> - **ring 2 `loop: clarify` (after MC (Master Composite, 主合成图))**: Axis 2 (Dimension), Axis 4 (Narrative Level), and Axis 5 (Emotional Arc) get their full specification when visual direction is confirmed by MC selection.
|
|
9
9
|
>
|
|
10
10
|
> **This framework is a thinking aid, not a production template.** Game Designer should focus on gameplay first; use these axes to check coverage, not to fill out forms.
|
|
11
11
|
|
|
12
12
|
The five axes are **orthogonal** — each is an independent decision. The same Match-3 game can be Flat 2D or 2.5D, Tap-only or Drag, L0 or L1, Competence Arc or Challenge Arc. Game Designer evaluates all five internally, then presents 2-3 complete options (covering all axes) for the user to choose from.
|
|
13
13
|
|
|
14
|
+
## Playable-Ad First Principle
|
|
15
|
+
|
|
16
|
+
The five axes are not a recipe for adding more ideas. They are a filter for one short playable-ad delight loop:
|
|
17
|
+
|
|
18
|
+
```text
|
|
19
|
+
Hook Conflict
|
|
20
|
+
→ First Action
|
|
21
|
+
→ Instant Feedback
|
|
22
|
+
→ Escalating Decision
|
|
23
|
+
→ Payoff Release
|
|
24
|
+
→ Install Curiosity
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Before applying any axis, Game Designer must know the primary player desire:
|
|
28
|
+
|
|
29
|
+
- What makes the user stop?
|
|
30
|
+
- What makes the first action obvious and tempting?
|
|
31
|
+
- What feedback makes the player feel in control?
|
|
32
|
+
- What payoff happens within 3-5 actions?
|
|
33
|
+
- What unfinished promise carries the CTA (Call To Action, 行动号召)?
|
|
34
|
+
|
|
35
|
+
Any axis choice or fusion idea is valid only if it strengthens at least one part of this loop. If a secondary mechanic only makes the concept sound richer, but does not improve hook, first action, feedback, payoff, or install curiosity, downgrade it to visual dressing or pseudo-fusion.
|
|
36
|
+
|
|
14
37
|
---
|
|
15
38
|
|
|
16
39
|
## Axis 1 — Mechanic (Gameplay Fusion Types)
|
|
@@ -28,6 +51,32 @@ The five axes are **orthogonal** — each is an independent decision. The same M
|
|
|
28
51
|
|
|
29
52
|
When the user insists on Type C/D, recommend pseudo-fusion: **visually appear as two systems, but the underlying mechanic is single**. Example: Match-3 elimination count drives a monster HP bar — the user only performs match-3 the entire time, but perceives "elimination + combat."
|
|
30
53
|
|
|
54
|
+
### Fusion Quality Bar
|
|
55
|
+
|
|
56
|
+
For every non-`Single` option, answer:
|
|
57
|
+
|
|
58
|
+
| Question | Pass Signal |
|
|
59
|
+
| ----------------------------------------------------------- | ---------------------------------------------------------------- |
|
|
60
|
+
| What is the primary joy? | One clear emotion source, not a list of features |
|
|
61
|
+
| Which loop stage does the secondary mechanic strengthen? | Hook, first action, feedback, payoff, or install curiosity |
|
|
62
|
+
| Does the secondary mechanic require a new tutorial gesture? | No, or the cost is explicitly justified |
|
|
63
|
+
| Does the payoff become stronger? | The player sees a bigger release than the primary mechanic alone |
|
|
64
|
+
| Can it be understood in the ad timeline? | The user can grasp it without reading a rules paragraph |
|
|
65
|
+
|
|
66
|
+
If these answers are weak, the fusion is probably decorative complexity.
|
|
67
|
+
|
|
68
|
+
### Mechanic Depth beyond Stitching
|
|
69
|
+
|
|
70
|
+
| Pattern | When to use | Depth signal |
|
|
71
|
+
| --------------------------------------------- | ------------------------------------------ | ---------------------------------------------------------- |
|
|
72
|
+
| **Composite atom** (`arrow_pick_match_rules`) | Path + tray match already fused in one PGS | Single interaction contract; prefer over stitching parents |
|
|
73
|
+
| **Pseudo-fusion B1** | Visual dual system, single reducer | Secondary mechanic auto-triggers — document reducer |
|
|
74
|
+
| **Board phase B1** | Same 8×8 grid, mask unlock | Board "changes" via phase, not resize |
|
|
75
|
+
|
|
76
|
+
**Forbidden in DESIGN:** listing two parent skill names without **interaction contract** (who acts, where match happens, what blocked teaches).
|
|
77
|
+
|
|
78
|
+
See also: `.claude/agents/refs/game-designer-fusion-depth-playbook.md`
|
|
79
|
+
|
|
31
80
|
### Decision Rules
|
|
32
81
|
|
|
33
82
|
```
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Creative Process — {Game Name}
|
|
2
|
+
|
|
3
|
+
<!--
|
|
4
|
+
Ring 1 clarify audit trail. Fill phases 1–4 BEFORE finalizing DESIGN.md.
|
|
5
|
+
Source: templates/game-designer-creative-process.template.md
|
|
6
|
+
-->
|
|
7
|
+
|
|
8
|
+
**Created**: {YYYY-MM-DD}
|
|
9
|
+
**Creative arm**: {A1 | A2 | A3 — from GD_CREATIVE_ARM env if harness}
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Phase 1 — Radical Diverge
|
|
14
|
+
|
|
15
|
+
List ≥3 **gameplay core-moment** candidates. Do not write DESIGN.md yet.
|
|
16
|
+
|
|
17
|
+
### Candidate A
|
|
18
|
+
|
|
19
|
+
- **Core moment**: {one sentence}
|
|
20
|
+
- **Story hook**: {15s filmable Hook frame}
|
|
21
|
+
- **Who / Stakes**: {one line each}
|
|
22
|
+
- **Why interesting**: {…}
|
|
23
|
+
- **Core challenge**: {…}
|
|
24
|
+
- **Worst case**: {what breaks if player ignores rules}
|
|
25
|
+
|
|
26
|
+
### Candidate B
|
|
27
|
+
|
|
28
|
+
- **Core moment**: {…}
|
|
29
|
+
- **Story hook**: {…}
|
|
30
|
+
- **Who / Stakes**: {…}
|
|
31
|
+
- **Why interesting**: {…}
|
|
32
|
+
- **Core challenge**: {…}
|
|
33
|
+
- **Worst case**: {…}
|
|
34
|
+
|
|
35
|
+
### Candidate C
|
|
36
|
+
|
|
37
|
+
- **Core moment**: {…}
|
|
38
|
+
- **Story hook**: {…}
|
|
39
|
+
- **Who / Stakes**: {…}
|
|
40
|
+
- **Why interesting**: {…}
|
|
41
|
+
- **Core challenge**: {…}
|
|
42
|
+
- **Worst case**: {…}
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Phase 2 — Adversarial Review (five questions)
|
|
47
|
+
|
|
48
|
+
For each candidate, answer **evidence-based** questions — not aesthetic veto.
|
|
49
|
+
|
|
50
|
+
| Candidate | Q1 Second tap? | Q2 Ignore mechanism? | Q3 White-box fun? | Q4 Depth ceiling? | Q5 Strip story — still care? |
|
|
51
|
+
| --------- | -------------- | -------------------- | ----------------- | ----------------- | ---------------------------- |
|
|
52
|
+
| A | {…} | {…} | {…} | {…} | {…} |
|
|
53
|
+
| B | {…} | {…} | {…} | {…} | {…} |
|
|
54
|
+
| C | {…} | {…} | {…} | {…} | {…} |
|
|
55
|
+
|
|
56
|
+
**Vulnerability list** (falsifiable, not "not fun"):
|
|
57
|
+
|
|
58
|
+
- {bullet}
|
|
59
|
+
- {bullet}
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Phase 3 — Commitment
|
|
64
|
+
|
|
65
|
+
**Chosen candidate**: {A | B | C}
|
|
66
|
+
|
|
67
|
+
**Narrative level**: {L0 | L1 | L2} — {CD decides; if L0, quote user's explicit opt-out request here}
|
|
68
|
+
|
|
69
|
+
**Patch promises** (how this design addresses vulnerabilities):
|
|
70
|
+
|
|
71
|
+
- {…}
|
|
72
|
+
|
|
73
|
+
**Rejected alternatives**:
|
|
74
|
+
|
|
75
|
+
- {B}: deferred because {…}
|
|
76
|
+
- {C}: deferred because {…}
|
|
77
|
+
|
|
78
|
+
**Pressure test memo** (≤30 lines):
|
|
79
|
+
|
|
80
|
+
- White-box: {…}
|
|
81
|
+
- 1-minute test: {…}
|
|
82
|
+
- Depth test: {…}
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## Phase 4 — Design Intent
|
|
87
|
+
|
|
88
|
+
**Design Intent**: {e.g. "Player aha at move 3 when blocked path unlocks tray combo"}
|
|
89
|
+
|
|
90
|
+
> **Mandatory sync**: Copy **Design Intent verbatim** into `docs/DESIGN.md` §0 **One-liner**. Copy **System Dynamic** into §0 second field. Copy **Micro-Story Spine** fields field-for-field from `docs/north-star.md`.
|
|
91
|
+
|
|
92
|
+
**Mechanic variation** (vs pure stitch): {one axis — tray pressure / phase unlock / segment density}
|
|
93
|
+
|
|
94
|
+
**Rejected shallow fusion**: {e.g. all-free 4 paths on 8×8}
|
|
95
|
+
|
|
96
|
+
**Pattern / skills locked after commitment**:
|
|
97
|
+
|
|
98
|
+
- Pattern: {from playcraft patterns match}
|
|
99
|
+
- Primary skill: {e.g. arrow_pick_match_rules.aigameplay}
|