nova64 0.2.5 → 0.2.7

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 (185) hide show
  1. package/README.md +25 -8
  2. package/bin/nova64.js +165 -0
  3. package/dist/assets/console-CY_kygm3.js +14 -0
  4. package/dist/assets/console-CY_kygm3.js.map +1 -0
  5. package/dist/assets/main-l0sNRNKZ.js.map +1 -0
  6. package/dist/assets/sky/studio/nx.png +0 -0
  7. package/dist/assets/sky/studio/ny.png +0 -0
  8. package/dist/assets/sky/studio/nz.png +0 -0
  9. package/dist/assets/sky/studio/px.png +0 -0
  10. package/dist/assets/sky/studio/py.png +0 -0
  11. package/dist/assets/sky/studio/pz.png +0 -0
  12. package/dist/assets/vanilla-Dcuy32gi.js +2 -0
  13. package/dist/assets/vanilla-Dcuy32gi.js.map +1 -0
  14. package/dist/console.html +899 -0
  15. package/dist/docs/BENCHMARK.md +77 -0
  16. package/dist/docs/CHEATSHEET.md +255 -0
  17. package/dist/docs/EFFECTS_API_GUIDE.md +577 -0
  18. package/dist/docs/EFFECTS_QUICK_REFERENCE.md +331 -0
  19. package/dist/docs/FONT_CHARACTER_REFERENCE.md +219 -0
  20. package/dist/docs/FREE_GLB_ASSETS.md +330 -0
  21. package/dist/docs/FULLSCREEN_BUTTON_FEATURE.md +296 -0
  22. package/dist/docs/GAMEPAD_SUPPORT.md +348 -0
  23. package/dist/docs/GAME_IMPROVEMENTS.md +278 -0
  24. package/dist/docs/GAME_QUALITY_STATUS.md +300 -0
  25. package/dist/docs/MIGRATION_GUIDE.md +553 -0
  26. package/dist/docs/NOVA64_3D_API.md +356 -0
  27. package/dist/docs/NOVA64_API_REFERENCE.md +1406 -0
  28. package/dist/docs/NOVA64_UI_API.md +503 -0
  29. package/dist/docs/UI_SYSTEM_SUMMARY.md +445 -0
  30. package/dist/docs/VOXEL_ENGINE_GUIDE.md +662 -0
  31. package/dist/docs/VOXEL_QUICK_REFERENCE.md +386 -0
  32. package/dist/docs/api-3d.html +750 -0
  33. package/dist/docs/api-effects.html +385 -0
  34. package/dist/docs/api-improvements.md +121 -0
  35. package/dist/docs/api-skybox.html +407 -0
  36. package/dist/docs/api-sprites.html +321 -0
  37. package/dist/docs/api-voxel.html +337 -0
  38. package/dist/docs/api.html +543 -0
  39. package/dist/docs/assets.html +306 -0
  40. package/dist/docs/audio.html +340 -0
  41. package/dist/docs/blogs.html +286 -0
  42. package/dist/docs/collision.html +316 -0
  43. package/dist/docs/console.html +247 -0
  44. package/dist/docs/editor.html +297 -0
  45. package/dist/docs/font.html +247 -0
  46. package/dist/docs/framebuffer.html +247 -0
  47. package/dist/docs/fullscreen-button.html +297 -0
  48. package/dist/docs/gpu-systems.html +247 -0
  49. package/dist/docs/index.html +580 -0
  50. package/dist/docs/input.html +491 -0
  51. package/dist/docs/physics.html +311 -0
  52. package/dist/docs/screens.html +311 -0
  53. package/dist/docs/storage.html +311 -0
  54. package/dist/docs/textinput.html +332 -0
  55. package/dist/docs/ui.html +488 -0
  56. package/dist/examples/3d-advanced/code.js +695 -0
  57. package/dist/examples/adventure-comic-3d/code.js +342 -0
  58. package/dist/examples/audio-lab/code.js +150 -0
  59. package/dist/examples/boids-flocking/code.js +270 -0
  60. package/dist/examples/crystal-cathedral-3d/code.js +706 -0
  61. package/dist/examples/cyberpunk-city-3d/code.js +1383 -0
  62. package/dist/examples/demoscene/README.md +192 -0
  63. package/dist/examples/demoscene/code.js +1081 -0
  64. package/dist/examples/demoscene/meta.json +21 -0
  65. package/dist/examples/dungeon-crawler-3d/code.js +1117 -0
  66. package/dist/examples/f-zero-nova-3d/code.js +865 -0
  67. package/dist/examples/f-zero-nova-3d/code_old.js +1555 -0
  68. package/dist/examples/fps-demo-3d/code.js +744 -0
  69. package/dist/examples/game-of-life-3d/code.js +338 -0
  70. package/dist/examples/generative-art/code.js +632 -0
  71. package/dist/examples/hello-3d/code.js +325 -0
  72. package/dist/examples/hello-skybox/code.js +183 -0
  73. package/dist/examples/hello-world/code.js +19 -0
  74. package/dist/examples/input-showcase/code.js +109 -0
  75. package/dist/examples/instancing-demo/code.js +315 -0
  76. package/dist/examples/minecraft-demo/code.js +387 -0
  77. package/dist/examples/model-viewer-3d/code.js +114 -0
  78. package/dist/examples/mystical-realm-3d/code.js +1203 -0
  79. package/dist/examples/nature-explorer-3d/code.js +1318 -0
  80. package/dist/examples/particles-demo/code.js +522 -0
  81. package/dist/examples/pbr-showcase/code.js +140 -0
  82. package/dist/examples/physics-demo-3d/code.js +948 -0
  83. package/dist/examples/screen-demo/code.js +267 -0
  84. package/dist/examples/shooter-demo-3d/code.js +1286 -0
  85. package/dist/examples/space-combat-3d/IMPLEMENTATION_SUMMARY.md +109 -0
  86. package/dist/examples/space-combat-3d/README.md +135 -0
  87. package/dist/examples/space-combat-3d/code.js +1332 -0
  88. package/dist/examples/space-harrier-3d/code.js +923 -0
  89. package/dist/examples/star-fox-nova-3d/code.js +1116 -0
  90. package/dist/examples/star-fox-nova-3d/code_backup.js +410 -0
  91. package/dist/examples/star-fox-nova-3d/code_broken.js +1821 -0
  92. package/dist/examples/storage-quest/code.js +209 -0
  93. package/dist/examples/strider-demo-3d/IMPROVEMENT_OPTIONS.md +285 -0
  94. package/dist/examples/strider-demo-3d/cache-test.html +132 -0
  95. package/dist/examples/strider-demo-3d/code-fixed.js +582 -0
  96. package/dist/examples/strider-demo-3d/code-old.js +1537 -0
  97. package/dist/examples/strider-demo-3d/code.js +1462 -0
  98. package/dist/examples/strider-demo-3d/code.js.bak2 +1169 -0
  99. package/dist/examples/strider-demo-3d/fix-game.sh +53 -0
  100. package/dist/examples/super-plumber-64/README.md +128 -0
  101. package/dist/examples/super-plumber-64/code.js +1185 -0
  102. package/dist/examples/super-plumber-64/index.html +88 -0
  103. package/dist/examples/test-2d-overlay/code.js +32 -0
  104. package/dist/examples/test-font/code.js +51 -0
  105. package/dist/examples/test-minimal/code.js +21 -0
  106. package/dist/examples/ui-demo/code.js +306 -0
  107. package/dist/examples/wing-commander-space/README.md +180 -0
  108. package/dist/examples/wing-commander-space/code.js +1285 -0
  109. package/dist/examples/wizardry-3d/CHANGELOG.md +366 -0
  110. package/dist/examples/wizardry-3d/code.js +3928 -0
  111. package/dist/index.html +666 -0
  112. package/dist/os9-shell/assets/index-DIHfrTaW.css +1 -0
  113. package/dist/os9-shell/assets/index-KchE_ngx.js +483 -0
  114. package/dist/os9-shell/assets/index-KchE_ngx.js.map +1 -0
  115. package/dist/os9-shell/index.html +23 -0
  116. package/dist/os9-shell/nova-icon.svg +12 -0
  117. package/dist/runtime/api-2d.js +1158 -0
  118. package/dist/runtime/api-3d/camera.js +73 -0
  119. package/dist/runtime/api-3d/instancing.js +180 -0
  120. package/dist/runtime/api-3d/lights.js +51 -0
  121. package/dist/runtime/api-3d/materials.js +47 -0
  122. package/dist/runtime/api-3d/models.js +84 -0
  123. package/dist/runtime/api-3d/particles.js +296 -0
  124. package/dist/runtime/api-3d/pbr.js +113 -0
  125. package/dist/runtime/api-3d/primitives.js +304 -0
  126. package/dist/runtime/api-3d/scene.js +169 -0
  127. package/dist/runtime/api-3d/transforms.js +161 -0
  128. package/dist/runtime/api-3d.js +166 -0
  129. package/dist/runtime/api-effects.js +840 -0
  130. package/dist/runtime/api-gameutils.js +476 -0
  131. package/dist/runtime/api-generative.js +610 -0
  132. package/dist/runtime/api-presets.js +85 -0
  133. package/dist/runtime/api-skybox.js +232 -0
  134. package/dist/runtime/api-sprites.js +100 -0
  135. package/dist/runtime/api-voxel.js +712 -0
  136. package/dist/runtime/api.js +201 -0
  137. package/dist/runtime/assets.js +27 -0
  138. package/dist/runtime/audio.js +114 -0
  139. package/dist/runtime/collision.js +47 -0
  140. package/dist/runtime/console.js +101 -0
  141. package/dist/runtime/editor.js +233 -0
  142. package/dist/runtime/font.js +233 -0
  143. package/dist/runtime/framebuffer.js +28 -0
  144. package/dist/runtime/fullscreen-button.js +185 -0
  145. package/dist/runtime/gpu-canvas2d.js +47 -0
  146. package/dist/runtime/gpu-threejs.js +643 -0
  147. package/dist/runtime/gpu-webgl2.js +310 -0
  148. package/dist/runtime/index.d.ts +682 -0
  149. package/dist/runtime/index.js +22 -0
  150. package/dist/runtime/input.js +225 -0
  151. package/dist/runtime/logger.js +60 -0
  152. package/dist/runtime/physics.js +101 -0
  153. package/dist/runtime/screens.js +213 -0
  154. package/dist/runtime/storage.js +38 -0
  155. package/dist/runtime/store.js +151 -0
  156. package/dist/runtime/textinput.js +68 -0
  157. package/dist/runtime/ui/buttons.js +124 -0
  158. package/dist/runtime/ui/panels.js +105 -0
  159. package/dist/runtime/ui/text.js +86 -0
  160. package/dist/runtime/ui/widgets.js +141 -0
  161. package/dist/runtime/ui.js +111 -0
  162. package/index.html +6 -1
  163. package/package.json +9 -2
  164. package/public/assets/sky/studio/nx.png +0 -0
  165. package/public/assets/sky/studio/ny.png +0 -0
  166. package/public/assets/sky/studio/nz.png +0 -0
  167. package/public/assets/sky/studio/px.png +0 -0
  168. package/public/assets/sky/studio/py.png +0 -0
  169. package/public/assets/sky/studio/pz.png +0 -0
  170. package/public/os9-shell/assets/index-KchE_ngx.js +483 -0
  171. package/public/os9-shell/assets/index-KchE_ngx.js.map +1 -0
  172. package/public/os9-shell/index.html +10 -1
  173. package/runtime/api-2d.js +301 -21
  174. package/runtime/api-3d/pbr.js +45 -1
  175. package/runtime/api-3d.js +1 -0
  176. package/runtime/api-effects.js +90 -3
  177. package/runtime/api-gameutils.js +476 -0
  178. package/runtime/api-generative.js +610 -0
  179. package/runtime/api-skybox.js +54 -0
  180. package/runtime/api-voxel.js +139 -28
  181. package/runtime/gpu-threejs.js +13 -9
  182. package/runtime/ui.js +2 -2
  183. package/src/main.js +20 -0
  184. package/public/os9-shell/assets/index-B1Uvacma.js +0 -32825
  185. package/public/os9-shell/assets/index-B1Uvacma.js.map +0 -1
@@ -0,0 +1,325 @@
1
+ // HELLO 3D WORLD - Simple Nintendo 64/PlayStation style 3D demo
2
+ // Demonstrates basic 3D rendering with GPU acceleration
3
+
4
+ let cubes = [];
5
+ let spheres = [];
6
+ let time = 0;
7
+ let cameraAngle = 0;
8
+ let initialized = false; // Prevents update() from running during scene transitions
9
+
10
+ // Screen management
11
+ let gameState = 'start'; // 'start', 'playing'
12
+ let startScreenTime = 0;
13
+ let uiButtons = [];
14
+
15
+ export async function init() {
16
+ // Clear arrays immediately to prevent update() from accessing deleted meshes
17
+ initialized = false; // Mark as not ready during initialization
18
+ cubes = [];
19
+ spheres = [];
20
+
21
+ cls();
22
+
23
+ // Setup 3D scene
24
+ setCameraPosition(0, 5, 15);
25
+ setCameraTarget(0, 0, 0);
26
+ setCameraFOV(60);
27
+
28
+ // Nintendo 64 style lighting
29
+ setLightDirection(-0.5, -1, -0.3);
30
+ setFog(0x202040, 20, 100);
31
+
32
+ // Enable retro effects
33
+ enablePixelation(1);
34
+ enableDithering(true);
35
+
36
+ // Create some basic 3D objects
37
+ createScene();
38
+
39
+ // Initialize start screen
40
+ initStartScreen();
41
+
42
+ initialized = true; // Now safe to update
43
+ console.log('🎮 Hello 3D World - Nintendo 64/PlayStation style demo loaded!');
44
+ }
45
+
46
+ function initStartScreen() {
47
+ uiButtons = [];
48
+
49
+ // Start button - positioned higher for easier clicking
50
+ uiButtons.push(
51
+ createButton(
52
+ centerX(220),
53
+ 150,
54
+ 220,
55
+ 55,
56
+ '▶ START DEMO',
57
+ () => {
58
+ console.log('🎯 START BUTTON CLICKED! Changing gameState to playing...');
59
+ gameState = 'playing';
60
+ console.log('✅ gameState is now:', gameState);
61
+ },
62
+ {
63
+ normalColor: rgba8(50, 180, 255, 255),
64
+ hoverColor: rgba8(80, 200, 255, 255),
65
+ pressedColor: rgba8(20, 140, 220, 255),
66
+ }
67
+ )
68
+ );
69
+
70
+ // Info button
71
+ uiButtons.push(
72
+ createButton(
73
+ centerX(200),
74
+ 350,
75
+ 200,
76
+ 45,
77
+ 'ℹ INFO',
78
+ () => {
79
+ console.log('Hello 3D World - Basic 3D demo with GPU acceleration');
80
+ },
81
+ {
82
+ normalColor: rgba8(100, 100, 255, 255),
83
+ hoverColor: rgba8(130, 130, 255, 255),
84
+ pressedColor: rgba8(70, 70, 220, 255),
85
+ }
86
+ )
87
+ );
88
+ }
89
+
90
+ function createScene() {
91
+ // Create stunning holographic spinning cubes with advanced materials
92
+ const colors = [0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff, 0x00ffff];
93
+ const emissiveColors = [0x330000, 0x003300, 0x000033, 0x333300, 0x330033, 0x003333];
94
+
95
+ for (let i = 0; i < 6; i++) {
96
+ const angle = (i / 6) * Math.PI * 2;
97
+ const x = Math.cos(angle) * 8;
98
+ const z = Math.sin(angle) * 8;
99
+
100
+ // Create cube with enhanced visual effects
101
+ const cube = createCube(2, colors[i], [x, 0, z]);
102
+
103
+ cubes.push({
104
+ mesh: cube,
105
+ x: x,
106
+ y: 0,
107
+ z: z,
108
+ rotationSpeed: 1 + Math.random() * 2,
109
+ bobPhase: Math.random() * Math.PI * 2,
110
+ glowPhase: Math.random() * Math.PI * 2,
111
+ });
112
+ }
113
+
114
+ // Create magical glowing spheres
115
+ for (let i = 0; i < 4; i++) {
116
+ const sphere = createSphere(1, 0x88ddff, [
117
+ (Math.random() - 0.5) * 12,
118
+ 2 + Math.random() * 4,
119
+ (Math.random() - 0.5) * 12,
120
+ ]);
121
+
122
+ spheres.push({
123
+ mesh: sphere,
124
+ x: (Math.random() - 0.5) * 12,
125
+ y: 2 + Math.random() * 4,
126
+ z: (Math.random() - 0.5) * 12,
127
+ vx: (Math.random() - 0.5) * 4,
128
+ vy: Math.random() * 2,
129
+ vz: (Math.random() - 0.5) * 4,
130
+ trail: [],
131
+ });
132
+ }
133
+
134
+ // Create stunning ground plane
135
+ const ground = createPlane(40, 40, 0x444466, [0, -2, 0]);
136
+ setRotation(ground, -Math.PI / 2, 0, 0);
137
+ }
138
+
139
+ export function update(dt) {
140
+ // Safety check: Don't update if not initialized OR if arrays are empty (prevents errors during scene transitions)
141
+ // When scene is cleared, arrays are emptied on next init() call
142
+ if (!initialized || cubes.length === 0) {
143
+ return;
144
+ }
145
+
146
+ time += dt;
147
+
148
+ if (gameState === 'start') {
149
+ startScreenTime += dt;
150
+ updateAllButtons();
151
+
152
+ // KEYBOARD FALLBACK: Press ENTER or SPACE to start
153
+ if (isKeyPressed('Enter') || isKeyPressed(' ') || isKeyPressed('Space')) {
154
+ console.log('🎮 Keyboard start pressed!');
155
+ gameState = 'playing';
156
+ return;
157
+ }
158
+
159
+ // Still animate 3D scene in background
160
+ cubes.forEach(cube => {
161
+ cube.bobPhase += dt * 2;
162
+ const bobY = Math.sin(cube.bobPhase) * 2;
163
+ setPosition(cube.mesh, cube.x, bobY, cube.z);
164
+ rotateMesh(cube.mesh, dt * cube.rotationSpeed, dt * cube.rotationSpeed * 0.7, 0);
165
+ });
166
+
167
+ cameraAngle += dt * 0.2;
168
+ const camX = Math.cos(cameraAngle) * 15;
169
+ const camZ = Math.sin(cameraAngle) * 15;
170
+ setCameraPosition(camX, 8, camZ);
171
+ setCameraTarget(0, 0, 0);
172
+ return;
173
+ }
174
+
175
+ // Playing state
176
+ // Rotate cubes
177
+ cubes.forEach(cube => {
178
+ cube.bobPhase += dt * 2;
179
+ const bobY = Math.sin(cube.bobPhase) * 2;
180
+
181
+ setPosition(cube.mesh, cube.x, bobY, cube.z);
182
+ rotateMesh(cube.mesh, dt * cube.rotationSpeed, dt * cube.rotationSpeed * 0.7, 0);
183
+ });
184
+
185
+ // Bounce spheres
186
+ spheres.forEach(sphere => {
187
+ sphere.x += sphere.vx * dt;
188
+ sphere.y += sphere.vy * dt;
189
+ sphere.z += sphere.vz * dt;
190
+
191
+ // Simple physics
192
+ sphere.vy -= 9.8 * dt; // gravity
193
+
194
+ // Ground bounce
195
+ if (sphere.y < 1) {
196
+ sphere.y = 1;
197
+ sphere.vy *= -0.8;
198
+ }
199
+
200
+ // Wall bounds
201
+ if (Math.abs(sphere.x) > 10) {
202
+ sphere.vx *= -1;
203
+ sphere.x = Math.sign(sphere.x) * 10;
204
+ }
205
+ if (Math.abs(sphere.z) > 10) {
206
+ sphere.vz *= -1;
207
+ sphere.z = Math.sign(sphere.z) * 10;
208
+ }
209
+
210
+ setPosition(sphere.mesh, sphere.x, sphere.y, sphere.z);
211
+ });
212
+
213
+ // Rotate camera
214
+ cameraAngle += dt * 0.3;
215
+ const camX = Math.cos(cameraAngle) * 15;
216
+ const camZ = Math.sin(cameraAngle) * 15;
217
+
218
+ setCameraPosition(camX, 8, camZ);
219
+ setCameraTarget(0, 0, 0);
220
+ }
221
+
222
+ export function draw() {
223
+ if (gameState === 'start') {
224
+ drawStartScreen();
225
+ return;
226
+ }
227
+
228
+ // Playing - Simple HUD
229
+ print('🎮 HELLO 3D WORLD', 8, 8, rgba8(0, 255, 255, 255));
230
+ print('Nintendo 64 / PlayStation Style', 8, 24, rgba8(255, 200, 0, 255));
231
+ print(`Time: ${time.toFixed(1)}s`, 8, 40, rgba8(255, 255, 255, 255));
232
+ print(`Objects: ${cubes.length + spheres.length + 1}`, 8, 56, rgba8(100, 255, 100, 255));
233
+
234
+ // 3D Stats
235
+ const stats = get3DStats();
236
+ if (stats && stats.render) {
237
+ print(`3D Meshes: ${stats.meshes || 0}`, 8, 72, rgba8(150, 150, 255, 255));
238
+ print(`GPU: ${stats.renderer || 'Three.js'}`, 8, 88, rgba8(150, 150, 255, 255));
239
+ }
240
+
241
+ print('WASD: Move camera manually', 8, 320, rgba8(200, 200, 200, 200));
242
+ print('Full GPU 3D acceleration with Three.js!', 8, 340, rgba8(100, 255, 100, 180));
243
+ }
244
+
245
+ function drawStartScreen() {
246
+ // Bright gradient background
247
+ drawGradientRect(0, 0, 640, 360, rgba8(30, 60, 120, 230), rgba8(10, 30, 80, 240), true);
248
+
249
+ // Animated title with rainbow glow
250
+ const rainbow = [
251
+ rgba8(255, 0, 0, 255),
252
+ rgba8(255, 127, 0, 255),
253
+ rgba8(255, 255, 0, 255),
254
+ rgba8(0, 255, 0, 255),
255
+ rgba8(0, 0, 255, 255),
256
+ rgba8(148, 0, 211, 255),
257
+ ];
258
+ const colorIndex = Math.floor(startScreenTime * 2) % rainbow.length;
259
+
260
+ setFont('huge');
261
+ setTextAlign('center');
262
+ const bounce = Math.sin(startScreenTime * 3) * 8;
263
+ drawTextShadow('HELLO', 320, 60 + bounce, rainbow[colorIndex], rgba8(0, 0, 0, 255), 5, 1);
264
+ drawTextShadow('3D WORLD', 320, 110 + bounce, rgba8(0, 255, 255, 255), rgba8(0, 0, 0, 255), 5, 1);
265
+
266
+ // Subtitle
267
+ setFont('large');
268
+ const pulse = Math.sin(startScreenTime * 4) * 0.3 + 0.7;
269
+ drawTextOutline(
270
+ 'Nintendo 64 / PlayStation Style',
271
+ 320,
272
+ 160,
273
+ rgba8(255, 200, 0, Math.floor(pulse * 255)),
274
+ rgba8(0, 0, 0, 255),
275
+ 1
276
+ );
277
+
278
+ // Info panel
279
+ const panel = createPanel(centerX(450), 200, 450, 180, {
280
+ bgColor: rgba8(20, 40, 80, 200),
281
+ borderColor: rgba8(50, 180, 255, 255),
282
+ borderWidth: 3,
283
+ shadow: true,
284
+ gradient: true,
285
+ gradientColor: rgba8(30, 50, 100, 200),
286
+ });
287
+ drawPanel(panel);
288
+
289
+ setFont('normal');
290
+ setTextAlign('center');
291
+ drawText('BASIC 3D RENDERING DEMO', 320, 220, rgba8(0, 255, 255, 255), 1);
292
+
293
+ setFont('small');
294
+ drawText('Experience GPU-accelerated 3D graphics with Three.js', 320, 245, uiColors.light, 1);
295
+ drawText('Watch spinning cubes and bouncing spheres', 320, 260, uiColors.light, 1);
296
+ drawText('Full retro Nintendo 64 visual effects enabled', 320, 275, uiColors.light, 1);
297
+
298
+ setFont('tiny');
299
+ drawText('Camera rotates automatically around the scene', 320, 300, uiColors.secondary, 1);
300
+
301
+ // Draw buttons
302
+ drawAllButtons();
303
+
304
+ // DEBUG: Show mouse position and button bounds
305
+ const mx = mouseX();
306
+ const my = mouseY();
307
+ if (mx >= 0 && my >= 0) {
308
+ setFont('tiny');
309
+ setTextAlign('left');
310
+ drawText(`Mouse: ${mx}, ${my}`, 10, 10, rgba8(255, 255, 0, 255), 1);
311
+ // Draw crosshair at mouse position
312
+ line(mx - 10, my, mx + 10, my, rgba8(0, 255, 0, 255));
313
+ line(mx, my - 10, mx, my + 10, rgba8(0, 255, 0, 255));
314
+ }
315
+
316
+ // Pulsing start prompt
317
+ const alpha = Math.floor((Math.sin(startScreenTime * 6) * 0.5 + 0.5) * 255);
318
+ setFont('normal');
319
+ setTextAlign('center');
320
+ drawText('▶ PRESS START DEMO ◀', 320, 425, rgba8(50, 200, 255, alpha), 1);
321
+
322
+ // Nova64 branding
323
+ setFont('tiny');
324
+ drawText('Nova64 v0.2.0 - Fantasy Console', 320, 345, rgba8(150, 150, 200, 150), 1);
325
+ }
@@ -0,0 +1,183 @@
1
+ // 🌌 NOVA DRIFT — Deep Space Crystal Hunter
2
+ // Free-fly through an asteroid field, collect all 15 energy crystals
3
+ //
4
+ // Controls: W / SPACE = thrust S = brake
5
+ // A / D = yaw Q / E = pitch
6
+
7
+ let time = 0;
8
+ let pos = { x: 0, y: 0, z: 10 };
9
+ let vel = { x: 0, y: 0, z: 0 };
10
+ let yaw = 0; // horizontal bearing, degrees
11
+ let pitch = 0; // vertical bearing, degrees
12
+ let score = 0;
13
+ let collected = 0;
14
+
15
+ const TOTAL = 15;
16
+ const THRUST = 14;
17
+ const TURN_SPD = 55;
18
+ const PITCH_SPD = 40;
19
+ const DRAG = 0.88;
20
+
21
+ let asteroids = [];
22
+ let crystals = [];
23
+
24
+ // ── Init ─────────────────────────────────────────────────────────────────────
25
+ export async function init() {
26
+ // Deep-space skybox
27
+ createSpaceSkybox({
28
+ starCount: 3000,
29
+ starSize: 2.2,
30
+ nebulae: true,
31
+ nebulaColor: 0x0a0033,
32
+ });
33
+ setFog(0x000511, 100, 500);
34
+
35
+ // Cinematic lighting
36
+ setAmbientLight(0x202040, 1.0);
37
+ setLightDirection(-0.4, -1, -0.3);
38
+ setLightColor(0xaaaaff);
39
+
40
+ // Post-processing
41
+ enableBloom(1.0, 0.5, 0.5);
42
+ enableFXAA();
43
+ enableVignette(1.0, 0.85);
44
+
45
+ // ── Asteroid field ──────────────────────────────────────────────────────
46
+ for (let i = 0; i < 45; i++) {
47
+ const angle = Math.random() * Math.PI * 2;
48
+ const dist = 30 + Math.random() * 200;
49
+ const x = Math.cos(angle) * dist + (Math.random() - 0.5) * 60;
50
+ const y = (Math.random() - 0.5) * 80;
51
+ const z = Math.sin(angle) * dist + (Math.random() - 0.5) * 60;
52
+ const r = 2 + Math.random() * 9;
53
+ const mesh = createSphere(r, 0x554433, [x, y, z], 7, { material: 'standard', roughness: 0.95 });
54
+ asteroids.push({
55
+ mesh,
56
+ x,
57
+ y,
58
+ z,
59
+ rx: (Math.random() - 0.5) * 0.3,
60
+ ry: (Math.random() - 0.5) * 0.3,
61
+ rz: (Math.random() - 0.5) * 0.15,
62
+ });
63
+ }
64
+
65
+ // ── Distant planets ─────────────────────────────────────────────────────
66
+ const planetDefs = [
67
+ { x: 250, y: -40, z: -300, r: 50, color: 0x3355aa },
68
+ { x: -400, y: 30, z: -150, r: 70, color: 0xaa3333 },
69
+ { x: 80, y: 60, z: 320, r: 35, color: 0x228855 },
70
+ ];
71
+ for (const p of planetDefs) {
72
+ createSphere(p.r, p.color, [p.x, p.y, p.z], 20, { material: 'standard', roughness: 0.7 });
73
+ }
74
+
75
+ // ── Energy crystals ──────────────────────────────────────────────────────
76
+ const palette = [0x00ffcc, 0xff44ff, 0xffdd00, 0x44aaff];
77
+ for (let i = 0; i < TOTAL; i++) {
78
+ const angle = (i / TOTAL) * Math.PI * 2 + (Math.random() - 0.5) * 0.8;
79
+ const dist = 40 + Math.random() * 120;
80
+ const x = Math.cos(angle) * dist;
81
+ const y = (Math.random() - 0.5) * 50;
82
+ const z = Math.sin(angle) * dist;
83
+ const color = palette[i % palette.length];
84
+ const mesh = createCube(2.5, color, [x, y, z], { material: 'emissive', emissive: color });
85
+ crystals.push({ mesh, x, y, z, active: true });
86
+ }
87
+
88
+ setCameraFOV(75);
89
+ setCameraPosition(pos.x, pos.y, pos.z);
90
+ setCameraTarget(0, 0, 0);
91
+ }
92
+
93
+ // ── Update ────────────────────────────────────────────────────────────────────
94
+ export function update(dt) {
95
+ time += dt;
96
+
97
+ // Yaw
98
+ if (key('KeyA') || key('ArrowLeft')) yaw += TURN_SPD * dt;
99
+ if (key('KeyD') || key('ArrowRight')) yaw -= TURN_SPD * dt;
100
+
101
+ // Pitch (clamp to ±75°)
102
+ if (key('KeyQ') || key('ArrowUp')) pitch = Math.min(pitch + PITCH_SPD * dt, 75);
103
+ if (key('KeyE') || key('ArrowDown')) pitch = Math.max(pitch - PITCH_SPD * dt, -75);
104
+
105
+ // Forward direction
106
+ const yRad = (yaw * Math.PI) / 180;
107
+ const pRad = (pitch * Math.PI) / 180;
108
+ const fdx = -Math.sin(yRad) * Math.cos(pRad);
109
+ const fdy = Math.sin(pRad);
110
+ const fdz = -Math.cos(yRad) * Math.cos(pRad);
111
+
112
+ // Thrust / brake
113
+ if (key('KeyW') || key('Space')) {
114
+ vel.x += fdx * THRUST * dt;
115
+ vel.y += fdy * THRUST * dt;
116
+ vel.z += fdz * THRUST * dt;
117
+ }
118
+ if (key('KeyS')) {
119
+ vel.x -= fdx * THRUST * 0.5 * dt;
120
+ vel.y -= fdy * THRUST * 0.5 * dt;
121
+ vel.z -= fdz * THRUST * 0.5 * dt;
122
+ }
123
+
124
+ // Drag + integrate
125
+ vel.x *= DRAG;
126
+ vel.y *= DRAG;
127
+ vel.z *= DRAG;
128
+ pos.x += vel.x;
129
+ pos.y += vel.y;
130
+ pos.z += vel.z;
131
+
132
+ setCameraPosition(pos.x, pos.y, pos.z);
133
+ setCameraTarget(pos.x + fdx, pos.y + fdy, pos.z + fdz);
134
+
135
+ // Rotate asteroids
136
+ for (const a of asteroids) {
137
+ rotateMesh(a.mesh, a.rx * dt, a.ry * dt, a.rz * dt);
138
+ }
139
+
140
+ // Spin crystals + collect on proximity
141
+ for (const c of crystals) {
142
+ if (!c.active) continue;
143
+ rotateMesh(c.mesh, 0.5 * dt, 1.2 * dt, 0.3 * dt);
144
+ const dx = pos.x - c.x,
145
+ dy = pos.y - c.y,
146
+ dz = pos.z - c.z;
147
+ if (dx * dx + dy * dy + dz * dz < 100) {
148
+ // ≤ 10 unit radius
149
+ removeMesh(c.mesh);
150
+ c.active = false;
151
+ collected++;
152
+ score += 100;
153
+ sfx('coin');
154
+ if (collected >= TOTAL) sfx('powerup');
155
+ }
156
+ }
157
+
158
+ animateSkybox(dt);
159
+ }
160
+
161
+ // ── Draw ──────────────────────────────────────────────────────────────────────
162
+ export function draw() {
163
+ const spd = Math.round(Math.sqrt(vel.x * vel.x + vel.y * vel.y + vel.z * vel.z) * 10);
164
+
165
+ // Top-left HUD
166
+ print(`CRYSTALS ${score.toString().padStart(6, '0')}`, 16, 16, rgba8(0, 255, 200, 255));
167
+ print(`SPEED ${spd.toString().padStart(3)} u/s`, 16, 36, rgba8(160, 200, 255, 210));
168
+ print(`REMAINING ${collected} / ${TOTAL}`, 16, 56, rgba8(255, 220, 80, 200));
169
+
170
+ // Controls hint — fades out after 8 s
171
+ if (time < 8) {
172
+ const a = Math.min(255, Math.floor((8 - time) * 50));
173
+ print('W / SPACE — Thrust S — Brake', 320, 328, rgba8(200, 200, 200, a));
174
+ print('A / D — Turn Q / E — Pitch', 320, 346, rgba8(200, 200, 200, a));
175
+ }
176
+
177
+ // Victory screen
178
+ if (collected >= TOTAL) {
179
+ const pulse = Math.floor((Math.sin(time * 4) * 0.5 + 0.5) * 255);
180
+ print('ALL CRYSTALS COLLECTED!', 320, 170, rgba8(0, 255, 200, pulse));
181
+ print(`FINAL SCORE ${score}`, 320, 194, rgba8(255, 220, 0, 255));
182
+ }
183
+ }
@@ -0,0 +1,19 @@
1
+ // Hello World — Nova64 minimal cart
2
+ // A spinning cube with a HUD label. Under 15 lines.
3
+
4
+ let cube;
5
+
6
+ export function init() {
7
+ cube = createCube(1, 0x00aaff, [0, 0, -4]);
8
+ setAmbientLight(0xffffff, 1.5);
9
+ setCameraPosition(0, 1, 4);
10
+ setCameraTarget(0, 0, 0);
11
+ }
12
+
13
+ export function update(dt) {
14
+ rotateMesh(cube, dt * 0.5, dt, 0);
15
+ }
16
+
17
+ export function draw() {
18
+ printCentered('Hello, Nova64!', 12, 0xffffff);
19
+ }
@@ -0,0 +1,109 @@
1
+ // examples/input-showcase/code.js
2
+ // Demonstrates every Nova64 input method: keyboard, gamepad, and mouse.
3
+ // All inputs are visualized live so you can see exactly what the engine detects.
4
+
5
+ const KEY_MAP = [
6
+ ['KeyW', 'W'],
7
+ ['KeyA', 'A'],
8
+ ['KeyS', 'S'],
9
+ ['KeyD', 'D'],
10
+ ['ArrowUp', '↑'],
11
+ ['ArrowDown', '↓'],
12
+ ['ArrowLeft', '←'],
13
+ ['ArrowRight', '→'],
14
+ ['Space', 'SPC'],
15
+ ['ShiftLeft', 'SHF'],
16
+ ['KeyE', 'E'],
17
+ ['KeyQ', 'Q'],
18
+ ['Enter', 'ENT'],
19
+ ['Escape', 'ESC'],
20
+ ];
21
+
22
+ const BTN_LABELS = ['A', 'B', 'X', 'Y', 'LB', 'RB', 'LT', 'RT', 'SEL', 'STR'];
23
+
24
+ // Spinning cube changes color based on WASD input
25
+ let cube;
26
+ let cubeColor = 0x0088ff;
27
+ let mouseTrail = [];
28
+
29
+ export function init() {
30
+ setCameraPosition(0, 0, 8);
31
+ setCameraTarget(0, 0, 0);
32
+ setAmbientLight(0xffffff, 1.0);
33
+ setFog(0x0a0a1a, 20, 50);
34
+
35
+ cube = createCube(2, cubeColor, [0, 0, 0], { material: 'holographic' });
36
+ }
37
+
38
+ export function update(dt) {
39
+ // Drive cube rotation with WASD
40
+ const rx = (key('KeyS') ? 1 : 0) - (key('KeyW') ? 1 : 0);
41
+ const ry = (key('KeyD') ? 1 : 0) - (key('KeyA') ? 1 : 0);
42
+ rotateMesh(cube, rx * dt * 2, ry * dt * 2, 0);
43
+
44
+ // Record mouse trail (last 20 positions)
45
+ const mx = getMouseX ? getMouseX() : 0;
46
+ const my = getMouseY ? getMouseY() : 0;
47
+ mouseTrail.push({ x: mx, y: my });
48
+ if (mouseTrail.length > 20) mouseTrail.shift();
49
+ }
50
+
51
+ export function draw() {
52
+ // ── Background gradient header ───────────────────────────────────────────
53
+ rect(0, 0, 320, 20, rgba8(20, 20, 50, 255), true);
54
+ printCentered('INPUT SHOWCASE', 4, 0xffffff);
55
+
56
+ // ── Keyboard section ─────────────────────────────────────────────────────
57
+ print('KEYBOARD', 8, 26, 0xaaaaff);
58
+ const kbCols = 7;
59
+ KEY_MAP.forEach(([code, label], i) => {
60
+ const col = i % kbCols;
61
+ const row = Math.floor(i / kbCols);
62
+ const x = 8 + col * 44;
63
+ const y = 36 + row * 14;
64
+ const pressed = key(code);
65
+ rect(x, y, 40, 12, pressed ? rgba8(0, 200, 80, 255) : rgba8(40, 40, 80, 200), true);
66
+ rect(x, y, 40, 12, rgba8(100, 100, 180, 180), false);
67
+ print(label, x + 2, y + 2, pressed ? 0x000000 : 0xdddddd);
68
+ });
69
+
70
+ // ── Gamepad section ───────────────────────────────────────────────────────
71
+ print('GAMEPAD', 8, 74, 0xaaaaff);
72
+ BTN_LABELS.forEach((label, i) => {
73
+ const x = 8 + i * 30;
74
+ const pressed = btn(i);
75
+ rect(x, 84, 26, 12, pressed ? rgba8(255, 160, 0, 255) : rgba8(40, 40, 80, 200), true);
76
+ rect(x, 84, 26, 12, rgba8(100, 100, 180, 180), false);
77
+ print(label, x + 2, 86, pressed ? 0x000000 : 0xdddddd);
78
+ });
79
+
80
+ // ── Analog sticks ─────────────────────────────────────────────────────────
81
+ print('STICKS', 8, 102, 0xaaaaff);
82
+ // Left stick area
83
+ rect(8, 112, 40, 22, rgba8(30, 30, 60, 200), true);
84
+ rect(8, 112, 40, 22, rgba8(80, 80, 150, 180), false);
85
+ print('L-STICK', 10, 114, 0x888888);
86
+ // Right stick area
87
+ rect(54, 112, 40, 22, rgba8(30, 30, 60, 200), true);
88
+ rect(54, 112, 40, 22, rgba8(80, 80, 150, 180), false);
89
+ print('R-STICK', 56, 114, 0x888888);
90
+ print('(connect gamepad)', 100, 118, 0x555577);
91
+
92
+ // ── Mouse section ─────────────────────────────────────────────────────────
93
+ print('MOUSE', 8, 140, 0xaaaaff);
94
+ const mx = mouseTrail.length ? mouseTrail[mouseTrail.length - 1].x : 0;
95
+ const my = mouseTrail.length ? mouseTrail[mouseTrail.length - 1].y : 0;
96
+ print(`X: ${Math.round(mx)} Y: ${Math.round(my)}`, 8, 150, 0xdddddd);
97
+
98
+ // Draw mouse trail
99
+ if (mouseTrail.length > 1) {
100
+ for (let i = 1; i < mouseTrail.length; i++) {
101
+ const alpha = Math.floor((i / mouseTrail.length) * 200);
102
+ pset(mouseTrail[i].x, mouseTrail[i].y, rgba8(80, 200, 255, alpha));
103
+ }
104
+ }
105
+
106
+ // ── Active key hint ───────────────────────────────────────────────────────
107
+ rect(0, 170, 320, 10, rgba8(15, 15, 40, 255), true);
108
+ print('WASD rotates cube | All inputs highlighted in real time', 4, 172, 0x555577);
109
+ }