create-spud 0.1.11 → 0.1.12

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.
Files changed (41) hide show
  1. package/{example → examples/minimal}/index.html +0 -2
  2. package/{example → examples/minimal}/package.json +4 -4
  3. package/examples/minimal/public/favicon.svg +1 -0
  4. package/{example → examples/minimal}/readme.md +3 -3
  5. package/examples/minimal/src/gameplay.ts +16 -0
  6. package/examples/pong/index.html +31 -0
  7. package/examples/pong/package.json +21 -0
  8. package/examples/pong/public/favicon.svg +1 -0
  9. package/examples/pong/readme.md +50 -0
  10. package/examples/pong/src/assets/audio/goal.wav +0 -0
  11. package/examples/pong/src/assets/audio/paddle-hit.wav +0 -0
  12. package/examples/pong/src/assets/audio/wall-hit.wav +0 -0
  13. package/examples/pong/src/audio.ts +20 -0
  14. package/examples/pong/src/camera.ts +50 -0
  15. package/examples/pong/src/canvas.ts +36 -0
  16. package/examples/pong/src/gameLoop.ts +46 -0
  17. package/examples/pong/src/gameplay.ts +165 -0
  18. package/examples/pong/src/main.ts +23 -0
  19. package/examples/pong/src/state.ts +38 -0
  20. package/examples/pong/tsconfig.json +37 -0
  21. package/examples/pong/vite.config.ts +15 -0
  22. package/index.js +153 -0
  23. package/package.json +10 -10
  24. package/readme.md +19 -18
  25. package/example/bun.lock +0 -212
  26. package/example/public/favicon.svg +0 -1
  27. package/example/public/poster.webp +0 -0
  28. package/example/src/assets/audio/menu-music.mp3 +0 -0
  29. package/example/src/assets/audio/shoot.wav +0 -0
  30. package/example/src/assets/images/sprite.png +0 -0
  31. package/example/src/audio.ts +0 -31
  32. package/example/src/gameplay.ts +0 -53
  33. package/example/src/sprite.ts +0 -53
  34. package/index.ts +0 -199
  35. /package/{example → examples/minimal}/src/canvas.ts +0 -0
  36. /package/{example → examples/minimal}/src/gameLoop.ts +0 -0
  37. /package/{example → examples/minimal}/src/main.ts +0 -0
  38. /package/{example → examples/minimal}/src/state.ts +0 -0
  39. /package/{example → examples/minimal}/tsconfig.json +0 -0
  40. /package/{example → examples/minimal}/vite.config.ts +0 -0
  41. /package/{example → examples/pong}/src/assets/fonts/atari.ttf +0 -0
@@ -1,53 +0,0 @@
1
- /*
2
- small sprites will be automatically inlined by Vite.
3
- sprite files larger than the default 4kb assetsInlineLimit will be
4
- optimized and fetched after the initial bundle has loaded.
5
- read more at https://vite.dev/guide/assets#importing-asset-as-url
6
-
7
- moon sprite credit: https://deep-fold.itch.io/pixel-planet-generator
8
- */
9
- import sprite from "./assets/images/sprite.png";
10
-
11
- type SpriteSheet = {
12
- image: HTMLImageElement;
13
- frameWidthPx: number;
14
- frameHeightPx: number;
15
- frameCount: number;
16
- };
17
-
18
- export const moonSheet: SpriteSheet = {
19
- image: new Image(),
20
- frameWidthPx: 50,
21
- frameHeightPx: 50,
22
- frameCount: 5000 / 50,
23
- };
24
-
25
- moonSheet.image.src = sprite;
26
-
27
- export function drawSprite(
28
- ctx: CanvasRenderingContext2D,
29
- sheet: SpriteSheet,
30
- frameIndex: number,
31
- x: number,
32
- y: number,
33
- scale = 1,
34
- ) {
35
- const frame = frameIndex % sheet.frameCount;
36
- const sourceX = frame * sheet.frameWidthPx;
37
- const sourceY = 0;
38
- const drawWidthPx = sheet.frameWidthPx * scale;
39
- const drawHeightPx = sheet.frameHeightPx * scale;
40
-
41
- // see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage
42
- ctx.drawImage(
43
- sheet.image,
44
- sourceX,
45
- sourceY,
46
- sheet.frameWidthPx,
47
- sheet.frameHeightPx,
48
- x,
49
- y,
50
- drawWidthPx,
51
- drawHeightPx,
52
- );
53
- }
package/index.ts DELETED
@@ -1,199 +0,0 @@
1
- #!/usr/bin/env bun
2
- import { mkdir, readdir } from "node:fs/promises";
3
- import path from "node:path";
4
- import { fileURLToPath } from "node:url";
5
- import { intro, note, cancel, text, confirm, tasks, group, multiselect } from "@clack/prompts";
6
- import kebabcase from "lodash.kebabcase";
7
-
8
- intro("It's spud time.");
9
-
10
- const Feature = {
11
- Audio: "Audio",
12
- Fonts: "Fonts",
13
- PixelArt: "PixelArt",
14
- } as const;
15
-
16
- type Feature = (typeof Feature)[keyof typeof Feature];
17
-
18
- const { rawGameName, features, shouldContinue } = await group(
19
- {
20
- rawGameName: () =>
21
- text({
22
- message: "Game name",
23
- placeholder: "e.g. Space Invaders",
24
- validate(value) {
25
- const trimmed = value?.trim();
26
- if (!trimmed) return "Game name is required. Esc to cancel.";
27
- if (!kebabcase(trimmed)) return "Game name must include letters or numbers.";
28
- },
29
- }),
30
- features: () =>
31
- multiselect({
32
- message: "Select features",
33
- initialValues: [],
34
- required: false,
35
- options: [
36
- { label: "Audio", value: Feature.Audio },
37
- { label: "Fonts", value: Feature.Fonts },
38
- { label: "Pixel art", value: Feature.PixelArt },
39
- ],
40
- }),
41
- shouldContinue: ({ results }) =>
42
- confirm({
43
- message: `Ready to write the files to "./${kebabcase((results.rawGameName ?? "").trim())}". Continue?`,
44
- }),
45
- },
46
- {
47
- onCancel() {
48
- cancel("Cancelled.");
49
- process.exit(0);
50
- },
51
- },
52
- );
53
-
54
- if (!shouldContinue) {
55
- cancel("Cancelled.");
56
- process.exit(0);
57
- }
58
-
59
- const gameName = rawGameName.trim();
60
- const slug = kebabcase(gameName);
61
- const targetDir = path.resolve(process.cwd(), slug);
62
- const templateDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "example");
63
-
64
- await tasks([
65
- {
66
- title: `Scaffolding project in ./${slug}/`,
67
- async task() {
68
- await mkdir(targetDir, { recursive: true });
69
- await copyDir(templateDir, targetDir, {
70
- gameName,
71
- slug,
72
- features,
73
- });
74
- return "Initialized project from spud starter template";
75
- },
76
- },
77
- {
78
- title: "Installing dependencies with Bun",
79
- async task() {
80
- const proc = Bun.spawn(["bun", "install"], { cwd: targetDir });
81
- const exitCode = await proc.exited;
82
- if (exitCode !== 0) {
83
- throw new Error(`bun install failed with exit code ${exitCode}`);
84
- }
85
- return "Installed via bun";
86
- },
87
- },
88
- ]);
89
-
90
- note(`cd ${slug}\nbun dev`, "Next steps");
91
-
92
- async function copyDir(
93
- sourceDir: string,
94
- destDir: string,
95
- replacements: { gameName: string; slug: string; features: Feature[] },
96
- ) {
97
- await mkdir(destDir, { recursive: true });
98
- const entries = await readdir(sourceDir, { withFileTypes: true });
99
-
100
- for (const entry of entries) {
101
- const sourcePath = path.join(sourceDir, entry.name);
102
- const outputName = entry.name === "_gitignore" ? ".gitignore" : entry.name;
103
- const destPath = path.join(destDir, outputName);
104
-
105
- if (entry.isDirectory()) {
106
- if (entry.name === "node_modules" || entry.name === "dist") continue;
107
- if (entry.name === "audio" && !replacements.features.includes(Feature.Audio)) continue;
108
- if (entry.name === "fonts" && !replacements.features.includes(Feature.Fonts)) continue;
109
- if (entry.name === "images" && !replacements.features.includes(Feature.PixelArt)) continue;
110
- await copyDir(sourcePath, destPath, replacements);
111
- continue;
112
- }
113
-
114
- if (!entry.isFile()) continue;
115
- if (entry.name === "audio.ts" && !replacements.features.includes(Feature.Audio)) continue;
116
- if (entry.name === "sprite.ts" && !replacements.features.includes(Feature.PixelArt)) continue;
117
-
118
- const ext = path.extname(entry.name);
119
- const extensionsThatMayContainTemplateVariables = [".html", ".json", ".md", ".ts"];
120
-
121
- if (!extensionsThatMayContainTemplateVariables.includes(ext)) {
122
- const file = Bun.file(sourcePath);
123
- await Bun.write(destPath, file);
124
- continue;
125
- }
126
-
127
- const text = await Bun.file(sourcePath).text();
128
- const replaced = text
129
- .replaceAll("game_name", replacements.gameName)
130
- .replaceAll("game_slug", replacements.slug)
131
- .replaceAll(
132
- " /* pixel_art_canvas_css */\n",
133
- replacements.features.includes(Feature.PixelArt)
134
- ? " image-rendering: pixelated;\n"
135
- : "",
136
- )
137
- .replaceAll(
138
- "/* pixel_art_canvas_css */\n",
139
- replacements.features.includes(Feature.PixelArt) ? "image-rendering: pixelated;\n" : "",
140
- )
141
- .replaceAll(
142
- " /* pixel_art_image_smoothing */\n",
143
- replacements.features.includes(Feature.PixelArt)
144
- ? " // ensure sharp edges for scaled pixel art\n ctx.imageSmoothingEnabled = false;\n"
145
- : "",
146
- )
147
- .replaceAll(
148
- "/* moon_sprite_import */\n",
149
- replacements.features.includes(Feature.PixelArt)
150
- ? 'import { drawSprite, moonSheet } from "./sprite";\n'
151
- : "",
152
- )
153
- .replaceAll(
154
- "/* audio_import */\n",
155
- replacements.features.includes(Feature.Audio) ? 'import { sfx } from "./audio";\n' : "",
156
- )
157
- .replaceAll(
158
- " /* fonts_face_css */\n",
159
- replacements.features.includes(Feature.Fonts)
160
- ? ` @font-face {
161
- font-family: 'Atari';
162
- font-display: block;
163
- src: url('/src/assets/fonts/atari.ttf') format('truetype');
164
- }
165
-
166
- `
167
- : "",
168
- )
169
- .replaceAll(
170
- " /* moon_sprite_draw */\n",
171
- replacements.features.includes(Feature.PixelArt)
172
- ? ` const moonFrameIndex = Math.floor(state.elapsedSeconds * 12);
173
- drawSprite(ctx, moonSheet, moonFrameIndex, moon.x - moon.radius, moon.y - moon.radius);
174
- `
175
- : ` ctx.beginPath();
176
- ctx.arc(moon.x, moon.y, moon.radius, 0, Math.PI * 2);
177
- ctx.fill();
178
- `,
179
- )
180
- .replaceAll(
181
- " /* audio_shoot_sfx */\n",
182
- replacements.features.includes(Feature.Audio)
183
- ? ` sfx("shoot").play({
184
- detune: -1000 + Math.random() * 2000,
185
- playbackRate: 0.5 + Math.random(),
186
- });
187
- `
188
- : " /* handle inputs and update state */\n",
189
- )
190
- .replaceAll(
191
- "/* fonts_ctx_font */",
192
- replacements.features.includes(Feature.Fonts)
193
- ? 'ctx.font = "48px Atari";'
194
- : 'ctx.font = "48px sans-serif";',
195
- );
196
-
197
- await Bun.write(destPath, replaced);
198
- }
199
- }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes