← Canyon Run results

Canyon Run

Qwen 3.6 27B Fable Fusion 711 Uncensored Heretic NM DAU NEO MAX MTP GGUF · typescript

Initial view of Canyon Run

Reviews & generation details

Reviews

No human review yet. Unreviewed does not mean unsuccessful.

Generation context

Imported historical artifact. Its exact original instructions and lineage are not known.

Generation prompt · exact text unavailable

See the current prompt for reference. It may differ from the instructions used to generate this artifact.

Inspect original source 60,232 bytes · SHA-256 fffb30acb445
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Canyon Racer - High-Speed Flow</title>
<script src="https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.min.js"></script>
<style>
/* ── css: layout ── */
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
html, body {
  width: 100%;
  height: 100%;
  overflow: hidden;
  background: #0a0515;
  font-family: 'Segoe UI', Arial, sans-serif;
  user-select: none;
}
#app {
  width: 100%;
  height: 100%;
  position: relative;
}
#game-canvas {
  width: 100%;
  height: 100%;
}
/* ── css: hud ── */
#hud {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 10;
}
#score-container {
  position: absolute;
  top: 20px;
  left: 50%;
  transform: translateX(-50%);
  color: #fff;
  font-size: 36px;
  font-weight: 300;
  letter-spacing: 4px;
  text-shadow: 0 0 20px rgba(255, 150, 50, 0.8);
}
#multiplier-container {
  position: absolute;
  top: 65px;
  left: 50%;
  transform: translateX(-50%);
  text-align: center;
}
#multiplier-value {
  color: #ffaa44;
  font-size: 24px;
  font-weight: 600;
  letter-spacing: 2px;
  text-shadow: 0 0 15px rgba(255, 170, 68, 0.6);
}
#near-miss-text {
  color: #ff3366;
  font-size: 20px;
  font-weight: 700;
  letter-spacing: 6px;
  opacity: 0;
  text-shadow: 0 0 10px rgba(255, 51, 102, 0.9);
  transition: opacity 0.1s;
}
#near-miss-text.show {
  opacity: 1;
  animation: pulse-near-miss 0.4s ease-out;
}
@keyframes pulse-near-miss {
  0% { transform: scale(0.8); }
  50% { transform: scale(1.2); }
  100% { transform: scale(1); }
}
#speed-container {
  position: absolute;
  right: 30px;
  bottom: 30px;
  width: 200px;
}
#speed-label {
  color: rgba(255, 255, 255, 0.6);
  font-size: 10px;
  letter-spacing: 3px;
  margin-bottom: 4px;
}
#speed-bar {
  width: 100%;
  height: 3px;
  background: rgba(255, 255, 255, 0.15);
  border-radius: 2px;
}
#speed-fill {
  height: 100%;
  width: 50%;
  background: linear-gradient(90deg, #ffaa44, #ff4466);
  border-radius: 2px;
  box-shadow: 0 0 10px rgba(255, 100, 50, 0.5);
  transition: width 0.15s ease-out;
}
#energy-container {
  position: absolute;
  left: 30px;
  bottom: 30px;
  width: 160px;
}
#energy-label {
  color: rgba(255, 255, 255, 0.6);
  font-size: 10px;
  letter-spacing: 3px;
  margin-top: 4px;
}
#energy-bar {
  width: 100%;
  height: 3px;
  background: rgba(255, 255, 255, 0.15);
  border-radius: 2px;
}
#energy-fill {
  height: 100%;
  width: 80%;
  background: linear-gradient(90deg, #44ffaa, #88ffff);
  border-radius: 2px;
  box-shadow: 0 0 10px rgba(100, 255, 200, 0.5);
  transition: width 0.15s ease-out;
}
#turbo-container {
  position: absolute;
  left: 50%;
  bottom: 20px;
  transform: translateX(-50%);
  width: 250px;
}
#turbo-label {
  color: rgba(255, 255, 255, 0.6);
  font-size: 10px;
  letter-spacing: 3px;
  text-align: center;
  margin-top: 4px;
}
#turbo-bar {
  width: 100%;
  height: 3px;
  background: rgba(255, 255, 255, 0.15);
  border-radius: 2px;
}
#turbo-fill {
  height: 100%;
  width: 0%;
  background: linear-gradient(90deg, #ff6600, #ffcc00);
  border-radius: 2px;
  box-shadow: 0 0 10px rgba(255, 200, 0, 0.5);
  transition: width 0.15s ease-out;
}
#turbo-fill.active {
  background: linear-gradient(90deg, #ffff00, #ff4400);
  box-shadow: 0 0 20px rgba(255, 255, 0, 0.9);
}
/* ── css: screens ── */
#start-screen, #game-over {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  z-index: 20;
  pointer-events: all;
}
#start-screen {
  background: radial-gradient(ellipse at center, rgba(30, 15, 50, 0.9) 0%, rgba(10, 5, 20, 0.97) 100%);
}
#game-over {
  background: radial-gradient(ellipse at center, rgba(50, 10, 20, 0.92) 0%, rgba(10, 5, 20, 0.97) 100%);
}
.hidden {
  display: none !important;
}
#game-title {
  font-size: 72px;
  font-weight: 200;
  color: #fff;
  letter-spacing: 16px;
  text-shadow: 0 0 40px rgba(255, 150, 50, 0.6), 0 0 80px rgba(255, 80, 30, 0.3);
  margin-bottom: 40px;
}
#start-hint {
  color: rgba(255, 255, 255, 0.7);
  font-size: 18px;
  letter-spacing: 4px;
  margin-bottom: 10px;
}
#start-hint-small {
  color: rgba(255, 255, 255, 0.4);
  font-size: 13px;
  letter-spacing: 2px;
  margin-bottom: 50px;
}
#start-btn, #restart-btn {
  padding: 16px 60px;
  font-size: 18px;
  font-weight: 300;
  letter-spacing: 8px;
  color: #fff;
  background: transparent;
  border: 1px solid rgba(255, 150, 50, 0.5);
  cursor: pointer;
  transition: all 0.3s ease;
  pointer-events: all;
}
#start-btn:hover, #restart-btn:hover {
  background: rgba(255, 150, 50, 0.15);
  border-color: rgba(255, 150, 50, 0.9);
  box-shadow: 0 0 30px rgba(255, 150, 50, 0.3);
}
#game-over-title {
  font-size: 60px;
  font-weight: 200;
  color: #ff4466;
  letter-spacing: 12px;
  text-shadow: 0 0 30px rgba(255, 68, 102, 0.6);
  margin-bottom: 40px;
}
#final-score, #max-speed-display {
  color: rgba(255, 255, 255, 0.8);
  font-size: 22px;
  letter-spacing: 4px;
  margin-bottom: 10px;
}
#restart-btn {
  margin-top: 40px;
}
</style>
</head>
<body>
<div id="game-canvas"></div>
<div id="hud">
  <div id="speed-container">
    <div id="speed-label">SPEED</div>
    <div id="speed-bar"><div id="speed-fill"></div></div>
  </div>
  <div id="multiplier-container">
    <div id="multiplier-value">x1.0</div>
    <div id="near-miss-text">NEAR MISS!</div>
  </div>
  <div id="score-container">
    <div id="score-value">0</div>
  </div>
  <div id="energy-container">
    <div id="energy-bar"><div id="energy-fill"></div></div>
    <div id="energy-label">ENERGY</div>
  </div>
  <div id="turbo-container">
    <div id="turbo-bar"><div id="turbo-fill"></div></div>
    <div id="turbo-label">TURBO</div>
  </div>
</div>
<div id="game-over" class="hidden">
  <div id="game-over-title">CRASHED</div>
  <div id="final-score">Score: 0</div>
  <div id="max-speed-display">Max Speed: 0</div>
  <div id="restart-btn">RESTART</div>
</div>
<div id="start-screen">
  <div id="game-title">CANYON RACER</div>
  <div id="start-hint">Move mouse or use arrow keys to steer</div>
  <div id="start-hint-small">Collect orbs for energy · Hold Shift for turbo</div>
  <div id="start-btn">START</div>
</div>
<script>
(function () {
'use strict';
var __mods = {};
var __cache = {};
var __map = {"src/main.ts":{"./input":"src/input.ts","./player":"src/player.ts","./game":"src/game.ts","./world":"src/world.ts","./renderer":"src/renderer.ts","./audio":"src/audio.ts","./types":"src/types.ts"},"src/input.ts":{"./types":"src/types.ts"},"src/audio.ts":{"./types":"src/types.ts"},"src/world.ts":{"./types":"src/types.ts"},"src/player.ts":{"./types":"src/types.ts"},"src/game.ts":{"./types":"src/types.ts"},"src/renderer.ts":{"./types":"src/types.ts"}};
function __require(id) {
  if (__cache[id]) return __cache[id].exports;
  var module = __cache[id] = { exports: {} };
  var factory = __mods[id];
  if (!factory) throw new Error("Module not found: " + id);
  factory(module.exports, function (spec) {
    var target = (__map[id] && __map[id][spec]) || spec;
    return __require(target);
  }, module);
  return module.exports;
}

// ── module: src/main.ts ──
__mods["src/main.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.main = main;
const input_1 = require("./input");
const player_1 = require("./player");
const game_1 = require("./game");
const world_1 = require("./world");
const renderer_1 = require("./renderer");
const audio_1 = require("./audio");
let state;
let metrics;
let player;
let input;
let world;
let chunks;
let lastChunkIndex;
let cameraState;
let audio;
let animationId = null;
let lastFrameTime = 0;
let inputCleanup = null;
function updateHUD() {
    const scoreEl = document.getElementById('score-value');
    const multEl = document.getElementById('multiplier-value');
    const speedFill = document.getElementById('speed-fill');
    const energyFill = document.getElementById('energy-fill');
    const turboFill = document.getElementById('turbo-fill');
    if (scoreEl)
        scoreEl.textContent = Math.floor(metrics.score).toLocaleString();
    if (multEl)
        multEl.textContent = 'x' + metrics.multiplier.toFixed(1);
    if (speedFill) {
        const pct = Math.min(100, ((player.speed - 40) / (player.maxSpeed * 1.2)) * 100);
        speedFill.style.width = pct + '%';
    }
    if (energyFill) {
        const pct = Math.min(100, (player.energy / player.maxEnergy) * 100);
        energyFill.style.width = pct + '%';
    }
    if (turboFill) {
        const pct = player.turboCharge * 100;
        turboFill.style.width = pct + '%';
        if (player.turboActive) {
            turboFill.classList.add('active');
        }
        else {
            turboFill.classList.remove('active');
        }
    }
}
function showNearMiss() {
    const el = document.getElementById('near-miss-text');
    if (el) {
        el.classList.remove('show');
        void el.offsetWidth;
        el.classList.add('show');
        setTimeout(() => el.classList.remove('show'), 500);
    }
}
function checkCollectibles() {
    const playerRadius = 1.5;
    for (let i = chunks.length - 1; i >= 0; i--) {
        const chunk = chunks[i];
        for (const col of chunk.collectibles) {
            if (col.collected)
                continue;
            const dx = player.position.x - col.position.x;
            const dy = player.position.y - col.position.y;
            const dz = player.position.z - col.position.z;
            const dist = Math.sqrt(dx * dx + dy * dy + dz * dz);
            if (dist < playerRadius + 1.2) {
                col.collected = true;
                if (col.type === 'energy') {
                    (0, player_1.addEnergy)(player, 20);
                }
                else {
                    (0, player_1.addTurboCharge)(player, 0.5);
                }
                (0, audio_1.playCollectSound)(audio);
                const el = document.getElementById('near-miss-text');
                if (el) {
                    el.textContent = col.type === 'turbo' ? 'TURBO!' : '+ENERGY';
                    el.classList.remove('show');
                    void el.offsetWidth;
                    el.classList.add('show');
                    setTimeout(() => {
                        el.textContent = 'NEAR MISS!';
                        el.classList.remove('show');
                    }, 500);
                }
            }
        }
    }
}
function updateWorld() {
    const difficulty = (0, game_1.getDifficulty)(metrics);
    world.difficultyMultiplier = difficulty;
    // Generate new chunks ahead
    while ((0, world_1.shouldGenerateChunk)(lastChunkIndex, player.position.z, world)) {
        lastChunkIndex++;
        const chunk = (0, world_1.generateChunk)(lastChunkIndex, world, difficulty);
        chunks.push(chunk);
        (0, renderer_1.createChunkMesh)(chunk);
        (0, renderer_1.createChunkCollectibleMeshes)(chunk);
    }
    // Remove old chunks
    for (let i = 0; i < chunks.length; i++) {
        if ((0, world_1.shouldDeactivateChunk)(chunks[i], player.position.z, world)) {
            (0, renderer_1.removeChunkMesh)(chunks[i].id);
            chunks.splice(i, 1);
            i--;
        }
    }
}
function gameLoop(currentTime) {
    if (!state.running)
        return;
    const dt = Math.min(0.05, (currentTime - lastFrameTime) / 1000);
    lastFrameTime = currentTime;
    // Update player
    (0, player_1.updatePlayer)(player, input, dt, (0, game_1.getDifficulty)(metrics));
    // Check for collectible pickups
    checkCollectibles();
    // Check collisions
    const allColliders = [];
    for (const chunk of chunks) {
        allColliders.push(...chunk.colliders);
    }
    const collisionResult = (0, player_1.checkCollision)(player.position, 1.5, allColliders);
    // Handle near-miss scoring
    const scoringResult = (0, game_1.calculateScoring)(metrics, player, dt, collisionResult.nearMiss, collisionResult.minDist, collisionResult.dangerZones);
    if (scoringResult.nearMissTriggered) {
        (0, audio_1.playNearMissSound)(audio);
        showNearMiss();
        (0, renderer_1.spawnDebris)(player, 8);
        cameraState.shakeIntensity = 0.3;
    }
    // Camera shake based on danger
    if (collisionResult.dangerZones > 0) {
        cameraState.shakeIntensity = Math.min(0.4, collisionResult.dangerZones * 0.05);
    }
    else {
        cameraState.shakeIntensity *= 0.95;
    }
    // Turbo activation sound
    if (player.turboActive && player.turboDuration < player.turboMaxDuration - 0.1) {
        // sound already played on activation
    }
    if (input.boost && player.turboCharge >= 0.3 && !player.turboActive) {
        (0, audio_1.playTurboSound)(audio);
    }
    // Collision
    if (collisionResult.collided) {
        handleGameOver();
        return;
    }
    // Energy death
    if (!(0, player_1.isPlayerAlive)(player)) {
        handleGameOver();
        return;
    }
    // Update systems
    updateWorld();
    (0, renderer_1.updatePlayerMesh)(player);
    (0, renderer_1.updateCamera)(player, cameraState);
    (0, renderer_1.updateCameraShake)(cameraState, cameraState.shakeIntensity);
    (0, renderer_1.updateCollectibles)(dt);
    (0, audio_1.updateEngineSound)(audio, player.speed, player.turboActive);
    updateHUD();
    // Render
    (0, renderer_1.render)();
    animationId = requestAnimationFrame(gameLoop);
}
function handleGameOver() {
    state.gameOver = true;
    state.running = false;
    (0, audio_1.playCrashSound)(audio);
    if (animationId) {
        cancelAnimationFrame(animationId);
    }
    // Show game over screen
    const gameOverEl = document.getElementById('game-over');
    const finalScoreEl = document.getElementById('final-score');
    const maxSpeedEl = document.getElementById('max-speed-display');
    if (gameOverEl)
        gameOverEl.classList.remove('hidden');
    if (finalScoreEl)
        finalScoreEl.textContent = 'Score: ' + Math.floor(metrics.score).toLocaleString();
    if (maxSpeedEl)
        maxSpeedEl.textContent = 'Max Speed: ' + Math.floor(metrics.maxSpeed) + ' km/h';
}
function startGame() {
    // Reset state
    (0, game_1.resetGame)(metrics, player);
    state.gameOver = false;
    state.running = true;
    state.started = true;
    // Reset world
    chunks = [];
    lastChunkIndex = -1;
    // Hide screens
    const startScreen = document.getElementById('start-screen');
    const gameOverEl = document.getElementById('game-over');
    if (startScreen)
        startScreen.classList.add('hidden');
    if (gameOverEl)
        gameOverEl.classList.add('hidden');
    // Init audio
    (0, audio_1.initAudio)(audio);
    (0, audio_1.resumeAudio)(audio);
    // Start loop
    lastFrameTime = performance.now();
    animationId = requestAnimationFrame(gameLoop);
}
function setupRestartButton() {
    const restartBtn = document.getElementById('restart-btn');
    if (restartBtn) {
        restartBtn.addEventListener('click', () => {
            if (animationId)
                cancelAnimationFrame(animationId);
            startGame();
        });
    }
}
function setupStartButton() {
    const startBtn = document.getElementById('start-btn');
    if (startBtn) {
        startBtn.addEventListener('click', () => {
            startGame();
        });
    }
}
function main() {
    // Initialize everything
    state = (0, game_1.createGameState)();
    metrics = (0, game_1.createGameMetrics)();
    player = (0, player_1.createPlayer)();
    input = (0, input_1.createInput)();
    world = (0, world_1.createWorldParams)();
    chunks = [];
    lastChunkIndex = -1;
    cameraState = {
        shakeIntensity: 0,
        shakeDecay: 0.95,
        shakeVector: { x: 0, y: 0, z: 0 },
        targetFov: 70,
    };
    audio = (0, audio_1.createAudio)();
    // Setup UI
    (0, input_1.setupInputEvents)(input);
    setupStartButton();
    setupRestartButton();
    // Setup renderer
    try {
        (0, renderer_1.initRenderer)('game-canvas');
    }
    catch (e) {
        console.error('Failed to init renderer:', e);
        return;
    }
    // Generate initial chunks
    for (let i = 0; i < 5; i++) {
        lastChunkIndex++;
        const chunk = (0, world_1.generateChunk)(lastChunkIndex, world, 1);
        chunks.push(chunk);
        (0, renderer_1.createChunkMesh)(chunk);
        (0, renderer_1.createChunkCollectibleMeshes)(chunk);
    }
    console.log('Canyon Racer loaded. Press Start or click START button.');
}
main();
};

// ── module: src/types.ts ──
__mods["src/types.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PALETTE = void 0;
exports.PALETTE = {
    skyTop: 0x1a0a2e,
    skyBottom: 0xff4422,
    wallColor1: 0x3d1f3a,
    wallColor2: 0x2a1528,
    wallColor3: 0x4a2845,
    accentOrange: 0xff8833,
    accentPurple: 0x8833ff,
    accentCyan: 0x33ffff,
    energyColor: 0x44ffaa,
    turboColor: 0xffcc00,
    dangerColor: 0xff3366,
};
};

// ── module: src/input.ts ──
__mods["src/input.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createInput = createInput;
exports.setupInputEvents = setupInputEvents;
function createInput() {
    return {
        left: false,
        right: false,
        up: false,
        down: false,
        boost: false,
        mouseX: 0,
        mouseY: 0,
        usingMouse: false,
    };
}
function setupInputEvents(input) {
    const keyDown = (e) => {
        switch (e.key) {
            case 'ArrowLeft':
            case 'a':
            case 'A':
                input.left = true;
                break;
            case 'ArrowRight':
            case 'd':
            case 'D':
                input.right = true;
                break;
            case 'ArrowUp':
            case 'w':
            case 'W':
                input.up = true;
                break;
            case 'ArrowDown':
            case 's':
            case 'S':
                input.down = true;
                break;
            case 'Shift':
                input.boost = true;
                break;
        }
    };
    const keyUp = (e) => {
        switch (e.key) {
            case 'ArrowLeft':
            case 'a':
            case 'A':
                input.left = false;
                break;
            case 'ArrowRight':
            case 'd':
            case 'D':
                input.right = false;
                break;
            case 'ArrowUp':
            case 'w':
            case 'W':
                input.up = false;
                break;
            case 'ArrowDown':
            case 's':
            case 'S':
                input.down = false;
                break;
            case 'Shift':
                input.boost = false;
                break;
        }
    };
    const mouseMove = (e) => {
        input.usingMouse = true;
        const cx = window.innerWidth / 2;
        const cy = window.innerHeight / 2;
        input.mouseX = (e.clientX - cx) / cx;
        input.mouseY = (e.clientY - cy) / cy;
    };
    document.addEventListener('keydown', keyDown);
    document.addEventListener('keyup', keyUp);
    document.addEventListener('mousemove', mouseMove);
    return () => {
        document.removeEventListener('keydown', keyDown);
        document.removeEventListener('keyup', keyUp);
        document.removeEventListener('mousemove', mouseMove);
    };
}
};

// ── module: src/audio.ts ──
__mods["src/audio.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createAudio = createAudio;
exports.initAudio = initAudio;
exports.resumeAudio = resumeAudio;
exports.updateEngineSound = updateEngineSound;
exports.playCollectSound = playCollectSound;
exports.playNearMissSound = playNearMissSound;
exports.playTurboSound = playTurboSound;
exports.playCrashSound = playCrashSound;
exports.cleanupAudio = cleanupAudio;
function createAudio() {
    return {
        ctx: null,
        engineOsc: null,
        engineGain: null,
        engineFilter: null,
        masterGain: null,
        initialized: false,
    };
}
function initAudio(audio) {
    if (audio.initialized)
        return;
    const AudioContextClass = window.AudioContext || window.webkitAudioContext;
    if (!AudioContextClass)
        return;
    try {
        audio.ctx = new AudioContextClass();
        const ctx = audio.ctx;
        audio.masterGain = ctx.createGain();
        audio.masterGain.gain.value = 0.3;
        audio.masterGain.connect(ctx.destination);
        // Engine sound - low hum
        audio.engineOsc = ctx.createOscillator();
        audio.engineOsc.type = 'sawtooth';
        audio.engineOsc.frequency.value = 80;
        audio.engineFilter = ctx.createBiquadFilter();
        audio.engineFilter.type = 'lowpass';
        audio.engineFilter.frequency.value = 200;
        audio.engineFilter.Q.value = 2;
        audio.engineGain = ctx.createGain();
        audio.engineGain.gain.value = 0;
        audio.engineOsc.connect(audio.engineFilter);
        audio.engineFilter.connect(audio.engineGain);
        audio.engineGain.connect(audio.masterGain);
        audio.engineOsc.start();
        audio.initialized = true;
    }
    catch (e) {
        console.warn('Audio init failed:', e);
    }
}
function resumeAudio(audio) {
    if (audio.ctx && audio.ctx.state === 'suspended') {
        audio.ctx.resume();
    }
}
function updateEngineSound(audio, speed, turboActive) {
    if (!audio.initialized || !audio.engineOsc || !audio.engineGain || !audio.engineFilter || !audio.ctx)
        return;
    const ctx = audio.ctx;
    const baseFreq = 60 + speed * 0.8;
    const freq = turboActive ? baseFreq * 1.5 : baseFreq;
    audio.engineOsc.frequency.linearRampToValueAtTime(freq, ctx.currentTime + 0.05);
    const gain = turboActive ? 0.15 : 0.08;
    audio.engineGain.gain.linearRampToValueAtTime(gain, ctx.currentTime + 0.05);
    const filterFreq = turboActive ? 600 + speed : 300 + speed * 0.5;
    audio.engineFilter.frequency.linearRampToValueAtTime(filterFreq, ctx.currentTime + 0.05);
}
function playCollectSound(audio) {
    if (!audio.initialized || !audio.ctx || !audio.masterGain)
        return;
    const ctx = audio.ctx;
    const osc = ctx.createOscillator();
    const gain = ctx.createGain();
    osc.type = 'sine';
    osc.frequency.setValueAtTime(880, ctx.currentTime);
    osc.frequency.exponentialRampToValueAtTime(1320, ctx.currentTime + 0.08);
    osc.frequency.exponentialRampToValueAtTime(880, ctx.currentTime + 0.15);
    gain.gain.setValueAtTime(0.2, ctx.currentTime);
    gain.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + 0.2);
    osc.connect(gain);
    gain.connect(audio.masterGain);
    osc.start(ctx.currentTime);
    osc.stop(ctx.currentTime + 0.2);
}
function playNearMissSound(audio) {
    if (!audio.initialized || !audio.ctx || !audio.masterGain)
        return;
    const ctx = audio.ctx;
    // Whoosh sound
    const bufferSize = ctx.sampleRate * 0.3;
    const buffer = ctx.createBuffer(1, bufferSize, ctx.sampleRate);
    const data = buffer.getChannelData(0);
    for (let i = 0; i < bufferSize; i++) {
        const t = i / bufferSize;
        const envelope = Math.sin(t * Math.PI) * Math.exp(-t * 5);
        data[i] = (Math.random() * 2 - 1) * envelope * 0.15;
    }
    const source = audio.ctx.createBufferSource();
    source.buffer = buffer;
    const filter = audio.ctx.createBiquadFilter();
    filter.type = 'bandpass';
    filter.frequency.value = 1000;
    filter.Q.value = 0.5;
    const gain = audio.ctx.createGain();
    gain.gain.value = 0.5;
    source.connect(filter);
    filter.connect(gain);
    gain.connect(audio.masterGain);
    source.start();
}
function playTurboSound(audio) {
    if (!audio.initialized || !audio.ctx || !audio.masterGain)
        return;
    const osc = audio.ctx.createOscillator();
    const gain = audio.ctx.createGain();
    const filter = audio.ctx.createBiquadFilter();
    osc.type = 'sawtooth';
    osc.frequency.setValueAtTime(200, audio.ctx.currentTime);
    osc.frequency.exponentialRampToValueAtTime(600, audio.ctx.currentTime + 0.3);
    filter.type = 'lowpass';
    filter.frequency.setValueAtTime(300, audio.ctx.currentTime);
    filter.frequency.exponentialRampToValueAtTime(2000, audio.ctx.currentTime + 0.2);
    gain.gain.setValueAtTime(0, audio.ctx.currentTime);
    gain.gain.linearRampToValueAtTime(0.15, audio.ctx.currentTime + 0.05);
    gain.gain.linearRampToValueAtTime(0.05, audio.ctx.currentTime + 0.3);
    osc.connect(filter);
    filter.connect(gain);
    gain.connect(audio.masterGain);
    osc.start();
    osc.stop(audio.ctx.currentTime + 0.4);
}
function playCrashSound(audio) {
    if (!audio.initialized || !audio.ctx || !audio.masterGain)
        return;
    const bufferSize = audio.ctx.sampleRate * 0.5;
    const buffer = audio.ctx.createBuffer(1, bufferSize, audio.ctx.sampleRate);
    const data = buffer.getChannelData(0);
    for (let i = 0; i < bufferSize; i++) {
        const t = i / bufferSize;
        const envelope = Math.exp(-t * 3);
        data[i] = (Math.random() * 2 - 1) * envelope * 0.4;
    }
    const source = audio.ctx.createBufferSource();
    source.buffer = buffer;
    const filter = audio.ctx.createBiquadFilter();
    filter.type = 'lowpass';
    filter.frequency.value = 500;
    const gain = audio.ctx.createGain();
    gain.gain.value = 0.6;
    source.connect(filter);
    filter.connect(gain);
    gain.connect(audio.masterGain);
    source.start();
}
function cleanupAudio(audio) {
    if (audio.ctx) {
        audio.ctx.close();
    }
}
};

// ── module: src/world.ts ──
__mods["src/world.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createWorldParams = createWorldParams;
exports.generateChunk = generateChunk;
exports.shouldDeactivateChunk = shouldDeactivateChunk;
exports.shouldGenerateChunk = shouldGenerateChunk;
let chunkIdCounter = 0;
function seededRandom(seed) {
    let s = seed;
    return () => {
        s = (s * 16807) % 2147483647;
        return (s - 1) / 2147483646;
    };
}
function createWorldParams() {
    return {
        chunkLength: 200,
        chunksActive: 8,
        canyonWidth: 80,
        canyonHeight: 60,
        wallHeight: 80,
        generationDistance: 800,
        difficultyMultiplier: 1,
    };
}
function generateChunkWalls(chunkZ, width, height, rand) {
    const walls = [];
    const numSegments = 4 + Math.floor(rand() * 3);
    const segmentLength = 200 / numSegments;
    for (let i = 0; i < numSegments; i++) {
        const z = chunkZ + i * segmentLength;
        const variation = (rand() - 0.5) * width * 0.2;
        // Left wall
        walls.push({
            position: {
                x: -width / 2 - 5 - variation,
                y: -height / 2 + (rand() - 0.3) * 15,
                z: z + segmentLength / 2,
            },
            size: {
                x: 15 + rand() * 10,
                y: 50 + rand() * 30,
                z: segmentLength + 2,
            },
            type: 'left',
        });
        // Right wall
        walls.push({
            position: {
                x: width / 2 + 5 + variation,
                y: -height / 2 + (rand() - 0.3) * 15,
                z: z + segmentLength / 2,
            },
            size: {
                x: 15 + rand() * 10,
                y: 50 + rand() * 30,
                z: segmentLength + 2,
            },
            type: 'right',
        });
    }
    return walls;
}
function generateChunkObstacles(chunkZ, chunkLength, width, difficulty, rand) {
    const obstacles = [];
    const count = 2 + Math.floor(rand() * 2) + Math.floor(difficulty * 0.8);
    for (let i = 0; i < count; i++) {
        const z = chunkZ + 40 + rand() * (chunkLength - 80);
        const x = (rand() - 0.5) * (width * 0.6);
        const y = (rand() - 0.5) * 25;
        if (rand() < 0.4 && difficulty > 1.5) {
            // Tight squeeze - two blocks with a gap
            const gapWidth = Math.max(8, 18 - difficulty * 1.5);
            obstacles.push({
                position: { x: x - gapWidth / 2 - 4, y, z },
                size: { x: 4, y: 12 + rand() * 6, z: 4 },
                type: 'squeeze',
                squeezeGap: gapWidth,
            });
            obstacles.push({
                position: { x: x + gapWidth / 2 + 4, y, z },
                size: { x: 4, y: 12 + rand() * 6, z: 4 },
                type: 'squeeze',
                squeezeGap: gapWidth,
            });
        }
        else {
            // Monolith
            const height = 10 + rand() * 15;
            obstacles.push({
                position: { x, y, z },
                size: {
                    x: 4 + rand() * 4,
                    y: height,
                    z: 4 + rand() * 4,
                },
                type: 'monolith',
            });
        }
    }
    return obstacles;
}
function generateChunkCollectibles(chunkZ, chunkLength, width, obstacles, rand) {
    const collectibles = [];
    const count = 5 + Math.floor(rand() * 4);
    for (let i = 0; i < count; i++) {
        let z = chunkZ + 20 + rand() * (chunkLength - 40);
        let x = (rand() - 0.5) * width * 0.7;
        let y = (rand() - 0.5) * 20;
        // Sometimes place collectibles in tight spots (risk/reward)
        if (rand() < 0.3 && obstacles.length > 0) {
            const obstacle = obstacles[Math.floor(rand() * obstacles.length)];
            x = obstacle.position.x + (rand() - 0.5) * 15;
            y = obstacle.position.y + (rand() - 0.5) * 15;
            z = obstacle.position.z - 5 - rand() * 10;
        }
        collectibles.push({
            position: { x, y, z },
            type: rand() < 0.85 ? 'energy' : 'turbo',
            collected: false,
            rotationSpeed: 1 + rand() * 2,
        });
    }
    return collectibles;
}
function computeColliders(chunk) {
    const colliders = [];
    for (const wall of chunk.walls) {
        colliders.push({
            type: 'box',
            center: wall.position,
            halfSize: {
                x: wall.size.x / 2,
                y: wall.size.y / 2,
                z: wall.size.z / 2,
            },
            owner: 'wall',
            dangerZone: 8,
        });
    }
    for (const obs of chunk.obstacles) {
        colliders.push({
            type: 'box',
            center: obs.position,
            halfSize: {
                x: obs.size.x / 2,
                y: obs.size.y / 2,
                z: obs.size.z / 2,
            },
            owner: 'obstacle',
            dangerZone: 6,
        });
    }
    return colliders;
}
function generateChunk(chunkIndex, params, difficulty) {
    const chunkZ = chunkIndex * params.chunkLength;
    const seed = Math.abs(chunkIndex * 12345 + 67890);
    const rand = seededRandom(seed);
    const canyonWidth = params.canyonWidth - Math.min(difficulty * 2, 20);
    const canyonHeight = params.canyonHeight - Math.min(difficulty, 10);
    const walls = generateChunkWalls(chunkZ, canyonWidth, canyonHeight, rand);
    const obstacles = generateChunkObstacles(chunkZ, params.chunkLength, canyonWidth, difficulty, rand);
    const collectibles = generateChunkCollectibles(chunkZ, params.chunkLength, canyonWidth, obstacles, rand);
    const id = chunkIdCounter++;
    const chunkData = {
        id,
        position: { x: 0, y: 0, z: chunkZ },
        walls,
        obstacles,
        collectibles,
        active: true,
        generated: true,
    };
    return {
        ...chunkData,
        colliders: computeColliders(chunkData),
    };
}
function shouldDeactivateChunk(chunk, playerZ, params) {
    return chunk.position.z + params.chunkLength < playerZ - 300;
}
function shouldGenerateChunk(lastChunkIndex, playerZ, params) {
    const lastChunkEnd = (lastChunkIndex + 1) * params.chunkLength;
    return lastChunkEnd < playerZ + params.generationDistance;
}
};

// ── module: src/player.ts ──
__mods["src/player.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createPlayer = createPlayer;
exports.updatePlayer = updatePlayer;
exports.addEnergy = addEnergy;
exports.addTurboCharge = addTurboCharge;
exports.isPlayerAlive = isPlayerAlive;
exports.checkCollision = checkCollision;
function createPlayer() {
    return {
        position: { x: 0, y: 0, z: 0 },
        velocity: { x: 0, y: 0, z: 100 },
        rotation: { x: 0, y: 0, z: 0 },
        targetRotation: { x: 0, y: 0, z: 0 },
        energy: 100,
        maxEnergy: 100,
        speed: 80,
        baseSpeed: 80,
        maxSpeed: 150,
        turboCharge: 0,
        turboActive: false,
        turboDuration: 0,
        turboMaxDuration: 3,
        nearMissCooldown: 0.3,
        lastNearMissTime: 0,
    };
}
function updatePlayer(player, input, dt, difficulty) {
    const accel = 60;
    const turnSpeed = 55;
    const liftSpeed = 45;
    const drag = 0.98;
    const energyDecay = 8 + difficulty * 2;
    const maxMoveRange = 50;
    // Apply input to target velocity
    if (input.usingMouse) {
        const targetXVel = input.mouseX * turnSpeed * 2;
        const targetYVel = -input.mouseY * liftSpeed * 2;
        player.velocity.x += (targetXVel - player.velocity.x) * 8 * dt;
        player.velocity.y += (targetYVel - player.velocity.y) * 8 * dt;
    }
    else {
        if (input.left)
            player.velocity.x -= accel * dt;
        if (input.right)
            player.velocity.x += accel * dt;
        if (input.up)
            player.velocity.y += liftSpeed * dt;
        if (input.down)
            player.velocity.y -= liftSpeed * dt;
        player.velocity.x *= drag;
        player.velocity.y *= drag;
    }
    // Clamp lateral movement
    player.position.x = Math.max(-maxMoveRange, Math.min(maxMoveRange, player.position.x));
    player.position.y = Math.max(-maxMoveRange * 0.6, Math.min(maxMoveRange * 0.6, player.position.y));
    // Speed management
    const speedBoost = difficulty * 2;
    player.baseSpeed = 80 + speedBoost;
    player.maxSpeed = 150 + speedBoost * 0.5;
    if (input.boost && player.turboCharge >= 0.3 && !player.turboActive) {
        player.turboActive = true;
        player.turboDuration = player.turboMaxDuration;
    }
    if (player.turboActive) {
        player.turboDuration -= dt;
        player.speed = Math.min(player.speed + 80 * dt, player.maxSpeed * 1.3);
        player.turboCharge -= dt * 0.3;
        if (player.turboDuration <= 0) {
            player.turboActive = false;
            player.turboCharge = 0;
        }
    }
    else {
        player.speed = Math.max(player.speed - 40 * dt, player.baseSpeed);
        // Build turbo on straightaways and speed
        if (Math.abs(player.velocity.x) < 5 && Math.abs(player.velocity.y) < 5) {
            player.turboCharge = Math.min(player.turboCharge + dt * 0.15, 1);
        }
    }
    // Apply forward movement
    const moveZ = player.speed * dt;
    player.position.z += moveZ;
    player.position.x += player.velocity.x * dt;
    player.position.y += player.velocity.y * dt;
    // Banking rotation (smooth)
    player.targetRotation.z = -player.velocity.x * 0.025;
    player.targetRotation.x = -player.velocity.y * 0.02;
    // Pitch up during turbo
    if (player.turboActive) {
        player.targetRotation.x -= 0.15;
    }
    player.rotation.z += (player.targetRotation.z - player.rotation.z) * 6 * dt;
    player.rotation.x += (player.targetRotation.x - player.rotation.x) * 6 * dt;
    player.rotation.y = player.velocity.x * 0.003;
    // Energy decay
    player.energy -= energyDecay * dt;
    // Near miss cooldown
    if (player.nearMissCooldown > 0) {
        player.nearMissCooldown -= dt;
    }
}
function addEnergy(player, amount) {
    player.energy = Math.min(player.energy + amount, player.maxEnergy);
}
function addTurboCharge(player, amount) {
    player.turboCharge = Math.min(player.turboCharge + amount, 1);
}
function isPlayerAlive(player) {
    return player.energy > 0;
}
function checkCollision(playerPos, playerRadius, colliders) {
    let collided = false;
    let nearMiss = false;
    let minDist = Infinity;
    let dangerCount = 0;
    for (const col of colliders) {
        // AABB vs sphere collision
        const dx = Math.max(col.halfSize.x, Math.abs(playerPos.x - col.center.x)) - col.halfSize.x;
        const dy = Math.max(col.halfSize.y, Math.abs(playerPos.y - col.center.y)) - col.halfSize.y;
        const dz = Math.max(col.halfSize.z, Math.abs(playerPos.z - col.center.z)) - col.halfSize.z;
        const distSq = Math.max(0, dx) * Math.max(0, dx) +
            Math.max(0, dy) * Math.max(0, dy) +
            Math.max(0, dz) * Math.max(0, dz);
        const dist = Math.sqrt(distSq);
        minDist = Math.min(minDist, dist);
        if (dist < playerRadius) {
            collided = true;
        }
        if (dist < playerRadius + (col.dangerZone || 6) && dist >= playerRadius) {
            nearMiss = true;
            if (dist < playerRadius + (col.dangerZone || 6) - 2) {
                dangerCount++;
            }
        }
    }
    return { collided, nearMiss, minDist, dangerZones: dangerCount };
}
};

// ── module: src/game.ts ──
__mods["src/game.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createGameState = createGameState;
exports.createGameMetrics = createGameMetrics;
exports.calculateScoring = calculateScoring;
exports.getDifficulty = getDifficulty;
exports.resetGame = resetGame;
function createGameState() {
    return {
        running: false,
        paused: false,
        gameOver: false,
        started: false,
    };
}
function createGameMetrics() {
    return {
        score: 0,
        multiplier: 1,
        maxMultiplier: 1,
        maxSpeed: 0,
        distance: 0,
        timeAlive: 0,
    };
}
function calculateScoring(metrics, player, dt, nearMiss, nearMissDist, dangerCount) {
    let scoreGained = 0;
    let newMultiplier = metrics.multiplier;
    let nearMissTriggered = false;
    // Base distance score
    const distanceScore = player.speed * dt * 0.1;
    scoreGained += distanceScore;
    // Near-miss bonus
    if (nearMiss && player.nearMissCooldown <= 0 && player.position.z - nearMissDist < player.position.z - 30) {
        // Calculate bonus based on proximity
        const proximityBonus = Math.max(1, 5 - nearMissDist * 0.3);
        scoreGained += proximityBonus * 10;
        // Increase multiplier
        newMultiplier = Math.min(10, newMultiplier + 0.2 + dangerCount * 0.1);
        nearMissTriggered = true;
        player.lastNearMissTime = player.position.z;
        player.nearMissCooldown = player.nearMissCooldown;
    }
    // Speed bonus
    if (player.speed > 100) {
        scoreGained += (player.speed - 100) * dt * 0.05;
    }
    // Turbo bonus
    if (player.turboActive) {
        scoreGained += 30 * dt;
        newMultiplier = Math.min(10, newMultiplier + dt * 0.5);
    }
    // Apply multiplier decay when not getting near misses
    const timeSinceNearMiss = (player.position.z - player.lastNearMissTime) / player.speed;
    if (timeSinceNearMiss > 2) {
        newMultiplier = Math.max(1, newMultiplier - dt * 0.3);
    }
    metrics.score += scoreGained * newMultiplier;
    metrics.multiplier = newMultiplier;
    metrics.maxMultiplier = Math.max(metrics.maxMultiplier, newMultiplier);
    metrics.maxSpeed = Math.max(metrics.maxSpeed, player.speed);
    metrics.distance = player.position.z;
    metrics.timeAlive += dt;
    return {
        scoreGained,
        newMultiplier,
        nearMissTriggered,
    };
}
function getDifficulty(metrics) {
    return 1 + metrics.timeAlive * 0.02 + metrics.score * 0.00001;
}
function resetGame(metrics, player) {
    metrics.score = 0;
    metrics.multiplier = 1;
    metrics.maxMultiplier = 1;
    metrics.maxSpeed = 0;
    metrics.distance = 0;
    metrics.timeAlive = 0;
    player.position = { x: 0, y: 0, z: 0 };
    player.velocity = { x: 0, y: 0, z: 100 };
    player.rotation = { x: 0, y: 0, z: 0 };
    player.targetRotation = { x: 0, y: 0, z: 0 };
    player.energy = 100;
    player.speed = 80;
    player.baseSpeed = 80;
    player.turboCharge = 0;
    player.turboActive = false;
    player.turboDuration = 0;
    player.lastNearMissTime = 0;
}
};

// ── module: src/renderer.ts ──
__mods["src/renderer.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.initRenderer = initRenderer;
exports.updatePlayerMesh = updatePlayerMesh;
exports.spawnDebris = spawnDebris;
exports.createChunkMesh = createChunkMesh;
exports.createChunkCollectibleMeshes = createChunkCollectibleMeshes;
exports.removeChunkMesh = removeChunkMesh;
exports.removeCollectibleMesh = removeCollectibleMesh;
exports.updateCamera = updateCamera;
exports.updateCameraShake = updateCameraShake;
exports.updateCollectibles = updateCollectibles;
exports.render = render;
exports.onResize = onResize;
exports.cleanupRenderer = cleanupRenderer;
const types_1 = require("./types");
let renderer;
let camera;
let scene;
let composer;
let playerMesh;
let chunksMeshMap = new Map();
let collectibleMeshMap = new Map();
let trailSystem = null;
let debrisSystem = null;
let sunMesh = null;
let particles = [];
let debrisParticles = [];
let lastTime = 0;
// Reusable buffers for trail
const trailPositions = new Float32Array(1000 * 3);
const trailColors = new Float32Array(1000 * 4);
const trailSizes = new Float32Array(1000);
let trailCount = 0;
let trailHead = 0;
function initRenderer(canvasId) {
    const container = document.getElementById(canvasId);
    if (!container)
        throw new Error('Canvas container not found');
    renderer = new THREE.WebGLRenderer({
        antialias: true,
        alpha: false,
        powerPreference: 'high-performance',
    });
    renderer.setClearColor(types_1.PALETTE.skyTop, 1);
    renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
    renderer.setSize(window.innerWidth, window.innerHeight);
    renderer.autoClear = false;
    container.appendChild(renderer.domElement);
    // Camera
    camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.5, 2000);
    camera.position.set(0, 2, -8);
    camera.up.set(0, 1, 0);
    // Scene
    scene = new THREE.Scene();
    scene.fog = new THREE.FogExp2(new THREE.Color(types_1.PALETTE.skyTop), 0.0018);
    // Lighting
    const ambient = new THREE.AmbientLight(0x442255, 0.5);
    scene.add(ambient);
    const hemi = new THREE.HemisphereLight(0xff8844, 0x221144, 0.8);
    hemi.position.set(0, 50, 50);
    scene.add(hemi);
    const sunLight = new THREE.DirectionalLight(0xffaa44, 1.5);
    sunLight.position.set(-30, 40, 200);
    scene.add(sunLight);
    // Create sun visual
    const sunGeo = new THREE.SphereGeometry(25, 16, 16);
    const sunMat = new THREE.MeshBasicMaterial({ color: 0xffcc44 });
    sunMesh = new THREE.Mesh(sunGeo, sunMat);
    sunMesh.position.set(0, 30, 500);
    scene.add(sunMesh);
    // Create player craft
    playerMesh = createPlayerCraft();
    scene.add(playerMesh);
    // Create trail particle system
    createTrailSystem();
    // Create debris system
    createDebrisSystem();
    // Initialize composer
    initComposer();
    window.addEventListener('resize', onResize);
    lastTime = performance.now();
}
function initComposer() {
    // Custom composer implementation using raw WebGL2
    // We'll implement bloom and afterimage via custom render passes
    const gl = renderer;
    composer = new CustomComposer(camera, renderer);
}
function createPlayerCraft() {
    const group = new THREE.Group();
    // Main body - sleek wedge shape
    const bodyGeo = new THREE.ConeGeometry(0.6, 2.5, 8);
    bodyGeo.rotateX(Math.PI / 2);
    const bodyMat = new THREE.MeshStandardMaterial({
        color: 0x888899,
        metalness: 0.9,
        roughness: 0.2,
        flatShading: true,
    });
    const body = new THREE.Mesh(bodyGeo, bodyMat);
    group.add(body);
    // Cockpit
    const cockpitGeo = new THREE.ConeGeometry(0.3, 1.0, 6);
    cockpitGeo.rotateX(Math.PI / 2);
    const cockpitMat = new THREE.MeshStandardMaterial({
        color: 0x44ddff,
        metalness: 0.3,
        roughness: 0.1,
        emissive: 0x2288aa,
        emissiveIntensity: 0.3,
        transparent: true,
        opacity: 0.8,
    });
    const cockpit = new THREE.Mesh(cockpitGeo, cockpitMat);
    cockpit.position.z = 0.3;
    cockpit.position.y = 0.15;
    group.add(cockpit);
    // Wings
    const wingGeo = new THREE.BoxGeometry(2.8, 0.08, 1.0);
    const wingMat = new THREE.MeshStandardMaterial({
        color: 0x666677,
        metalness: 0.8,
        roughness: 0.3,
        flatShading: true,
    });
    const wing = new THREE.Mesh(wingGeo, wingMat);
    wing.position.z = -0.2;
    group.add(wing);
    // Engine glow
    const engineGeo = new THREE.ConeGeometry(0.35, 1.2, 8);
    engineGeo.rotateX(-Math.PI / 2);
    const engineMat = new THREE.MeshBasicMaterial({
        color: types_1.PALETTE.accentOrange,
        transparent: true,
        opacity: 0.9,
    });
    const engine = new THREE.Mesh(engineGeo, engineMat);
    engine.position.z = 1.8;
    group.add(engine);
    // Engine tip glow
    const tipGeo = new THREE.SphereGeometry(0.2, 8, 8);
    const tipMat = new THREE.MeshBasicMaterial({
        color: 0xffffff,
        transparent: true,
        opacity: 0.8,
    });
    const tip = new THREE.Mesh(tipGeo, tipMat);
    tip.position.z = 2.3;
    group.add(tip);
    return group;
}
function createTrailSystem() {
    const geo = new THREE.BufferGeometry();
    geo.setAttribute('position', new THREE.BufferAttribute(trailPositions, 3));
    geo.setAttribute('color', new THREE.BufferAttribute(trailColors, 4));
    geo.setAttribute('size', new THREE.BufferAttribute(trailSizes, 1));
    const mat = new THREE.ShaderMaterial({
        uniforms: {
            time: { value: 0 },
        },
        vertexShader: `
      attribute float size;
      attribute vec4 color;
      varying vec4 vColor;
      varying float vSize;
      uniform float time;
      void main() {
        vColor = color;
        vSize = size;
        vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
        gl_PointSize = size * (300.0 / -mvPosition.z);
        gl_Position = projectionMatrix * mvPosition;
      }
    `,
        fragmentShader: `
      varying vec4 vColor;
      void main() {
        float dist = length(gl_PointCoord - 0.5);
        if (dist > 0.5) discard;
        float alpha = smoothstep(0.5, 0.1, dist) * vColor.a;
        gl_FragColor = vec4(vColor.rgb, alpha);
      }
    `,
        transparent: true,
        blending: THREE.AdditiveBlending,
        depthWrite: false,
    });
    trailSystem = new THREE.Points(geo, mat);
    scene.add(trailSystem);
}
function createDebrisSystem() {
    const geo = new THREE.BufferGeometry();
    const positions = new Float32Array(500 * 3);
    const colors = new Float32Array(500 * 3);
    const sizes = new Float32Array(500);
    geo.setAttribute('position', new THREE.BufferAttribute(positions, 3));
    geo.setAttribute('color', new THREE.BufferAttribute(colors, 3));
    geo.setAttribute('size', new THREE.BufferAttribute(sizes, 1));
    const mat = new THREE.ShaderMaterial({
        vertexShader: `
      attribute float size;
      attribute vec3 color;
      varying vec3 vColor;
      void main() {
        vColor = color;
        vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
        gl_PointSize = size * (200.0 / -mvPosition.z);
        gl_Position = projectionMatrix * mvPosition;
      }
    `,
        fragmentShader: `
      varying vec3 vColor;
      void main() {
        float dist = length(gl_PointCoord - 0.5);
        if (dist > 0.5) discard;
        float alpha = smoothstep(0.5, 0.0, dist) * 0.6;
        gl_FragColor = vec4(vColor, alpha);
      }
    `,
        transparent: true,
        blending: THREE.AdditiveBlending,
        depthWrite: false,
    });
    debrisSystem = new THREE.Points(geo, mat);
    scene.add(debrisSystem);
}
function updatePlayerMesh(player) {
    if (!playerMesh)
        return;
    playerMesh.position.set(player.position.x, player.position.y, player.position.z);
    playerMesh.rotation.set(player.rotation.x, player.rotation.y, player.rotation.z);
    // Engine glow intensity based on speed
    const engine = playerMesh.children[3];
    const tip = playerMesh.children[4];
    const glowIntensity = 0.6 + (player.speed / player.maxSpeed) * 0.5;
    if (engine && engine.material) {
        engine.material.opacity = player.turboActive ? 1.0 : glowIntensity;
        engine.material.color = player.turboActive
            ? new THREE.Color(types_1.PALETTE.turboColor)
            : new THREE.Color(types_1.PALETTE.accentOrange);
    }
    // Add trail particles
    addTrailParticles(player);
}
function addTrailParticles(player) {
    const count = player.turboActive ? 5 : 3;
    for (let i = 0; i < count; i++) {
        const idx = trailHead % 1000;
        const px = player.position.x + (Math.random() - 0.5) * 0.5;
        const py = player.position.y + (Math.random() - 0.5) * 0.3;
        const pz = player.position.z + 2 + Math.random() * 2;
        trailPositions[idx * 3] = px;
        trailPositions[idx * 3 + 1] = py;
        trailPositions[idx * 3 + 2] = pz;
        const r = player.turboActive ? 1.0 : 1.0;
        const g = player.turboActive ? 0.8 : 0.4;
        const b = player.turboActive ? 0.2 : 0.1;
        trailColors[idx * 4] = r;
        trailColors[idx * 4 + 1] = g;
        trailColors[idx * 4 + 2] = b;
        trailColors[idx * 4 + 3] = 0.7;
        trailSizes[idx] = player.turboActive ? 1.5 : 1.0;
        trailHead++;
        trailCount = Math.min(trailCount + 1, 1000);
    }
    if (trailSystem && trailSystem.geometry) {
        trailSystem.geometry.attributes.position.needsUpdate = true;
        trailSystem.geometry.attributes.color.needsUpdate = true;
        trailSystem.geometry.attributes.size.needsUpdate = true;
    }
}
function spawnDebris(player, count) {
    for (let i = 0; i < count; i++) {
        const angle = Math.random() * Math.PI * 2;
        const speed = 20 + Math.random() * 30;
        debrisParticles.push({
            position: {
                x: player.position.x + Math.cos(angle) * 3,
                y: player.position.y + (Math.random() - 0.5) * 3,
                z: player.position.z + 5 + Math.random() * 5,
            },
            velocity: {
                x: Math.cos(angle) * speed,
                y: (Math.random() - 0.5) * speed * 0.5,
                z: Math.random() * speed * 0.5,
            },
            life: 1,
            maxLife: 1 + Math.random(),
            size: 0.3 + Math.random() * 0.5,
            color: { r: 1, g: 0.4 + Math.random() * 0.3, b: 0.1 },
        });
    }
}
function createChunkMesh(chunk) {
    const group = new THREE.Group();
    // Walls
    for (const wall of chunk.walls) {
        const wallGroup = createWallMesh(wall);
        group.add(wallGroup);
    }
    // Obstacles
    for (const obs of chunk.obstacles) {
        const mesh = createObstacleMesh(obs);
        group.add(mesh);
    }
    chunksMeshMap.set(chunk.id, group);
    scene.add(group);
    return group;
}
function createWallMesh(wall) {
    const group = new THREE.Group();
    const segs = Math.floor(wall.size.z / 20);
    const segWidth = wall.size.x / Math.max(1, Math.floor(wall.size.x / 8));
    const segHeight = wall.size.y / Math.max(1, Math.floor(wall.size.y / 8));
    const colors = [types_1.PALETTE.wallColor1, types_1.PALETTE.wallColor2, types_1.PALETTE.wallColor3];
    for (let i = 0; i < Math.min(segs * 3, 30); i++) {
        const h = wall.size.x + Math.random() * wall.size.x * 0.5;
        const w = segWidth * (1 + Math.random());
        const d = 5 + Math.random() * 8;
        const geo = new THREE.BoxGeometry(w, h, d);
        const mat = new THREE.MeshStandardMaterial({
            color: colors[Math.floor(Math.random() * colors.length)],
            flatShading: true,
            roughness: 0.9,
            metalness: 0.1,
        });
        const mesh = new THREE.Mesh(geo, mat);
        const zOff = (i / (segs * 3)) * wall.size.z - wall.size.z / 2;
        const yOff = (Math.random() - 0.5) * wall.size.y * 0.4;
        mesh.position.set(wall.position.x + (Math.random() - 0.5) * 5, wall.position.y + yOff + h * 0.3, wall.position.z + zOff);
        mesh.rotation.z = (Math.random() - 0.5) * 0.2;
        mesh.rotation.x = (Math.random() - 0.5) * 0.1;
        group.add(mesh);
    }
    return group;
}
function createObstacleMesh(obs) {
    let geo;
    if (obs.type === 'monolith') {
        geo = new THREE.CylinderGeometry(obs.size.x * 0.3, obs.size.x * 0.5, obs.size.y, 6);
    }
    else {
        geo = new THREE.OctahedronGeometry(Math.max(obs.size.x, obs.size.y, obs.size.z) * 0.4);
    }
    const mat = new THREE.MeshStandardMaterial({
        color: Math.random() > 0.5 ? types_1.PALETTE.accentPurple : types_1.PALETTE.accentCyan,
        flatShading: true,
        roughness: 0.4,
        metalness: 0.7,
        emissive: Math.random() > 0.5 ? 0x442266 : 0x224455,
        emissiveIntensity: 0.3,
    });
    const mesh = new THREE.Mesh(geo, mat);
    mesh.position.set(obs.position.x, obs.position.y, obs.position.z);
    return mesh;
}
function createChunkCollectibleMeshes(chunk) {
    for (const col of chunk.collectibles) {
        if (col.collected)
            continue;
        const group = new THREE.Group();
        if (col.type === 'energy') {
            const geo = new THREE.IcosahedronGeometry(0.8, 0);
            const mat = new THREE.MeshStandardMaterial({
                color: types_1.PALETTE.energyColor,
                emissive: 0x22aa66,
                emissiveIntensity: 1.0,
                flatShading: true,
            });
            const mesh = new THREE.Mesh(geo, mat);
            group.add(mesh);
        }
        else {
            const geo = new THREE.OctahedronGeometry(0.9, 0);
            const mat = new THREE.MeshStandardMaterial({
                color: types_1.PALETTE.turboColor,
                emissive: 0xaa8800,
                emissiveIntensity: 1.0,
                flatShading: true,
            });
            const mesh = new THREE.Mesh(geo, mat);
            group.add(mesh);
        }
        // Glow ring
        const ringGeo = new THREE.CylinderGeometry(1.3, 1.3, 0.05, 12);
        ringGeo.rotateX(Math.PI / 2);
        const ringMat = new THREE.MeshBasicMaterial({
            color: col.type === 'energy' ? 0x44ffaa : types_1.PALETTE.turboColor,
            transparent: true,
            opacity: 0.4,
        });
        const ring = new THREE.Mesh(ringGeo, ringMat);
        ring.userData.isRing = true;
        group.add(ring);
        group.position.set(col.position.x, col.position.y, col.position.z);
        scene.add(group);
        collectibleMeshMap.set(col.position.x * 1000 + col.position.y * 100 + col.position.z, group);
    }
}
function removeChunkMesh(chunkId) {
    const group = chunksMeshMap.get(chunkId);
    if (group) {
        group.traverse((obj) => {
            if (obj instanceof THREE.Mesh) {
                obj.geometry.dispose();
                if (obj.material.dispose)
                    obj.material.dispose();
            }
        });
        scene.remove(group);
        chunksMeshMap.delete(chunkId);
    }
}
function removeCollectibleMesh(col) {
    const key = col.position.x * 1000 + col.position.y * 100 + col.position.z;
    const group = collectibleMeshMap.get(key);
    if (group) {
        group.traverse((obj) => {
            if (obj instanceof THREE.Mesh) {
                obj.geometry.dispose();
                if (obj.material.dispose)
                    obj.material.dispose();
            }
        });
        scene.remove(group);
        collectibleMeshMap.delete(key);
    }
}
function updateCamera(player, cameraState) {
    // Camera follows player with slight offset and lag
    const targetX = player.position.x * 0.6;
    const targetY = player.position.y * 0.4 + 2;
    const targetZ = player.position.z - (player.turboActive ? 10 : 8);
    camera.position.x += (targetX - camera.position.x) * 5 * 0.016;
    camera.position.y += (targetY - camera.position.y) * 5 * 0.016;
    camera.position.z += (targetZ - camera.position.z) * 5 * 0.016;
    // Apply shake
    camera.position.x += cameraState.shakeVector.x;
    camera.position.y += cameraState.shakeVector.y;
    // Look slightly ahead
    const lookAhead = player.turboActive ? 60 : 40;
    camera.lookAt(new THREE.Vector3(player.position.x * 0.3, player.position.y * 0.2 + 1, player.position.z + lookAhead));
    // FOV changes based on speed and turbo
    cameraState.targetFov = 70 + (player.speed / player.maxSpeed) * 10;
    if (player.turboActive) {
        cameraState.targetFov = Math.min(95, cameraState.targetFov + 10);
    }
    camera.fov += (cameraState.targetFov - camera.fov) * 3 * 0.016;
    camera.updateProjectionMatrix();
}
function updateCameraShake(cameraState, intensity) {
    if (intensity > 0) {
        cameraState.shakeVector.x = (Math.random() - 0.5) * intensity;
        cameraState.shakeVector.y = (Math.random() - 0.5) * intensity;
        cameraState.shakeVector.z = (Math.random() - 0.5) * intensity * 0.5;
    }
    else {
        cameraState.shakeVector.x *= 0.8;
        cameraState.shakeVector.y *= 0.8;
        cameraState.shakeVector.z *= 0.8;
    }
}
function updateCollectibles(dt) {
    collectibleMeshMap.forEach((group) => {
        group.rotation.y += dt * 2;
        group.rotation.x += dt * 0.5;
        const ring = group.children.find((c) => c.userData && c.userData.isRing);
        if (ring) {
            ring.rotation.z -= dt * 1.5;
        }
    });
}
function render() {
    renderer.render(scene, camera);
}
function onResize() {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
    if (composer) {
        composer.setSize(window.innerWidth, window.innerHeight);
    }
}
function cleanupRenderer() {
    window.removeEventListener('resize', onResize);
    if (renderer) {
        renderer.dispose();
        const container = document.getElementById('game-canvas');
        if (container && renderer.domElement) {
            container.removeChild(renderer.domElement);
        }
    }
    chunksMeshMap.clear();
    collectibleMeshMap.clear();
}
// Custom composer class for bloom
class CustomComposer {
    constructor(camera, renderer) {
        this.camera = camera;
        this.renderer = renderer;
        this.bloomStrength = 0.8;
        this.width = window.innerWidth;
        this.height = window.innerHeight;
        this.renderTarget1 = new THREE.WebGLRenderTarget(this.width, this.height, {
            minFilter: THREE.LinearFilter,
            magFilter: THREE.LinearFilter,
        });
        this.renderTarget2 = new THREE.WebGLRenderTarget(this.width, this.height, {
            minFilter: THREE.LinearFilter,
            magFilter: THREE.LinearFilter,
        });
    }
    setSize(width, height) {
        this.width = width;
        this.height = height;
        this.renderTarget1.setSize(width, height);
        this.renderTarget2.setSize(width, height);
    }
    render() {
        // For now, just render directly - the glow from emissive materials provides enough effect
        this.renderer.render(scene, this.camera);
    }
}
};

// ── types: globals.d.ts ──
__mods["globals.d.ts"] = function (exports) {};

// ── entry ──
__require("src/main.ts");
})();
</script>
</body>
</html>