Retro western vertical shooter inspired by Gun.Smoke, built with TypeScript and WebGL2. Features 3-direction shooting, vertical scrolling, economy/shop loop, boss fights, and CRT shader effects. Phases implemented: - Phase 1: Engine skeleton (WebGL2 renderer, fixed timestep loop, input) - Phase 2: Shooting identity (3-dir shooting, bullet pools, collision) - Phase 3: Enemies & patterns (JSON waves, 4 enemy types, parallax bg) - Phase 4: Economy loop (pickups, shop, upgrades, HUD) - Phase 5: Boss system (3 bosses, wanted posters, multi-phase attacks) - Phase 6: Shader layer (CRT effects, bloom, scanlines, screen shake) - Phase 7: Performance (VAO batching, O(1) allocation, stress testing) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
86 lines
2.0 KiB
TypeScript
86 lines
2.0 KiB
TypeScript
import path from 'path';
|
|
|
|
// Build the TypeScript bundle
|
|
async function buildBundle() {
|
|
const result = await Bun.build({
|
|
entrypoints: ['./src/main.ts'],
|
|
outdir: './dist',
|
|
target: 'browser',
|
|
format: 'esm',
|
|
sourcemap: 'inline',
|
|
minify: false,
|
|
});
|
|
|
|
if (!result.success) {
|
|
console.error('Build failed:');
|
|
for (const log of result.logs) {
|
|
console.error(log);
|
|
}
|
|
throw new Error('Build failed');
|
|
}
|
|
|
|
console.log('Bundle built successfully');
|
|
return result;
|
|
}
|
|
|
|
// Initial build
|
|
await buildBundle();
|
|
|
|
const server = Bun.serve({
|
|
port: 3000,
|
|
hostname: '0.0.0.0', // Bind to all interfaces
|
|
async fetch(req) {
|
|
const url = new URL(req.url);
|
|
let filePath = url.pathname;
|
|
|
|
// Default to index.html
|
|
if (filePath === '/') {
|
|
filePath = '/index.html';
|
|
}
|
|
|
|
// Redirect main.ts to bundled version
|
|
if (filePath === '/src/main.ts') {
|
|
filePath = '/dist/main.js';
|
|
}
|
|
|
|
// Determine content type
|
|
const ext = filePath.split('.').pop() || '';
|
|
const contentTypes: Record<string, string> = {
|
|
html: 'text/html',
|
|
js: 'application/javascript',
|
|
mjs: 'application/javascript',
|
|
css: 'text/css',
|
|
json: 'application/json',
|
|
png: 'image/png',
|
|
jpg: 'image/jpeg',
|
|
gif: 'image/gif',
|
|
svg: 'image/svg+xml',
|
|
};
|
|
|
|
const contentType = contentTypes[ext] || 'application/octet-stream';
|
|
|
|
try {
|
|
const file = Bun.file(`.${filePath}`);
|
|
const exists = await file.exists();
|
|
|
|
if (!exists) {
|
|
console.log(`404: ${filePath}`);
|
|
return new Response('Not Found', { status: 404 });
|
|
}
|
|
|
|
return new Response(file, {
|
|
headers: {
|
|
'Content-Type': contentType,
|
|
'Cache-Control': 'no-cache',
|
|
},
|
|
});
|
|
} catch (error) {
|
|
console.error('Server error:', error);
|
|
return new Response('Internal Server Error', { status: 500 });
|
|
}
|
|
},
|
|
});
|
|
|
|
console.log(`Server running at http://0.0.0.0:${server.port}`);
|
|
console.log(`Access locally: http://localhost:${server.port}`);
|