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,695 @@
1
+ // examples/3d-advanced/code.js
2
+ // Epic space battle scene with capital ships, fighters, and spectacular effects
3
+
4
+ let capitalShips = [];
5
+ let fighters = [];
6
+ let projectiles = [];
7
+ let explosions = [];
8
+ let stars = [];
9
+ let nebula = [];
10
+ let time = 0;
11
+ let battleIntensity = 0;
12
+ let cameraTarget = { x: 0, y: 0, z: 0 };
13
+
14
+ // Screen management
15
+ let gameState = 'start'; // 'start', 'battle'
16
+ let startScreenTime = 0;
17
+ let uiButtons = [];
18
+
19
+ export async function init() {
20
+ clearScene();
21
+
22
+ // Cinematic camera setup
23
+ setCameraPosition(0, 15, 30);
24
+ setCameraTarget(0, 0, 0);
25
+ setCameraFOV(80); // Wide cinematic FOV
26
+
27
+ // Space atmosphere
28
+ enablePixelation(1.1);
29
+ enableDithering(true);
30
+ enableBloom(1.0, 0.4, 0.5); // Capital ship engine glow
31
+ enableFXAA(); // Smooth ship silhouettes
32
+ enableVignette(1.3, 0.9); // Cinematic space feel
33
+ setFog(0x0a0a30, 50, 150); // Lighter space fog with blue tint
34
+
35
+ // 💡 PROPER LIGHTING SETUP - Make scene visible!
36
+ setLightDirection(0.5, -0.7, -0.5); // Directional key light
37
+ setLightColor(0xaabbff); // Bright blue-white light
38
+ setAmbientLight(0x334466); // Essential ambient light to see everything!
39
+
40
+ // Create the epic space battle
41
+ createStarfield();
42
+ createNebula();
43
+ createCapitalShips();
44
+ createFighterSquadrons();
45
+ createBattleEffects();
46
+
47
+ // Initialize start screen
48
+ initStartScreen();
49
+
50
+ console.log('Epic Space Battle initialized');
51
+ }
52
+
53
+ function initStartScreen() {
54
+ uiButtons = [];
55
+
56
+ uiButtons.push(
57
+ createButton(
58
+ centerX(240),
59
+ 150,
60
+ 240,
61
+ 60,
62
+ '▶ START BATTLE',
63
+ () => {
64
+ console.log('🎯 START BATTLE CLICKED! Changing gameState to battle...');
65
+ gameState = 'battle';
66
+ console.log('✅ gameState is now:', gameState);
67
+ },
68
+ {
69
+ normalColor: rgba8(200, 50, 50, 255),
70
+ hoverColor: rgba8(230, 80, 80, 255),
71
+ pressedColor: rgba8(170, 30, 30, 255),
72
+ }
73
+ )
74
+ );
75
+
76
+ uiButtons.push(
77
+ createButton(
78
+ centerX(200),
79
+ 355,
80
+ 200,
81
+ 45,
82
+ '⚡ INFO',
83
+ () => {
84
+ console.log('3D Advanced - Epic space battle with advanced effects');
85
+ },
86
+ {
87
+ normalColor: rgba8(100, 150, 255, 255),
88
+ hoverColor: rgba8(130, 180, 255, 255),
89
+ pressedColor: rgba8(70, 120, 220, 255),
90
+ }
91
+ )
92
+ );
93
+ }
94
+
95
+ function createStarfield() {
96
+ // ✨ Dense, beautiful starfield (BRIGHTER for visibility)
97
+ for (let i = 0; i < 200; i++) {
98
+ const distance = 50 + Math.random() * 100;
99
+ const angle1 = Math.random() * Math.PI * 2;
100
+ const angle2 = (Math.random() - 0.5) * Math.PI;
101
+
102
+ const x = Math.cos(angle1) * Math.cos(angle2) * distance;
103
+ const y = Math.sin(angle2) * distance;
104
+ const z = Math.sin(angle1) * Math.cos(angle2) * distance;
105
+
106
+ const brightness = Math.random();
107
+ // Brighter star colors
108
+ const color = brightness > 0.8 ? 0xeeffff : brightness > 0.6 ? 0xffdddd : 0xffffdd;
109
+ const size = brightness > 0.9 ? 0.4 : 0.15;
110
+
111
+ const star = createCube(size, color);
112
+ setPosition(star, x, y, z);
113
+ stars.push({
114
+ mesh: star,
115
+ brightness,
116
+ twinkle: Math.random() * Math.PI * 2,
117
+ originalPos: [x, y, z],
118
+ });
119
+ }
120
+ }
121
+
122
+ function createNebula() {
123
+ // 🌌 Colorful nebula clouds for visual depth (BRIGHTER!)
124
+ for (let i = 0; i < 15; i++) {
125
+ const x = (Math.random() - 0.5) * 120;
126
+ const y = (Math.random() - 0.5) * 60;
127
+ const z = (Math.random() - 0.5) * 120;
128
+
129
+ // Brighter nebula colors for visibility
130
+ const colors = [0x8844aa, 0x4488aa, 0xaa8844, 0xaa4488, 0x44aa88];
131
+ const cloud = createSphere(8 + Math.random() * 12, colors[i % colors.length], [0, 0, 0], 6);
132
+ setPosition(cloud, x, y, z);
133
+
134
+ nebula.push({
135
+ mesh: cloud,
136
+ drift: [
137
+ (Math.random() - 0.5) * 0.1,
138
+ (Math.random() - 0.5) * 0.05,
139
+ (Math.random() - 0.5) * 0.1,
140
+ ],
141
+ rotation: [
142
+ (Math.random() - 0.5) * 0.01,
143
+ (Math.random() - 0.5) * 0.01,
144
+ (Math.random() - 0.5) * 0.01,
145
+ ],
146
+ originalPos: [x, y, z],
147
+ });
148
+ }
149
+ }
150
+
151
+ function createCapitalShips() {
152
+ // 🚀 Massive capital ships engaged in battle (BRIGHTER!)
153
+ const shipConfigs = [
154
+ { pos: [-25, 5, -15], color: 0x4499ff, faction: 'blue', size: [8, 3, 15] },
155
+ { pos: [20, -3, 10], color: 0xff4499, faction: 'red', size: [6, 2, 12] },
156
+ { pos: [-15, 8, 25], color: 0x4499ff, faction: 'blue', size: [10, 4, 18] },
157
+ { pos: [30, -5, -20], color: 0xff4499, faction: 'red', size: [7, 3, 14] },
158
+ ];
159
+
160
+ shipConfigs.forEach((config, i) => {
161
+ // Main hull
162
+ const hull = createCube(1, config.color);
163
+ setScale(hull, ...config.size);
164
+ setPosition(hull, ...config.pos);
165
+ setRotation(hull, 0, Math.random() * Math.PI * 2, Math.sin(i) * 0.1);
166
+
167
+ // Command bridge (brighter)
168
+ const bridge = createCube(1, config.color + 0x222222);
169
+ setScale(bridge, config.size[0] * 0.3, config.size[1] * 1.5, config.size[2] * 0.2);
170
+ setPosition(bridge, config.pos[0], config.pos[1] + config.size[1] * 0.8, config.pos[2]);
171
+
172
+ // Engine glow (brighter cyan)
173
+ const engine = createCube(1, 0x44ffff);
174
+ setScale(engine, config.size[0] * 0.6, config.size[1] * 0.4, config.size[2] * 0.1);
175
+ setPosition(engine, config.pos[0], config.pos[1], config.pos[2] - config.size[2] * 0.6);
176
+
177
+ // Weapon turrets (brighter)
178
+ for (let t = 0; t < 4; t++) {
179
+ const turret = createSphere(0.8, 0x999999, [0, 0, 0], 8);
180
+ const offsetX = (t % 2 === 0 ? -1 : 1) * config.size[0] * 0.4;
181
+ const offsetZ = (t < 2 ? -1 : 1) * config.size[2] * 0.3;
182
+ setPosition(
183
+ turret,
184
+ config.pos[0] + offsetX,
185
+ config.pos[1] + config.size[1] * 0.6,
186
+ config.pos[2] + offsetZ
187
+ );
188
+
189
+ capitalShips.push({
190
+ mesh: turret,
191
+ parent: hull,
192
+ type: 'turret',
193
+ targetAngle: Math.random() * Math.PI * 2,
194
+ });
195
+ }
196
+
197
+ capitalShips.push({
198
+ mesh: hull,
199
+ bridge,
200
+ engine,
201
+ faction: config.faction,
202
+ pos: config.pos,
203
+ size: config.size,
204
+ health: 100,
205
+ fireTimer: Math.random() * 3,
206
+ type: 'capital',
207
+ });
208
+ });
209
+ }
210
+
211
+ function createFighterSquadrons() {
212
+ // ✈️ Swarms of small fighters (BRIGHTER!)
213
+ for (let squad = 0; squad < 4; squad++) {
214
+ const squadColor = squad % 2 === 0 ? 0x44aaff : 0xff44aa;
215
+ const squadCenter = [
216
+ (Math.random() - 0.5) * 60,
217
+ (Math.random() - 0.5) * 20,
218
+ (Math.random() - 0.5) * 60,
219
+ ];
220
+
221
+ for (let f = 0; f < 8; f++) {
222
+ const fighter = createCube(0.3, squadColor);
223
+ setScale(fighter, 1.5, 0.4, 2);
224
+
225
+ const angle = (f / 8) * Math.PI * 2;
226
+ const radius = 3;
227
+ const x = squadCenter[0] + Math.cos(angle) * radius;
228
+ const y = squadCenter[1] + Math.sin(f * 0.5) * 2;
229
+ const z = squadCenter[2] + Math.sin(angle) * radius;
230
+
231
+ setPosition(fighter, x, y, z);
232
+ setRotation(fighter, 0, angle, 0);
233
+
234
+ // Engine trail (brighter)
235
+ const trail = createCube(0.1, 0x66ddff);
236
+ setScale(trail, 0.3, 0.1, 1);
237
+ setPosition(trail, x - Math.cos(angle) * 1.2, y, z - Math.sin(angle) * 1.2);
238
+
239
+ fighters.push({
240
+ mesh: fighter,
241
+ trail,
242
+ squad,
243
+ formation: { angle, radius },
244
+ speed: 0.5 + Math.random() * 0.3,
245
+ squadCenter,
246
+ dodgeTimer: Math.random() * 2,
247
+ fireTimer: Math.random() * 1.5,
248
+ });
249
+ }
250
+ }
251
+ }
252
+
253
+ function createBattleEffects() {
254
+ // Weapon fire and explosions
255
+ for (let i = 0; i < 50; i++) {
256
+ const projectile = createCube(0.1, 0xffff00);
257
+ setPosition(
258
+ projectile,
259
+ Math.random() * 100 - 50,
260
+ Math.random() * 40 - 20,
261
+ Math.random() * 100 - 50
262
+ );
263
+
264
+ projectiles.push({
265
+ mesh: projectile,
266
+ velocity: [
267
+ (Math.random() - 0.5) * 20,
268
+ (Math.random() - 0.5) * 10,
269
+ (Math.random() - 0.5) * 20,
270
+ ],
271
+ life: Math.random() * 2,
272
+ maxLife: 0.5 + Math.random(),
273
+ type: Math.random() > 0.7 ? 'torpedo' : 'laser',
274
+ });
275
+ }
276
+ }
277
+
278
+ export function update(dt) {
279
+ time += dt;
280
+ battleIntensity = 0.5 + Math.sin(time * 0.3) * 0.5;
281
+
282
+ if (gameState === 'start') {
283
+ startScreenTime += dt;
284
+ updateAllButtons();
285
+
286
+ // Animate scene in background (with safety check)
287
+ if (stars && stars.length > 0) {
288
+ stars.forEach(star => {
289
+ if (star && star.mesh) {
290
+ star.twinkle += dt * 2;
291
+ const twinkleIntensity = (Math.sin(star.twinkle) + 1) * 0.5;
292
+ const scale = star.brightness * (0.8 + twinkleIntensity * 0.4);
293
+ setScale(star.mesh, scale);
294
+ }
295
+ });
296
+ }
297
+
298
+ // Slow camera orbit
299
+ const angle = time * 0.15;
300
+ setCameraPosition(Math.cos(angle) * 35, 20, Math.sin(angle) * 35);
301
+ setCameraTarget(0, 0, 0);
302
+ return;
303
+ }
304
+
305
+ // Twinkling stars (with safety check)
306
+ if (stars && stars.length > 0) {
307
+ stars.forEach(star => {
308
+ if (star && star.mesh) {
309
+ star.twinkle += dt * 2;
310
+ const twinkleIntensity = (Math.sin(star.twinkle) + 1) * 0.5;
311
+ const scale = star.brightness * (0.8 + twinkleIntensity * 0.4);
312
+ setScale(star.mesh, scale);
313
+ }
314
+ });
315
+ }
316
+
317
+ // Drifting nebula clouds (with safety check)
318
+ if (nebula && nebula.length > 0) {
319
+ nebula.forEach(cloud => {
320
+ if (cloud && cloud.mesh) {
321
+ const pos = getPosition(cloud.mesh);
322
+ setPosition(
323
+ cloud.mesh,
324
+ pos[0] + cloud.drift[0] * dt,
325
+ pos[1] + cloud.drift[1] * dt,
326
+ pos[2] + cloud.drift[2] * dt
327
+ );
328
+ rotateMesh(
329
+ cloud.mesh,
330
+ cloud.rotation[0] * dt,
331
+ cloud.rotation[1] * dt,
332
+ cloud.rotation[2] * dt
333
+ );
334
+ }
335
+ });
336
+ }
337
+
338
+ // Capital ship battle animations (with safety check)
339
+ if (capitalShips && capitalShips.length > 0) {
340
+ capitalShips.forEach((ship, i) => {
341
+ if (ship.type === 'capital') {
342
+ // Ship movement and combat
343
+ ship.fireTimer -= dt;
344
+ if (ship.fireTimer <= 0) {
345
+ ship.fireTimer = 2 + Math.random() * 3;
346
+ spawnWeaponFire(ship.pos, ship.faction);
347
+ }
348
+
349
+ // Ship rocking from battle damage
350
+ const rockIntensity = (100 - ship.health) / 100;
351
+ const rock = Math.sin(time * 2 + i) * rockIntensity * 0.1;
352
+ const yawAngle = Math.sin(time * 0.5 + i) * 0.2;
353
+ setRotation(ship.mesh, rock * 0.5, yawAngle, rock);
354
+
355
+ // Engine pulse
356
+ const enginePulse = 0.8 + Math.sin(time * 5 + i) * 0.2;
357
+ setScale(
358
+ ship.engine,
359
+ ship.size[0] * 0.6,
360
+ ship.size[1] * 0.4,
361
+ ship.size[2] * 0.1 * enginePulse
362
+ );
363
+ } else if (ship.type === 'turret') {
364
+ // Turret tracking and rotation
365
+ ship.targetAngle += dt * 0.5;
366
+ rotateMesh(ship.mesh, 0, dt * 0.3, 0);
367
+ }
368
+ });
369
+ }
370
+
371
+ // Fighter squadron maneuvers (with safety check)
372
+ if (fighters && fighters.length > 0) {
373
+ fighters.forEach((fighter, i) => {
374
+ fighter.dodgeTimer -= dt;
375
+ fighter.fireTimer -= dt;
376
+
377
+ // Formation flying with evasive maneuvers
378
+ const formationAngle = fighter.formation.angle + time * fighter.speed;
379
+ const evasion = fighter.dodgeTimer > 0 ? Math.sin(time * 10 + i) * 2 : 0;
380
+
381
+ const targetX =
382
+ fighter.squadCenter[0] + Math.cos(formationAngle) * (fighter.formation.radius + evasion);
383
+ const targetY = fighter.squadCenter[1] + Math.sin(time * 2 + i) * 3 + evasion * 0.5;
384
+ const targetZ =
385
+ fighter.squadCenter[2] + Math.sin(formationAngle) * (fighter.formation.radius + evasion);
386
+
387
+ setPosition(fighter.mesh, targetX, targetY, targetZ);
388
+ setRotation(fighter.mesh, evasion * 0.1, formationAngle + Math.PI / 2, evasion * 0.05);
389
+
390
+ // Engine trail follows
391
+ setPosition(
392
+ fighter.trail,
393
+ targetX - Math.cos(formationAngle + Math.PI / 2) * 1.5,
394
+ targetY,
395
+ targetZ - Math.sin(formationAngle + Math.PI / 2) * 1.5
396
+ );
397
+
398
+ // Random evasive maneuvers
399
+ if (Math.random() < 0.02) {
400
+ fighter.dodgeTimer = 1 + Math.random();
401
+ }
402
+
403
+ // Weapon fire
404
+ if (fighter.fireTimer <= 0) {
405
+ fighter.fireTimer = 0.5 + Math.random() * 1.5;
406
+ spawnProjectile([targetX, targetY, targetZ], 'fighter');
407
+ }
408
+ });
409
+ }
410
+
411
+ // Projectile physics and combat (with safety check)
412
+ if (projectiles && projectiles.length > 0) {
413
+ projectiles.forEach(proj => {
414
+ proj.life -= dt;
415
+ if (proj.life <= 0) {
416
+ proj.life = proj.maxLife;
417
+ // Respawn projectile from random ship position
418
+ const randomShip = capitalShips[Math.floor(Math.random() * capitalShips.length)];
419
+ if (randomShip && randomShip.pos) {
420
+ setPosition(proj.mesh, ...randomShip.pos);
421
+ proj.velocity = [
422
+ (Math.random() - 0.5) * 15,
423
+ (Math.random() - 0.5) * 8,
424
+ (Math.random() - 0.5) * 15,
425
+ ];
426
+ }
427
+ }
428
+
429
+ const pos = getPosition(proj.mesh);
430
+ setPosition(
431
+ proj.mesh,
432
+ pos[0] + proj.velocity[0] * dt,
433
+ pos[1] + proj.velocity[1] * dt,
434
+ pos[2] + proj.velocity[2] * dt
435
+ );
436
+
437
+ // Projectile glow effect
438
+ const glowIntensity = proj.life / proj.maxLife;
439
+ setScale(proj.mesh, glowIntensity * (proj.type === 'torpedo' ? 0.3 : 0.1));
440
+
441
+ // Spin torpedoes
442
+ if (proj.type === 'torpedo') {
443
+ rotateMesh(proj.mesh, dt * 5, dt * 3, 0);
444
+ }
445
+ });
446
+ }
447
+
448
+ // Dynamic cinematic camera
449
+ const cameraDistance = 35 + Math.sin(time * 0.1) * 15;
450
+ const cameraHeight = 8 + Math.sin(time * 0.15) * 12;
451
+ const cameraAngle = time * 0.08 + Math.sin(time * 0.05) * 0.5;
452
+
453
+ const camX = Math.cos(cameraAngle) * cameraDistance;
454
+ const camY = cameraHeight;
455
+ const camZ = Math.sin(cameraAngle) * cameraDistance;
456
+
457
+ setCameraPosition(camX, camY, camZ);
458
+
459
+ // Camera focuses on different parts of the battle
460
+ const focusTarget = Math.floor(time * 0.1) % 3;
461
+ switch (focusTarget) {
462
+ case 0:
463
+ cameraTarget = { x: 0, y: 0, z: 0 }; // Center of battle
464
+ break;
465
+ case 1: {
466
+ // Find first capital ship (not turret)
467
+ const capitalShip = capitalShips.find(s => s.type === 'capital');
468
+ if (capitalShip && capitalShip.pos) {
469
+ cameraTarget = { x: capitalShip.pos[0], y: capitalShip.pos[1], z: capitalShip.pos[2] };
470
+ }
471
+ break;
472
+ }
473
+ case 2:
474
+ if (fighters.length > 0 && fighters[0].squadCenter) {
475
+ cameraTarget = {
476
+ x: fighters[0].squadCenter[0],
477
+ y: fighters[0].squadCenter[1],
478
+ z: fighters[0].squadCenter[2],
479
+ };
480
+ }
481
+ break;
482
+ }
483
+
484
+ setCameraTarget(cameraTarget.x, cameraTarget.y, cameraTarget.z);
485
+
486
+ // 💡 Dynamic lighting effects (maintains visibility!)
487
+ const lightIntensity = battleIntensity;
488
+ const lightX = 0.3 + Math.cos(time * 0.3) * 0.3;
489
+ const lightY = -0.7 + Math.sin(time * 0.4) * 0.2;
490
+ const lightZ = -0.5 + Math.sin(time * 0.2) * 0.3;
491
+ setLightDirection(lightX, lightY, lightZ);
492
+
493
+ // Pulsing light color for battle effects (ensure it stays bright!)
494
+ const lightColorVariation = Math.floor(lightIntensity * 0x110033);
495
+ setLightColor(0xaabbff + lightColorVariation);
496
+
497
+ // Keep ambient light consistent for visibility (CRITICAL!)
498
+ const ambientVariation = Math.floor(lightIntensity * 0x081018);
499
+ setAmbientLight(0x334466 + ambientVariation);
500
+
501
+ // 🌫️ Battle fog effects (lighter for visibility)
502
+ const fogNear = 50 + battleIntensity * 15;
503
+ const fogFar = 150 + battleIntensity * 30;
504
+ const fogColor = Math.floor(0x0a0a30 + battleIntensity * 0x0a0a20);
505
+ setFog(fogColor, fogNear, fogFar);
506
+
507
+ // 🔧 DEBUG: Log lighting values to ensure they're set
508
+ if (time < 1) {
509
+ console.log('🔦 Battle Lighting:', {
510
+ lightColor: (0xaabbff + lightColorVariation).toString(16),
511
+ ambientLight: (0x334466 + ambientVariation).toString(16),
512
+ fogColor: fogColor.toString(16),
513
+ });
514
+ }
515
+ }
516
+
517
+ function spawnWeaponFire(pos, faction) {
518
+ // Create muzzle flash effect
519
+ const flash = createCube(0.5, 0xffaa00);
520
+ setPosition(flash, pos[0], pos[1], pos[2]);
521
+ setTimeout(() => destroyMesh(flash), 100);
522
+ }
523
+
524
+ function spawnProjectile(pos, type) {
525
+ // Find available projectile to reuse
526
+ const availableProj = projectiles.find(p => p.life <= 0);
527
+ if (availableProj) {
528
+ availableProj.life = availableProj.maxLife;
529
+ setPosition(availableProj.mesh, ...pos);
530
+ availableProj.velocity = [
531
+ (Math.random() - 0.5) * 12,
532
+ (Math.random() - 0.5) * 6,
533
+ (Math.random() - 0.5) * 12,
534
+ ];
535
+ }
536
+ }
537
+
538
+ export function draw() {
539
+ if (gameState === 'start') {
540
+ drawStartScreen();
541
+ return;
542
+ }
543
+
544
+ // DON'T call cls() here - it clears the 3D scene!
545
+ // The 3D scene is automatically rendered by the GPU
546
+
547
+ // Epic space battle HUD (2D overlay on top of 3D scene)
548
+ const hudColor = rgba8(0, 255, 100, 255);
549
+ const warningColor = rgba8(255, 50, 50, 255);
550
+ const infoColor = rgba8(100, 200, 255, 255);
551
+
552
+ // Battle status
553
+ print('GALACTIC BATTLE COMMAND', 8, 8, hudColor);
554
+
555
+ const battlePhase = Math.floor(time / 10) % 3;
556
+ const phases = ['ENGAGEMENT', 'HEAVY COMBAT', 'CRITICAL PHASE'];
557
+ print(`STATUS: ${phases[battlePhase]}`, 8, 24, battlePhase === 2 ? warningColor : hudColor);
558
+
559
+ // Fleet status
560
+ const blueFleet = capitalShips.filter(s => s.faction === 'blue').length;
561
+ const redFleet = capitalShips.filter(s => s.faction === 'red').length;
562
+ print(`BLUE FLEET: ${blueFleet} SHIPS`, 8, 40, rgba8(100, 150, 255, 255));
563
+ print(`RED FLEET: ${redFleet} SHIPS`, 8, 56, rgba8(255, 100, 150, 255));
564
+
565
+ // Fighter status
566
+ const activeFighters = fighters.length;
567
+ print(`FIGHTERS: ${activeFighters} ACTIVE`, 8, 72, rgba8(255, 255, 100, 255));
568
+
569
+ // Battle intensity
570
+ const intensity = Math.floor(battleIntensity * 100);
571
+ const intensityColor =
572
+ intensity > 70 ? warningColor : intensity > 40 ? rgba8(255, 200, 0, 255) : hudColor;
573
+ print(`INTENSITY: ${intensity}%`, 8, 88, intensityColor);
574
+
575
+ // Tactical display (mini radar)
576
+ const radarX = 220,
577
+ radarY = 20,
578
+ radarSize = 80;
579
+ rect(radarX, radarY, radarSize, radarSize, rgba8(0, 50, 0, 150), true);
580
+ rect(radarX, radarY, radarSize, radarSize, hudColor, false);
581
+
582
+ // Grid lines
583
+ for (let i = 1; i < 4; i++) {
584
+ const gridPos = radarX + (radarSize / 4) * i;
585
+ line(gridPos, radarY, gridPos, radarY + radarSize, rgba8(0, 100, 0, 100));
586
+ line(
587
+ radarX,
588
+ radarY + (radarSize / 4) * i,
589
+ radarX + radarSize,
590
+ radarY + (radarSize / 4) * i,
591
+ rgba8(0, 100, 0, 100)
592
+ );
593
+ }
594
+
595
+ // Radar blips for capital ships
596
+ capitalShips.forEach(ship => {
597
+ if (ship.type === 'capital') {
598
+ const radarPosX = radarX + radarSize / 2 + ((ship.pos[0] / 60) * radarSize) / 2;
599
+ const radarPosY = radarY + radarSize / 2 + ((ship.pos[2] / 60) * radarSize) / 2;
600
+ const color = ship.faction === 'blue' ? rgba8(100, 150, 255, 255) : rgba8(255, 100, 150, 255);
601
+ rect(radarPosX - 2, radarPosY - 2, 4, 4, color, true);
602
+ }
603
+ });
604
+
605
+ // Radar sweep
606
+ const sweepAngle = time * 2;
607
+ const sweepX = radarX + radarSize / 2 + (Math.cos(sweepAngle) * radarSize) / 2;
608
+ const sweepY = radarY + radarSize / 2 + (Math.sin(sweepAngle) * radarSize) / 2;
609
+ line(radarX + radarSize / 2, radarY + radarSize / 2, sweepX, sweepY, rgba8(0, 255, 0, 150));
610
+
611
+ // Performance stats
612
+ if (typeof get3DStats === 'function') {
613
+ const stats = get3DStats();
614
+ if (stats.render) {
615
+ print(`OBJECTS: ${stats.render.geometries}`, 8, 120, infoColor);
616
+ print(`TRIANGLES: ${stats.render.triangles}`, 8, 136, infoColor);
617
+ print(`DRAW CALLS: ${stats.render.calls}`, 8, 152, infoColor);
618
+ }
619
+ }
620
+
621
+ // Mission briefing
622
+ print('MISSION: Secure the sector', 8, 172, rgba8(255, 255, 255, 200));
623
+
624
+ // Stardate
625
+ const stardate = (2387 + time * 0.1).toFixed(1);
626
+ print(`STARDATE: ${stardate}`, 200, 172, rgba8(200, 200, 200, 200));
627
+ }
628
+
629
+ function drawStartScreen() {
630
+ // Deep space gradient background
631
+ drawGradientRect(0, 0, 640, 360, rgba8(10, 5, 20, 235), rgba8(5, 2, 10, 250), true);
632
+
633
+ // Animated title
634
+ setFont('huge');
635
+ setTextAlign('center');
636
+ const pulse = Math.sin(startScreenTime * 3) * 0.4 + 0.6;
637
+ const combatColor = rgba8(
638
+ Math.floor(pulse * 255),
639
+ Math.floor(pulse * 100),
640
+ Math.floor(pulse * 50),
641
+ 255
642
+ );
643
+
644
+ const shake = Math.random() > 0.9 ? Math.floor(Math.random() * 6) - 3 : 0;
645
+ drawTextShadow('3D ADVANCED', 320 + shake, 50, combatColor, rgba8(0, 0, 0, 255), 7, 1);
646
+ drawTextShadow('SPACE BATTLE', 320, 105, rgba8(100, 150, 255, 255), rgba8(0, 0, 0, 255), 7, 1);
647
+
648
+ // Subtitle
649
+ setFont('large');
650
+ const glow = Math.sin(startScreenTime * 4) * 0.2 + 0.8;
651
+ drawTextOutline(
652
+ '⚡ Epic Capital Ship Combat ⚡',
653
+ 320,
654
+ 165,
655
+ rgba8(255, 200, 100, Math.floor(glow * 255)),
656
+ rgba8(0, 0, 0, 255),
657
+ 1
658
+ );
659
+
660
+ // Info panel
661
+ const panel = createPanel(centerX(480), 210, 480, 190, {
662
+ bgColor: rgba8(15, 10, 25, 215),
663
+ borderColor: rgba8(200, 50, 50, 255),
664
+ borderWidth: 3,
665
+ shadow: true,
666
+ gradient: true,
667
+ gradientColor: rgba8(25, 15, 35, 215),
668
+ });
669
+ drawPanel(panel);
670
+
671
+ setFont('normal');
672
+ setTextAlign('center');
673
+ drawText('GALACTIC WAR SIMULATION', 320, 230, rgba8(255, 100, 100, 255), 1);
674
+
675
+ setFont('small');
676
+ drawText('⚡ Capital Ships + Fighter Squadrons', 320, 255, uiColors.light, 1);
677
+ drawText('⚡ Real-time Combat with Projectiles & Explosions', 320, 270, uiColors.light, 1);
678
+ drawText('⚡ 200+ Stars + Nebula Backgrounds', 320, 285, uiColors.light, 1);
679
+ drawText('⚡ Advanced 3D Effects & Cinematic Camera', 320, 300, uiColors.light, 1);
680
+
681
+ setFont('tiny');
682
+ drawText('Demonstrates advanced Three.js features', 320, 320, uiColors.secondary, 1);
683
+
684
+ // Draw buttons
685
+ drawAllButtons();
686
+
687
+ // Pulsing prompt
688
+ const alpha = Math.floor((Math.sin(startScreenTime * 6) * 0.5 + 0.5) * 255);
689
+ setFont('normal');
690
+ drawText('⚡ PREPARE FOR BATTLE ⚡', 320, 430, rgba8(255, 100, 50, alpha), 1);
691
+
692
+ // Info
693
+ setFont('tiny');
694
+ drawText('Full GPU-Accelerated 3D Combat', 320, 345, rgba8(150, 100, 150, 150), 1);
695
+ }