nova64 0.2.4 → 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.
- package/README.md +25 -8
- package/bin/nova64.js +165 -0
- package/dist/assets/console-CY_kygm3.js +14 -0
- package/dist/assets/console-CY_kygm3.js.map +1 -0
- package/dist/assets/main-l0sNRNKZ.js.map +1 -0
- package/dist/assets/sky/studio/nx.png +0 -0
- package/dist/assets/sky/studio/ny.png +0 -0
- package/dist/assets/sky/studio/nz.png +0 -0
- package/dist/assets/sky/studio/px.png +0 -0
- package/dist/assets/sky/studio/py.png +0 -0
- package/dist/assets/sky/studio/pz.png +0 -0
- package/dist/assets/vanilla-Dcuy32gi.js +2 -0
- package/dist/assets/vanilla-Dcuy32gi.js.map +1 -0
- package/dist/console.html +899 -0
- package/dist/docs/BENCHMARK.md +77 -0
- package/dist/docs/CHEATSHEET.md +255 -0
- package/dist/docs/EFFECTS_API_GUIDE.md +577 -0
- package/dist/docs/EFFECTS_QUICK_REFERENCE.md +331 -0
- package/dist/docs/FONT_CHARACTER_REFERENCE.md +219 -0
- package/dist/docs/FREE_GLB_ASSETS.md +330 -0
- package/dist/docs/FULLSCREEN_BUTTON_FEATURE.md +296 -0
- package/dist/docs/GAMEPAD_SUPPORT.md +348 -0
- package/dist/docs/GAME_IMPROVEMENTS.md +278 -0
- package/dist/docs/GAME_QUALITY_STATUS.md +300 -0
- package/dist/docs/MIGRATION_GUIDE.md +553 -0
- package/dist/docs/NOVA64_3D_API.md +356 -0
- package/dist/docs/NOVA64_API_REFERENCE.md +1406 -0
- package/dist/docs/NOVA64_UI_API.md +503 -0
- package/dist/docs/UI_SYSTEM_SUMMARY.md +445 -0
- package/dist/docs/VOXEL_ENGINE_GUIDE.md +662 -0
- package/dist/docs/VOXEL_QUICK_REFERENCE.md +386 -0
- package/dist/docs/api-3d.html +750 -0
- package/dist/docs/api-effects.html +385 -0
- package/dist/docs/api-improvements.md +121 -0
- package/dist/docs/api-skybox.html +407 -0
- package/dist/docs/api-sprites.html +321 -0
- package/dist/docs/api-voxel.html +337 -0
- package/dist/docs/api.html +543 -0
- package/dist/docs/assets.html +306 -0
- package/dist/docs/audio.html +340 -0
- package/dist/docs/blogs.html +286 -0
- package/dist/docs/collision.html +316 -0
- package/dist/docs/console.html +247 -0
- package/dist/docs/editor.html +297 -0
- package/dist/docs/font.html +247 -0
- package/dist/docs/framebuffer.html +247 -0
- package/dist/docs/fullscreen-button.html +297 -0
- package/dist/docs/gpu-systems.html +247 -0
- package/dist/docs/index.html +580 -0
- package/dist/docs/input.html +491 -0
- package/dist/docs/physics.html +311 -0
- package/dist/docs/screens.html +311 -0
- package/dist/docs/storage.html +311 -0
- package/dist/docs/textinput.html +332 -0
- package/dist/docs/ui.html +488 -0
- package/dist/examples/3d-advanced/code.js +695 -0
- package/dist/examples/adventure-comic-3d/code.js +342 -0
- package/dist/examples/audio-lab/code.js +150 -0
- package/dist/examples/boids-flocking/code.js +270 -0
- package/dist/examples/crystal-cathedral-3d/code.js +706 -0
- package/dist/examples/cyberpunk-city-3d/code.js +1383 -0
- package/dist/examples/demoscene/README.md +192 -0
- package/dist/examples/demoscene/code.js +1081 -0
- package/dist/examples/demoscene/meta.json +21 -0
- package/dist/examples/dungeon-crawler-3d/code.js +1117 -0
- package/dist/examples/f-zero-nova-3d/code.js +865 -0
- package/dist/examples/f-zero-nova-3d/code_old.js +1555 -0
- package/dist/examples/fps-demo-3d/code.js +744 -0
- package/dist/examples/game-of-life-3d/code.js +338 -0
- package/dist/examples/generative-art/code.js +632 -0
- package/dist/examples/hello-3d/code.js +325 -0
- package/dist/examples/hello-skybox/code.js +183 -0
- package/dist/examples/hello-world/code.js +19 -0
- package/dist/examples/input-showcase/code.js +109 -0
- package/dist/examples/instancing-demo/code.js +315 -0
- package/dist/examples/minecraft-demo/code.js +387 -0
- package/dist/examples/model-viewer-3d/code.js +114 -0
- package/dist/examples/mystical-realm-3d/code.js +1203 -0
- package/dist/examples/nature-explorer-3d/code.js +1318 -0
- package/dist/examples/particles-demo/code.js +522 -0
- package/dist/examples/pbr-showcase/code.js +140 -0
- package/dist/examples/physics-demo-3d/code.js +948 -0
- package/dist/examples/screen-demo/code.js +267 -0
- package/dist/examples/shooter-demo-3d/code.js +1286 -0
- package/dist/examples/space-combat-3d/IMPLEMENTATION_SUMMARY.md +109 -0
- package/dist/examples/space-combat-3d/README.md +135 -0
- package/dist/examples/space-combat-3d/code.js +1332 -0
- package/dist/examples/space-harrier-3d/code.js +923 -0
- package/dist/examples/star-fox-nova-3d/code.js +1116 -0
- package/dist/examples/star-fox-nova-3d/code_backup.js +410 -0
- package/dist/examples/star-fox-nova-3d/code_broken.js +1821 -0
- package/dist/examples/storage-quest/code.js +209 -0
- package/dist/examples/strider-demo-3d/IMPROVEMENT_OPTIONS.md +285 -0
- package/dist/examples/strider-demo-3d/cache-test.html +132 -0
- package/dist/examples/strider-demo-3d/code-fixed.js +582 -0
- package/dist/examples/strider-demo-3d/code-old.js +1537 -0
- package/dist/examples/strider-demo-3d/code.js +1462 -0
- package/dist/examples/strider-demo-3d/code.js.bak2 +1169 -0
- package/dist/examples/strider-demo-3d/fix-game.sh +53 -0
- package/dist/examples/super-plumber-64/README.md +128 -0
- package/dist/examples/super-plumber-64/code.js +1185 -0
- package/dist/examples/super-plumber-64/index.html +88 -0
- package/dist/examples/test-2d-overlay/code.js +32 -0
- package/dist/examples/test-font/code.js +51 -0
- package/dist/examples/test-minimal/code.js +21 -0
- package/dist/examples/ui-demo/code.js +306 -0
- package/dist/examples/wing-commander-space/README.md +180 -0
- package/dist/examples/wing-commander-space/code.js +1285 -0
- package/dist/examples/wizardry-3d/CHANGELOG.md +366 -0
- package/dist/examples/wizardry-3d/code.js +3928 -0
- package/dist/index.html +666 -0
- package/dist/os9-shell/assets/index-DIHfrTaW.css +1 -0
- package/dist/os9-shell/assets/index-KchE_ngx.js +483 -0
- package/dist/os9-shell/assets/index-KchE_ngx.js.map +1 -0
- package/dist/os9-shell/index.html +23 -0
- package/dist/os9-shell/nova-icon.svg +12 -0
- package/index.html +6 -1
- package/package.json +37 -32
- package/public/assets/sky/studio/nx.png +0 -0
- package/public/assets/sky/studio/ny.png +0 -0
- package/public/assets/sky/studio/nz.png +0 -0
- package/public/assets/sky/studio/px.png +0 -0
- package/public/assets/sky/studio/py.png +0 -0
- package/public/assets/sky/studio/pz.png +0 -0
- package/public/os9-shell/assets/index-KchE_ngx.js +483 -0
- package/public/os9-shell/assets/index-KchE_ngx.js.map +1 -0
- package/public/os9-shell/index.html +10 -1
- package/runtime/api-2d.js +301 -21
- package/runtime/api-3d/pbr.js +45 -1
- package/runtime/api-3d.js +1 -0
- package/runtime/api-effects.js +90 -3
- package/runtime/api-gameutils.js +476 -0
- package/runtime/api-generative.js +610 -0
- package/runtime/api-skybox.js +54 -0
- package/runtime/api-voxel.js +139 -28
- package/runtime/gpu-threejs.js +13 -9
- package/runtime/ui.js +2 -2
- package/src/main.js +24 -1
- package/public/os9-shell/assets/index-B1Uvacma.js +0 -32825
- 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
|
+
}
|