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.
- 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 +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,209 @@
|
|
|
1
|
+
// examples/storage-quest/code.js
|
|
2
|
+
// Demonstrates Nova64's persistent storage API.
|
|
3
|
+
// Collect gems to earn points, upgrade your ship, and watch everything
|
|
4
|
+
// persist across full page reloads via saveData/loadData.
|
|
5
|
+
|
|
6
|
+
const SAVE_KEY = 'storagequest';
|
|
7
|
+
const GEM_COLORS = [0xffdd00, 0x00ffaa, 0xff4488, 0x44aaff];
|
|
8
|
+
const UPGRADE_COST = [0, 50, 150, 400]; // cumulative cost per level (level 0 = start)
|
|
9
|
+
|
|
10
|
+
let state; // the persistent game state
|
|
11
|
+
let ship;
|
|
12
|
+
let gems = [];
|
|
13
|
+
let particles = [];
|
|
14
|
+
let upgradeFlash = 0;
|
|
15
|
+
let saveFlash = 0;
|
|
16
|
+
|
|
17
|
+
function defaultState() {
|
|
18
|
+
return { score: 0, highScore: 0, level: 1, shipLevel: 0, totalGems: 0, runs: 0 };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function spawnGems(count) {
|
|
22
|
+
for (let i = 0; i < count; i++) {
|
|
23
|
+
const angle = Math.random() * Math.PI * 2;
|
|
24
|
+
const r = 3 + Math.random() * 6;
|
|
25
|
+
const color = GEM_COLORS[(gems.length + i) % GEM_COLORS.length];
|
|
26
|
+
gems.push({
|
|
27
|
+
mesh: createSphere(0.25, color, [Math.cos(angle) * r, 0.5, Math.sin(angle) * r], 6, {
|
|
28
|
+
material: 'holographic',
|
|
29
|
+
emissive: color,
|
|
30
|
+
}),
|
|
31
|
+
x: Math.cos(angle) * r,
|
|
32
|
+
z: Math.sin(angle) * r,
|
|
33
|
+
spin: Math.random() * Math.PI * 2,
|
|
34
|
+
color,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function init() {
|
|
40
|
+
// Load persistent state or create fresh
|
|
41
|
+
state = loadData(SAVE_KEY, defaultState());
|
|
42
|
+
state.runs = (state.runs || 0) + 1;
|
|
43
|
+
|
|
44
|
+
setCameraPosition(0, 12, 16);
|
|
45
|
+
setCameraTarget(0, 0, 0);
|
|
46
|
+
setAmbientLight(0x334466, 1.1);
|
|
47
|
+
setFog(0x050515, 20, 45);
|
|
48
|
+
|
|
49
|
+
// Ground
|
|
50
|
+
const ground = createPlane(30, 30, 0x0a0a22, [0, 0, 0]);
|
|
51
|
+
rotateMesh(ground, -Math.PI / 2, 0, 0);
|
|
52
|
+
|
|
53
|
+
// Ship color reflects upgrade level
|
|
54
|
+
const shipColors = [0x0088ff, 0x00ffaa, 0xffaa00, 0xff44aa];
|
|
55
|
+
const sz = 0.5 + state.shipLevel * 0.15;
|
|
56
|
+
ship = createCube(sz * 2, shipColors[state.shipLevel % shipColors.length], [0, 0.6, 0], {
|
|
57
|
+
material: 'metallic',
|
|
58
|
+
emissive: 0x002244,
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
spawnGems(5 + state.level * 2);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function update(dt) {
|
|
65
|
+
const speed = 4 + state.shipLevel * 0.8;
|
|
66
|
+
let mx = 0,
|
|
67
|
+
mz = 0;
|
|
68
|
+
if (key('KeyW') || key('ArrowUp')) mz -= speed * dt;
|
|
69
|
+
if (key('KeyS') || key('ArrowDown')) mz += speed * dt;
|
|
70
|
+
if (key('KeyA') || key('ArrowLeft')) mx -= speed * dt;
|
|
71
|
+
if (key('KeyD') || key('ArrowRight')) mx += speed * dt;
|
|
72
|
+
|
|
73
|
+
// Move ship — read position, apply delta, write back
|
|
74
|
+
const pos = getMeshPosition(ship);
|
|
75
|
+
const nx = Math.max(-13, Math.min(13, pos.x + mx));
|
|
76
|
+
const nz = Math.max(-13, Math.min(13, pos.z + mz));
|
|
77
|
+
setPosition(ship, nx, pos.y, nz);
|
|
78
|
+
rotateMesh(ship, 0, dt * 0.5, 0);
|
|
79
|
+
|
|
80
|
+
// Gem spin and collection
|
|
81
|
+
gems = gems.filter(g => {
|
|
82
|
+
g.spin += dt * 1.2;
|
|
83
|
+
rotateMesh(g.mesh, dt * 0.5, dt, 0);
|
|
84
|
+
const dist = Math.hypot(nx - g.x, nz - g.z);
|
|
85
|
+
if (dist < 1.0) {
|
|
86
|
+
// Collect!
|
|
87
|
+
removeMesh(g.mesh);
|
|
88
|
+
state.score += 10 * state.level;
|
|
89
|
+
state.totalGems++;
|
|
90
|
+
if (state.score > state.highScore) state.highScore = state.score;
|
|
91
|
+
sfx('coin');
|
|
92
|
+
// Spawn particle burst
|
|
93
|
+
for (let p = 0; p < 6; p++) {
|
|
94
|
+
particles.push({
|
|
95
|
+
x: g.x,
|
|
96
|
+
y: 0.5,
|
|
97
|
+
z: g.z,
|
|
98
|
+
vx: (Math.random() - 0.5) * 4,
|
|
99
|
+
vy: 1 + Math.random() * 2,
|
|
100
|
+
vz: (Math.random() - 0.5) * 4,
|
|
101
|
+
life: 0.6,
|
|
102
|
+
color: g.color,
|
|
103
|
+
mesh: createSphere(0.1, g.color, [g.x, 0.5, g.z], 4),
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
return true;
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// Auto-save and level up when all gems collected
|
|
112
|
+
if (gems.length === 0) {
|
|
113
|
+
state.level++;
|
|
114
|
+
saveData(SAVE_KEY, state);
|
|
115
|
+
saveFlash = 1.5;
|
|
116
|
+
sfx('powerup');
|
|
117
|
+
spawnGems(5 + state.level * 2);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Particles
|
|
121
|
+
particles = particles.filter(p => {
|
|
122
|
+
p.vy -= 3 * dt;
|
|
123
|
+
p.x += p.vx * dt;
|
|
124
|
+
p.y += p.vy * dt;
|
|
125
|
+
p.z += p.vz * dt;
|
|
126
|
+
p.life -= dt;
|
|
127
|
+
setPosition(p.mesh, p.x, p.y, p.z);
|
|
128
|
+
if (p.life <= 0) {
|
|
129
|
+
removeMesh(p.mesh);
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
return true;
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// Upgrade ship (press U)
|
|
136
|
+
if (keyp('KeyU') && state.shipLevel < 3) {
|
|
137
|
+
const cost = UPGRADE_COST[state.shipLevel + 1];
|
|
138
|
+
if (state.score >= cost) {
|
|
139
|
+
state.score -= cost;
|
|
140
|
+
state.shipLevel++;
|
|
141
|
+
saveData(SAVE_KEY, state);
|
|
142
|
+
saveFlash = 1.5;
|
|
143
|
+
upgradeFlash = 2;
|
|
144
|
+
sfx('powerup');
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Manual save (press Enter)
|
|
149
|
+
if (keyp('Enter')) {
|
|
150
|
+
saveData(SAVE_KEY, state);
|
|
151
|
+
saveFlash = 1.5;
|
|
152
|
+
sfx('confirm');
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Reset save (press R + Shift together)
|
|
156
|
+
if (key('ShiftLeft') && keyp('KeyR')) {
|
|
157
|
+
deleteData(SAVE_KEY);
|
|
158
|
+
state = defaultState();
|
|
159
|
+
saveFlash = 1.5;
|
|
160
|
+
sfx('error');
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
upgradeFlash = Math.max(0, upgradeFlash - dt);
|
|
164
|
+
saveFlash = Math.max(0, saveFlash - dt);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function draw() {
|
|
168
|
+
// Header
|
|
169
|
+
rect(0, 0, 320, 18, rgba8(10, 5, 30, 255), true);
|
|
170
|
+
printCentered('STORAGE QUEST', 4, 0xffffff);
|
|
171
|
+
|
|
172
|
+
// Score panel
|
|
173
|
+
rect(4, 22, 150, 60, rgba8(15, 10, 40, 200), true);
|
|
174
|
+
rect(4, 22, 150, 60, rgba8(60, 40, 120, 180), false);
|
|
175
|
+
print(`SCORE`, 10, 27, 0xaaaaff);
|
|
176
|
+
print(`${state.score}`, 10, 36, 0xffffff);
|
|
177
|
+
print(`BEST: ${state.highScore}`, 10, 46, 0x888888);
|
|
178
|
+
print(`LEVEL: ${state.level}`, 10, 56, 0x88aaff);
|
|
179
|
+
print(`GEMS: ${state.totalGems}`, 10, 66, 0xffdd44);
|
|
180
|
+
|
|
181
|
+
// Persistence panel
|
|
182
|
+
rect(160, 22, 156, 60, rgba8(15, 10, 40, 200), true);
|
|
183
|
+
rect(160, 22, 156, 60, rgba8(60, 40, 120, 180), false);
|
|
184
|
+
print('PERSISTENT DATA', 166, 27, 0xaaaaff);
|
|
185
|
+
print(`Runs: ${state.runs}`, 166, 37, 0xdddddd);
|
|
186
|
+
print(`Ship lvl: ${state.shipLevel}`, 166, 47, 0xdddddd);
|
|
187
|
+
const nextCost = state.shipLevel < 3 ? UPGRADE_COST[state.shipLevel + 1] : 'MAX';
|
|
188
|
+
print(`Upgrade: ${nextCost}pts`, 166, 57, 0xffaa44);
|
|
189
|
+
print('(U to upgrade)', 166, 67, 0x555577);
|
|
190
|
+
|
|
191
|
+
// Save indicator
|
|
192
|
+
if (saveFlash > 0) {
|
|
193
|
+
const alpha = Math.floor((saveFlash / 1.5) * 220);
|
|
194
|
+
rect(100, 86, 120, 12, rgba8(0, 180, 100, alpha), true);
|
|
195
|
+
print(
|
|
196
|
+
upgradeFlash > 0 ? 'SHIP UPGRADED!' : 'DATA SAVED!',
|
|
197
|
+
112,
|
|
198
|
+
89,
|
|
199
|
+
upgradeFlash > 0 ? 0xffdd00 : 0xffffff
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Gem counter
|
|
204
|
+
print(`Gems remaining: ${gems.length}`, 10, 90, 0xdddddd);
|
|
205
|
+
|
|
206
|
+
// Controls footer
|
|
207
|
+
rect(0, 170, 320, 10, rgba8(10, 5, 30, 255), true);
|
|
208
|
+
print('WASD: move U: upgrade Enter: save Shift+R: reset', 2, 172, 0x333355);
|
|
209
|
+
}
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
# Shadow Ninja 3D - Improvement Options
|
|
2
|
+
|
|
3
|
+
## Current Problems & Solutions
|
|
4
|
+
|
|
5
|
+
### 🔴 **What Was Wrong:**
|
|
6
|
+
|
|
7
|
+
1. **Broken Physics**
|
|
8
|
+
- Collision detection was unreliable
|
|
9
|
+
- Player would fall through platforms
|
|
10
|
+
- No proper ground detection
|
|
11
|
+
2. **Bad Level Design**
|
|
12
|
+
- Platforms at random Z positions (confusing depth)
|
|
13
|
+
- No progression or tutorial
|
|
14
|
+
- Unclear where to go
|
|
15
|
+
3. **Poor Controls**
|
|
16
|
+
- No coyote time (grace period after leaving platform)
|
|
17
|
+
- No jump buffering (early jump presses ignored)
|
|
18
|
+
- Difficult to land jumps
|
|
19
|
+
4. **Terrible Visuals**
|
|
20
|
+
- Dark colors that hide gameplay
|
|
21
|
+
- Abstract shapes instead of characters
|
|
22
|
+
- No visual feedback
|
|
23
|
+
|
|
24
|
+
### ✅ **What's Been Fixed (code-fixed.js):**
|
|
25
|
+
|
|
26
|
+
1. **Proper Platformer Physics**
|
|
27
|
+
- Reliable AABB collision detection
|
|
28
|
+
- Coyote time (150ms grace after leaving platform)
|
|
29
|
+
- Jump buffering (100ms early jump window)
|
|
30
|
+
- Air control vs ground control
|
|
31
|
+
- Proper gravity and friction
|
|
32
|
+
|
|
33
|
+
2. **Professional Level Design**
|
|
34
|
+
- Tutorial section (teaches basics)
|
|
35
|
+
- Easy section (builds confidence)
|
|
36
|
+
- Medium section (introduces challenges)
|
|
37
|
+
- Hard section (tests skills)
|
|
38
|
+
- Victory area
|
|
39
|
+
- All platforms on same Z plane (no confusing depth)
|
|
40
|
+
|
|
41
|
+
3. **Responsive Controls**
|
|
42
|
+
- Arrow keys for movement
|
|
43
|
+
- Space for jump
|
|
44
|
+
- Works immediately, feels good
|
|
45
|
+
|
|
46
|
+
4. **Clear Visual Design**
|
|
47
|
+
- Bright, visible colors
|
|
48
|
+
- Clear character with eyes and feet
|
|
49
|
+
- Animated movement (foot bobbing when walking)
|
|
50
|
+
- Coin rotation and bobbing
|
|
51
|
+
|
|
52
|
+
## 🎨 **Options to Make It Even Better:**
|
|
53
|
+
|
|
54
|
+
### Option 1: Use Free GLB Models (Best Quality)
|
|
55
|
+
|
|
56
|
+
**Where to get models:**
|
|
57
|
+
|
|
58
|
+
- [Poly Pizza](https://poly.pizza) - Free low-poly models
|
|
59
|
+
- [Sketchfab](https://sketchfab.com/search?features=downloadable&licenses=7c23a1ba438d4306920229c12afcb5f&type=models) - Free Creative Commons models
|
|
60
|
+
- [Quaternius](http://quaternius.com/assets.html) - Free game assets
|
|
61
|
+
- [Kenney Assets](https://kenney.nl/assets) - Free game models
|
|
62
|
+
|
|
63
|
+
**To use GLB models:**
|
|
64
|
+
|
|
65
|
+
1. Download a ninja/character GLB file
|
|
66
|
+
2. Place in `/public/models/ninja.glb`
|
|
67
|
+
3. Set `CONFIG.USE_GLB_MODELS = true` in code-fixed.js
|
|
68
|
+
4. The game will automatically load and render the model
|
|
69
|
+
|
|
70
|
+
**Example models:**
|
|
71
|
+
|
|
72
|
+
```javascript
|
|
73
|
+
// Ninja character
|
|
74
|
+
await loadModel('/models/ninja.glb', [0, 2, 0], 1);
|
|
75
|
+
|
|
76
|
+
// Platform models
|
|
77
|
+
await loadModel('/models/platform.glb', [x, y, 0], 1);
|
|
78
|
+
|
|
79
|
+
// Coin models
|
|
80
|
+
await loadModel('/models/coin.glb', [x, y, 0], 0.3);
|
|
81
|
+
|
|
82
|
+
// Enemy models
|
|
83
|
+
await loadModel('/models/enemy.glb', [x, y, 0], 0.8);
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Option 2: Improve Graphics with Textures
|
|
87
|
+
|
|
88
|
+
Add textures to the simple geometry:
|
|
89
|
+
|
|
90
|
+
```javascript
|
|
91
|
+
const texture = await loadTexture('/textures/character.png');
|
|
92
|
+
const material = createTexturedMaterial(texture);
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Option 3: Add More Game Features
|
|
96
|
+
|
|
97
|
+
**Easy additions:**
|
|
98
|
+
|
|
99
|
+
- Double jump
|
|
100
|
+
- Dash ability
|
|
101
|
+
- Wall jump
|
|
102
|
+
- Moving platforms
|
|
103
|
+
- Collectible power-ups
|
|
104
|
+
- Particle effects on landing
|
|
105
|
+
- Sound effects
|
|
106
|
+
|
|
107
|
+
**Example - Double Jump:**
|
|
108
|
+
|
|
109
|
+
```javascript
|
|
110
|
+
let doubleJumpAvailable = true;
|
|
111
|
+
|
|
112
|
+
// In updateInput:
|
|
113
|
+
if (isKeyPressed('Space')) {
|
|
114
|
+
if (player.grounded) {
|
|
115
|
+
player.vel.y = CONFIG.JUMP_POWER;
|
|
116
|
+
doubleJumpAvailable = true;
|
|
117
|
+
} else if (doubleJumpAvailable) {
|
|
118
|
+
player.vel.y = CONFIG.JUMP_POWER;
|
|
119
|
+
doubleJumpAvailable = false;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Option 4: Add Procedural Level Generation
|
|
125
|
+
|
|
126
|
+
Generate levels automatically:
|
|
127
|
+
|
|
128
|
+
```javascript
|
|
129
|
+
function generateLevel(difficulty) {
|
|
130
|
+
for (let i = 0; i < 50; i++) {
|
|
131
|
+
const x = i * 5;
|
|
132
|
+
const y = 2 + Math.random() * difficulty * 3;
|
|
133
|
+
addPlatform(x, y, 3 + Math.random() * 2);
|
|
134
|
+
|
|
135
|
+
if (Math.random() < 0.3) addCoin(x, y + 2);
|
|
136
|
+
if (Math.random() < 0.2) addEnemy(x, y + 1);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Option 5: Add Visual Polish
|
|
142
|
+
|
|
143
|
+
**Particle systems:**
|
|
144
|
+
|
|
145
|
+
```javascript
|
|
146
|
+
// Jump dust particles
|
|
147
|
+
function createJumpDust() {
|
|
148
|
+
for (let i = 0; i < 5; i++) {
|
|
149
|
+
const particle = createSphere(0.1, 0xcccccc, [
|
|
150
|
+
player.pos.x + (Math.random() - 0.5),
|
|
151
|
+
player.pos.y - 1,
|
|
152
|
+
player.pos.z,
|
|
153
|
+
]);
|
|
154
|
+
// Animate and fade out
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
**Camera shake on impact:**
|
|
160
|
+
|
|
161
|
+
```javascript
|
|
162
|
+
function shakeCamera(intensity) {
|
|
163
|
+
camera.pos.x += (Math.random() - 0.5) * intensity;
|
|
164
|
+
camera.pos.y += (Math.random() - 0.5) * intensity;
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## 🚀 **Recommended Next Steps:**
|
|
169
|
+
|
|
170
|
+
### Immediate (Do This Now):
|
|
171
|
+
|
|
172
|
+
1. ✅ **Use code-fixed.js** instead of current code.js
|
|
173
|
+
2. Test the game - it should actually work!
|
|
174
|
+
3. Adjust CONFIG values for difficulty
|
|
175
|
+
|
|
176
|
+
### Short Term (1-2 hours):
|
|
177
|
+
|
|
178
|
+
1. Download free GLB models from Poly Pizza
|
|
179
|
+
2. Add them to the project
|
|
180
|
+
3. Enable `USE_GLB_MODELS` flag
|
|
181
|
+
4. Add double jump ability
|
|
182
|
+
5. Add particle effects
|
|
183
|
+
|
|
184
|
+
### Medium Term (1 day):
|
|
185
|
+
|
|
186
|
+
1. Create multiple levels
|
|
187
|
+
2. Add boss fights
|
|
188
|
+
3. Add combo system
|
|
189
|
+
4. Add checkpoints
|
|
190
|
+
5. Add sound effects
|
|
191
|
+
|
|
192
|
+
### Long Term (1 week):
|
|
193
|
+
|
|
194
|
+
1. Procedural level generation
|
|
195
|
+
2. Multiplayer racing mode
|
|
196
|
+
3. Level editor
|
|
197
|
+
4. Mobile controls
|
|
198
|
+
5. Leaderboards
|
|
199
|
+
|
|
200
|
+
## 📝 **How to Use code-fixed.js:**
|
|
201
|
+
|
|
202
|
+
1. **Rename files:**
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
cd /Users/brendonsmith/exp/nova64/examples/strider-demo-3d
|
|
206
|
+
mv code.js code-old.js
|
|
207
|
+
mv code-fixed.js code.js
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
2. **Refresh browser** - game should now work properly!
|
|
211
|
+
|
|
212
|
+
3. **Test the improvements:**
|
|
213
|
+
- Jump should feel responsive
|
|
214
|
+
- Landing on platforms should be reliable
|
|
215
|
+
- Level progression should be clear
|
|
216
|
+
- Character should be visible
|
|
217
|
+
|
|
218
|
+
## 🎮 **Expected Results:**
|
|
219
|
+
|
|
220
|
+
### Before (code-old.js):
|
|
221
|
+
|
|
222
|
+
- ❌ Player falls through platforms
|
|
223
|
+
- ❌ Jumps feel unresponsive
|
|
224
|
+
- ❌ Unclear where to go
|
|
225
|
+
- ❌ Dark and confusing visuals
|
|
226
|
+
- ❌ Not fun to play
|
|
227
|
+
|
|
228
|
+
### After (code-fixed.js):
|
|
229
|
+
|
|
230
|
+
- ✅ Reliable collision detection
|
|
231
|
+
- ✅ Jumps feel good (coyote time + buffering)
|
|
232
|
+
- ✅ Clear level progression
|
|
233
|
+
- ✅ Bright, visible graphics
|
|
234
|
+
- ✅ Actually fun to play!
|
|
235
|
+
|
|
236
|
+
## 🔧 **Tuning the Game:**
|
|
237
|
+
|
|
238
|
+
Edit CONFIG values in code-fixed.js:
|
|
239
|
+
|
|
240
|
+
```javascript
|
|
241
|
+
const CONFIG = {
|
|
242
|
+
PLAYER_SPEED: 8, // ← Make character faster/slower
|
|
243
|
+
JUMP_POWER: 12, // ← Make jumps higher/lower
|
|
244
|
+
GRAVITY: 30, // ← Change gravity strength
|
|
245
|
+
CAMERA_DISTANCE: 15, // ← Zoom in/out
|
|
246
|
+
// etc...
|
|
247
|
+
};
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
## 📊 **Performance:**
|
|
251
|
+
|
|
252
|
+
The fixed version is actually MORE performant because:
|
|
253
|
+
|
|
254
|
+
- Simpler collision detection
|
|
255
|
+
- Fewer objects in scene
|
|
256
|
+
- Better organized code
|
|
257
|
+
- No unnecessary calculations
|
|
258
|
+
|
|
259
|
+
## 🆘 **Still Having Issues?**
|
|
260
|
+
|
|
261
|
+
If the game still doesn't feel right:
|
|
262
|
+
|
|
263
|
+
1. **Check console for errors**
|
|
264
|
+
2. **Verify all platforms are at Z=0**
|
|
265
|
+
3. **Test jump timing (should be forgiving)**
|
|
266
|
+
4. **Check collision box sizes match visuals**
|
|
267
|
+
5. **Ensure camera follows smoothly**
|
|
268
|
+
|
|
269
|
+
## 🎯 **The Real Problem:**
|
|
270
|
+
|
|
271
|
+
The original code tried to do too much:
|
|
272
|
+
|
|
273
|
+
- Complex enemy AI before basic physics worked
|
|
274
|
+
- Grappling hooks before basic jumps worked
|
|
275
|
+
- Wall running before collision detection worked
|
|
276
|
+
- Fancy graphics before gameplay worked
|
|
277
|
+
|
|
278
|
+
**Good game design order:**
|
|
279
|
+
|
|
280
|
+
1. ✅ Physics and collision (make it work)
|
|
281
|
+
2. ✅ Basic movement (make it feel good)
|
|
282
|
+
3. ✅ Simple level (prove it's fun)
|
|
283
|
+
4. Then add: Polish, features, graphics, etc.
|
|
284
|
+
|
|
285
|
+
The new code follows this principle. Start simple, make it work, then iterate!
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title>Knight Platformer - Cache Diagnostic</title>
|
|
5
|
+
<style>
|
|
6
|
+
body {
|
|
7
|
+
font-family: monospace;
|
|
8
|
+
padding: 20px;
|
|
9
|
+
background: #1a1a1a;
|
|
10
|
+
color: #00ff00;
|
|
11
|
+
}
|
|
12
|
+
.status {
|
|
13
|
+
padding: 10px;
|
|
14
|
+
margin: 10px 0;
|
|
15
|
+
border-radius: 5px;
|
|
16
|
+
}
|
|
17
|
+
.good { background: #002200; border: 2px solid #00ff00; }
|
|
18
|
+
.bad { background: #220000; border: 2px solid #ff0000; color: #ff6666; }
|
|
19
|
+
.info { background: #001122; border: 2px solid #6666ff; color: #aaaaff; }
|
|
20
|
+
button {
|
|
21
|
+
background: #4444ff;
|
|
22
|
+
color: white;
|
|
23
|
+
padding: 10px 20px;
|
|
24
|
+
border: none;
|
|
25
|
+
border-radius: 5px;
|
|
26
|
+
cursor: pointer;
|
|
27
|
+
margin: 5px;
|
|
28
|
+
}
|
|
29
|
+
button:hover { background: #6666ff; }
|
|
30
|
+
pre {
|
|
31
|
+
background: #000;
|
|
32
|
+
padding: 10px;
|
|
33
|
+
border-radius: 5px;
|
|
34
|
+
overflow-x: auto;
|
|
35
|
+
}
|
|
36
|
+
</style>
|
|
37
|
+
</head>
|
|
38
|
+
<body>
|
|
39
|
+
<h1>🥷 Knight Platformer - Cache Diagnostic</h1>
|
|
40
|
+
|
|
41
|
+
<div id="results"></div>
|
|
42
|
+
|
|
43
|
+
<button onclick="runTest()">🔍 Test Cache Status</button>
|
|
44
|
+
<button onclick="clearAndReload()">🔄 Clear & Reload Game</button>
|
|
45
|
+
<button onclick="openIncognito()">🕵️ Instructions for Incognito</button>
|
|
46
|
+
|
|
47
|
+
<script>
|
|
48
|
+
async function runTest() {
|
|
49
|
+
const results = document.getElementById('results');
|
|
50
|
+
results.innerHTML = '<h2>Testing...</h2>';
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
// Fetch the code.js file with cache-busting
|
|
54
|
+
const response = await fetch('./code.js?t=' + Date.now());
|
|
55
|
+
const code = await response.text();
|
|
56
|
+
|
|
57
|
+
let html = '<h2>Test Results:</h2>';
|
|
58
|
+
|
|
59
|
+
// Check for triple emoji (correct code)
|
|
60
|
+
if (code.includes('🎯🎯🎯 START BUTTON CLICKED AT')) {
|
|
61
|
+
html += '<div class="status good">✅ SERVER HAS CORRECT CODE (Triple emoji found)</div>';
|
|
62
|
+
} else if (code.includes('🎯 START BUTTON CLICKED!')) {
|
|
63
|
+
html += '<div class="status bad">❌ SERVER HAS OLD CODE (Single emoji found)</div>';
|
|
64
|
+
} else {
|
|
65
|
+
html += '<div class="status bad">❌ CANNOT FIND BUTTON CALLBACK</div>';
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Check cache buster
|
|
69
|
+
if (code.includes('CACHE-BUST-FINAL-001-REFRESH-NOW')) {
|
|
70
|
+
html += '<div class="status good">✅ Latest cache-buster present</div>';
|
|
71
|
+
} else {
|
|
72
|
+
html += '<div class="status bad">❌ Old or missing cache-buster</div>';
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Check for module-level callback
|
|
76
|
+
if (code.includes('const startGameCallback = ()')) {
|
|
77
|
+
html += '<div class="status good">✅ Callback at module level</div>';
|
|
78
|
+
} else {
|
|
79
|
+
html += '<div class="status bad">❌ Callback not at module level</div>';
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Show first 50 lines
|
|
83
|
+
const lines = code.split('\n').slice(0, 50);
|
|
84
|
+
html += '<div class="status info">';
|
|
85
|
+
html += '<h3>First 50 lines of code.js from server:</h3>';
|
|
86
|
+
html += '<pre>' + lines.join('\n').replace(/</g, '<').replace(/>/g, '>') + '</pre>';
|
|
87
|
+
html += '</div>';
|
|
88
|
+
|
|
89
|
+
// Instructions
|
|
90
|
+
html += '<div class="status info">';
|
|
91
|
+
html += '<h3>📋 To Fix Browser Cache:</h3>';
|
|
92
|
+
html += '<ol>';
|
|
93
|
+
html += '<li><strong>Mac:</strong> Press Cmd + Shift + R</li>';
|
|
94
|
+
html += '<li><strong>Windows/Linux:</strong> Press Ctrl + Shift + F5</li>';
|
|
95
|
+
html += '<li><strong>Or:</strong> Open Developer Tools (F12) → Right-click refresh → "Empty Cache and Hard Reload"</li>';
|
|
96
|
+
html += '<li><strong>Or:</strong> Open in incognito/private window</li>';
|
|
97
|
+
html += '</ol>';
|
|
98
|
+
html += '</div>';
|
|
99
|
+
|
|
100
|
+
results.innerHTML = html;
|
|
101
|
+
|
|
102
|
+
} catch (error) {
|
|
103
|
+
results.innerHTML = `<div class="status bad">❌ Error: ${error.message}</div>`;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function clearAndReload() {
|
|
108
|
+
// Try to clear cache via JavaScript (limited effect)
|
|
109
|
+
if ('caches' in window) {
|
|
110
|
+
caches.keys().then(names => {
|
|
111
|
+
names.forEach(name => caches.delete(name));
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
alert('Cache cleared (partial). Now do a HARD RELOAD:\n\nMac: Cmd + Shift + R\nWindows: Ctrl + Shift + F5');
|
|
116
|
+
|
|
117
|
+
// Reload with cache bust
|
|
118
|
+
setTimeout(() => {
|
|
119
|
+
window.location.href = '../strider-demo-3d/?cachebust=' + Date.now();
|
|
120
|
+
}, 1000);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function openIncognito() {
|
|
124
|
+
const url = window.location.origin + '/examples/strider-demo-3d/';
|
|
125
|
+
alert(`Open this URL in an incognito/private window:\n\n${url}\n\nChrome/Edge: Ctrl+Shift+N\nFirefox: Ctrl+Shift+P\nSafari: Cmd+Shift+N`);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Run test on load
|
|
129
|
+
runTest();
|
|
130
|
+
</script>
|
|
131
|
+
</body>
|
|
132
|
+
</html>
|