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