@loonylabs/create-game 0.1.0
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/bin/create-game.js +213 -0
- package/package.json +18 -0
- package/template/.claude/skills/aigdtk-create-game-stories/SKILL.md +177 -0
- package/template/.claude/skills/aigdtk-create-game-stories/story-template.md +85 -0
- package/template/.claude/skills/aigdtk-implement-game-stories/SKILL.md +129 -0
- package/template/.claude/skills/aigdtk-new-game/SKILL.md +126 -0
- package/template/.claude/skills/aigdtk-shared/ascii-grammar.md +133 -0
- package/template/.claude/skills/aigdtk-shared/enemies.md +112 -0
- package/template/.claude/skills/aigdtk-shared/framework.md +93 -0
- package/template/.claude/skills/aigdtk-shared/visuals.md +125 -0
- package/template/apps/client/index.html +14 -0
- package/template/apps/client/package.json +31 -0
- package/template/apps/client/public/assets/audio/enemy_killed.wav +0 -0
- package/template/apps/client/src/components/App.svelte +290 -0
- package/template/apps/client/src/components/CraftingPanel.svelte +253 -0
- package/template/apps/client/src/components/DevPanel.svelte +180 -0
- package/template/apps/client/src/components/DungeonClearOverlay.svelte +53 -0
- package/template/apps/client/src/components/EquipmentPanel.svelte +191 -0
- package/template/apps/client/src/components/HealthBar.svelte +50 -0
- package/template/apps/client/src/components/Hub/BackToHubButton.svelte +37 -0
- package/template/apps/client/src/components/Hub/ExperienceCard.svelte +115 -0
- package/template/apps/client/src/components/Hub/Hub.svelte +88 -0
- package/template/apps/client/src/components/Inventory.svelte +174 -0
- package/template/apps/client/src/components/Runner/RunnerDeathScreen.svelte +182 -0
- package/template/apps/client/src/components/Runner/RunnerHUD.svelte +157 -0
- package/template/apps/client/src/components/Shooter/DamageNumbers.svelte +96 -0
- package/template/apps/client/src/components/Shooter/GameOverScreen.svelte +109 -0
- package/template/apps/client/src/components/Shooter/ShooterHUD.svelte +95 -0
- package/template/apps/client/src/components/SkillBar.svelte +146 -0
- package/template/apps/client/src/components/ToastSystem.svelte +158 -0
- package/template/apps/client/src/game.ts +918 -0
- package/template/apps/client/src/input.ts +92 -0
- package/template/apps/client/src/lib/audio/CombatDetector.test.ts +59 -0
- package/template/apps/client/src/lib/audio/CombatDetector.ts +53 -0
- package/template/apps/client/src/lib/audio/MusicManager.ts +137 -0
- package/template/apps/client/src/lib/audio/SoundManager.ts +59 -0
- package/template/apps/client/src/lib/audio/index.ts +9 -0
- package/template/apps/client/src/main.ts +32 -0
- package/template/apps/client/src/renderer/basecamp.ts +126 -0
- package/template/apps/client/src/renderer/dungeon.ts +250 -0
- package/template/apps/client/src/renderer/dungeonPortal.ts +73 -0
- package/template/apps/client/src/renderer/dungeonZone.ts +301 -0
- package/template/apps/client/src/renderer/entities.ts +197 -0
- package/template/apps/client/src/renderer/runnerTrack.ts +221 -0
- package/template/apps/client/src/renderer/shaders/SkyShader.ts +190 -0
- package/template/apps/client/src/renderer/shaders/TerrainShaderMaterial.ts +133 -0
- package/template/apps/client/src/renderer/shaders/floor.frag.glsl.ts +17 -0
- package/template/apps/client/src/renderer/shaders/shaderConfig.ts +18 -0
- package/template/apps/client/src/renderer/shaders/spawn.frag.glsl.ts +19 -0
- package/template/apps/client/src/renderer/shaders/terrain.frag.glsl.ts +314 -0
- package/template/apps/client/src/renderer/shaders/terrain.vert.glsl.ts +16 -0
- package/template/apps/client/src/renderer/shaders/wall.frag.glsl.ts +20 -0
- package/template/apps/client/src/renderer/shooterArena.ts +102 -0
- package/template/apps/client/src/renderer/voxelChunkStreamer.ts +79 -0
- package/template/apps/client/src/renderer/voxelMesh.ts +86 -0
- package/template/apps/client/src/renderer/voxelTerrain.ts +74 -0
- package/template/apps/client/src/socket.ts +268 -0
- package/template/apps/client/src/store.ts +74 -0
- package/template/apps/client/src/style.css +60 -0
- package/template/apps/client/tsconfig.json +11 -0
- package/template/apps/client/vite.config.ts +10 -0
- package/template/apps/client/vitest.config.ts +8 -0
- package/template/apps/experiences/diablo/index.ts +94 -0
- package/template/apps/experiences/diablo/systems/dungeonClearSystem.ts +60 -0
- package/template/apps/experiences/diablo/systems/enemyAISystem.ts +11 -0
- package/template/apps/experiences/diablo/systems/entitySyncSystem.ts +80 -0
- package/template/apps/experiences/diablo/systems/itemPickupSystem.ts +11 -0
- package/template/apps/experiences/diablo/systems/movementSystem.ts +13 -0
- package/template/apps/experiences/diablo/systems/physicsSystem.ts +92 -0
- package/template/apps/experiences/diablo/systems/transitionSystem.ts +105 -0
- package/template/apps/experiences/runner/data/runner-config.ts +54 -0
- package/template/apps/experiences/runner/index.ts +143 -0
- package/template/apps/experiences/runner/systems/collectibleSystem.ts +157 -0
- package/template/apps/experiences/runner/systems/deathSystem.ts +42 -0
- package/template/apps/experiences/runner/systems/entitySyncSystem.ts +59 -0
- package/template/apps/experiences/runner/systems/obstacleSystem.ts +91 -0
- package/template/apps/experiences/runner/systems/runnerPhysicsSystem.ts +82 -0
- package/template/apps/experiences/runner/systems/trackStreamSystem.ts +19 -0
- package/template/apps/experiences/runner/track/laneSystem.ts +53 -0
- package/template/apps/experiences/runner/track/segmentTypes.ts +141 -0
- package/template/apps/experiences/runner/track/trackGenerator.ts +292 -0
- package/template/apps/experiences/shooter/ai/aiStateMachine.ts +394 -0
- package/template/apps/experiences/shooter/ai/lineOfSight.ts +32 -0
- package/template/apps/experiences/shooter/arena/arenaGenerator.ts +101 -0
- package/template/apps/experiences/shooter/arena/arenaTypes.ts +49 -0
- package/template/apps/experiences/shooter/arena/hitscan.ts +101 -0
- package/template/apps/experiences/shooter/data/enemy-types.ts +108 -0
- package/template/apps/experiences/shooter/data/wave-definitions.ts +40 -0
- package/template/apps/experiences/shooter/data/weapon-config.ts +80 -0
- package/template/apps/experiences/shooter/index.ts +127 -0
- package/template/apps/experiences/shooter/systems/enemyAISystem.ts +113 -0
- package/template/apps/experiences/shooter/systems/entitySyncSystem.ts +68 -0
- package/template/apps/experiences/shooter/systems/shooterPhysicsSystem.ts +89 -0
- package/template/apps/experiences/shooter/systems/waveSpawnerSystem.ts +87 -0
- package/template/apps/experiences/shooter/systems/weaponSystem.ts +157 -0
- package/template/apps/game-data/src/areas/area-manifest.json +18 -0
- package/template/apps/game-data/src/assets/migration.test.ts +291 -0
- package/template/apps/game-data/src/audio/music-config.json +21 -0
- package/template/apps/game-data/src/audio/sound-config.json +11 -0
- package/template/apps/game-data/src/combat/action-types.ts +2 -0
- package/template/apps/game-data/src/combat/enemy-def.ts +12 -0
- package/template/apps/game-data/src/combat/hitboxes.ts +23 -0
- package/template/apps/game-data/src/dungeon/cell-types.ts +20 -0
- package/template/apps/game-data/src/dungeon/cell-visuals.ts +13 -0
- package/template/apps/game-data/src/dungeon/door-directions.ts +2 -0
- package/template/apps/game-data/src/enemies/enemy-defs.json +32 -0
- package/template/apps/game-data/src/equipment/slots.json +5 -0
- package/template/apps/game-data/src/events/event-defs.ts +20 -0
- package/template/apps/game-data/src/events/event-types.ts +10 -0
- package/template/apps/game-data/src/events/toast-config.json +49 -0
- package/template/apps/game-data/src/items/item-pool.json +13 -0
- package/template/apps/game-data/src/loot/item-pool.ts +14 -0
- package/template/apps/game-data/src/loot/rarities.ts +2 -0
- package/template/apps/game-data/src/physics/dungeon-physics-config.ts +12 -0
- package/template/apps/game-data/src/physics/jump-config.ts +17 -0
- package/template/apps/game-data/src/recipes/recipe-book.json +68 -0
- package/template/apps/game-data/src/rooms/room_basecamp.json +16 -0
- package/template/apps/game-data/src/rooms/room_corridor_ew.json +9 -0
- package/template/apps/game-data/src/rooms/room_corridor_ns.json +11 -0
- package/template/apps/game-data/src/rooms/room_crossroads.json +11 -0
- package/template/apps/game-data/src/rooms/room_dead_end.json +10 -0
- package/template/apps/game-data/src/rooms/room_staircase.json +12 -0
- package/template/apps/game-data/src/rooms/room_start.json +11 -0
- package/template/apps/game-data/src/skills/skill-book.json +20 -0
- package/template/apps/game-data/src/voxel/biome-terrain.ts +76 -0
- package/template/apps/game-data/src/voxel/materials.ts +45 -0
- package/template/apps/game-data/src/voxel/sandbox-terrain-config.ts +19 -0
- package/template/apps/game-data/src/world/area-config.ts +33 -0
- package/template/apps/game-data/src/world/biome-def.ts +15 -0
- package/template/apps/game-data/src/world/biomes.json +57 -0
- package/template/apps/game-data/src/world/movement.ts +2 -0
- package/template/apps/game-data/src/world/overworld-layout.test.ts +93 -0
- package/template/apps/game-data/src/world/overworld-layout.ts +127 -0
- package/template/apps/server/data/game.db +0 -0
- package/template/apps/server/package.json +30 -0
- package/template/apps/server/src/areaManager.ts +346 -0
- package/template/apps/server/src/db/client.ts +45 -0
- package/template/apps/server/src/db/schema.ts +40 -0
- package/template/apps/server/src/gameLoop.ts +267 -0
- package/template/apps/server/src/gameState.ts +3 -0
- package/template/apps/server/src/handlers/actionEvent.ts +55 -0
- package/template/apps/server/src/handlers/craftHandler.ts +59 -0
- package/template/apps/server/src/handlers/equipHandler.ts +73 -0
- package/template/apps/server/src/handlers/raycastHandler.ts +97 -0
- package/template/apps/server/src/handlers/skillHandler.ts +87 -0
- package/template/apps/server/src/handlers/terraformHandler.ts +74 -0
- package/template/apps/server/src/index.ts +597 -0
- package/template/apps/server/src/persistence.ts +135 -0
- package/template/apps/server/src/rooms.ts +20 -0
- package/template/apps/server/src/systems/dungeonPhysics.test.ts +32 -0
- package/template/apps/server/src/systems/dungeonPhysics.ts +16 -0
- package/template/apps/server/src/systems/enemyAI.ts +129 -0
- package/template/apps/server/src/systems/itemPickup.ts +31 -0
- package/template/apps/server/src/tests/areaManager.test.ts +77 -0
- package/template/apps/server/src/tests/diablo-experience.test.ts +60 -0
- package/template/apps/server/src/tests/runner-experience.test.ts +273 -0
- package/template/apps/server/src/tests/runner-powerups-scoring.test.ts +221 -0
- package/template/apps/server/src/tests/server.integration.test.ts +92 -0
- package/template/apps/server/src/tests/shooter-enemy-ai.test.ts +328 -0
- package/template/apps/server/src/tests/shooter-experience.test.ts +281 -0
- package/template/apps/server/src/tests/voxelChunkCache.test.ts +29 -0
- package/template/apps/server/src/tests/voxelSandbox.test.ts +133 -0
- package/template/apps/server/src/voxelChunkCache.ts +31 -0
- package/template/apps/server/src/voxelPlayerState.ts +23 -0
- package/template/apps/server/tsconfig.json +17 -0
- package/template/apps/server/vitest.config.ts +8 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* create-game.js — Scaffold a new game project from @loonylabs/create-game templates.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* npx @loonylabs/create-game <game-name> <target-dir>
|
|
7
|
+
*
|
|
8
|
+
* Example:
|
|
9
|
+
* npx @loonylabs/create-game shadow-dungeon ~/Desktop/shadow-dungeon
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import fs from 'fs';
|
|
13
|
+
import path from 'path';
|
|
14
|
+
import { fileURLToPath } from 'url';
|
|
15
|
+
|
|
16
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
const TEMPLATE_DIR = path.resolve(__dirname, '..', 'template');
|
|
18
|
+
const FRAMEWORK_VERSION = '0.1.0';
|
|
19
|
+
|
|
20
|
+
// ─── Args ────────────────────────────────────────────────────────────────────
|
|
21
|
+
|
|
22
|
+
const [, , gameName, targetDir] = process.argv;
|
|
23
|
+
|
|
24
|
+
if (!gameName || !targetDir) {
|
|
25
|
+
console.error('Usage: npx @loonylabs/create-game <game-name> <target-dir>');
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const target = path.resolve(targetDir);
|
|
30
|
+
const gameDataPackage = `@${gameName}/game-data`;
|
|
31
|
+
|
|
32
|
+
if (fs.existsSync(target)) {
|
|
33
|
+
console.error(`Error: Target directory already exists: ${target}`);
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
console.log(`\n🎮 Creating game project: ${gameName}`);
|
|
38
|
+
console.log(` Target: ${target}\n`);
|
|
39
|
+
|
|
40
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
41
|
+
|
|
42
|
+
const SKIP_DIRS = new Set(['node_modules', 'dist', '.turbo', '.git']);
|
|
43
|
+
|
|
44
|
+
function copyDir(src, dest, transformFn = null) {
|
|
45
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
46
|
+
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
47
|
+
if (SKIP_DIRS.has(entry.name)) continue;
|
|
48
|
+
const srcPath = path.join(src, entry.name);
|
|
49
|
+
const destPath = path.join(dest, entry.name);
|
|
50
|
+
const stat = fs.statSync(srcPath);
|
|
51
|
+
if (stat.isDirectory()) {
|
|
52
|
+
copyDir(srcPath, destPath, transformFn);
|
|
53
|
+
} else if (stat.isFile()) {
|
|
54
|
+
let content = fs.readFileSync(srcPath);
|
|
55
|
+
if (transformFn && (srcPath.endsWith('.json') || srcPath.endsWith('.ts') || srcPath.endsWith('.js') || srcPath.endsWith('.md') || srcPath.endsWith('.svelte') || srcPath.endsWith('.html'))) {
|
|
56
|
+
content = Buffer.from(transformFn(content.toString(), srcPath));
|
|
57
|
+
}
|
|
58
|
+
fs.writeFileSync(destPath, content);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function writeJson(filePath, obj) {
|
|
64
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
65
|
+
fs.writeFileSync(filePath, JSON.stringify(obj, null, 2) + '\n');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function writeText(filePath, text) {
|
|
69
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
70
|
+
fs.writeFileSync(filePath, text);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// ─── Step 1: Root config files ───────────────────────────────────────────────
|
|
74
|
+
|
|
75
|
+
console.log('📁 Creating project structure...');
|
|
76
|
+
|
|
77
|
+
writeJson(path.join(target, 'package.json'), {
|
|
78
|
+
name: gameName,
|
|
79
|
+
private: true,
|
|
80
|
+
type: 'module',
|
|
81
|
+
scripts: {
|
|
82
|
+
dev: 'turbo dev --ui=stream',
|
|
83
|
+
build: 'turbo build',
|
|
84
|
+
test: 'turbo test',
|
|
85
|
+
},
|
|
86
|
+
packageManager: 'pnpm@10.30.2',
|
|
87
|
+
devDependencies: {
|
|
88
|
+
turbo: 'latest',
|
|
89
|
+
typescript: '^5.0.0',
|
|
90
|
+
},
|
|
91
|
+
engines: { node: '>=18.0.0', pnpm: '>=8.0.0' },
|
|
92
|
+
pnpm: { onlyBuiltDependencies: ['better-sqlite3', 'esbuild'] },
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
writeText(path.join(target, 'pnpm-workspace.yaml'), 'packages:\n - \'apps/*\'\n');
|
|
96
|
+
|
|
97
|
+
writeJson(path.join(target, 'turbo.json'), {
|
|
98
|
+
$schema: 'https://turbo.build/schema.json',
|
|
99
|
+
tasks: {
|
|
100
|
+
build: { dependsOn: ['^build'], outputs: ['dist/**'] },
|
|
101
|
+
dev: { persistent: true, cache: false },
|
|
102
|
+
test: { dependsOn: ['^build'] },
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
writeText(path.join(target, '.gitignore'),
|
|
107
|
+
'node_modules/\ndist/\n.turbo/\n*.db\n*.db-shm\n*.db-wal\n');
|
|
108
|
+
|
|
109
|
+
writeText(path.join(target, '.npmrc'),
|
|
110
|
+
'public-hoist-pattern[]=@loonylabs/*\n');
|
|
111
|
+
|
|
112
|
+
// ─── Step 2: game-data app ───────────────────────────────────────────────────
|
|
113
|
+
|
|
114
|
+
console.log('📦 Copying game-data...');
|
|
115
|
+
|
|
116
|
+
copyDir(
|
|
117
|
+
path.join(TEMPLATE_DIR, 'apps', 'game-data', 'src'),
|
|
118
|
+
path.join(target, 'apps', 'game-data', 'src')
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
writeJson(path.join(target, 'apps', 'game-data', 'package.json'), {
|
|
122
|
+
name: gameDataPackage,
|
|
123
|
+
version: '0.1.0',
|
|
124
|
+
type: 'module',
|
|
125
|
+
private: true,
|
|
126
|
+
exports: { '.': './src/index.ts' },
|
|
127
|
+
dependencies: {
|
|
128
|
+
'@loonylabs/gamedev-core': FRAMEWORK_VERSION,
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// ─── Step 3: client app ──────────────────────────────────────────────────────
|
|
133
|
+
|
|
134
|
+
console.log('🖥️ Copying client app...');
|
|
135
|
+
|
|
136
|
+
function transformClientDeps(content, filePath) {
|
|
137
|
+
if (!filePath.endsWith('package.json')) return content;
|
|
138
|
+
const pkg = JSON.parse(content);
|
|
139
|
+
if (pkg.dependencies?.['@loonylabs/game-data']) {
|
|
140
|
+
pkg.dependencies[gameDataPackage] = 'workspace:*';
|
|
141
|
+
delete pkg.dependencies['@loonylabs/game-data'];
|
|
142
|
+
}
|
|
143
|
+
for (const key of ['@loonylabs/gamedev-core', '@loonylabs/gamedev-client', '@loonylabs/gamedev-server', '@loonylabs/gamedev-protocol']) {
|
|
144
|
+
if (pkg.dependencies?.[key]) pkg.dependencies[key] = FRAMEWORK_VERSION;
|
|
145
|
+
if (pkg.devDependencies?.[key]) pkg.devDependencies[key] = FRAMEWORK_VERSION;
|
|
146
|
+
}
|
|
147
|
+
return JSON.stringify(pkg, null, 2) + '\n';
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function transformImports(content, filePath) {
|
|
151
|
+
if (filePath.endsWith('package.json')) return transformClientDeps(content, filePath);
|
|
152
|
+
let result = content;
|
|
153
|
+
result = result.replaceAll('gamedev-ai-toolkit', gameName);
|
|
154
|
+
result = result.replaceAll('@loonylabs/game-data', gameDataPackage);
|
|
155
|
+
result = result.replace(/['"](?:\.\.\/)+packages\/core\/src\/[^'"]+['"]/g, "'@loonylabs/gamedev-core'");
|
|
156
|
+
result = result.replace(/['"](?:\.\.\/)+packages\/server\/src\/[^'"]+['"]/g, "'@loonylabs/gamedev-server'");
|
|
157
|
+
result = result.replace(/['"](?:\.\.\/)+packages\/client\/src\/[^'"]+['"]/g, "'@loonylabs/gamedev-client'");
|
|
158
|
+
return result;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
copyDir(
|
|
162
|
+
path.join(TEMPLATE_DIR, 'apps', 'client'),
|
|
163
|
+
path.join(target, 'apps', 'client'),
|
|
164
|
+
transformImports
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
// ─── Step 4: server app ──────────────────────────────────────────────────────
|
|
168
|
+
|
|
169
|
+
console.log('🖥️ Copying server app...');
|
|
170
|
+
|
|
171
|
+
copyDir(
|
|
172
|
+
path.join(TEMPLATE_DIR, 'apps', 'server'),
|
|
173
|
+
path.join(target, 'apps', 'server'),
|
|
174
|
+
transformImports
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
// ─── Step 4b: experiences ────────────────────────────────────────────────────
|
|
178
|
+
|
|
179
|
+
console.log('🗺️ Copying experiences...');
|
|
180
|
+
|
|
181
|
+
copyDir(
|
|
182
|
+
path.join(TEMPLATE_DIR, 'apps', 'experiences'),
|
|
183
|
+
path.join(target, 'apps', 'experiences'),
|
|
184
|
+
transformImports
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
// ─── Step 5: aigdtk skills ───────────────────────────────────────────────────
|
|
188
|
+
|
|
189
|
+
console.log('🤖 Copying aigdtk skills...');
|
|
190
|
+
|
|
191
|
+
const skillsToCopy = ['aigdtk-new-game', 'aigdtk-create-game-stories', 'aigdtk-implement-game-stories', 'aigdtk-shared'];
|
|
192
|
+
for (const skill of skillsToCopy) {
|
|
193
|
+
const src = path.join(TEMPLATE_DIR, '.claude', 'skills', skill);
|
|
194
|
+
if (fs.existsSync(src)) {
|
|
195
|
+
copyDir(src, path.join(target, '.claude', 'skills', skill));
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// ─── Done ────────────────────────────────────────────────────────────────────
|
|
200
|
+
|
|
201
|
+
console.log(`
|
|
202
|
+
✅ Game project created: ${gameName}
|
|
203
|
+
|
|
204
|
+
Next steps:
|
|
205
|
+
cd ${target}
|
|
206
|
+
pnpm install
|
|
207
|
+
pnpm dev
|
|
208
|
+
|
|
209
|
+
Then open http://localhost:3200 in your browser.
|
|
210
|
+
|
|
211
|
+
To start building your game with AI:
|
|
212
|
+
/aigdtk-new-game
|
|
213
|
+
`);
|
package/package.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@loonylabs/create-game",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"bin": {
|
|
6
|
+
"create-game": "./bin/create-game.js"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
"bin",
|
|
10
|
+
"template"
|
|
11
|
+
],
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"access": "public"
|
|
14
|
+
},
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": ">=18.0.0"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: aigdtk-create-game-stories
|
|
3
|
+
description: Creates implementation-ready game stories from a saved Game Brief. Run after /aigdtk-new-game in a new session.
|
|
4
|
+
disable-model-invocation: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Create Game Stories
|
|
8
|
+
|
|
9
|
+
## Supporting files (shared knowledge base)
|
|
10
|
+
- Framework reference: [../aigdtk-shared/framework.md](../aigdtk-shared/framework.md)
|
|
11
|
+
- ASCII room grammar: [../aigdtk-shared/ascii-grammar.md](../aigdtk-shared/ascii-grammar.md)
|
|
12
|
+
- Enemy definitions reference: [../aigdtk-shared/enemies.md](../aigdtk-shared/enemies.md)
|
|
13
|
+
- Cell visuals / palettes: [../aigdtk-shared/visuals.md](../aigdtk-shared/visuals.md)
|
|
14
|
+
|
|
15
|
+
<activation CRITICAL="TRUE">
|
|
16
|
+
You are now in game story generation mode. Your role is **technical architect**.
|
|
17
|
+
You translate a developer's game vision into implementation-ready stories.
|
|
18
|
+
|
|
19
|
+
## NON-NEGOTIABLE RULES
|
|
20
|
+
|
|
21
|
+
1. **NEVER ask technical questions** to the developer.
|
|
22
|
+
2. **NEVER ask the developer to make technical decisions.** You decide everything.
|
|
23
|
+
3. **Ask only if something is genuinely ambiguous about the game vision** — and only
|
|
24
|
+
product-level questions (e.g. "Should the second area feel like an overworld or
|
|
25
|
+
another dungeon?"), never technical ones.
|
|
26
|
+
4. **Generate complete, ready-to-implement stories** — no placeholders, no TODOs
|
|
27
|
+
in acceptance criteria, no "TBD" in technical specs.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## STEP 1 — Read the Game Brief
|
|
32
|
+
|
|
33
|
+
Read `docs/aigdtk/game-brief.md` now.
|
|
34
|
+
|
|
35
|
+
If the file does not exist, stop and tell the developer:
|
|
36
|
+
> "Kein Game Brief gefunden. Bitte starte zuerst mit `/aigdtk-new-game`."
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## STEP 2 — Read framework reference and derive area type
|
|
41
|
+
|
|
42
|
+
Read `../aigdtk-shared/framework.md`.
|
|
43
|
+
|
|
44
|
+
From the Game Brief's Core Loop and Happy When, decide:
|
|
45
|
+
- **Area type**: `dungeon` (room-based exploration/combat) or `voxel` (open world) or both
|
|
46
|
+
- **Number of rooms**: 2–3 minimal, 4–6 if the Happy When demands more variety
|
|
47
|
+
- **Number of enemy types**: 1 base + 1–2 variants minimum
|
|
48
|
+
|
|
49
|
+
Do NOT ask — decide.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## STEP 3 — Read enemy reference and derive enemy definitions
|
|
54
|
+
|
|
55
|
+
Read `../aigdtk-shared/enemies.md`.
|
|
56
|
+
|
|
57
|
+
Generate complete enemy stats for:
|
|
58
|
+
- The enemy described in the Game Brief (map description to archetype)
|
|
59
|
+
- 1–2 additional variants (one easier, one harder — or a boss if Happy When mentions it)
|
|
60
|
+
|
|
61
|
+
Derive color from description keywords using the color table in enemies.md.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## STEP 4 — Read ASCII grammar and plan rooms
|
|
66
|
+
|
|
67
|
+
Read `../aigdtk-shared/ascii-grammar.md`.
|
|
68
|
+
|
|
69
|
+
Plan the room set:
|
|
70
|
+
- 1 start room (with `S`, `tags: ["start"]`)
|
|
71
|
+
- 1–2 combat rooms with doors
|
|
72
|
+
- 1 special room if Happy When mentions a goal (boss, loot, exit)
|
|
73
|
+
|
|
74
|
+
Draft the ASCII layouts mentally — they go in the stories.
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## STEP 5 — Read visuals reference and derive palette
|
|
79
|
+
|
|
80
|
+
Read `../aigdtk-shared/visuals.md`.
|
|
81
|
+
|
|
82
|
+
Pick or compose a color palette that fits the reference game + enemy description.
|
|
83
|
+
Derive all 7 `GameCellType` colors and heights. Do not leave any unset.
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## STEP 6 — Derive story prefix
|
|
88
|
+
|
|
89
|
+
From the game name, derive a short prefix:
|
|
90
|
+
- Take first letter of each word, lowercase
|
|
91
|
+
- Examples: "Shadow Dungeon" → `sd`, "Bone Crawler" → `bc`, "The Last Keeper" → `tlk`
|
|
92
|
+
- If the result is less than 2 chars, use first 2–3 letters of the first word
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## STEP 7 — Write the stories
|
|
97
|
+
|
|
98
|
+
Write 2–3 stories to `docs/stories/`.
|
|
99
|
+
**Do NOT read any existing files in `docs/stories/`.**
|
|
100
|
+
|
|
101
|
+
Use `story-template.md` (in this skill's directory) as the format reference.
|
|
102
|
+
Read it now and follow it exactly for every story you write.
|
|
103
|
+
|
|
104
|
+
### Story 1: `[prefix]01_enemies-and-visuals.md`
|
|
105
|
+
|
|
106
|
+
**Scope**: Generate and write `enemy-defs.json` + `cell-visuals.ts`
|
|
107
|
+
|
|
108
|
+
Technical spec must include:
|
|
109
|
+
- Complete `enemy-defs.json` array with all enemy objects (filled, not placeholder)
|
|
110
|
+
- Complete `cell-visuals.ts` with all 7 cell types and derived colors/heights
|
|
111
|
+
|
|
112
|
+
Task list:
|
|
113
|
+
- [ ] Write `apps/game-data/src/enemies/enemy-defs.json`
|
|
114
|
+
- [ ] Write `apps/game-data/src/dungeon/cell-visuals.ts`
|
|
115
|
+
- [ ] Run `pnpm test:changed` — all green
|
|
116
|
+
- [ ] Lessons Learned ausfüllen
|
|
117
|
+
- [ ] Handover für Story 2 schreiben
|
|
118
|
+
|
|
119
|
+
### Story 2: `[prefix]02_rooms-and-areas.md`
|
|
120
|
+
|
|
121
|
+
**Scope**: Generate and write `room_*.json` files + `area-manifest.json`
|
|
122
|
+
|
|
123
|
+
Technical spec must include:
|
|
124
|
+
- All planned room files with complete ASCII layouts (filled, valid per ascii-grammar.md rules)
|
|
125
|
+
- Complete `area-manifest.json` with correct area type
|
|
126
|
+
|
|
127
|
+
Task list:
|
|
128
|
+
- [ ] Write `apps/game-data/src/rooms/room_start.json`
|
|
129
|
+
- [ ] Write `apps/game-data/src/rooms/room_combat.json` (and others if planned)
|
|
130
|
+
- [ ] Write `apps/game-data/src/areas/area-manifest.json`
|
|
131
|
+
- [ ] Run `pnpm test:changed` — all green
|
|
132
|
+
- [ ] Run `pnpm dev` und prüfen ob Welt sichtbar ist
|
|
133
|
+
- [ ] Lessons Learned ausfüllen
|
|
134
|
+
- [ ] Handover schreiben (oder "Fertig" wenn keine weitere Story)
|
|
135
|
+
|
|
136
|
+
### Story 3 (optional): `[prefix]03_[topic].md`
|
|
137
|
+
|
|
138
|
+
Only create if the developer's "Happy When" requires it (e.g. boss room, second area, transitions).
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## STEP 8 — Update Derived Decisions in Game Brief
|
|
143
|
+
|
|
144
|
+
Append to the `## Derived Decisions` section of `docs/aigdtk/game-brief.md`:
|
|
145
|
+
|
|
146
|
+
```markdown
|
|
147
|
+
## Derived Decisions
|
|
148
|
+
|
|
149
|
+
_Filled by /aigdtk-create-game-stories_
|
|
150
|
+
|
|
151
|
+
- **Area type:** [dungeon / voxel / both]
|
|
152
|
+
- **Color palette:** [mood name]
|
|
153
|
+
- **Enemy archetypes:** [list]
|
|
154
|
+
- **Rooms planned:** [count + types]
|
|
155
|
+
- **Story prefix:** [prefix]
|
|
156
|
+
- **Stories created:** [prefix]01, [prefix]02, ...
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## STEP 9 — Handover message
|
|
162
|
+
|
|
163
|
+
Output:
|
|
164
|
+
|
|
165
|
+
```
|
|
166
|
+
✅ [N] Stories erstellt unter docs/stories/
|
|
167
|
+
|
|
168
|
+
- [prefix]01_enemies-and-visuals.md
|
|
169
|
+
- [prefix]02_rooms-and-areas.md
|
|
170
|
+
[- prefix03_... falls erstellt]
|
|
171
|
+
|
|
172
|
+
Starte eine neue Claude Code Session und führe aus:
|
|
173
|
+
|
|
174
|
+
/aigdtk-implement-game-stories docs/stories/[prefix]01_enemies-and-visuals.md docs/stories/[prefix]02_rooms-and-areas.md
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
</activation>
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Story [N]: [Title]
|
|
2
|
+
|
|
3
|
+
**Feature**: [Feature Name]
|
|
4
|
+
**Type**: New Feature
|
|
5
|
+
**Estimated Effort**: 1 Session (~3-4h)
|
|
6
|
+
**Dependencies**: Story [N-1] or "None"
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Context & Background
|
|
11
|
+
|
|
12
|
+
### The Problem
|
|
13
|
+
[1-2 sentences: what is missing or broken]
|
|
14
|
+
|
|
15
|
+
### The Solution
|
|
16
|
+
[1-2 sentences: what this story builds]
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Acceptance Criteria
|
|
21
|
+
|
|
22
|
+
### ✅ Definition of Done
|
|
23
|
+
|
|
24
|
+
#### [Category, e.g. Enemies]
|
|
25
|
+
- [ ] [Concrete, verifiable criterion]
|
|
26
|
+
- [ ] [Concrete, verifiable criterion]
|
|
27
|
+
|
|
28
|
+
#### [Category, e.g. Visuals]
|
|
29
|
+
- [ ] [Concrete, verifiable criterion]
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Technical Specification
|
|
34
|
+
|
|
35
|
+
### [File: path/to/file.json]
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
// Complete file contents — no placeholders
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### [File: path/to/file.ts]
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
// Complete file contents — no placeholders
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Task List
|
|
50
|
+
|
|
51
|
+
- [ ] Write [file 1]
|
|
52
|
+
- [ ] Write [file 2]
|
|
53
|
+
- [ ] Run `pnpm test:changed` — all green
|
|
54
|
+
- [ ] **Lessons Learned ausfüllen**
|
|
55
|
+
- [ ] **Handover für Follow-up Story schreiben**
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Lessons Learned
|
|
60
|
+
|
|
61
|
+
<!-- FILL IN AFTER IMPLEMENTATION -->
|
|
62
|
+
|
|
63
|
+
### What went well
|
|
64
|
+
|
|
65
|
+
### Architecture decisions
|
|
66
|
+
|
|
67
|
+
### Important files created/changed
|
|
68
|
+
|
|
69
|
+
### Open items for follow-up
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Handover for Follow-up Story
|
|
74
|
+
|
|
75
|
+
<!-- FILL IN AFTER IMPLEMENTATION -->
|
|
76
|
+
|
|
77
|
+
### Context Summary
|
|
78
|
+
|
|
79
|
+
### Relevant files to read
|
|
80
|
+
|
|
81
|
+
### Start prompt for next story
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
**End of Story [N]**
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: aigdtk-implement-game-stories
|
|
3
|
+
description: Implements game stories generated by /aigdtk-create-game-stories. Pass story file paths as arguments.
|
|
4
|
+
disable-model-invocation: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Implement Game Stories
|
|
8
|
+
|
|
9
|
+
$ARGUMENTS
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
<activation CRITICAL="TRUE">
|
|
14
|
+
You are now in game implementation mode. Your role is **developer executing stories**.
|
|
15
|
+
Implement exactly ONE story per session. Stop after completing it.
|
|
16
|
+
|
|
17
|
+
## SETUP — Branch check
|
|
18
|
+
|
|
19
|
+
Check the current git branch. If on `master` or `main`:
|
|
20
|
+
- Create a new branch: `git checkout -b game/[game-name]-implementation`
|
|
21
|
+
- Tell the developer which branch was created
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## STEP 1 — Read all stories
|
|
26
|
+
|
|
27
|
+
Read ALL story files passed as arguments in `$ARGUMENTS` completely:
|
|
28
|
+
- Read every section including Lessons Learned and Handover (may contain prior context)
|
|
29
|
+
- Note which tasks are already checked off
|
|
30
|
+
- Identify the **first story with incomplete tasks** — that is the one to implement
|
|
31
|
+
|
|
32
|
+
If no story files are passed, look for story files in `docs/stories/` that match a game prefix
|
|
33
|
+
(non-`ng` and non-`cb` prefixes). If ambiguous, ask the developer which to implement.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## STEP 2 — Implement the story
|
|
38
|
+
|
|
39
|
+
Implement **only the identified story** — do not touch the next story.
|
|
40
|
+
|
|
41
|
+
During implementation:
|
|
42
|
+
- Check off each task in the story file as you complete it (`- [ ]` → `- [x]`)
|
|
43
|
+
- All game content files go into `apps/game-data/src/` — never anywhere else
|
|
44
|
+
- Do not modify framework code (`packages/`, `apps/client/`, `apps/server/`, `apps/protocol/`)
|
|
45
|
+
- Use the technical spec in the story as the source of truth for file contents
|
|
46
|
+
|
|
47
|
+
### Game content file locations
|
|
48
|
+
|
|
49
|
+
| Content | Path |
|
|
50
|
+
|---------|------|
|
|
51
|
+
| Enemy definitions | `apps/game-data/src/enemies/enemy-defs.json` |
|
|
52
|
+
| Room files | `apps/game-data/src/rooms/room_*.json` |
|
|
53
|
+
| Area manifest | `apps/game-data/src/areas/area-manifest.json` |
|
|
54
|
+
| Cell visuals | `apps/game-data/src/dungeon/cell-visuals.ts` |
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## STEP 3 — Run tests
|
|
59
|
+
|
|
60
|
+
Run: `pnpm test:changed`
|
|
61
|
+
|
|
62
|
+
- If all green: proceed to commit
|
|
63
|
+
- If tests fail: fix the issue first, then re-run. Do not commit with failing tests.
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## STEP 4 — Commit
|
|
68
|
+
|
|
69
|
+
Create a commit with:
|
|
70
|
+
```
|
|
71
|
+
feat([game-name]): implement [story name] — [short description]
|
|
72
|
+
|
|
73
|
+
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## STEP 5 — Fill in Lessons Learned and Handover
|
|
79
|
+
|
|
80
|
+
In the story file, fill in:
|
|
81
|
+
|
|
82
|
+
### Lessons Learned
|
|
83
|
+
- What went well
|
|
84
|
+
- Architecture decisions (with reasoning)
|
|
85
|
+
- Technical debt or workarounds
|
|
86
|
+
- Important files created/changed
|
|
87
|
+
|
|
88
|
+
### Handover for Follow-up Story
|
|
89
|
+
- Context summary (2–3 sentences)
|
|
90
|
+
- Relevant files to read
|
|
91
|
+
- Start prompt for next story (copy-paste ready `/aigdtk-implement-game-stories [next-path]`)
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## STEP 6 — Completion report
|
|
96
|
+
|
|
97
|
+
Output a compact report:
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
## Story abgeschlossen: [Story Name]
|
|
101
|
+
|
|
102
|
+
**Was wurde gebaut:**
|
|
103
|
+
- [Punkt 1]
|
|
104
|
+
- [Punkt 2]
|
|
105
|
+
- [Punkt 3]
|
|
106
|
+
|
|
107
|
+
**Geänderte Dateien:**
|
|
108
|
+
- [Datei 1]
|
|
109
|
+
- [Datei 2]
|
|
110
|
+
|
|
111
|
+
**Tests:** pnpm test:changed — ✅ alle grün
|
|
112
|
+
**Commit:** [commit hash / message]
|
|
113
|
+
|
|
114
|
+
**Manuell testen:**
|
|
115
|
+
pnpm dev → http://localhost:5173
|
|
116
|
+
[Was der Dev konkret prüfen soll]
|
|
117
|
+
|
|
118
|
+
**Nächste Story:**
|
|
119
|
+
/aigdtk-implement-game-stories [next-story-path]
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## STEP 7 — STOP
|
|
125
|
+
|
|
126
|
+
**Stop here.** Do NOT implement the next story automatically.
|
|
127
|
+
The developer decides when and whether to continue.
|
|
128
|
+
|
|
129
|
+
</activation>
|