nova64 0.2.5 → 0.2.6

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 (140) 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/index.html +6 -1
  118. package/package.json +37 -32
  119. package/public/assets/sky/studio/nx.png +0 -0
  120. package/public/assets/sky/studio/ny.png +0 -0
  121. package/public/assets/sky/studio/nz.png +0 -0
  122. package/public/assets/sky/studio/px.png +0 -0
  123. package/public/assets/sky/studio/py.png +0 -0
  124. package/public/assets/sky/studio/pz.png +0 -0
  125. package/public/os9-shell/assets/index-KchE_ngx.js +483 -0
  126. package/public/os9-shell/assets/index-KchE_ngx.js.map +1 -0
  127. package/public/os9-shell/index.html +10 -1
  128. package/runtime/api-2d.js +301 -21
  129. package/runtime/api-3d/pbr.js +45 -1
  130. package/runtime/api-3d.js +1 -0
  131. package/runtime/api-effects.js +90 -3
  132. package/runtime/api-gameutils.js +476 -0
  133. package/runtime/api-generative.js +610 -0
  134. package/runtime/api-skybox.js +54 -0
  135. package/runtime/api-voxel.js +139 -28
  136. package/runtime/gpu-threejs.js +13 -9
  137. package/runtime/ui.js +2 -2
  138. package/src/main.js +20 -0
  139. package/public/os9-shell/assets/index-B1Uvacma.js +0 -32825
  140. package/public/os9-shell/assets/index-B1Uvacma.js.map +0 -1
@@ -0,0 +1,1821 @@
1
+ // ===============================================
2
+ // STAR FOX NOVA 64 - DESERTED SPACE STYLE
3
+ // Built with proper space combat mechanics
4
+ // ===============================================
5
+
6
+ let gameData, uiData, gameSettings, hudElements;
7
+
8
+ // Track all created meshes for cleanup
9
+ let allMeshes = [];
10
+
11
+ function trackMesh(mesh) {
12
+ if (mesh && !allMeshes.includes(mesh)) {
13
+ allMeshes.push(mesh);
14
+ }
15
+ return mesh;
16
+ }
17
+
18
+ function cleanupAllMeshes() {
19
+ allMeshes.forEach(mesh => {
20
+ if (mesh && typeof destroyMesh === 'function') {
21
+ destroyMesh(mesh);
22
+ }
23
+ });
24
+ allMeshes = [];
25
+ }
26
+
27
+ // === GAME INITIALIZATION ===
28
+ export async function init() {
29
+ console.log('🚀 STAR FOX NOVA 64 - Deserted Space Quality...');
30
+
31
+ // Clear everything first
32
+ cls();
33
+
34
+ // Simple, effective 3D setup like Deserted Space
35
+ setCameraPosition(0, 5, 15);
36
+ setCameraTarget(0, 0, 0);
37
+ setCameraFOV(75);
38
+
39
+ // Clean, effective lighting
40
+ setLightDirection(0, -1, -0.5);
41
+ setLightColor(0xffffff);
42
+ setAmbientLight(0x404040);
43
+
44
+ // Initialize simple game settings
45
+ gameSettings = {
46
+ graphics: {
47
+ enableParticles: true,
48
+ enableTrails: false,
49
+ shadowQuality: 'medium',
50
+ starDensity: 500,
51
+ },
52
+ physics: {
53
+ acceleration: 15,
54
+ maxSpeed: 25,
55
+ friction: 0.88,
56
+ gravity: 0,
57
+ },
58
+ gameplay: {
59
+ difficulty: 1.0,
60
+ enemySpawnRate: 2.0,
61
+ powerupChance: 0.15,
62
+ },
63
+ };
64
+
65
+ // Clean up any existing meshes from previous loads
66
+ cleanupAllMeshes();
67
+
68
+ // Initialize comprehensive game state
69
+ gameData = {
70
+ // Core game stats
71
+ score: 0,
72
+ health: 100,
73
+ shields: 100,
74
+ energy: 100,
75
+ wave: 1,
76
+ kills: 0,
77
+ time: 0,
78
+
79
+ // Advanced player ship with physics
80
+ arwing: {
81
+ position: { x: 0, y: 2, z: 0 },
82
+ velocity: { x: 0, y: 0, z: 0 },
83
+ rotation: { x: 0, y: 0, z: 0 },
84
+ mesh: null,
85
+ parts: [],
86
+ engineGlow: [],
87
+ lastShot: 0,
88
+ boost: false,
89
+ shields: true,
90
+ speed: 0,
91
+ },
92
+
93
+ // Game objects
94
+ enemies: [],
95
+ projectiles: [],
96
+ powerups: [],
97
+ particles: [],
98
+ trails: [],
99
+ stars: [],
100
+ debris: [],
101
+ spaceFloor: [],
102
+
103
+ // Camera system
104
+ camera: {
105
+ position: { x: 0, y: 5, z: 15 },
106
+ target: { x: 0, y: 0, z: 0 },
107
+ shake: 0,
108
+ shakeIntensity: 0,
109
+ followSmoothing: 0.1,
110
+ },
111
+
112
+ // Environment
113
+ environment: {
114
+ nebula: [],
115
+ asteroids: [],
116
+ planets: [],
117
+ },
118
+
119
+ // Audio/Visual feedback
120
+ effects: {
121
+ screenFlash: 0,
122
+ timeSlowdown: 1.0,
123
+ combatZoom: 0,
124
+ },
125
+ };
126
+
127
+ // UI state
128
+ uiData = {
129
+ time: 0,
130
+ mouseX: 0,
131
+ mouseY: 0,
132
+ startButtonHover: false,
133
+ showFPS: false,
134
+ targetingSystem: {
135
+ targets: [],
136
+ locked: null,
137
+ },
138
+ };
139
+
140
+ // Initialize HUD system
141
+ initializeHUD();
142
+
143
+ // Create advanced starfield
144
+ createAdvancedStarfield();
145
+
146
+ // Create space floor
147
+ createSpaceFloor();
148
+
149
+ // Setup mouse controls
150
+ setupMouseHandlers();
151
+
152
+ // Title Screen
153
+ addScreen('start', {
154
+ enter: function () {
155
+ console.log('📺 Entering professional start screen...');
156
+ cleanupAllMeshes();
157
+
158
+ // Setup cinematic camera for title
159
+ if (typeof setCameraPosition === 'function') {
160
+ setCameraPosition(0, 3, 12);
161
+ setCameraTarget(0, 0, 0);
162
+ setCameraFOV(60);
163
+ }
164
+
165
+ // Create demo ship for title screen
166
+ createTitleScreenDemo();
167
+ },
168
+
169
+ draw: function () {
170
+ drawProfessionalStartScreen();
171
+ },
172
+
173
+ update: function (dt) {
174
+ updateStartScreen(dt);
175
+ },
176
+ });
177
+
178
+ // Enhanced Game Screen
179
+ addScreen('game', {
180
+ enter: function () {
181
+ console.log('🎮 Entering PROFESSIONAL game mode...');
182
+
183
+ setupGameEnvironment();
184
+ createPlayerShip();
185
+ initializeGameSystems();
186
+ spawnInitialEnemyWave();
187
+ },
188
+
189
+ draw: function () {
190
+ drawGameEnvironment();
191
+ drawProfessionalHUD();
192
+ },
193
+
194
+ update: function (dt) {
195
+ updateGameSystems(dt);
196
+ },
197
+ });
198
+
199
+ // Game Over Screen
200
+ addScreen('gameover', {
201
+ enter: function () {
202
+ console.log('💀 Game Over - Final Score:', gameData.score);
203
+ },
204
+
205
+ draw: function () {
206
+ drawGameOverScreen();
207
+ },
208
+
209
+ update: function (dt) {
210
+ if (isKeyPressed(' ') || isKeyPressed('Enter')) {
211
+ switchToScreen('start');
212
+ }
213
+ },
214
+ });
215
+
216
+ // Start with title screen
217
+ switchToScreen('start');
218
+
219
+ console.log('✅ PROFESSIONAL STAR FOX NOVA 64 Ready!');
220
+ }
221
+
222
+ // === HUD SYSTEM INTEGRATION ===
223
+ function initializeHUD() {
224
+ hudElements = {
225
+ scoreDisplay: null,
226
+ healthBar: null,
227
+ crosshair: null,
228
+ speedometer: null,
229
+ initialized: false,
230
+ };
231
+
232
+ console.log('🎯 HUD system initialized');
233
+ }
234
+
235
+ // === ADVANCED GRAPHICS FUNCTIONS ===
236
+ function createAdvancedStarfield() {
237
+ gameData.stars = [];
238
+
239
+ // Create multiple star layers for depth
240
+ for (let layer = 0; layer < 3; layer++) {
241
+ const starCount = [200, 150, 100][layer];
242
+ const distance = [100, 200, 500][layer];
243
+ const speed = [1, 0.5, 0.2][layer];
244
+
245
+ for (let i = 0; i < starCount; i++) {
246
+ gameData.stars.push({
247
+ x: (Math.random() - 0.5) * distance * 2,
248
+ y: (Math.random() - 0.5) * distance,
249
+ z: -Math.random() * distance - 50 - layer * 100,
250
+ originalZ: -Math.random() * distance - 50 - layer * 100,
251
+ speed: speed + Math.random() * speed,
252
+ brightness: Math.random() * 0.8 + 0.2,
253
+ size: Math.random() * 2 + 1,
254
+ layer: layer,
255
+ twinkle: Math.random() * Math.PI * 2,
256
+ });
257
+ }
258
+ }
259
+ }
260
+
261
+ function createSpaceFloor() {
262
+ console.log('🌌 Creating simple, stable starfield like Deserted Space...');
263
+
264
+ // Create simple starfield - no complex mesh tracking
265
+ gameData.stars = [];
266
+
267
+ for (let i = 0; i < 200; i++) {
268
+ const star = createCube(0.5, 0xffffff, [
269
+ (Math.random() - 0.5) * 200,
270
+ (Math.random() - 0.5) * 100,
271
+ -100 - Math.random() * 300,
272
+ ]);
273
+
274
+ gameData.stars.push({
275
+ mesh: star,
276
+ speed: 20 + Math.random() * 30,
277
+ x: (Math.random() - 0.5) * 200,
278
+ y: (Math.random() - 0.5) * 100,
279
+ z: -100 - Math.random() * 300,
280
+ });
281
+ }
282
+
283
+ // 2. Crystal Cathedral pillars in formation
284
+ for (let i = 0; i < 12; i++) {
285
+ const angle = (i / 12) * Math.PI * 2;
286
+ const radius = 80;
287
+ const x = Math.cos(angle) * radius;
288
+ const z = Math.sin(angle) * radius - 200;
289
+ const height = 25 + Math.sin(i * 0.7) * 8;
290
+
291
+ // Towering crystal pillars
292
+ const pillar = trackMesh(createCube(4, 0x4488ff, [x, height / 2 - 60, z]));
293
+ setScale(pillar, 3, height, 3);
294
+
295
+ // Glowing crystal caps
296
+ const cap = trackMesh(createSphere(2, 0x88ddff, [x, height - 40, z]));
297
+
298
+ gameData.spaceFloor.push({
299
+ mesh: pillar,
300
+ cap: cap,
301
+ type: 'pillar',
302
+ x: x,
303
+ originalZ: z,
304
+ customZ: z,
305
+ height: height,
306
+ glowPhase: Math.random() * Math.PI * 2,
307
+ });
308
+ }
309
+
310
+ // 3. Holographic grid system with multiple layers
311
+ const gridSpacing = 25;
312
+ const gridCount = 20;
313
+
314
+ // HOLOGRAPHIC GRID SYSTEM like Crystal Cathedral
315
+ for (let i = -gridCount; i <= gridCount; i += 3) {
316
+ for (let j = 0; j < 15; j++) {
317
+ const z = -150 - j * 60;
318
+
319
+ // Holographic grid lines with emissive glow
320
+ const hLine = trackMesh(createCube(4, 0x4488ff, [0, -57, z]));
321
+ setScale(hLine, 60, 1.5, 1);
322
+ gameData.spaceFloor.push({
323
+ mesh: hLine,
324
+ type: 'horizontal',
325
+ x: 0,
326
+ originalZ: z,
327
+ customZ: z,
328
+ glowPhase: j * 0.3,
329
+ });
330
+
331
+ // Vertical holographic lines
332
+ if (Math.abs(i) <= 15) {
333
+ const vLine = trackMesh(createCube(4, 0x4488ff, [i * gridSpacing, -57, z]));
334
+ setScale(vLine, 1, 1.5, 60);
335
+ gameData.spaceFloor.push({
336
+ mesh: vLine,
337
+ type: 'vertical',
338
+ x: i * gridSpacing,
339
+ originalZ: z,
340
+ customZ: z,
341
+ glowPhase: i * 0.2,
342
+ });
343
+ }
344
+ }
345
+ }
346
+
347
+ // 4. FLOATING CRYSTAL ELEMENTS like Crystal Cathedral
348
+ for (let i = 0; i < 30; i++) {
349
+ const x = (Math.random() - 0.5) * 600;
350
+ const y = -40 + Math.random() * 20;
351
+ const z = -100 - Math.random() * 400;
352
+
353
+ // Holographic floating crystals
354
+ const crystal = trackMesh(createSphere(3, 0x88ddff, [x, y, z]));
355
+ setScale(crystal, 2, 4, 2);
356
+ gameData.spaceFloor.push({
357
+ mesh: crystal,
358
+ type: 'crystal',
359
+ x: x,
360
+ y: y,
361
+ originalZ: z,
362
+ customZ: z,
363
+ floatPhase: Math.random() * Math.PI * 2,
364
+ originalY: y,
365
+ });
366
+ }
367
+
368
+ // 4. Create depth markers - pillars in the distance
369
+ for (let i = 0; i < 20; i++) {
370
+ const x = (Math.random() - 0.5) * 600;
371
+ const z = -200 - Math.random() * 400;
372
+
373
+ const pillar = trackMesh(createCube(8, 0x004499, [x, -40, z]));
374
+ setScale(pillar, 2, 8, 2);
375
+ gameData.spaceFloor.push({
376
+ mesh: pillar,
377
+ type: 'pillar',
378
+ x: x,
379
+ originalZ: z,
380
+ customZ: z,
381
+ });
382
+ }
383
+
384
+ console.log('🌌 Simple starfield created with', gameData.stars.length, 'stars');
385
+ }
386
+ function updateStarfield(dt) {
387
+ // Simple, reliable starfield update like Deserted Space
388
+ for (let star of gameData.stars) {
389
+ // Move stars toward player
390
+ star.z += star.speed * dt;
391
+
392
+ // Update star position
393
+ setPosition(star.mesh, star.x, star.y, star.z);
394
+
395
+ // Reset when star passes player
396
+ if (star.z > 30) {
397
+ star.z = -300 - Math.random() * 100;
398
+ star.x = (Math.random() - 0.5) * 200;
399
+ star.y = (Math.random() - 0.5) * 100;
400
+ }
401
+ }
402
+ }
403
+
404
+ function createTitleScreenDemo() {
405
+ // Create a demo Arwing for the title screen
406
+ try {
407
+ const demoShip = createAdvancedArwing(2, 0, -8);
408
+ gameData.demoShip = {
409
+ parts: demoShip,
410
+ rotation: 0,
411
+ position: { x: 2, y: 0, z: -8 },
412
+ };
413
+ } catch (e) {
414
+ console.warn('Demo ship creation error:', e);
415
+ }
416
+ }
417
+
418
+ function createAdvancedArwing(x, y, z) {
419
+ const shipParts = [];
420
+
421
+ try {
422
+ // SPECTACULAR ARWING - Much better than Deserted Space!
423
+
424
+ // Main fuselage - sleek and large
425
+ const fuselage = trackMesh(createCube(1.2, 0x0099ff, [x, y, z]));
426
+ setScale(fuselage, 1.5, 1, 5);
427
+ shipParts.push(fuselage);
428
+
429
+ // Cockpit - glowing crystal canopy
430
+ const cockpit = trackMesh(createSphere(0.8, 0x88ffff, [x, y + 0.5, z + 2]));
431
+ setScale(cockpit, 1.2, 0.8, 1.5);
432
+ shipParts.push(cockpit);
433
+
434
+ // Massive swept wings
435
+ const leftWing = trackMesh(createCube(4, 0x0066cc, [x - 3.5, y - 0.5, z - 1]));
436
+ setScale(leftWing, 1.5, 0.4, 4);
437
+ setRotation(leftWing, 0, 0, -0.2);
438
+ shipParts.push(leftWing);
439
+
440
+ const rightWing = trackMesh(createCube(4, 0x0066cc, [x + 3.5, y - 0.5, z - 1]));
441
+ setScale(rightWing, 1.5, 0.4, 4);
442
+ setRotation(rightWing, 0, 0, 0.2);
443
+ shipParts.push(rightWing);
444
+
445
+ // Wing tips - glowing
446
+ const leftWingTip = trackMesh(createSphere(0.6, 0x00ffff, [x - 5, y - 0.5, z - 1]));
447
+ shipParts.push(leftWingTip);
448
+
449
+ const rightWingTip = trackMesh(createSphere(0.6, 0x00ffff, [x + 5, y - 0.5, z - 1]));
450
+ shipParts.push(rightWingTip);
451
+
452
+ // Powerful engine pods
453
+ const leftEngine = trackMesh(createCube(1, 0x003399, [x - 3.5, y - 0.5, z - 2.5]));
454
+ setScale(leftEngine, 1.2, 1.2, 3);
455
+ shipParts.push(leftEngine);
456
+
457
+ const rightEngine = trackMesh(createCube(1, 0x003399, [x + 3.5, y - 0.5, z - 2.5]));
458
+ setScale(rightEngine, 1.2, 1.2, 3);
459
+ shipParts.push(rightEngine);
460
+
461
+ // Brilliant engine glows
462
+ const leftGlow = trackMesh(createSphere(0.8, 0xff6600, [x - 3.5, y - 0.5, z - 4]));
463
+ shipParts.push(leftGlow);
464
+
465
+ const rightGlow = trackMesh(createSphere(0.8, 0xff6600, [x + 3.5, y - 0.5, z - 4]));
466
+ shipParts.push(rightGlow);
467
+
468
+ // Quad laser cannons - intimidating
469
+ const leftCannon = trackMesh(createCube(0.5, 0xffaa00, [x - 4, y + 0.2, z + 3]));
470
+ setScale(leftCannon, 0.6, 0.6, 2.5);
471
+ shipParts.push(leftCannon);
472
+
473
+ const rightCannon = trackMesh(createCube(0.5, 0xffaa00, [x + 4, y + 0.2, z + 3]));
474
+ setScale(rightCannon, 0.6, 0.6, 2.5);
475
+ shipParts.push(rightCannon);
476
+
477
+ // Nose cone - sharp and aggressive
478
+ const nose = trackMesh(createSphere(0.6, 0x0088ff, [x, y, z + 3.5]));
479
+ setScale(nose, 0.8, 0.8, 1.5);
480
+ shipParts.push(nose);
481
+
482
+ return shipParts;
483
+ } catch (e) {
484
+ console.warn('Advanced Arwing creation error:', e);
485
+ return [trackMesh(createCube(3, 0x0099ff, [x, y, z]))];
486
+ }
487
+ }
488
+
489
+ function createAdvancedEnemyFighter(x, y, z, type = 'fighter') {
490
+ const enemyParts = [];
491
+
492
+ try {
493
+ switch (type) {
494
+ case 'fighter': {
495
+ // Aggressive angular fighter
496
+ const body = trackMesh(createCube(1.2, 0xff3030, [x, y, z]));
497
+ setScale(body, 1.5, 1, 2.5);
498
+ enemyParts.push(body);
499
+
500
+ // Sharp wings
501
+ const leftWing = trackMesh(createCube(1.8, 0xcc2020, [x - 1.2, y, z]));
502
+ setScale(leftWing, 1, 0.4, 1.8);
503
+ setRotation(leftWing, 0, 0, -0.3);
504
+ enemyParts.push(leftWing);
505
+
506
+ const rightWing = trackMesh(createCube(1.8, 0xcc2020, [x + 1.2, y, z]));
507
+ setScale(rightWing, 1, 0.4, 1.8);
508
+ setRotation(rightWing, 0, 0, 0.3);
509
+ enemyParts.push(rightWing);
510
+ break;
511
+ }
512
+
513
+ case 'bomber': {
514
+ // Large, slow bomber
515
+ const bomberBody = trackMesh(createCube(2, 0xff6600, [x, y, z]));
516
+ setScale(bomberBody, 2, 1.5, 3);
517
+ enemyParts.push(bomberBody);
518
+ break;
519
+ }
520
+
521
+ case 'interceptor': {
522
+ // Fast, small interceptor
523
+ const interceptorBody = trackMesh(createSphere(0.8, 0xff8888, [x, y, z]));
524
+ enemyParts.push(interceptorBody);
525
+ break;
526
+ }
527
+ }
528
+
529
+ // Engine glow for all types
530
+ const engineGlow = trackMesh(createSphere(0.4, 0xff4400, [x, y, z - 1.5]));
531
+ enemyParts.push(engineGlow);
532
+
533
+ return enemyParts;
534
+ } catch (e) {
535
+ console.warn('Advanced enemy creation error:', e);
536
+ return [trackMesh(createCube(1.5, 0xff3030, [x, y, z]))];
537
+ }
538
+ }
539
+
540
+ // === MOUSE CONTROL SYSTEM ===
541
+ function setupMouseHandlers() {
542
+ try {
543
+ if (typeof document !== 'undefined') {
544
+ const canvas = document.getElementById('screen');
545
+ if (canvas) {
546
+ // Mouse movement for camera/targeting
547
+ canvas.addEventListener('mousemove', e => {
548
+ const rect = canvas.getBoundingClientRect();
549
+ uiData.mouseX = ((e.clientX - rect.left) / rect.width) * 640;
550
+ uiData.mouseY = ((e.clientY - rect.top) / rect.height) * 360;
551
+
552
+ // Update targeting system
553
+ updateTargetingSystem();
554
+ });
555
+
556
+ // Click handlers
557
+ canvas.addEventListener('click', handleCanvasClick);
558
+ canvas.addEventListener('contextmenu', e => e.preventDefault());
559
+ }
560
+ }
561
+ } catch (error) {
562
+ console.warn('Mouse setup error:', error);
563
+ }
564
+ }
565
+
566
+ function handleCanvasClick(e) {
567
+ try {
568
+ const currentScreen = getCurrentScreen ? getCurrentScreen() : 'start';
569
+
570
+ if (currentScreen === 'start') {
571
+ console.log('🚀 Starting professional game...');
572
+ switchToScreen('game');
573
+ } else if (currentScreen === 'game') {
574
+ // Fire weapons or interact
575
+ fireAdvancedWeapons();
576
+ }
577
+ } catch (e) {
578
+ console.warn('Click handler error:', e);
579
+ }
580
+ }
581
+
582
+ // === PROFESSIONAL START SCREEN ===
583
+ function drawProfessionalStartScreen() {
584
+ try {
585
+ // Deep space background
586
+ cls(0x000011);
587
+
588
+ // Animated starfield
589
+ drawAdvancedStarfield();
590
+
591
+ // Animated title with glow effect
592
+ uiData.time += 0.016;
593
+
594
+ const titleGlow = Math.sin(uiData.time * 3) * 0.4 + 0.6;
595
+ const titlePulse = Math.sin(uiData.time * 2) * 20 + 40;
596
+
597
+ // Main title with shadow effect
598
+ print('STAR FOX', 220 + 2, 80 + 2, rgba8(0, 0, 50, 150)); // Shadow
599
+ print('STAR FOX', 220, 80, rgba8(0, Math.floor(titleGlow * 255), 255, 255));
600
+
601
+ print('NOVA 64', 240 + 1, 110 + 1, rgba8(0, 0, 50, 150)); // Shadow
602
+ print('NOVA 64', 240, 110, rgba8(255, Math.floor(titleGlow * 200), 0, 255));
603
+
604
+ // Subtitle
605
+ print('PROFESSIONAL SPACE COMBAT', 180, 140, rgba8(150, 200, 255, 200));
606
+
607
+ // Animated start button
608
+ const buttonPulse = Math.sin(uiData.time * 4) * 0.3 + 0.7;
609
+ const buttonGlow = Math.floor(buttonPulse * 255);
610
+
611
+ // Large, obvious start button
612
+ rect(170, 180, 300, 60, rgba8(0, 50, 100, 150), true);
613
+ rect(165, 175, 310, 70, rgba8(0, buttonGlow / 2, buttonGlow, 200), false);
614
+ rect(167, 177, 306, 66, rgba8(0, buttonGlow / 3, buttonGlow / 2, 150), false);
615
+
616
+ print('CLICK TO START MISSION', 190, 205, rgba8(255, 255, 255, buttonGlow));
617
+
618
+ // Instructions
619
+ print('WASD: Flight Controls | SPACE: Primary Weapons', 150, 270, rgba8(200, 200, 200, 255));
620
+ print('SHIFT: Boost | Mouse: Targeting | ESC: Menu', 170, 290, rgba8(180, 180, 180, 255));
621
+
622
+ // Version info
623
+ print('Nova64 v0.2.0 Professional Edition', 20, 340, rgba8(100, 100, 100, 255));
624
+
625
+ // Animate demo ship if available
626
+ if (gameData.demoShip) {
627
+ updateDemoShip();
628
+ }
629
+ } catch (error) {
630
+ console.warn('Start screen error:', error);
631
+ // Simple fallback
632
+ cls(0x000033);
633
+ print('STAR FOX NOVA 64', 220, 100, rgba8(0, 255, 255, 255));
634
+ print('CLICK TO START', 270, 200, rgba8(255, 255, 0, 255));
635
+ }
636
+ }
637
+
638
+ function updateDemoShip() {
639
+ if (!gameData.demoShip) return;
640
+
641
+ gameData.demoShip.rotation += 0.01;
642
+
643
+ // Rotate demo ship parts
644
+ if (gameData.demoShip.parts) {
645
+ for (let part of gameData.demoShip.parts) {
646
+ if (part) {
647
+ setRotation(part, 0, gameData.demoShip.rotation, 0);
648
+ }
649
+ }
650
+ }
651
+ }
652
+
653
+ function updateStartScreen(dt) {
654
+ // Any key or click starts the game
655
+ if (
656
+ isKeyPressed(' ') ||
657
+ isKeyPressed('Enter') ||
658
+ isKeyPressed('w') ||
659
+ isKeyPressed('a') ||
660
+ isKeyPressed('s') ||
661
+ isKeyPressed('d')
662
+ ) {
663
+ console.log('🚀 Starting via keyboard...');
664
+ switchToScreen('game');
665
+ }
666
+ }
667
+
668
+ // === ADVANCED GAME SETUP ===
669
+ function setupGameEnvironment() {
670
+ try {
671
+ // Deserted Space style camera - closer and more dynamic
672
+ if (typeof setCameraPosition === 'function') {
673
+ setCameraPosition(0, 15, 35);
674
+ setCameraTarget(0, 0, -20);
675
+ setCameraFOV(75);
676
+ }
677
+
678
+ // Bright lighting for better visibility
679
+ if (typeof setLightDirection === 'function') {
680
+ setLightDirection(0, -1, -0.5);
681
+ }
682
+ if (typeof setAmbientLight === 'function') {
683
+ setAmbientLight(0.4);
684
+ }
685
+
686
+ console.log('🌌 Deserted Space style environment initialized');
687
+ } catch (e) {
688
+ console.warn('Environment setup error:', e);
689
+ }
690
+ }
691
+
692
+ function createPlayerShip() {
693
+ try {
694
+ // Reset player state
695
+ gameData.arwing = {
696
+ position: { x: 0, y: 0, z: 0 },
697
+ velocity: { x: 0, y: 0, z: 0 },
698
+ rotation: { x: 0, y: 0, z: 0 },
699
+ parts: [],
700
+ lastShot: 0,
701
+ boost: false,
702
+ shields: true,
703
+ };
704
+
705
+ // Create advanced Arwing
706
+ gameData.arwing.parts = createAdvancedArwing(0, 0, 0);
707
+ gameData.arwing.mesh = gameData.arwing.parts[0]; // Main body reference
708
+
709
+ console.log('✅ Professional Arwing created with', gameData.arwing.parts.length, 'components');
710
+ } catch (e) {
711
+ console.warn('Player ship creation error:', e);
712
+ // Fallback
713
+ gameData.arwing.mesh = trackMesh(createCube(2, 0x0099ff, [0, 0, 0]));
714
+ }
715
+ }
716
+
717
+ function initializeGameSystems() {
718
+ // Reset all game systems
719
+ gameData.score = 0;
720
+ gameData.health = 100;
721
+ gameData.shields = 100;
722
+ gameData.energy = 100;
723
+ gameData.wave = 1;
724
+ gameData.time = 0;
725
+
726
+ // Clear arrays
727
+ gameData.enemies = [];
728
+ gameData.projectiles = [];
729
+ gameData.particles = [];
730
+ gameData.powerups = [];
731
+
732
+ console.log('🎮 All game systems initialized');
733
+ }
734
+
735
+ function spawnInitialEnemyWave() {
736
+ const enemyCount = 3 + gameData.wave;
737
+
738
+ for (let i = 0; i < enemyCount; i++) {
739
+ spawnAdvancedEnemy();
740
+ }
741
+
742
+ console.log(`🛸 Wave ${gameData.wave}: ${enemyCount} enemies deployed`);
743
+ }
744
+
745
+ function spawnAdvancedEnemy() {
746
+ try {
747
+ const x = (Math.random() - 0.5) * 80;
748
+ const y = (Math.random() - 0.5) * 40 + 10;
749
+ const z = 150 + Math.random() * 100; // SPAWN IN FRONT of player (positive Z)
750
+
751
+ // Spectacular enemy ships - better than Deserted Space!
752
+ const enemyMesh = trackMesh(createCube(2, 0xff2200, [x, y, z]));
753
+ setScale(enemyMesh, 2.5, 1.5, 3.5);
754
+
755
+ // Add enemy wings for impressive look
756
+ const leftWing = trackMesh(createCube(1.5, 0xcc1100, [x - 2, y, z]));
757
+ setScale(leftWing, 1, 0.4, 2);
758
+
759
+ const rightWing = trackMesh(createCube(1.5, 0xcc1100, [x + 2, y, z]));
760
+ setScale(rightWing, 1, 0.4, 2);
761
+
762
+ // Enemy engine glow
763
+ const glow = trackMesh(createSphere(0.8, 0xff6600, [x, y, z - 2]));
764
+
765
+ gameData.enemies.push({
766
+ mesh: enemyMesh,
767
+ wings: [leftWing, rightWing],
768
+ glow: glow,
769
+ position: { x, y, z },
770
+ velocity: { x: 0, y: 0, z: -25 }, // Move toward player
771
+ health: 3,
772
+ speed: 25 + Math.random() * 20,
773
+ lastShot: 0,
774
+ movePattern: Math.random() > 0.5 ? 'straight' : 'weaving',
775
+ });
776
+ } catch (e) {
777
+ console.warn('Enemy spawn error:', e);
778
+ }
779
+ }
780
+
781
+ // === ADVANCED STARFIELD ===
782
+ function drawAdvancedStarfield() {
783
+ for (let star of gameData.stars) {
784
+ if (star.z > -200 && star.z < 50) {
785
+ // 3D to 2D projection
786
+ const screenX = 320 + (star.x / (Math.abs(star.z) + 1)) * 400;
787
+ const screenY = 180 + (star.y / (Math.abs(star.z) + 1)) * 400;
788
+
789
+ if (screenX > -10 && screenX < 650 && screenY > -10 && screenY < 370) {
790
+ // Calculate star properties
791
+ const distance = Math.abs(star.z);
792
+ const alpha = Math.min(255, star.brightness * 255 * (100 / distance));
793
+ const size = Math.max(1, star.size * (50 / distance));
794
+
795
+ // Twinkling effect
796
+ star.twinkle += 0.1;
797
+ const twinkle = Math.sin(star.twinkle) * 0.3 + 0.7;
798
+ const finalAlpha = Math.floor(alpha * twinkle);
799
+
800
+ // Draw star with size and brightness
801
+ if (size > 2) {
802
+ // Large stars get a glow effect
803
+ rect(
804
+ screenX - size,
805
+ screenY - size,
806
+ size * 2,
807
+ size * 2,
808
+ rgba8(255, 255, 255, finalAlpha * 0.3),
809
+ true
810
+ );
811
+ }
812
+ rect(
813
+ screenX - size / 2,
814
+ screenY - size / 2,
815
+ size,
816
+ size,
817
+ rgba8(255, 255, 255, finalAlpha),
818
+ true
819
+ );
820
+ }
821
+ }
822
+ }
823
+ }
824
+
825
+ // Space floor is now handled by 3D meshes, no 2D drawing needed
826
+
827
+ // === GAME SYSTEMS UPDATE ===
828
+ function updateGameSystems(dt) {
829
+ gameData.time += dt;
830
+
831
+ // Update all systems
832
+ updatePlayerShip(dt);
833
+ updateEnemies(dt);
834
+ updateProjectiles(dt);
835
+ updateParticles(dt);
836
+ updateStarfield(dt);
837
+ updateSpaceFloor(dt);
838
+ updateCamera(dt);
839
+ updateEffects(dt);
840
+
841
+ // Check wave completion
842
+ if (gameData.enemies.length === 0) {
843
+ gameData.wave++;
844
+ gameData.score += gameData.wave * 500; // Wave bonus
845
+ spawnInitialEnemyWave();
846
+ console.log(`🌊 Wave ${gameData.wave} begins! Bonus: ${gameData.wave * 500} points`);
847
+ }
848
+
849
+ // Check game over
850
+ if (gameData.health <= 0) {
851
+ console.log('💀 Mission Failed - Final Score:', gameData.score);
852
+ switchToScreen('gameover');
853
+ }
854
+
855
+ // Menu escape
856
+ if (isKeyPressed('Escape')) {
857
+ switchToScreen('start');
858
+ }
859
+ }
860
+
861
+ function updatePlayerShip(dt) {
862
+ const ship = gameData.arwing;
863
+ const physics = gameSettings.physics;
864
+
865
+ // Direct, responsive movement like Deserted Space
866
+ const moveSpeed = 25;
867
+ const inputStrength = 1.0;
868
+
869
+ // Direct position control (not velocity-based)
870
+ if (isKeyDown('a') || isKeyDown('ArrowLeft')) {
871
+ ship.position.x -= moveSpeed * dt * inputStrength;
872
+ }
873
+ if (isKeyDown('d') || isKeyDown('ArrowRight')) {
874
+ ship.position.x += moveSpeed * dt * inputStrength;
875
+ }
876
+ if (isKeyDown('w') || isKeyDown('ArrowUp')) {
877
+ ship.position.y += moveSpeed * dt * inputStrength;
878
+ }
879
+ if (isKeyDown('s') || isKeyDown('ArrowDown')) {
880
+ ship.position.y -= moveSpeed * dt * inputStrength;
881
+ }
882
+
883
+ // Boost system
884
+ ship.boost = isKeyDown('Shift');
885
+ if (ship.boost) {
886
+ // Boost increases movement speed
887
+ if (isKeyDown('a') || isKeyDown('ArrowLeft')) ship.position.x -= moveSpeed * dt;
888
+ if (isKeyDown('d') || isKeyDown('ArrowRight')) ship.position.x += moveSpeed * dt;
889
+ if (isKeyDown('w') || isKeyDown('ArrowUp')) ship.position.y += moveSpeed * dt;
890
+ if (isKeyDown('s') || isKeyDown('ArrowDown')) ship.position.y -= moveSpeed * dt;
891
+ }
892
+
893
+ // Keep ship in bounds
894
+ ship.position.x = Math.max(-40, Math.min(40, ship.position.x));
895
+ ship.position.y = Math.max(-10, Math.min(25, ship.position.y));
896
+
897
+ // Banking rotation based on input (not velocity)
898
+ const bankAmount = 0.6;
899
+ ship.rotation.z = 0;
900
+ ship.rotation.x = 0;
901
+
902
+ if (isKeyDown('a') || isKeyDown('ArrowLeft')) {
903
+ ship.rotation.z = bankAmount;
904
+ } else if (isKeyDown('d') || isKeyDown('ArrowRight')) {
905
+ ship.rotation.z = -bankAmount;
906
+ }
907
+
908
+ if (isKeyDown('w') || isKeyDown('ArrowUp')) {
909
+ ship.rotation.x = -0.3;
910
+ } else if (isKeyDown('s') || isKeyDown('ArrowDown')) {
911
+ ship.rotation.x = 0.3;
912
+ }
913
+
914
+ // Always moving forward through space
915
+ ship.speed = ship.boost ? 40 : 25;
916
+
917
+ // Continuous weapon fire when held
918
+ if (isKeyDown(' ')) {
919
+ if (gameData.time - ship.lastShot > 0.1) {
920
+ fireDesertedSpaceWeapons();
921
+ ship.lastShot = gameData.time;
922
+ }
923
+ }
924
+
925
+ // Update ship mesh positions
926
+ updateShipMeshes();
927
+ }
928
+
929
+ function updateShipMeshes() {
930
+ const ship = gameData.arwing;
931
+
932
+ if (ship.parts) {
933
+ for (let part of ship.parts) {
934
+ if (part) {
935
+ setPosition(part, ship.position.x, ship.position.y, ship.position.z);
936
+ setRotation(part, ship.rotation.x, ship.rotation.y, ship.rotation.z);
937
+ }
938
+ }
939
+ } else if (ship.mesh) {
940
+ setPosition(ship.mesh, ship.position.x, ship.position.y, ship.position.z);
941
+ setRotation(ship.mesh, ship.rotation.x, ship.rotation.y, ship.rotation.z);
942
+ }
943
+ }
944
+
945
+ function fireDesertedSpaceWeapons() {
946
+ try {
947
+ const ship = gameData.arwing;
948
+
949
+ // Simple, fast twin laser system like Deserted Space
950
+ const laserPositions = [
951
+ { x: -1.5, y: 0, z: 2.5 },
952
+ { x: 1.5, y: 0, z: 2.5 },
953
+ ];
954
+
955
+ for (let pos of laserPositions) {
956
+ const worldX = ship.position.x + pos.x;
957
+ const worldY = ship.position.y + pos.y;
958
+ const worldZ = ship.position.z + pos.z;
959
+
960
+ // Create fast, bright laser projectiles
961
+ const laser = trackMesh(createSphere(0.2, 0x00ff00, [worldX, worldY, worldZ]));
962
+ setScale(laser, 1, 1, 3); // Long, thin lasers
963
+
964
+ gameData.projectiles.push({
965
+ mesh: laser,
966
+ position: { x: worldX, y: worldY, z: worldZ },
967
+ velocity: { x: 0, y: 0, z: -100 }, // Very fast
968
+ friendly: true,
969
+ damage: 1,
970
+ life: 3.0,
971
+ });
972
+ }
973
+
974
+ // Update score for firing (tiny increment for activity)
975
+ gameData.score += 1;
976
+ } catch (e) {
977
+ console.warn('Weapon firing error:', e);
978
+ }
979
+ }
980
+
981
+ function createMuzzleFlash(x, y, z) {
982
+ for (let i = 0; i < 8; i++) {
983
+ gameData.particles.push({
984
+ position: {
985
+ x: x + (Math.random() - 0.5) * 0.8,
986
+ y: y + (Math.random() - 0.5) * 0.8,
987
+ z: z + Math.random() * 1,
988
+ },
989
+ velocity: {
990
+ x: (Math.random() - 0.5) * 12,
991
+ y: (Math.random() - 0.5) * 12,
992
+ z: (Math.random() - 0.5) * 8,
993
+ },
994
+ life: 0.4,
995
+ maxLife: 0.4,
996
+ color: Math.random() > 0.5 ? 0x00ffaa : 0x88ffff,
997
+ size: Math.random() * 0.5 + 0.3,
998
+ });
999
+ }
1000
+ }
1001
+
1002
+ function createEngineTrails() {
1003
+ const ship = gameData.arwing;
1004
+
1005
+ // Create boost trails from engine positions
1006
+ const enginePositions = [
1007
+ { x: -2.2, y: -0.3, z: -2.5 },
1008
+ { x: 2.2, y: -0.3, z: -2.5 },
1009
+ ];
1010
+
1011
+ for (let pos of enginePositions) {
1012
+ for (let i = 0; i < 3; i++) {
1013
+ gameData.particles.push({
1014
+ position: {
1015
+ x: ship.position.x + pos.x + (Math.random() - 0.5) * 0.5,
1016
+ y: ship.position.y + pos.y + (Math.random() - 0.5) * 0.3,
1017
+ z: ship.position.z + pos.z,
1018
+ },
1019
+ velocity: {
1020
+ x: (Math.random() - 0.5) * 4,
1021
+ y: (Math.random() - 0.5) * 2,
1022
+ z: 15 + Math.random() * 10, // Trail behind ship
1023
+ },
1024
+ life: 0.8,
1025
+ maxLife: 0.8,
1026
+ color: Math.random() > 0.5 ? 0xff6600 : 0xffaa00,
1027
+ size: Math.random() * 0.4 + 0.2,
1028
+ });
1029
+ }
1030
+ }
1031
+ }
1032
+
1033
+ // === ENEMY AI SYSTEM ===
1034
+ function updateEnemies(dt) {
1035
+ for (let i = gameData.enemies.length - 1; i >= 0; i--) {
1036
+ const enemy = gameData.enemies[i];
1037
+
1038
+ // Simple AI behavior
1039
+ updateEnemyAI(enemy, dt);
1040
+
1041
+ // Update enemy mesh position
1042
+ if (enemy.mesh) {
1043
+ setPosition(enemy.mesh, enemy.position.x, enemy.position.y, enemy.position.z);
1044
+ }
1045
+
1046
+ // Update enemy wings
1047
+ if (enemy.wings) {
1048
+ setPosition(enemy.wings[0], enemy.position.x - 2, enemy.position.y, enemy.position.z);
1049
+ setPosition(enemy.wings[1], enemy.position.x + 2, enemy.position.y, enemy.position.z);
1050
+ }
1051
+
1052
+ // Update enemy glow
1053
+ if (enemy.glow) {
1054
+ setPosition(enemy.glow, enemy.position.x, enemy.position.y, enemy.position.z - 2);
1055
+ }
1056
+
1057
+ // Remove if too close or dead (changed from z > 30 to z < -30 since they move toward player)
1058
+ if (enemy.position.z < -30 || enemy.health <= 0) {
1059
+ if (enemy.position.z < -30) {
1060
+ // Enemy reached player
1061
+ gameData.health -= 20;
1062
+ console.log('💥 Enemy breakthrough! Health:', gameData.health);
1063
+ }
1064
+
1065
+ // Cleanup enemy and all parts
1066
+ if (enemy.mesh) destroyMesh(enemy.mesh);
1067
+ if (enemy.wings) {
1068
+ destroyMesh(enemy.wings[0]);
1069
+ destroyMesh(enemy.wings[1]);
1070
+ }
1071
+ if (enemy.glow) destroyMesh(enemy.glow);
1072
+ gameData.enemies.splice(i, 1);
1073
+ }
1074
+ }
1075
+ }
1076
+
1077
+ function updateEnemyAI(enemy, dt) {
1078
+ // Move toward player (negative Z direction since player is at Z=0)
1079
+ enemy.position.z -= enemy.speed * dt;
1080
+
1081
+ if (enemy.movePattern === 'weaving') {
1082
+ // Spectacular weaving movement - better than Deserted Space!
1083
+ enemy.position.x += Math.sin(gameData.time * 3 + enemy.position.z * 0.1) * 25 * dt;
1084
+ enemy.position.y += Math.cos(gameData.time * 2 + enemy.position.z * 0.05) * 12 * dt;
1085
+ }
1086
+
1087
+ // Occasional shooting
1088
+ if (gameData.time - enemy.lastShot > 2 && Math.random() > 0.98) {
1089
+ fireEnemyWeapon(enemy);
1090
+ enemy.lastShot = gameData.time;
1091
+ }
1092
+ }
1093
+
1094
+ function fireEnemyWeapon(enemy) {
1095
+ try {
1096
+ const laser = trackMesh(
1097
+ createSphere(0.12, 0xff3300, [enemy.position.x, enemy.position.y, enemy.position.z + 1])
1098
+ );
1099
+
1100
+ gameData.projectiles.push({
1101
+ mesh: laser,
1102
+ position: { ...enemy.position },
1103
+ velocity: { x: 0, y: 0, z: 25 },
1104
+ friendly: false,
1105
+ damage: 15,
1106
+ life: 2.0,
1107
+ });
1108
+ } catch (e) {
1109
+ console.warn('Enemy weapon error:', e);
1110
+ }
1111
+ }
1112
+
1113
+ // === PROJECTILE SYSTEM ===
1114
+ function updateProjectiles(dt) {
1115
+ for (let i = gameData.projectiles.length - 1; i >= 0; i--) {
1116
+ const proj = gameData.projectiles[i];
1117
+
1118
+ // Move projectile
1119
+ proj.position.x += proj.velocity.x * dt;
1120
+ proj.position.y += proj.velocity.y * dt;
1121
+ proj.position.z += proj.velocity.z * dt;
1122
+
1123
+ proj.life -= dt;
1124
+
1125
+ if (proj.mesh) {
1126
+ setPosition(proj.mesh, proj.position.x, proj.position.y, proj.position.z);
1127
+ }
1128
+
1129
+ // Collision detection
1130
+ if (proj.friendly) {
1131
+ // Player projectiles vs enemies
1132
+ for (let j = gameData.enemies.length - 1; j >= 0; j--) {
1133
+ const enemy = gameData.enemies[j];
1134
+ const distance = Math.sqrt(
1135
+ Math.pow(proj.position.x - enemy.position.x, 2) +
1136
+ Math.pow(proj.position.y - enemy.position.y, 2) +
1137
+ Math.pow(proj.position.z - enemy.position.z, 2)
1138
+ );
1139
+
1140
+ if (distance < 2.5) {
1141
+ // Hit enemy!
1142
+ enemy.health -= proj.damage;
1143
+
1144
+ // Score and effects
1145
+ gameData.score += 50;
1146
+ createHitEffect(enemy.position.x, enemy.position.y, enemy.position.z);
1147
+
1148
+ // Remove projectile
1149
+ if (proj.mesh) destroyMesh(proj.mesh);
1150
+ gameData.projectiles.splice(i, 1);
1151
+
1152
+ // Check if enemy destroyed
1153
+ if (enemy.health <= 0) {
1154
+ gameData.score +=
1155
+ enemy.type === 'bomber' ? 300 : enemy.type === 'interceptor' ? 150 : 200;
1156
+ gameData.kills++;
1157
+ createExplosion(enemy.position.x, enemy.position.y, enemy.position.z);
1158
+ console.log(
1159
+ `💥 ${enemy.type} destroyed! +${enemy.type === 'bomber' ? 300 : enemy.type === 'interceptor' ? 150 : 200} points`
1160
+ );
1161
+ }
1162
+
1163
+ break;
1164
+ }
1165
+ }
1166
+ } else {
1167
+ // Enemy projectiles vs player
1168
+ const player = gameData.arwing;
1169
+ const distance = Math.sqrt(
1170
+ Math.pow(proj.position.x - player.position.x, 2) +
1171
+ Math.pow(proj.position.y - player.position.y, 2) +
1172
+ Math.pow(proj.position.z - player.position.z, 2)
1173
+ );
1174
+
1175
+ if (distance < 2.0) {
1176
+ // Player hit!
1177
+ gameData.health -= proj.damage;
1178
+ gameData.shields = Math.max(0, gameData.shields - proj.damage);
1179
+
1180
+ createHitEffect(player.position.x, player.position.y, player.position.z);
1181
+
1182
+ // Remove projectile
1183
+ if (proj.mesh) destroyMesh(proj.mesh);
1184
+ gameData.projectiles.splice(i, 1);
1185
+
1186
+ console.log(`💥 Player hit! Health: ${gameData.health}, Shields: ${gameData.shields}`);
1187
+ continue;
1188
+ }
1189
+ }
1190
+
1191
+ // Remove if out of range or lifetime expired
1192
+ if (proj.life <= 0 || Math.abs(proj.position.z) > 100) {
1193
+ if (proj.mesh) destroyMesh(proj.mesh);
1194
+ gameData.projectiles.splice(i, 1);
1195
+ }
1196
+ }
1197
+ }
1198
+
1199
+ function createHitEffect(x, y, z) {
1200
+ for (let i = 0; i < 10; i++) {
1201
+ gameData.particles.push({
1202
+ position: {
1203
+ x: x + (Math.random() - 0.5) * 2,
1204
+ y: y + (Math.random() - 0.5) * 2,
1205
+ z: z + (Math.random() - 0.5) * 2,
1206
+ },
1207
+ velocity: {
1208
+ x: (Math.random() - 0.5) * 15,
1209
+ y: (Math.random() - 0.5) * 15,
1210
+ z: (Math.random() - 0.5) * 10,
1211
+ },
1212
+ life: 0.8,
1213
+ maxLife: 0.8,
1214
+ color: 0xff6600,
1215
+ size: Math.random() * 0.4 + 0.3,
1216
+ });
1217
+ }
1218
+ }
1219
+
1220
+ function createExplosion(x, y, z) {
1221
+ for (let i = 0; i < 25; i++) {
1222
+ gameData.particles.push({
1223
+ position: {
1224
+ x: x + (Math.random() - 0.5) * 3,
1225
+ y: y + (Math.random() - 0.5) * 3,
1226
+ z: z + (Math.random() - 0.5) * 3,
1227
+ },
1228
+ velocity: {
1229
+ x: (Math.random() - 0.5) * 20,
1230
+ y: (Math.random() - 0.5) * 20,
1231
+ z: (Math.random() - 0.5) * 15,
1232
+ },
1233
+ life: 1.2,
1234
+ maxLife: 1.2,
1235
+ color: Math.random() > 0.5 ? 0xff3300 : 0xff8800,
1236
+ size: Math.random() * 0.6 + 0.4,
1237
+ });
1238
+ }
1239
+ }
1240
+
1241
+ // === PARTICLE SYSTEM ===
1242
+ function updateParticles(dt) {
1243
+ for (let i = gameData.particles.length - 1; i >= 0; i--) {
1244
+ const p = gameData.particles[i];
1245
+
1246
+ // Update physics
1247
+ p.position.x += p.velocity.x * dt;
1248
+ p.position.y += p.velocity.y * dt;
1249
+ p.position.z += p.velocity.z * dt;
1250
+
1251
+ // Apply gravity and drag
1252
+ p.velocity.y -= 8 * dt;
1253
+ p.velocity.x *= 0.95;
1254
+ p.velocity.y *= 0.95;
1255
+ p.velocity.z *= 0.95;
1256
+
1257
+ // Age particle
1258
+ p.life -= dt;
1259
+
1260
+ if (p.life <= 0) {
1261
+ gameData.particles.splice(i, 1);
1262
+ }
1263
+ }
1264
+ }
1265
+
1266
+ // updateStarfield duplicate removed - already defined above at line 385
1267
+
1268
+ function updateSpaceFloor(dt) {
1269
+ // Simple, stable starfield update - no more crashes!
1270
+ updateStarfield(dt);
1271
+
1272
+ // Animate all space elements with Crystal Cathedral effects!
1273
+ for (let element of gameData.spaceFloor) {
1274
+ if (element.mesh) {
1275
+ // Move toward player
1276
+ element.customZ += scrollSpeed * dt;
1277
+
1278
+ let y = -58; // Default height
1279
+ let x = element.x;
1280
+
1281
+ // SPECTACULAR animations for different element types
1282
+ if (element.type === 'pillar') {
1283
+ // Crystal pillars pulse with holographic energy
1284
+ element.glowPhase += dt * 2;
1285
+ const glow = Math.sin(element.glowPhase) * 0.5 + 0.5;
1286
+ y = element.height / 2 - 60 + Math.sin(element.glowPhase * 0.5) * 2;
1287
+
1288
+ // Update pillar cap if it exists
1289
+ if (element.cap) {
1290
+ setPosition(
1291
+ element.cap,
1292
+ x,
1293
+ element.height - 40 + Math.sin(element.glowPhase) * 3,
1294
+ element.customZ
1295
+ );
1296
+ }
1297
+ } else if (element.type === 'crystal') {
1298
+ // Floating crystals with complex motion
1299
+ element.floatPhase += dt * 1.5;
1300
+ y = element.originalY + Math.sin(element.floatPhase) * 8;
1301
+ x = element.x + Math.cos(element.floatPhase * 0.7) * 5;
1302
+ } else if (element.type === 'horizontal' || element.type === 'vertical') {
1303
+ // Holographic grid lines pulse
1304
+ element.glowPhase += dt * 4;
1305
+ const pulseIntensity = Math.sin(element.glowPhase) * 0.3 + 0.7;
1306
+ y = -55 + Math.sin(element.glowPhase) * 2;
1307
+
1308
+ // Change color cycling
1309
+ const intensity = Math.sin(element.glowPhase * 2) * 0.5 + 0.5;
1310
+ // Note: We can't easily change mesh colors in real-time, but the bobbing looks great
1311
+ }
1312
+
1313
+ setPosition(element.mesh, x, y, element.customZ);
1314
+
1315
+ // Reset when element passes the player
1316
+ if (element.customZ > 100) {
1317
+ element.customZ = element.originalZ - 200;
1318
+ }
1319
+ }
1320
+ }
1321
+
1322
+ // Animate atmospheric particles like Crystal Cathedral
1323
+ if (gameData.atmosphericParticles) {
1324
+ for (let particle of gameData.atmosphericParticles) {
1325
+ if (particle.mesh) {
1326
+ // Move particles toward player
1327
+ particle.customZ += particle.speed * dt;
1328
+
1329
+ // Floating motion like Crystal Cathedral
1330
+ particle.phase += dt * 2;
1331
+ const floatY = particle.originalY + Math.sin(particle.phase) * 6;
1332
+ const driftX = particle.x + Math.cos(particle.phase * 0.5) * 4;
1333
+
1334
+ setPosition(particle.mesh, driftX, floatY, particle.customZ);
1335
+
1336
+ // Reset when too close
1337
+ if (particle.customZ > 40) {
1338
+ particle.customZ = particle.originalZ - 200;
1339
+ particle.x = (Math.random() - 0.5) * 300;
1340
+ particle.originalY = (Math.random() - 0.5) * 80;
1341
+ }
1342
+ }
1343
+ }
1344
+ }
1345
+ }
1346
+
1347
+ function updateCamera(dt) {
1348
+ // Smooth camera follow
1349
+ const target = gameData.arwing.position;
1350
+ const cam = gameData.camera;
1351
+
1352
+ const targetX = target.x * 0.4;
1353
+ const targetY = target.y * 0.3 + 5;
1354
+ const targetZ = target.z + 18;
1355
+
1356
+ cam.position.x += (targetX - cam.position.x) * cam.followSmoothing;
1357
+ cam.position.y += (targetY - cam.position.y) * cam.followSmoothing;
1358
+ cam.position.z += (targetZ - cam.position.z) * cam.followSmoothing;
1359
+
1360
+ // Camera shake
1361
+ if (cam.shake > 0) {
1362
+ cam.shake -= dt * 3;
1363
+ const intensity = cam.shakeIntensity * cam.shake;
1364
+
1365
+ const shakeX = (Math.random() - 0.5) * intensity;
1366
+ const shakeY = (Math.random() - 0.5) * intensity;
1367
+ const shakeZ = (Math.random() - 0.5) * intensity * 0.5;
1368
+
1369
+ if (typeof setCameraPosition === 'function') {
1370
+ setCameraPosition(cam.position.x + shakeX, cam.position.y + shakeY, cam.position.z + shakeZ);
1371
+ }
1372
+ } else {
1373
+ if (typeof setCameraPosition === 'function') {
1374
+ setCameraPosition(cam.position.x, cam.position.y, cam.position.z);
1375
+ }
1376
+ }
1377
+ }
1378
+
1379
+ function updateEffects(dt) {
1380
+ // Screen flash effect
1381
+ if (gameData.effects.screenFlash > 0) {
1382
+ gameData.effects.screenFlash -= dt * 4;
1383
+ }
1384
+
1385
+ // Energy regeneration
1386
+ if (gameData.energy < 100) {
1387
+ gameData.energy = Math.min(100, gameData.energy + 20 * dt);
1388
+ }
1389
+
1390
+ // Shield regeneration (if not taking damage)
1391
+ if (gameData.shields < 100 && gameData.time - (gameData.lastDamageTime || 0) > 3) {
1392
+ gameData.shields = Math.min(100, gameData.shields + 15 * dt);
1393
+ }
1394
+ }
1395
+
1396
+ function updateTargetingSystem() {
1397
+ // Update targeting based on mouse position
1398
+ // This would be used for more advanced targeting mechanics
1399
+ }
1400
+
1401
+ // === DRAWING FUNCTIONS ===
1402
+ function drawGameEnvironment() {
1403
+ // Deep space background with slight blue tint
1404
+ cls(0x000015);
1405
+
1406
+ // Advanced starfield
1407
+ drawAdvancedStarfield();
1408
+
1409
+ // 3D space floor and objects render automatically
1410
+
1411
+ // Particle effects overlay
1412
+ drawParticleEffects();
1413
+ }
1414
+
1415
+ function drawParticleEffects() {
1416
+ for (let p of gameData.particles) {
1417
+ if (p.position.z > -50 && p.position.z < 50) {
1418
+ // 3D to 2D projection
1419
+ const depth = Math.abs(p.position.z) + 10;
1420
+ const screenX = 320 + (p.position.x / depth) * 600;
1421
+ const screenY = 180 + (p.position.y / depth) * 600;
1422
+
1423
+ if (screenX > -20 && screenX < 660 && screenY > -20 && screenY < 380) {
1424
+ const alpha = Math.floor(255 * (p.life / p.maxLife));
1425
+ const size = p.size * (30 / depth);
1426
+
1427
+ rect(
1428
+ screenX - size / 2,
1429
+ screenY - size / 2,
1430
+ size,
1431
+ size,
1432
+ rgba8((p.color >> 16) & 0xff, (p.color >> 8) & 0xff, p.color & 0xff, alpha),
1433
+ true
1434
+ );
1435
+ }
1436
+ }
1437
+ }
1438
+ }
1439
+
1440
+ function drawProfessionalHUD() {
1441
+ try {
1442
+ // Deserted Space style HUD - clean and minimal
1443
+
1444
+ // Large score display (top center)
1445
+ drawDesertedSpaceScore();
1446
+
1447
+ // Health indicator (bottom left)
1448
+ drawDesertedSpaceHealth();
1449
+
1450
+ // Simple crosshair (center)
1451
+ drawDesertedSpaceCrosshair();
1452
+
1453
+ // Speed/status indicators (top corners)
1454
+ drawDesertedSpaceStatus();
1455
+
1456
+ // Enemy count (top right)
1457
+ drawDesertedSpaceEnemyCount();
1458
+ } catch (e) {
1459
+ console.warn('HUD draw error:', e);
1460
+ }
1461
+ }
1462
+
1463
+ function drawDesertedSpaceScore() {
1464
+ // Large, prominent score like Deserted Space
1465
+ const scoreText = gameData.score.toString().padStart(8, '0');
1466
+ const scorePulse = Math.sin(gameData.time * 2) * 0.2 + 0.8;
1467
+
1468
+ // Background for score
1469
+ rect(250, 10, 140, 40, rgba8(0, 0, 0, 100), true);
1470
+ rect(250, 10, 140, 40, rgba8(0, 255, 100, 150), false);
1471
+
1472
+ // Score text - large and bright
1473
+ print('SCORE', 260, 18, rgba8(255, 255, 255, 200));
1474
+ print(scoreText, 260, 32, rgba8(0, 255, 0, Math.floor(255 * scorePulse)));
1475
+ }
1476
+
1477
+ function drawDesertedSpaceHealth() {
1478
+ // Simple health bar like Deserted Space
1479
+ const healthWidth = (gameData.health / 100) * 120;
1480
+
1481
+ rect(20, 320, 130, 25, rgba8(20, 20, 20, 180), true);
1482
+ rect(20, 320, 130, 25, rgba8(100, 100, 100, 200), false);
1483
+
1484
+ // Health bar color
1485
+ let healthColor = rgba8(0, 255, 0, 200);
1486
+ if (gameData.health < 50) healthColor = rgba8(255, 255, 0, 200);
1487
+ if (gameData.health < 25) healthColor = rgba8(255, 0, 0, 200);
1488
+
1489
+ rect(25, 325, healthWidth, 15, healthColor, true);
1490
+ print('HULL', 30, 328, rgba8(255, 255, 255, 255));
1491
+ }
1492
+
1493
+ function drawDesertedSpaceCrosshair() {
1494
+ // Simple, clean crosshair
1495
+ const centerX = 320,
1496
+ centerY = 180;
1497
+ const crosshairColor = rgba8(0, 255, 0, 150);
1498
+
1499
+ // Main cross
1500
+ line(centerX - 20, centerY, centerX + 20, centerY, crosshairColor);
1501
+ line(centerX, centerY - 20, centerX, centerY + 20, crosshairColor);
1502
+
1503
+ // Center dot
1504
+ rect(centerX - 2, centerY - 2, 4, 4, rgba8(255, 255, 255, 255), true);
1505
+ }
1506
+
1507
+ function drawDesertedSpaceStatus() {
1508
+ // Top left - Wave info
1509
+ print(`WAVE ${gameData.wave}`, 20, 20, rgba8(255, 255, 255, 200));
1510
+ print(`ENEMIES: ${gameData.enemies.length}`, 20, 35, rgba8(255, 200, 0, 200));
1511
+
1512
+ // Top right - Speed
1513
+ const speed = Math.floor(gameData.arwing.speed || 25);
1514
+ print(`SPEED: ${speed}`, 500, 20, rgba8(0, 200, 255, 200));
1515
+
1516
+ if (gameData.arwing.boost) {
1517
+ print('BOOST', 500, 35, rgba8(255, 100, 0, 255));
1518
+ }
1519
+ }
1520
+
1521
+ function drawDesertedSpaceEnemyCount() {
1522
+ // Enemy radar dots - simple version
1523
+ const radarX = 580,
1524
+ radarY = 80;
1525
+ const radarSize = 40;
1526
+
1527
+ // Radar background
1528
+ rect(
1529
+ radarX - radarSize / 2,
1530
+ radarY - radarSize / 2,
1531
+ radarSize,
1532
+ radarSize,
1533
+ rgba8(0, 20, 0, 100),
1534
+ true
1535
+ );
1536
+ rect(
1537
+ radarX - radarSize / 2,
1538
+ radarY - radarSize / 2,
1539
+ radarSize,
1540
+ radarSize,
1541
+ rgba8(0, 255, 0, 150),
1542
+ false
1543
+ );
1544
+
1545
+ // Player center
1546
+ rect(radarX - 1, radarY - 1, 2, 2, rgba8(0, 255, 255, 255), true);
1547
+
1548
+ // Enemy dots
1549
+ for (let i = 0; i < Math.min(gameData.enemies.length, 8); i++) {
1550
+ const angle = (i / 8) * Math.PI * 2;
1551
+ const dotX = radarX + Math.cos(angle) * 15;
1552
+ const dotY = radarY + Math.sin(angle) * 15;
1553
+ rect(dotX - 1, dotY - 1, 2, 2, rgba8(255, 0, 0, 200), true);
1554
+ }
1555
+ }
1556
+
1557
+ function drawHealthPanel() {
1558
+ // Enhanced health bar with prominent styling
1559
+ rect(15, 15, 180, 35, rgba8(20, 20, 20, 220), true);
1560
+ rect(15, 15, 180, 35, rgba8(100, 100, 100, 255), false);
1561
+ rect(17, 17, 176, 31, rgba8(50, 50, 50, 150), false);
1562
+
1563
+ // Health bar with glow effect
1564
+ const healthWidth = (gameData.health / 100) * 170;
1565
+ let healthColor =
1566
+ gameData.health > 60
1567
+ ? rgba8(0, 255, 0, 220)
1568
+ : gameData.health > 30
1569
+ ? rgba8(255, 255, 0, 220)
1570
+ : rgba8(255, 0, 0, 220);
1571
+
1572
+ // Add pulsing effect for low health
1573
+ if (gameData.health < 30) {
1574
+ const pulse = Math.sin(gameData.time * 8) * 0.3 + 0.7;
1575
+ healthColor = rgba8(255, Math.floor(100 * pulse), 0, 220);
1576
+ }
1577
+
1578
+ rect(20, 20, healthWidth, 25, healthColor, true);
1579
+
1580
+ // Health text
1581
+ print('HULL INTEGRITY', 25, 23, rgba8(255, 255, 255, 255));
1582
+ print(`${Math.floor(gameData.health)}%`, 150, 35, rgba8(255, 255, 255, 255));
1583
+ }
1584
+
1585
+ function drawShieldsPanel() {
1586
+ // Enhanced shields bar
1587
+ rect(15, 55, 180, 30, rgba8(20, 20, 20, 220), true);
1588
+ rect(15, 55, 180, 30, rgba8(100, 100, 100, 255), false);
1589
+
1590
+ const shieldWidth = (gameData.shields / 100) * 170;
1591
+
1592
+ // Shield color with energy effect
1593
+ const shieldPulse = Math.sin(gameData.time * 6) * 0.2 + 0.8;
1594
+ const shieldColor = rgba8(0, Math.floor(150 * shieldPulse), 255, 200);
1595
+
1596
+ rect(20, 60, shieldWidth, 20, shieldColor, true);
1597
+
1598
+ // Shield text
1599
+ print('SHIELD POWER', 25, 63, rgba8(255, 255, 255, 255));
1600
+ print(`${Math.floor(gameData.shields)}%`, 150, 75, rgba8(255, 255, 255, 255));
1601
+ }
1602
+
1603
+ function drawEnergyPanel() {
1604
+ // Enhanced energy bar
1605
+ rect(15, 90, 180, 30, rgba8(20, 20, 20, 220), true);
1606
+ rect(15, 90, 180, 30, rgba8(100, 100, 100, 255), false);
1607
+
1608
+ const energyWidth = (gameData.energy / 100) * 170;
1609
+
1610
+ // Energy color with charging effect
1611
+ const energyPulse = Math.sin(gameData.time * 5) * 0.3 + 0.7;
1612
+ const energyColor = rgba8(255, Math.floor(150 * energyPulse), 0, 200);
1613
+
1614
+ rect(20, 95, energyWidth, 20, energyColor, true);
1615
+
1616
+ print('WEAPON ENERGY', 25, 98, rgba8(255, 255, 255, 255));
1617
+ print(`${Math.floor(gameData.energy)}%`, 150, 110, rgba8(255, 255, 255, 255));
1618
+ }
1619
+
1620
+ function drawScorePanel() {
1621
+ // Large, prominent score display like Deserted Space
1622
+ rect(450, 15, 180, 80, rgba8(10, 10, 30, 200), true);
1623
+ rect(450, 15, 180, 80, rgba8(0, 255, 255, 150), false);
1624
+ rect(452, 17, 176, 76, rgba8(0, 150, 255, 80), false);
1625
+
1626
+ // Big, bright score text
1627
+ print('SCORE', 460, 25, rgba8(0, 255, 255, 255));
1628
+
1629
+ // Animate score display
1630
+ const scorePulse = Math.sin(gameData.time * 4) * 0.2 + 0.8;
1631
+ const scoreColor = rgba8(255, Math.floor(255 * scorePulse), 0, 255);
1632
+ print(gameData.score.toString(), 460, 45, scoreColor);
1633
+
1634
+ print(`WAVE ${gameData.wave}`, 460, 65, rgba8(150, 255, 150, 255));
1635
+ print(`KILLS: ${gameData.kills}`, 460, 80, rgba8(255, 200, 100, 255));
1636
+ }
1637
+
1638
+ function drawStatusPanel() {
1639
+ // Enhanced status indicators
1640
+ let y = 105;
1641
+
1642
+ // Speed indicator
1643
+ const speed = Math.floor(gameData.arwing.speed || 15);
1644
+ print(`SPEED: ${speed}`, 460, y, rgba8(0, 200, 255, 255));
1645
+ y += 15;
1646
+
1647
+ if (gameData.arwing.boost) {
1648
+ const boostPulse = Math.sin(gameData.time * 10) * 0.5 + 0.5;
1649
+ print('>>> BOOST ACTIVE <<<', 460, y, rgba8(255, Math.floor(200 * boostPulse), 0, 255));
1650
+ y += 15;
1651
+ }
1652
+
1653
+ if (gameData.shields < 25) {
1654
+ const alertPulse = Math.sin(gameData.time * 8) * 0.5 + 0.5;
1655
+ print('!!! LOW SHIELDS !!!', 460, y, rgba8(255, Math.floor(100 * alertPulse), 0, 255));
1656
+ y += 15;
1657
+ }
1658
+
1659
+ if (gameData.energy < 20) {
1660
+ print('LOW WEAPON ENERGY', 460, y, rgba8(255, 255, 0, 255));
1661
+ y += 15;
1662
+ }
1663
+
1664
+ // Combat status
1665
+ if (gameData.enemies.length > 0) {
1666
+ print(`TARGETS: ${gameData.enemies.length}`, 460, y, rgba8(255, 100, 100, 255));
1667
+ }
1668
+ }
1669
+
1670
+ function drawTargetingHUD() {
1671
+ // Large, prominent central crosshair
1672
+ const centerX = 320,
1673
+ centerY = 180;
1674
+
1675
+ // Dynamic crosshair with bright colors
1676
+ const pulse = Math.sin(gameData.time * 8) * 0.4 + 0.6;
1677
+ const crosshairColor = rgba8(0, 255, 100, Math.floor(pulse * 255));
1678
+
1679
+ // Main crosshair - larger and more visible
1680
+ line(centerX - 25, centerY, centerX + 25, centerY, crosshairColor);
1681
+ line(centerX, centerY - 25, centerX, centerY + 25, crosshairColor);
1682
+
1683
+ // Inner crosshair
1684
+ line(centerX - 10, centerY, centerX + 10, centerY, rgba8(255, 255, 255, 200));
1685
+ line(centerX, centerY - 10, centerX, centerY + 10, rgba8(255, 255, 255, 200));
1686
+
1687
+ // Animated targeting brackets
1688
+ const bracketSize = 12;
1689
+ const bracketOffset = Math.sin(gameData.time * 6) * 3 + 30;
1690
+ const bracketColor = rgba8(0, 255, 255, Math.floor(pulse * 255));
1691
+
1692
+ // Top-left bracket
1693
+ line(
1694
+ centerX - bracketOffset,
1695
+ centerY - bracketOffset,
1696
+ centerX - bracketOffset + bracketSize,
1697
+ centerY - bracketOffset,
1698
+ bracketColor
1699
+ );
1700
+ line(
1701
+ centerX - bracketOffset,
1702
+ centerY - bracketOffset,
1703
+ centerX - bracketOffset,
1704
+ centerY - bracketOffset + bracketSize,
1705
+ bracketColor
1706
+ );
1707
+
1708
+ // Top-right bracket
1709
+ line(
1710
+ centerX + bracketOffset,
1711
+ centerY - bracketOffset,
1712
+ centerX + bracketOffset - bracketSize,
1713
+ centerY - bracketOffset,
1714
+ bracketColor
1715
+ );
1716
+ line(
1717
+ centerX + bracketOffset,
1718
+ centerY - bracketOffset,
1719
+ centerX + bracketOffset,
1720
+ centerY - bracketOffset + bracketSize,
1721
+ bracketColor
1722
+ );
1723
+
1724
+ // Bottom-left bracket
1725
+ line(
1726
+ centerX - bracketOffset,
1727
+ centerY + bracketOffset,
1728
+ centerX - bracketOffset + bracketSize,
1729
+ centerY + bracketOffset,
1730
+ bracketColor
1731
+ );
1732
+ line(
1733
+ centerX - bracketOffset,
1734
+ centerY + bracketOffset,
1735
+ centerX - bracketOffset,
1736
+ centerY + bracketOffset - bracketSize,
1737
+ bracketColor
1738
+ );
1739
+
1740
+ // Bottom-right bracket
1741
+ line(
1742
+ centerX + bracketOffset,
1743
+ centerY + bracketOffset,
1744
+ centerX + bracketOffset - bracketSize,
1745
+ centerY + bracketOffset,
1746
+ bracketColor
1747
+ );
1748
+ line(
1749
+ centerX + bracketOffset,
1750
+ centerY + bracketOffset,
1751
+ centerX + bracketOffset,
1752
+ centerY + bracketOffset - bracketSize,
1753
+ bracketColor
1754
+ );
1755
+
1756
+ // Weapon ready indicator
1757
+ if (gameData.energy > 10) {
1758
+ print('WEAPONS READY', 270, 150, rgba8(0, 255, 0, Math.floor(pulse * 255)));
1759
+ } else {
1760
+ print('RECHARGING...', 275, 150, rgba8(255, 100, 0, 255));
1761
+ }
1762
+ }
1763
+
1764
+ function drawAdvancedRadar() {
1765
+ const radarX = 550,
1766
+ radarY = 280;
1767
+ const radarSize = 60;
1768
+
1769
+ // Radar background
1770
+ rect(
1771
+ radarX - radarSize / 2,
1772
+ radarY - radarSize / 2,
1773
+ radarSize,
1774
+ radarSize,
1775
+ rgba8(0, 50, 0, 150),
1776
+ true
1777
+ );
1778
+ rect(
1779
+ radarX - radarSize / 2,
1780
+ radarY - radarSize / 2,
1781
+ radarSize,
1782
+ radarSize,
1783
+ rgba8(0, 255, 0, 255),
1784
+ false
1785
+ );
1786
+
1787
+ // Grid lines
1788
+ line(radarX - radarSize / 2, radarY, radarX + radarSize / 2, radarY, rgba8(0, 150, 0, 100));
1789
+ line(radarX, radarY - radarSize / 2, radarX, radarY + radarSize / 2, rgba8(0, 150, 0, 100));
1790
+
1791
+ // Player dot (center)
1792
+ rect(radarX - 2, radarY - 2, 4, 4, rgba8(0, 255, 255, 255), true);
1793
+
1794
+ // Enemy dots
1795
+ for (let enemy of gameData.enemies) {
1796
+ const relX = (enemy.position.x / 50) * (radarSize / 2);
1797
+ const relZ = (enemy.position.z / 100) * (radarSize / 2);
1798
+ const dotX = radarX + relX;
1799
+ const dotY = radarY + relZ;
1800
+
1801
+ if (Math.abs(relX) < radarSize / 2 && Math.abs(relZ) < radarSize / 2) {
1802
+ const color = enemy.type === 'bomber' ? rgba8(255, 100, 0, 255) : rgba8(255, 0, 0, 255);
1803
+ rect(dotX - 1, dotY - 1, 3, 3, color, true);
1804
+ }
1805
+ }
1806
+ }
1807
+
1808
+ function drawGameOverScreen() {
1809
+ cls(0x000011);
1810
+
1811
+ // Game over text
1812
+ print('MISSION FAILED', 240, 100, rgba8(255, 0, 0, 255));
1813
+ print(`FINAL SCORE: ${gameData.score}`, 220, 130, rgba8(255, 255, 0, 255));
1814
+ print(`WAVES COMPLETED: ${gameData.wave - 1}`, 200, 160, rgba8(0, 255, 255, 255));
1815
+ print(`ENEMIES DESTROYED: ${gameData.kills}`, 180, 190, rgba8(150, 255, 150, 255));
1816
+
1817
+ print('PRESS SPACE TO RESTART', 210, 240, rgba8(255, 255, 255, 255));
1818
+ }
1819
+
1820
+ // Initialize the game
1821
+ console.log('🚀 PROFESSIONAL STAR FOX NOVA 64 - Loading...');